mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-15 07:43:07 +00:00
218 lines
5.4 KiB
C
218 lines
5.4 KiB
C
|
// 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),
|
||
|
};
|