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