blob: 3b45071ccd085feca8225b8a5d5ce8ce8dcbde41 [file] [log] [blame]
Per Åstrand2cd53972021-04-12 13:46:04 +02001/*
Per Åstrand415de582022-04-19 15:51:09 +02002 * Copyright (c) 2021-2022 Arm Limited. All rights reserved.
Per Åstrand2cd53972021-04-12 13:46:04 +02003 *
4 * This program is free software and is provided to you under the terms of the
5 * GNU General Public License version 2 as published by the Free Software
6 * Foundation, and any use by you of this program is subject to the terms
7 * of such GNU licence.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, you can access it online at
16 * http://www.gnu.org/licenses/gpl-2.0.html.
17 *
18 * SPDX-License-Identifier: GPL-2.0-only
19 */
20
21#include <linux/firmware.h>
22#include <linux/io.h>
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/of.h>
26#include <linux/of_address.h>
27#include <linux/of_device.h>
28#include <linux/platform_device.h>
29#include <linux/remoteproc.h>
30#include <linux/reset.h>
Nir Ekhauz48239212021-11-23 10:28:34 +020031#include <linux/version.h>
Per Åstrand2cd53972021-04-12 13:46:04 +020032
33#define ETHOSU_RPROC_DRIVER_VERSION "0.0.1"
34
35#define DEFAULT_FW_FILE "arm-ethos-u65.fw"
36#define DEFAULT_AUTO_BOOT (false)
37
38/* firmware naming module parameter */
39static char fw_filename_param[256] = DEFAULT_FW_FILE;
40/* As the remoteproc is setup at probe, just allow the filename readonly */
41module_param_string(filename, fw_filename_param, sizeof(fw_filename_param),
42 0444);
43MODULE_PARM_DESC(filename,
44 "Filename for firmware image for Ethos-U remoteproc");
45
46static bool auto_boot = DEFAULT_AUTO_BOOT;
47module_param(auto_boot, bool, DEFAULT_AUTO_BOOT);
48MODULE_PARM_DESC(auto_boot, "Set to one to auto boot at load.");
49
50struct ethosu_rproc {
51 struct device *dev;
52 struct reset_control *rstc;
53 struct rproc_mem_mapping *map;
54 size_t map_size;
55};
56
57struct rproc_mem_mapping {
58 const char *name;
59 phys_addr_t rproc_addr;
60 void __iomem *vaddr;
61 size_t size;
62};
63
64struct ethosu_rproc_config {
65 struct fw_config *fw;
66};
67
68/*****************************************************************************/
69
70static int ethosu_rproc_start(struct rproc *rproc)
71{
72 struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
73 struct device *dev = ethosu->dev;
74
75 dev_info(dev, "Starting up Ethos-U subsystem CPU!");
76
77 return reset_control_deassert(ethosu->rstc);
78}
79
80static int ethosu_rproc_stop(struct rproc *rproc)
81{
82 struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
83 struct device *dev = ethosu->dev;
84
85 dev_info(dev, "Stopping Ethos-U subsystem CPU!");
86
87 return reset_control_assert(ethosu->rstc);
88}
89
90static void ethosu_rproc_kick(struct rproc *rproc,
91 int vqid)
92{
93 return;
94}
95
Per Åstrand415de582022-04-19 15:51:09 +020096#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
Nir Ekhauz48239212021-11-23 10:28:34 +020097static void *ethosu_da_to_va(struct rproc *rproc,
98 u64 da,
99 size_t len,
100 bool *is_iomem)
Per Åstrand415de582022-04-19 15:51:09 +0200101#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
102static void *ethosu_da_to_va(struct rproc *rproc,
103 u64 da,
104 size_t len)
105#else
106static void *ethosu_da_to_va(struct rproc *rproc,
107 u64 da,
108 int len)
Nir Ekhauz48239212021-11-23 10:28:34 +0200109#endif
Per Åstrand2cd53972021-04-12 13:46:04 +0200110{
111 struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
112 int offset;
113 int i;
114
115 for (i = 0; i < ethosu->map_size; i++)
116 if (da >= ethosu->map[i].rproc_addr &&
117 da < (ethosu->map[i].rproc_addr + ethosu->map[i].size)) {
118 offset = da - ethosu->map[i].rproc_addr;
119 dev_info(ethosu->dev,
120 "mapping %llx to %p (offset: 0x%x)", da,
121 (void *)(ethosu->map[i].vaddr + offset),
122 offset);
123
124 return (void *)(ethosu->map[i].vaddr + offset);
125 }
126
127 return NULL;
128}
129
130static const struct rproc_ops ethosu_rproc_ops = {
131 .start = &ethosu_rproc_start,
132 .stop = &ethosu_rproc_stop,
133 .kick = &ethosu_rproc_kick,
134 .da_to_va = &ethosu_da_to_va,
135};
136
137/**
138 * Since the remote side doesn't yet support rpmsg just return an
139 * empty resource table when asked about it.
140 */
141struct resource_table *ethosu_rproc_find_rsc_table(struct rproc *rproc,
142 const struct firmware *fw,
143 int *tablesz)
144{
145 static struct resource_table table = { .ver = 1, };
146 struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv;
147
148 dev_info(ethosu->dev, "Sizeof struct resource_table : %zu",
149 sizeof(table));
150 *tablesz = sizeof(table);
151
152 return &table;
153}
154
155/*****************************************************************************/
156
157static int ethosu_rproc_of_memory_translations(struct platform_device *pdev,
158 struct ethosu_rproc *ethosu_rproc)
159{
160 const char *const of_rproc_address_cells =
161 "#ethosu,rproc-address-cells";
162 const char *const of_rproc_ranges = "ethosu,rproc-ranges";
163 const char *const of_rproc_ranges_names = "ethosu,rproc-names";
164
165 struct device *dev = &pdev->dev;
166 struct device_node *np = dev->of_node;
167 struct rproc_mem_mapping *mem_map;
168 const __be32 *rproc_ranges;
169
170 int addr_cells, rproc_addr_cells, size_cells, cells_for_array_element;
171 int i, len, cnt, name_cnt, ret = 0;
172
173 if (of_property_read_u32(np, of_rproc_address_cells,
174 &rproc_addr_cells)) {
175 dev_info(dev, "%s not defined in dtb", of_rproc_address_cells);
176
177 return -ENODEV;
178 }
179
180 addr_cells = of_n_addr_cells(np);
181 size_cells = of_n_size_cells(np);
182
183 dev_dbg(dev, "Using %d remote proc address cells for parsing mapping",
184 rproc_addr_cells);
185 dev_dbg(dev,
186 "Using %d of size %d parent address cells for parsing mapping",
187 addr_cells, size_cells);
188
189 cells_for_array_element = addr_cells + rproc_addr_cells + size_cells;
190
191 cnt = of_property_count_elems_of_size(np, of_rproc_ranges,
192 cells_for_array_element);
193 cnt /= sizeof(u32);
194
195 if (cnt <= 0) {
196 dev_info(dev, "No remoteproc memory mapping ranges found.");
197
198 return 0;
199 }
200
201 name_cnt = of_property_count_strings(np, of_rproc_ranges_names);
202 if (name_cnt > 0 && name_cnt != cnt) {
203 dev_err(dev, "Mismatch length for %s and %s", of_rproc_ranges,
204 of_rproc_ranges_names);
205
206 return -EINVAL;
207 }
208
209 mem_map = devm_kcalloc(dev, cnt, sizeof(*mem_map), GFP_KERNEL);
210 if (!mem_map)
211 return -ENOMEM;
212
213 rproc_ranges = of_get_property(np, of_rproc_ranges, &len);
214
215 for (i = 0; i < cnt; i++) {
216 struct resource *r;
217 const char *name = NULL;
218 int n;
219
220 of_property_read_string_index(np, of_rproc_ranges_names, i,
221 &name);
222 mem_map[i].name = name;
223 n = i * cells_for_array_element;
224 mem_map[i].rproc_addr =
225 of_read_number(&rproc_ranges[n + addr_cells],
226 rproc_addr_cells);
227 mem_map[i].size =
228 of_read_number(&rproc_ranges[n + addr_cells +
229 rproc_addr_cells],
230 size_cells);
231
232 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
233 if (!r) {
234 dev_err(&pdev->dev, "Failed to get '%s' resource.\n",
235 name);
236
237 return -EINVAL;
238 }
239
240 mem_map[i].vaddr = devm_ioremap_wc(dev, r->start,
241 mem_map[i].size);
242 if (IS_ERR(mem_map[i].vaddr)) {
243 dev_err(dev, "Failed to remap '%s'", name);
244
245 return PTR_ERR(mem_map[i].vaddr);
246 }
247
248 dev_dbg(dev,
249 "rproc memory mapping[%i]=%s: da %llx, va, %pa, size %zx:\n",
250 i, name, mem_map[i].rproc_addr, &mem_map[i].vaddr,
251 mem_map[i].size);
252 }
253
254 ethosu_rproc->map = mem_map;
255 ethosu_rproc->map_size = cnt;
256 dev_dbg(dev, "rproc memory mapped %zx regions", ethosu_rproc->map_size);
257
258 return ret;
259}
260
261static const struct of_device_id ethosu_rproc_match[] = {
Per Åstrand9f36f2e2021-09-30 09:57:34 +0200262 { .compatible = "arm,ethosu-rproc" },
Per Åstrandb9248a42022-05-18 16:05:16 +0200263 { /* sentinel */ },
Per Åstrand2cd53972021-04-12 13:46:04 +0200264};
265
266static int ethosu_rproc_probe(struct platform_device *pdev)
267{
268 struct device *dev = &pdev->dev;
269 struct device_node *np = dev->of_node;
270 struct ethosu_rproc *ethosu_rproc;
271 struct rproc *rproc;
272 int ret = -ENODEV;
273
274 rproc = rproc_alloc(dev, np->name, &ethosu_rproc_ops,
275 fw_filename_param,
276 sizeof(*ethosu_rproc));
277 if (!rproc) {
278 ret = -ENOMEM;
279 goto out;
280 }
281
282 /* Configure rproc */
283 rproc->has_iommu = false;
284 rproc->auto_boot = auto_boot;
285
286 platform_set_drvdata(pdev, rproc);
287
288 ethosu_rproc = rproc->priv;
289 ethosu_rproc->dev = dev;
290
291 /* Get the reset handler for the subsystem */
292 ethosu_rproc->rstc = devm_reset_control_get_exclusive_by_index(dev, 0);
293 if (IS_ERR(ethosu_rproc->rstc)) {
294 dev_err(&pdev->dev, "Failed to get reset controller.\n");
295 ret = PTR_ERR(ethosu_rproc->rstc);
296 goto free_rproc;
297 }
298
299 /* Get the translation from device memory to kernel space */
300 ret = ethosu_rproc_of_memory_translations(pdev, ethosu_rproc);
301 if (ret)
302 goto free_rproc;
303
304 ret = rproc_add(rproc);
305
306free_rproc:
307 if (ret)
308 rproc_free(rproc);
309
310out:
311
312 return ret;
313}
314
315static int ethosu_rproc_remove(struct platform_device *pdev)
316{
317 struct rproc *rproc = platform_get_drvdata(pdev);
318
319 rproc_del(rproc);
320 rproc_free(rproc);
321
322 return 0;
323}
324
325static struct platform_driver ethosu_rproc_driver = {
326 .probe = ethosu_rproc_probe,
327 .remove = ethosu_rproc_remove,
328 .driver = {
329 .name = "ethosu-rproc",
330 .of_match_table = of_match_ptr(ethosu_rproc_match),
331 },
332};
333
334module_platform_driver(ethosu_rproc_driver);
335
336MODULE_LICENSE("GPL v2");
337MODULE_AUTHOR("Arm Ltd");
338MODULE_DESCRIPTION("Arm Ethos-U NPU RemoteProc Driver");
339MODULE_VERSION(ETHOSU_RPROC_DRIVER_VERSION);