Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | // 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 <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"}, { .compatible = "ti,am642-icssg"}, {} }; U_BOOT_DRIVER(pruss) = { .name = "pruss", .of_match = pruss_ids, .id = UCLASS_MISC, .probe = pruss_probe, .priv_auto = sizeof(struct pruss), }; |