// 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 #include #include #include #include #include #include #include #include #include #include #include #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), };