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 */
10 #include <linux/dma-mapping.h>
11 #include <linux/io.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
14 #include <linux/of_address.h>
15 #include <linux/of_device.h>
16 #include <linux/pruss_driver.h>
18 /**
19 * struct pruss_private_data - PRUSS driver private data
20 * @has_no_sharedram: flag to indicate the absence of PRUSS Shared Data RAM
21 */
22 struct pruss_private_data {
23 bool has_no_sharedram;
24 };
26 /**
27 * struct pruss_match_private_data - private data to handle multiple instances
28 * @device_name: device name of the PRUSS instance
29 * @priv_data: PRUSS driver private data for this PRUSS instance
30 */
31 struct pruss_match_private_data {
32 const char *device_name;
33 const struct pruss_private_data *priv_data;
34 };
36 static const
37 struct pruss_private_data *pruss_get_private_data(struct platform_device *pdev)
38 {
39 const struct pruss_match_private_data *data;
41 if (!of_device_is_compatible(pdev->dev.of_node, "ti,am4376-pruss"))
42 return NULL;
44 data = of_device_get_match_data(&pdev->dev);
45 for (; data && data->device_name; data++) {
46 if (!strcmp(dev_name(&pdev->dev), data->device_name))
47 return data->priv_data;
48 }
50 return ERR_PTR(-ENODEV);
51 }
53 static int pruss_probe(struct platform_device *pdev)
54 {
55 struct device *dev = &pdev->dev;
56 struct device_node *node = dev->of_node;
57 struct device_node *np;
58 struct pruss *pruss;
59 struct resource res;
60 int ret, i, index;
61 const struct pruss_private_data *data;
62 const char *mem_names[PRUSS_MEM_MAX] = { "dram0", "dram1", "shrdram2" };
64 if (!node) {
65 dev_err(dev, "Non-DT platform device not supported\n");
66 return -ENODEV;
67 }
69 data = pruss_get_private_data(pdev);
70 if (IS_ERR(data)) {
71 dev_err(dev, "missing private data\n");
72 return -ENODEV;
73 }
75 ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
76 if (ret) {
77 dev_err(dev, "dma_set_coherent_mask: %d\n", ret);
78 return ret;
79 }
81 pruss = devm_kzalloc(dev, sizeof(*pruss), GFP_KERNEL);
82 if (!pruss)
83 return -ENOMEM;
85 pruss->dev = dev;
87 np = of_get_child_by_name(node, "cfg");
88 if (!np) {
89 dev_err(dev, "%pOF is missing cfg node\n", np);
90 return -ENODEV;
91 }
93 pruss->cfg = syscon_node_to_regmap(np);
94 of_node_put(np);
95 if (IS_ERR(pruss->cfg))
96 return -ENODEV;
98 np = of_get_child_by_name(node, "iep");
99 if (!np) {
100 dev_err(dev, "%pOF is missing iep node\n", np);
101 return -ENODEV;
102 }
104 pruss->iep = syscon_node_to_regmap(np);
105 of_node_put(np);
106 if (IS_ERR(pruss->iep))
107 return -ENODEV;
109 np = of_get_child_by_name(node, "mii-rt");
110 if (!np) {
111 dev_err(dev, "%pOF is missing mii-rt node\n", np);
112 return -ENODEV;
113 }
115 pruss->mii_rt = syscon_node_to_regmap(np);
116 of_node_put(np);
117 if (IS_ERR(pruss->mii_rt))
118 return -ENODEV;
120 np = of_get_child_by_name(node, "memories");
121 if (!np) {
122 dev_err(dev, "%pOF is missing memories node\n", np);
123 return -ENODEV;
124 }
126 for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
127 if (data && data->has_no_sharedram &&
128 !strcmp(mem_names[i], "shrdram2"))
129 continue;
131 index = of_property_match_string(np, "reg-names", mem_names[i]);
132 if (index < 0) {
133 of_node_put(np);
134 return index;
135 }
137 if (of_address_to_resource(np, index, &res)) {
138 of_node_put(np);
139 return -EINVAL;
140 }
142 pruss->mem_regions[i].va = devm_ioremap(dev, res.start,
143 resource_size(&res));
144 if (!pruss->mem_regions[i].va) {
145 dev_err(dev, "failed to parse and map memory resource %d %s\n",
146 i, mem_names[i]);
147 of_node_put(np);
148 return -ENOMEM;
149 }
150 pruss->mem_regions[i].pa = res.start;
151 pruss->mem_regions[i].size = resource_size(&res);
153 dev_dbg(dev, "memory %8s: pa %pa size 0x%zx va %pK\n",
154 mem_names[i], &pruss->mem_regions[i].pa,
155 pruss->mem_regions[i].size, pruss->mem_regions[i].va);
156 }
157 of_node_put(np);
159 platform_set_drvdata(pdev, pruss);
161 dev_dbg(&pdev->dev, "creating PRU cores and other child platform devices\n");
162 ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
163 if (ret)
164 dev_err(dev, "of_platform_populate failed\n");
166 return ret;
167 }
169 static int pruss_remove(struct platform_device *pdev)
170 {
171 struct device *dev = &pdev->dev;
173 dev_dbg(dev, "remove PRU cores and other child platform devices\n");
174 of_platform_depopulate(dev);
176 return 0;
177 }
179 /* instance-specific driver private data */
180 static const struct pruss_private_data am437x_pruss1_priv_data = {
181 .has_no_sharedram = false,
182 };
184 static const struct pruss_private_data am437x_pruss0_priv_data = {
185 .has_no_sharedram = true,
186 };
188 static const struct pruss_match_private_data am437x_match_data[] = {
189 {
190 .device_name = "54400000.pruss",
191 .priv_data = &am437x_pruss1_priv_data,
192 },
193 {
194 .device_name = "54440000.pruss",
195 .priv_data = &am437x_pruss0_priv_data,
196 },
197 {
198 /* sentinel */
199 },
200 };
202 static const struct of_device_id pruss_of_match[] = {
203 { .compatible = "ti,am3356-pruss", .data = NULL, },
204 { .compatible = "ti,am4376-pruss", .data = &am437x_match_data, },
205 { .compatible = "ti,am5728-pruss", .data = NULL, },
206 { .compatible = "ti,k2g-pruss", .data = NULL, },
207 { /* sentinel */ },
208 };
209 MODULE_DEVICE_TABLE(of, pruss_of_match);
211 static struct platform_driver pruss_driver = {
212 .driver = {
213 .name = "pruss",
214 .of_match_table = pruss_of_match,
215 },
216 .probe = pruss_probe,
217 .remove = pruss_remove,
218 };
219 module_platform_driver(pruss_driver);
221 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
222 MODULE_DESCRIPTION("PRU-ICSS Subsystem Driver");
223 MODULE_LICENSE("GPL v2");