1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * PRU-ICSS platform driver for various TI SoCs
4 *
5 * Copyright (C) 2014-2019 Texas Instruments Incorporated - http://www.ti.com/
6 * Suman Anna <s-anna@ti.com>
7 * Andrew F. Davis <afd@ti.com>
8 * Tero Kristo <t-kristo@ti.com>
9 */
11 #include <linux/dma-mapping.h>
12 #include <linux/io.h>
13 #include <linux/mfd/syscon.h>
14 #include <linux/module.h>
15 #include <linux/of_address.h>
16 #include <linux/of_device.h>
17 #include <linux/pruss_driver.h>
18 #include <linux/remoteproc.h>
20 /**
21 * struct pruss_private_data - PRUSS driver private data
22 * @has_no_sharedram: flag to indicate the absence of PRUSS Shared Data RAM
23 */
24 struct pruss_private_data {
25 bool has_no_sharedram;
26 };
28 /**
29 * struct pruss_match_private_data - private data to handle multiple instances
30 * @device_name: device name of the PRUSS instance
31 * @priv_data: PRUSS driver private data for this PRUSS instance
32 */
33 struct pruss_match_private_data {
34 const char *device_name;
35 const struct pruss_private_data *priv_data;
36 };
38 /**
39 * pruss_get() - get the pruss for a given PRU remoteproc
40 * @rproc: remoteproc handle of a PRU instance
41 *
42 * Finds the parent pruss device for a PRU given the @rproc handle of the
43 * PRU remote processor. This function increments the pruss device's refcount,
44 * so always use pruss_put() to decrement it back once pruss isn't needed
45 * anymore.
46 *
47 * Returns the pruss handle on success, and an ERR_PTR on failure using one
48 * of the following error values
49 * -EINVAL if invalid parameter
50 * -ENODEV if PRU device or PRUSS device is not found
51 */
52 struct pruss *pruss_get(struct rproc *rproc)
53 {
54 struct pruss *pruss;
55 struct device *dev;
56 struct platform_device *ppdev;
58 if (IS_ERR_OR_NULL(rproc))
59 return ERR_PTR(-EINVAL);
61 dev = &rproc->dev;
62 if (!dev->parent)
63 return ERR_PTR(-ENODEV);
65 /* rudimentary check to make sure rproc handle is for a PRU */
66 if (!strstr(dev_name(dev->parent), "pru"))
67 return ERR_PTR(-ENODEV);
69 ppdev = to_platform_device(dev->parent->parent);
70 pruss = platform_get_drvdata(ppdev);
71 if (!pruss)
72 return ERR_PTR(-ENODEV);
74 get_device(pruss->dev);
76 return pruss;
77 }
78 EXPORT_SYMBOL_GPL(pruss_get);
80 /**
81 * pruss_put() - decrement pruss device's usecount
82 * @pruss: pruss handle
83 *
84 * Complimentary function for pruss_get(). Needs to be called
85 * after the PRUSS is used, and only if the pruss_get() succeeds.
86 */
87 void pruss_put(struct pruss *pruss)
88 {
89 if (IS_ERR_OR_NULL(pruss))
90 return;
92 put_device(pruss->dev);
93 }
94 EXPORT_SYMBOL_GPL(pruss_put);
96 /**
97 * pruss_request_mem_region() - request a memory resource
98 * @pruss: the pruss instance
99 * @mem_id: the memory resource id
100 * @region: pointer to memory region structure to be filled in
101 *
102 * This function allows a client driver to request a memory resource,
103 * and if successful, will let the client driver own the particular
104 * memory region until released using the pruss_release_mem_region()
105 * API.
106 *
107 * Returns the memory region if requested resource is available, an
108 * error otherwise
109 */
110 int pruss_request_mem_region(struct pruss *pruss, enum pruss_mem mem_id,
111 struct pruss_mem_region *region)
112 {
113 if (!pruss || !region)
114 return -EINVAL;
116 if (mem_id >= PRUSS_MEM_MAX)
117 return -EINVAL;
119 mutex_lock(&pruss->lock);
121 if (pruss->mem_in_use[mem_id]) {
122 mutex_unlock(&pruss->lock);
123 return -EBUSY;
124 }
126 *region = pruss->mem_regions[mem_id];
127 pruss->mem_in_use[mem_id] = region;
129 mutex_unlock(&pruss->lock);
131 return 0;
132 }
133 EXPORT_SYMBOL_GPL(pruss_request_mem_region);
135 /**
136 * pruss_release_mem_region() - release a memory resource
137 * @pruss: the pruss instance
138 * @region: the memory region to release
139 *
140 * This function is the complimentary function to
141 * pruss_request_mem_region(), and allows the client drivers to
142 * release back a memory resource.
143 *
144 * Returns 0 on success, an error code otherwise
145 */
146 int pruss_release_mem_region(struct pruss *pruss,
147 struct pruss_mem_region *region)
148 {
149 int id;
151 if (!pruss || !region)
152 return -EINVAL;
154 mutex_lock(&pruss->lock);
156 /* find out the memory region being released */
157 for (id = 0; id < PRUSS_MEM_MAX; id++) {
158 if (pruss->mem_in_use[id] == region)
159 break;
160 }
162 if (id == PRUSS_MEM_MAX) {
163 mutex_unlock(&pruss->lock);
164 return -EINVAL;
165 }
167 pruss->mem_in_use[id] = NULL;
169 mutex_unlock(&pruss->lock);
171 return 0;
172 }
173 EXPORT_SYMBOL_GPL(pruss_release_mem_region);
175 static const
176 struct pruss_private_data *pruss_get_private_data(struct platform_device *pdev)
177 {
178 const struct pruss_match_private_data *data;
180 if (!of_device_is_compatible(pdev->dev.of_node, "ti,am4376-pruss"))
181 return NULL;
183 data = of_device_get_match_data(&pdev->dev);
184 for (; data && data->device_name; data++) {
185 if (!strcmp(dev_name(&pdev->dev), data->device_name))
186 return data->priv_data;
187 }
189 return ERR_PTR(-ENODEV);
190 }
192 static int pruss_probe(struct platform_device *pdev)
193 {
194 struct device *dev = &pdev->dev;
195 struct device_node *node = dev->of_node;
196 struct device_node *np;
197 struct pruss *pruss;
198 struct resource res;
199 int ret, i, index;
200 const struct pruss_private_data *data;
201 const char *mem_names[PRUSS_MEM_MAX] = { "dram0", "dram1", "shrdram2" };
203 if (!node) {
204 dev_err(dev, "Non-DT platform device not supported\n");
205 return -ENODEV;
206 }
208 data = pruss_get_private_data(pdev);
209 if (IS_ERR(data)) {
210 dev_err(dev, "missing private data\n");
211 return -ENODEV;
212 }
214 ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
215 if (ret) {
216 dev_err(dev, "dma_set_coherent_mask: %d\n", ret);
217 return ret;
218 }
220 pruss = devm_kzalloc(dev, sizeof(*pruss), GFP_KERNEL);
221 if (!pruss)
222 return -ENOMEM;
224 pruss->dev = dev;
225 mutex_init(&pruss->lock);
227 np = of_get_child_by_name(node, "cfg");
228 if (!np) {
229 dev_err(dev, "%pOF is missing cfg node\n", np);
230 return -ENODEV;
231 }
233 pruss->cfg = syscon_node_to_regmap(np);
234 of_node_put(np);
235 if (IS_ERR(pruss->cfg))
236 return -ENODEV;
238 np = of_get_child_by_name(node, "iep");
239 if (!np) {
240 dev_err(dev, "%pOF is missing iep node\n", np);
241 return -ENODEV;
242 }
244 pruss->iep = syscon_node_to_regmap(np);
245 of_node_put(np);
246 if (IS_ERR(pruss->iep))
247 return -ENODEV;
249 np = of_get_child_by_name(node, "mii-rt");
250 if (!np) {
251 dev_err(dev, "%pOF is missing mii-rt node\n", np);
252 return -ENODEV;
253 }
255 pruss->mii_rt = syscon_node_to_regmap(np);
256 of_node_put(np);
257 if (IS_ERR(pruss->mii_rt))
258 return -ENODEV;
260 np = of_get_child_by_name(node, "memories");
261 if (!np) {
262 dev_err(dev, "%pOF is missing memories node\n", np);
263 return -ENODEV;
264 }
266 for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
267 if (data && data->has_no_sharedram &&
268 !strcmp(mem_names[i], "shrdram2"))
269 continue;
271 index = of_property_match_string(np, "reg-names", mem_names[i]);
272 if (index < 0) {
273 of_node_put(np);
274 return index;
275 }
277 if (of_address_to_resource(np, index, &res)) {
278 of_node_put(np);
279 return -EINVAL;
280 }
282 pruss->mem_regions[i].va = devm_ioremap(dev, res.start,
283 resource_size(&res));
284 if (!pruss->mem_regions[i].va) {
285 dev_err(dev, "failed to parse and map memory resource %d %s\n",
286 i, mem_names[i]);
287 of_node_put(np);
288 return -ENOMEM;
289 }
290 pruss->mem_regions[i].pa = res.start;
291 pruss->mem_regions[i].size = resource_size(&res);
293 dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n",
294 mem_names[i], &pruss->mem_regions[i].pa,
295 pruss->mem_regions[i].size, pruss->mem_regions[i].va);
296 }
297 of_node_put(np);
299 platform_set_drvdata(pdev, pruss);
301 dev_dbg(&pdev->dev, "creating PRU cores and other child platform devices\n");
302 ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
303 if (ret)
304 dev_err(dev, "of_platform_populate failed\n");
306 return ret;
307 }
309 static int pruss_remove(struct platform_device *pdev)
310 {
311 struct device *dev = &pdev->dev;
313 dev_dbg(dev, "remove PRU cores and other child platform devices\n");
314 of_platform_depopulate(dev);
316 return 0;
317 }
319 /* instance-specific driver private data */
320 static const struct pruss_private_data am437x_pruss1_priv_data = {
321 .has_no_sharedram = false,
322 };
324 static const struct pruss_private_data am437x_pruss0_priv_data = {
325 .has_no_sharedram = true,
326 };
328 static const struct pruss_match_private_data am437x_match_data[] = {
329 {
330 .device_name = "54400000.pruss",
331 .priv_data = &am437x_pruss1_priv_data,
332 },
333 {
334 .device_name = "54440000.pruss",
335 .priv_data = &am437x_pruss0_priv_data,
336 },
337 {
338 /* sentinel */
339 },
340 };
342 static const struct of_device_id pruss_of_match[] = {
343 { .compatible = "ti,am3356-pruss", .data = NULL, },
344 { .compatible = "ti,am4376-pruss", .data = &am437x_match_data, },
345 { .compatible = "ti,am5728-pruss", .data = NULL, },
346 { .compatible = "ti,k2g-pruss", .data = NULL, },
347 { /* sentinel */ },
348 };
349 MODULE_DEVICE_TABLE(of, pruss_of_match);
351 static struct platform_driver pruss_driver = {
352 .driver = {
353 .name = "pruss",
354 .of_match_table = pruss_of_match,
355 },
356 .probe = pruss_probe,
357 .remove = pruss_remove,
358 };
359 module_platform_driver(pruss_driver);
361 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
362 MODULE_DESCRIPTION("PRU-ICSS Subsystem Driver");
363 MODULE_LICENSE("GPL v2");