]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
ddf56bc7 NM |
2 | /* |
3 | * (C) Copyright 2015 | |
4 | * Texas Instruments Incorporated - http://www.ti.com/ | |
ddf56bc7 NM |
5 | */ |
6 | #define pr_fmt(fmt) "%s: " fmt, __func__ | |
7 | #include <common.h> | |
8 | #include <errno.h> | |
9 | #include <fdtdec.h> | |
f7ae49fc | 10 | #include <log.h> |
ddf56bc7 NM |
11 | #include <malloc.h> |
12 | #include <remoteproc.h> | |
13 | #include <asm/io.h> | |
14 | #include <dm/device-internal.h> | |
15 | #include <dm.h> | |
16 | #include <dm/uclass.h> | |
17 | #include <dm/uclass-internal.h> | |
18 | ||
19 | DECLARE_GLOBAL_DATA_PTR; | |
20 | ||
21 | /** | |
22 | * for_each_remoteproc_device() - iterate through the list of rproc devices | |
23 | * @fn: check function to call per match, if this function returns fail, | |
24 | * iteration is aborted with the resultant error value | |
25 | * @skip_dev: Device to skip calling the callback about. | |
26 | * @data: Data to pass to the callback function | |
27 | * | |
28 | * Return: 0 if none of the callback returned a non 0 result, else returns the | |
29 | * result from the callback function | |
30 | */ | |
31 | static int for_each_remoteproc_device(int (*fn) (struct udevice *dev, | |
32 | struct dm_rproc_uclass_pdata *uc_pdata, | |
33 | const void *data), | |
34 | struct udevice *skip_dev, | |
35 | const void *data) | |
36 | { | |
37 | struct udevice *dev; | |
38 | struct dm_rproc_uclass_pdata *uc_pdata; | |
39 | int ret; | |
40 | ||
41 | for (ret = uclass_find_first_device(UCLASS_REMOTEPROC, &dev); dev; | |
42 | ret = uclass_find_next_device(&dev)) { | |
43 | if (ret || dev == skip_dev) | |
44 | continue; | |
caa4daa2 | 45 | uc_pdata = dev_get_uclass_plat(dev); |
ddf56bc7 NM |
46 | ret = fn(dev, uc_pdata, data); |
47 | if (ret) | |
48 | return ret; | |
49 | } | |
50 | ||
51 | return 0; | |
52 | } | |
53 | ||
54 | /** | |
55 | * _rproc_name_is_unique() - iteration helper to check if rproc name is unique | |
56 | * @dev: device that we are checking name for | |
57 | * @uc_pdata: uclass platform data | |
58 | * @data: compare data (this is the name we want to ensure is unique) | |
59 | * | |
60 | * Return: 0 is there is no match(is unique); if there is a match(we dont | |
61 | * have a unique name), return -EINVAL. | |
62 | */ | |
63 | static int _rproc_name_is_unique(struct udevice *dev, | |
64 | struct dm_rproc_uclass_pdata *uc_pdata, | |
65 | const void *data) | |
66 | { | |
67 | const char *check_name = data; | |
68 | ||
69 | /* devices not yet populated with data - so skip them */ | |
9cb05a8f | 70 | if (!uc_pdata->name || !check_name) |
ddf56bc7 NM |
71 | return 0; |
72 | ||
73 | /* Return 0 to search further if we dont match */ | |
74 | if (strlen(uc_pdata->name) != strlen(check_name)) | |
75 | return 0; | |
76 | ||
77 | if (!strcmp(uc_pdata->name, check_name)) | |
78 | return -EINVAL; | |
79 | ||
80 | return 0; | |
81 | } | |
82 | ||
83 | /** | |
84 | * rproc_name_is_unique() - Check if the rproc name is unique | |
85 | * @check_dev: Device we are attempting to ensure is unique | |
86 | * @check_name: Name we are trying to ensure is unique. | |
87 | * | |
88 | * Return: true if we have a unique name, false if name is not unique. | |
89 | */ | |
90 | static bool rproc_name_is_unique(struct udevice *check_dev, | |
91 | const char *check_name) | |
92 | { | |
93 | int ret; | |
94 | ||
95 | ret = for_each_remoteproc_device(_rproc_name_is_unique, | |
96 | check_dev, check_name); | |
97 | return ret ? false : true; | |
98 | } | |
99 | ||
100 | /** | |
101 | * rproc_pre_probe() - Pre probe accessor for the uclass | |
102 | * @dev: device for which we are preprobing | |
103 | * | |
104 | * Parses and fills up the uclass pdata for use as needed by core and | |
105 | * remote proc drivers. | |
106 | * | |
107 | * Return: 0 if all wernt ok, else appropriate error value. | |
108 | */ | |
109 | static int rproc_pre_probe(struct udevice *dev) | |
110 | { | |
111 | struct dm_rproc_uclass_pdata *uc_pdata; | |
112 | const struct dm_rproc_ops *ops; | |
113 | ||
caa4daa2 | 114 | uc_pdata = dev_get_uclass_plat(dev); |
ddf56bc7 NM |
115 | |
116 | /* See if we need to populate via fdt */ | |
117 | ||
caa4daa2 | 118 | if (!dev->plat) { |
ddf56bc7 | 119 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
e160f7d4 | 120 | int node = dev_of_offset(dev); |
ddf56bc7 NM |
121 | const void *blob = gd->fdt_blob; |
122 | bool tmp; | |
123 | if (!blob) { | |
124 | debug("'%s' no dt?\n", dev->name); | |
125 | return -EINVAL; | |
126 | } | |
127 | debug("'%s': using fdt\n", dev->name); | |
128 | uc_pdata->name = fdt_getprop(blob, node, | |
129 | "remoteproc-name", NULL); | |
130 | ||
131 | /* Default is internal memory mapped */ | |
132 | uc_pdata->mem_type = RPROC_INTERNAL_MEMORY_MAPPED; | |
133 | tmp = fdtdec_get_bool(blob, node, | |
134 | "remoteproc-internal-memory-mapped"); | |
135 | if (tmp) | |
136 | uc_pdata->mem_type = RPROC_INTERNAL_MEMORY_MAPPED; | |
137 | #else | |
138 | /* Nothing much we can do about this, can we? */ | |
139 | return -EINVAL; | |
140 | #endif | |
141 | ||
142 | } else { | |
caa4daa2 | 143 | struct dm_rproc_uclass_pdata *pdata = dev->plat; |
ddf56bc7 NM |
144 | |
145 | debug("'%s': using legacy data\n", dev->name); | |
146 | if (pdata->name) | |
147 | uc_pdata->name = pdata->name; | |
148 | uc_pdata->mem_type = pdata->mem_type; | |
149 | uc_pdata->driver_plat_data = pdata->driver_plat_data; | |
150 | } | |
151 | ||
152 | /* Else try using device Name */ | |
153 | if (!uc_pdata->name) | |
154 | uc_pdata->name = dev->name; | |
155 | if (!uc_pdata->name) { | |
156 | debug("Unnamed device!"); | |
157 | return -EINVAL; | |
158 | } | |
159 | ||
160 | if (!rproc_name_is_unique(dev, uc_pdata->name)) { | |
161 | debug("%s duplicate name '%s'\n", dev->name, uc_pdata->name); | |
162 | return -EINVAL; | |
163 | } | |
164 | ||
165 | ops = rproc_get_ops(dev); | |
166 | if (!ops) { | |
167 | debug("%s driver has no ops?\n", dev->name); | |
168 | return -EINVAL; | |
169 | } | |
170 | ||
171 | if (!ops->load || !ops->start) { | |
172 | debug("%s driver has missing mandatory ops?\n", dev->name); | |
173 | return -EINVAL; | |
174 | } | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | /** | |
180 | * rproc_post_probe() - post probe accessor for the uclass | |
181 | * @dev: deivce we finished probing | |
182 | * | |
183 | * initiate init function after the probe is completed. This allows | |
184 | * the remote processor drivers to split up the initializations between | |
185 | * probe and init as needed. | |
186 | * | |
187 | * Return: if the remote proc driver has a init routine, invokes it and | |
188 | * hands over the return value. overall, 0 if all went well, else appropriate | |
189 | * error value. | |
190 | */ | |
191 | static int rproc_post_probe(struct udevice *dev) | |
192 | { | |
193 | const struct dm_rproc_ops *ops; | |
194 | ||
195 | ops = rproc_get_ops(dev); | |
196 | if (!ops) { | |
197 | debug("%s driver has no ops?\n", dev->name); | |
198 | return -EINVAL; | |
199 | } | |
200 | ||
201 | if (ops->init) | |
202 | return ops->init(dev); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | UCLASS_DRIVER(rproc) = { | |
208 | .id = UCLASS_REMOTEPROC, | |
209 | .name = "remoteproc", | |
210 | .flags = DM_UC_FLAG_SEQ_ALIAS, | |
211 | .pre_probe = rproc_pre_probe, | |
212 | .post_probe = rproc_post_probe, | |
caa4daa2 | 213 | .per_device_plat_auto = |
ddf56bc7 NM |
214 | sizeof(struct dm_rproc_uclass_pdata), |
215 | }; | |
216 | ||
217 | /* Remoteproc subsystem access functions */ | |
218 | /** | |
219 | * _rproc_probe_dev() - iteration helper to probe a rproc device | |
220 | * @dev: device to probe | |
221 | * @uc_pdata: uclass data allocated for the device | |
222 | * @data: unused | |
223 | * | |
224 | * Return: 0 if all ok, else appropriate error value. | |
225 | */ | |
226 | static int _rproc_probe_dev(struct udevice *dev, | |
227 | struct dm_rproc_uclass_pdata *uc_pdata, | |
228 | const void *data) | |
229 | { | |
230 | int ret; | |
231 | ||
232 | ret = device_probe(dev); | |
233 | ||
234 | if (ret) | |
235 | debug("%s: Failed to initialize - %d\n", dev->name, ret); | |
236 | return ret; | |
237 | } | |
238 | ||
239 | /** | |
240 | * _rproc_dev_is_probed() - check if the device has been probed | |
241 | * @dev: device to check | |
242 | * @uc_pdata: unused | |
243 | * @data: unused | |
244 | * | |
245 | * Return: -EAGAIN if not probed else return 0 | |
246 | */ | |
247 | static int _rproc_dev_is_probed(struct udevice *dev, | |
248 | struct dm_rproc_uclass_pdata *uc_pdata, | |
249 | const void *data) | |
250 | { | |
251 | if (dev->flags & DM_FLAG_ACTIVATED) | |
252 | return 0; | |
253 | ||
254 | return -EAGAIN; | |
255 | } | |
256 | ||
257 | bool rproc_is_initialized(void) | |
258 | { | |
259 | int ret = for_each_remoteproc_device(_rproc_dev_is_probed, NULL, NULL); | |
260 | return ret ? false : true; | |
261 | } | |
262 | ||
263 | int rproc_init(void) | |
264 | { | |
265 | int ret; | |
266 | ||
267 | if (rproc_is_initialized()) { | |
268 | debug("Already initialized\n"); | |
269 | return -EINVAL; | |
270 | } | |
271 | ||
272 | ret = for_each_remoteproc_device(_rproc_probe_dev, NULL, NULL); | |
273 | return ret; | |
274 | } | |
275 | ||
81ae6e6d LV |
276 | int rproc_dev_init(int id) |
277 | { | |
278 | struct udevice *dev = NULL; | |
279 | int ret; | |
280 | ||
281 | ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); | |
282 | if (ret) { | |
283 | debug("Unknown remote processor id '%d' requested(%d)\n", | |
284 | id, ret); | |
285 | return ret; | |
286 | } | |
287 | ||
288 | ret = device_probe(dev); | |
289 | if (ret) | |
290 | debug("%s: Failed to initialize - %d\n", dev->name, ret); | |
291 | ||
292 | return ret; | |
293 | } | |
294 | ||
ddf56bc7 NM |
295 | int rproc_load(int id, ulong addr, ulong size) |
296 | { | |
297 | struct udevice *dev = NULL; | |
298 | struct dm_rproc_uclass_pdata *uc_pdata; | |
299 | const struct dm_rproc_ops *ops; | |
300 | int ret; | |
301 | ||
302 | ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); | |
303 | if (ret) { | |
304 | debug("Unknown remote processor id '%d' requested(%d)\n", | |
305 | id, ret); | |
306 | return ret; | |
307 | } | |
308 | ||
caa4daa2 | 309 | uc_pdata = dev_get_uclass_plat(dev); |
ddf56bc7 NM |
310 | |
311 | ops = rproc_get_ops(dev); | |
312 | if (!ops) { | |
313 | debug("%s driver has no ops?\n", dev->name); | |
314 | return -EINVAL; | |
315 | } | |
316 | ||
317 | debug("Loading to '%s' from address 0x%08lX size of %lu bytes\n", | |
318 | uc_pdata->name, addr, size); | |
319 | if (ops->load) | |
320 | return ops->load(dev, addr, size); | |
321 | ||
322 | debug("%s: data corruption?? mandatory function is missing!\n", | |
323 | dev->name); | |
324 | ||
325 | return -EINVAL; | |
326 | }; | |
327 | ||
328 | /* | |
329 | * Completely internal helper enums.. | |
330 | * Keeping this isolated helps this code evolve independent of other | |
331 | * parts.. | |
332 | */ | |
333 | enum rproc_ops { | |
334 | RPROC_START, | |
335 | RPROC_STOP, | |
336 | RPROC_RESET, | |
337 | RPROC_PING, | |
338 | RPROC_RUNNING, | |
339 | }; | |
340 | ||
341 | /** | |
342 | * _rproc_ops_wrapper() - wrapper for invoking remote proc driver callback | |
343 | * @id: id of the remote processor | |
344 | * @op: one of rproc_ops that indicate what operation to invoke | |
345 | * | |
346 | * Most of the checks and verification for remoteproc operations are more | |
347 | * or less same for almost all operations. This allows us to put a wrapper | |
348 | * and use the common checks to allow the driver to function appropriately. | |
349 | * | |
350 | * Return: 0 if all ok, else appropriate error value. | |
351 | */ | |
352 | static int _rproc_ops_wrapper(int id, enum rproc_ops op) | |
353 | { | |
354 | struct udevice *dev = NULL; | |
355 | struct dm_rproc_uclass_pdata *uc_pdata; | |
356 | const struct dm_rproc_ops *ops; | |
357 | int (*fn)(struct udevice *dev); | |
358 | bool mandatory = false; | |
359 | char *op_str; | |
360 | int ret; | |
361 | ||
362 | ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); | |
363 | if (ret) { | |
364 | debug("Unknown remote processor id '%d' requested(%d)\n", | |
365 | id, ret); | |
366 | return ret; | |
367 | } | |
368 | ||
caa4daa2 | 369 | uc_pdata = dev_get_uclass_plat(dev); |
ddf56bc7 NM |
370 | |
371 | ops = rproc_get_ops(dev); | |
372 | if (!ops) { | |
373 | debug("%s driver has no ops?\n", dev->name); | |
374 | return -EINVAL; | |
375 | } | |
376 | switch (op) { | |
377 | case RPROC_START: | |
378 | fn = ops->start; | |
379 | mandatory = true; | |
380 | op_str = "Starting"; | |
381 | break; | |
382 | case RPROC_STOP: | |
383 | fn = ops->stop; | |
384 | op_str = "Stopping"; | |
385 | break; | |
386 | case RPROC_RESET: | |
387 | fn = ops->reset; | |
388 | op_str = "Resetting"; | |
389 | break; | |
390 | case RPROC_RUNNING: | |
391 | fn = ops->is_running; | |
392 | op_str = "Checking if running:"; | |
393 | break; | |
394 | case RPROC_PING: | |
395 | fn = ops->ping; | |
396 | op_str = "Pinging"; | |
397 | break; | |
398 | default: | |
399 | debug("what is '%d' operation??\n", op); | |
400 | return -EINVAL; | |
401 | } | |
402 | ||
403 | debug("%s %s...\n", op_str, uc_pdata->name); | |
404 | if (fn) | |
405 | return fn(dev); | |
406 | ||
407 | if (mandatory) | |
408 | debug("%s: data corruption?? mandatory function is missing!\n", | |
409 | dev->name); | |
410 | ||
411 | return -ENOSYS; | |
412 | } | |
413 | ||
414 | int rproc_start(int id) | |
415 | { | |
416 | return _rproc_ops_wrapper(id, RPROC_START); | |
417 | }; | |
418 | ||
419 | int rproc_stop(int id) | |
420 | { | |
421 | return _rproc_ops_wrapper(id, RPROC_STOP); | |
422 | }; | |
423 | ||
424 | int rproc_reset(int id) | |
425 | { | |
426 | return _rproc_ops_wrapper(id, RPROC_RESET); | |
427 | }; | |
428 | ||
429 | int rproc_ping(int id) | |
430 | { | |
431 | return _rproc_ops_wrapper(id, RPROC_PING); | |
432 | }; | |
433 | ||
434 | int rproc_is_running(int id) | |
435 | { | |
436 | return _rproc_ops_wrapper(id, RPROC_RUNNING); | |
437 | }; |