blob: 461390925d2ceac578622efdea2dc63163757a60 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* PRU-ICSS platform driver for various TI SoCs
*
* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/
*/
#include <common.h>
#include <dm.h>
#include <dm/of_access.h>
#include <errno.h>
#include <clk.h>
#include <reset.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <power-domain.h>
#include <linux/pruss_driver.h>
#include <dm/device_compat.h>
#define PRUSS_CFG_IEPCLK 0x30
#define ICSSG_CFG_CORE_SYNC 0x3c
#define ICSSG_TASK_MGR_OFFSET 0x2a000
/* PRUSS_IEPCLK register bits */
#define PRUSS_IEPCLK_IEP_OCP_CLK_EN BIT(0)
/* ICSSG CORE_SYNC register bits */
#define ICSSG_CORE_VBUSP_SYNC_EN BIT(0)
/*
* pruss_request_tm_region() - Request pruss for task manager region
* @dev: corresponding k3 device
* @loc: the task manager physical address
*
* Return: 0 if all goes good, else appropriate error message.
*/
int pruss_request_tm_region(struct udevice *dev, phys_addr_t *loc)
{
struct pruss *priv;
priv = dev_get_priv(dev);
if (!priv || !priv->mem_regions[PRUSS_MEM_DRAM0].pa)
return -EINVAL;
*loc = priv->mem_regions[PRUSS_MEM_DRAM0].pa + ICSSG_TASK_MGR_OFFSET;
return 0;
}
/**
* pruss_request_mem_region() - request a memory resource
* @dev: the pruss device
* @mem_id: the memory resource id
* @region: pointer to memory region structure to be filled in
*
* This function allows a client driver to request a memory resource,
* and if successful, will let the client driver own the particular
* memory region until released using the pruss_release_mem_region()
* API.
*
* Returns the memory region if requested resource is available, an
* error otherwise
*/
int pruss_request_mem_region(struct udevice *dev, enum pruss_mem mem_id,
struct pruss_mem_region *region)
{
struct pruss *pruss;
pruss = dev_get_priv(dev);
if (!pruss || !region)
return -EINVAL;
if (mem_id >= PRUSS_MEM_MAX)
return -EINVAL;
if (pruss->mem_in_use[mem_id])
return -EBUSY;
*region = pruss->mem_regions[mem_id];
pruss->mem_in_use[mem_id] = region;
return 0;
}
/**
* pruss_release_mem_region() - release a memory resource
* @dev: the pruss device
* @region: the memory region to release
*
* This function is the complimentary function to
* pruss_request_mem_region(), and allows the client drivers to
* release back a memory resource.
*
* Returns 0 on success, an error code otherwise
*/
int pruss_release_mem_region(struct udevice *dev,
struct pruss_mem_region *region)
{
struct pruss *pruss;
int id;
pruss = dev_get_priv(dev);
if (!pruss || !region)
return -EINVAL;
/* find out the memory region being released */
for (id = 0; id < PRUSS_MEM_MAX; id++) {
if (pruss->mem_in_use[id] == region)
break;
}
if (id == PRUSS_MEM_MAX)
return -EINVAL;
pruss->mem_in_use[id] = NULL;
return 0;
}
/**
* pruss_cfg_update() - configure a PRUSS CFG sub-module register
* @dev: the pruss device
* @reg: register offset within the CFG sub-module
* @mask: bit mask to use for programming the @val
* @val: value to write
*
* Programs a given register within the PRUSS CFG sub-module
*
* Returns 0 on success, or an error code otherwise
*/
int pruss_cfg_update(struct udevice *dev, unsigned int reg,
unsigned int mask, unsigned int val)
{
struct pruss *pruss;
pruss = dev_get_priv(dev);
if (IS_ERR_OR_NULL(pruss))
return -EINVAL;
return regmap_update_bits(pruss->cfg, reg, mask, val);
}
/**
* pruss_probe() - Basic probe
* @dev: corresponding k3 device
*
* Return: 0 if all goes good, else appropriate error message.
*/
static int pruss_probe(struct udevice *dev)
{
const char *mem_names[PRUSS_MEM_MAX] = { "dram0", "dram1", "shrdram2" };
ofnode sub_node, node, memories;
struct udevice *syscon;
struct pruss *priv;
int ret, idx, i;
priv = dev_get_priv(dev);
node = dev_ofnode(dev);
priv->dev = dev;
memories = ofnode_find_subnode(node, "memories");
for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
idx = ofnode_stringlist_search(memories, "reg-names", mem_names[i]);
priv->mem_regions[i].pa = ofnode_get_addr_size_index(memories, idx,
(u64 *)&priv->mem_regions[i].size);
}
sub_node = ofnode_find_subnode(node, "cfg");
ret = uclass_get_device_by_ofnode(UCLASS_SYSCON, sub_node,
&syscon);
priv->cfg = syscon_get_regmap(syscon);
if (IS_ERR(priv->cfg)) {
dev_err(dev, "unable to get cfg regmap (%ld)\n",
PTR_ERR(priv->cfg));
return -ENODEV;
}
/*
* ToDo: To be modelled as clocks.
* The CORE block uses two multiplexers to allow software to
* select one of three source clocks (ICSSGn_CORE_CLK, ICSSGn_ICLK or
* ICSSGn_IEP_CLK) for the final clock source of the CORE block.
* The user needs to configure ICSSG_CORE_SYNC_REG[0] CORE_VBUSP_SYNC_EN
* bit & ICSSG_IEPCLK_REG[0] IEP_OCP_CLK_EN bit in order to select the
* clock source to the CORE block.
*/
ret = regmap_update_bits(priv->cfg, ICSSG_CFG_CORE_SYNC,
ICSSG_CORE_VBUSP_SYNC_EN,
ICSSG_CORE_VBUSP_SYNC_EN);
if (ret)
return ret;
ret = regmap_update_bits(priv->cfg, PRUSS_CFG_IEPCLK,
PRUSS_IEPCLK_IEP_OCP_CLK_EN,
PRUSS_IEPCLK_IEP_OCP_CLK_EN);
if (ret)
return ret;
dev_dbg(dev, "pruss successfully probed %s\n", dev->name);
return 0;
}
static const struct udevice_id pruss_ids[] = {
{ .compatible = "ti,am654-icssg"},
{}
};
U_BOOT_DRIVER(pruss) = {
.name = "pruss",
.of_match = pruss_ids,
.id = UCLASS_MISC,
.probe = pruss_probe,
.priv_auto = sizeof(struct pruss),
};