]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/blob - drivers/soc/ti/pruss.c
soc: ti: pruss: add pruss_{request,release}_mem_region() API
[rpmsg/rpmsg.git] / drivers / soc / ti / pruss.c
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)
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;
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)
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;
173 EXPORT_SYMBOL_GPL(pruss_release_mem_region);
175 static const
176 struct pruss_private_data *pruss_get_private_data(struct platform_device *pdev)
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);
192 static int pruss_probe(struct platform_device *pdev)
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;
309 static int pruss_remove(struct platform_device *pdev)
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;
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");