Commit | Line | Data |
---|---|---|
6b291e80 TS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * ZynqMP R5 Remote Processor driver | |
4 | * | |
5 | */ | |
6 | ||
7 | #include <dt-bindings/power/xlnx-zynqmp-power.h> | |
8 | #include <linux/dma-mapping.h> | |
9 | #include <linux/firmware/xlnx-zynqmp.h> | |
10 | #include <linux/kernel.h> | |
5dfb28c2 TS |
11 | #include <linux/mailbox_client.h> |
12 | #include <linux/mailbox/zynqmp-ipi-message.h> | |
6b291e80 TS |
13 | #include <linux/module.h> |
14 | #include <linux/of_address.h> | |
15 | #include <linux/of_platform.h> | |
16 | #include <linux/of_reserved_mem.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/remoteproc.h> | |
6b291e80 TS |
19 | |
20 | #include "remoteproc_internal.h" | |
21 | ||
5dfb28c2 TS |
22 | /* IPI buffer MAX length */ |
23 | #define IPI_BUF_LEN_MAX 32U | |
24 | ||
25 | /* RX mailbox client buffer max length */ | |
26 | #define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \ | |
27 | sizeof(struct zynqmp_ipi_message)) | |
6b291e80 TS |
28 | /* |
29 | * settings for RPU cluster mode which | |
30 | * reflects possible values of xlnx,cluster-mode dt-property | |
31 | */ | |
32 | enum zynqmp_r5_cluster_mode { | |
33 | SPLIT_MODE = 0, /* When cores run as separate processor */ | |
34 | LOCKSTEP_MODE = 1, /* cores execute same code in lockstep,clk-for-clk */ | |
35 | SINGLE_CPU_MODE = 2, /* core0 is held in reset and only core1 runs */ | |
36 | }; | |
37 | ||
38 | /** | |
39 | * struct mem_bank_data - Memory Bank description | |
40 | * | |
41 | * @addr: Start address of memory bank | |
6013727f | 42 | * @da: device address |
6b291e80 TS |
43 | * @size: Size of Memory bank |
44 | * @pm_domain_id: Power-domains id of memory bank for firmware to turn on/off | |
45 | * @bank_name: name of the bank for remoteproc framework | |
46 | */ | |
47 | struct mem_bank_data { | |
48 | phys_addr_t addr; | |
6013727f | 49 | u32 da; |
6b291e80 TS |
50 | size_t size; |
51 | u32 pm_domain_id; | |
52 | char *bank_name; | |
53 | }; | |
54 | ||
5dfb28c2 TS |
55 | /** |
56 | * struct mbox_info | |
57 | * | |
58 | * @rx_mc_buf: to copy data from mailbox rx channel | |
59 | * @tx_mc_buf: to copy data to mailbox tx channel | |
60 | * @r5_core: this mailbox's corresponding r5_core pointer | |
61 | * @mbox_work: schedule work after receiving data from mailbox | |
62 | * @mbox_cl: mailbox client | |
63 | * @tx_chan: mailbox tx channel | |
64 | * @rx_chan: mailbox rx channel | |
65 | */ | |
66 | struct mbox_info { | |
67 | unsigned char rx_mc_buf[MBOX_CLIENT_BUF_MAX]; | |
68 | unsigned char tx_mc_buf[MBOX_CLIENT_BUF_MAX]; | |
69 | struct zynqmp_r5_core *r5_core; | |
70 | struct work_struct mbox_work; | |
71 | struct mbox_client mbox_cl; | |
72 | struct mbox_chan *tx_chan; | |
73 | struct mbox_chan *rx_chan; | |
74 | }; | |
75 | ||
6b291e80 TS |
76 | /* |
77 | * Hardcoded TCM bank values. This will be removed once TCM bindings are | |
78 | * accepted for system-dt specifications and upstreamed in linux kernel | |
79 | */ | |
9af45bbd | 80 | static const struct mem_bank_data zynqmp_tcm_banks_split[] = { |
6013727f TS |
81 | {0xffe00000UL, 0x0, 0x10000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 64KB each */ |
82 | {0xffe20000UL, 0x20000, 0x10000UL, PD_R5_0_BTCM, "btcm0"}, | |
83 | {0xffe90000UL, 0x0, 0x10000UL, PD_R5_1_ATCM, "atcm1"}, | |
84 | {0xffeb0000UL, 0x20000, 0x10000UL, PD_R5_1_BTCM, "btcm1"}, | |
6b291e80 TS |
85 | }; |
86 | ||
9af45bbd TS |
87 | /* In lockstep mode cluster combines each 64KB TCM and makes 128KB TCM */ |
88 | static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { | |
6013727f TS |
89 | {0xffe00000UL, 0x0, 0x20000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 128KB each */ |
90 | {0xffe20000UL, 0x20000, 0x20000UL, PD_R5_0_BTCM, "btcm0"}, | |
91 | {0, 0, 0, PD_R5_1_ATCM, ""}, | |
92 | {0, 0, 0, PD_R5_1_BTCM, ""}, | |
9af45bbd TS |
93 | }; |
94 | ||
6b291e80 TS |
95 | /** |
96 | * struct zynqmp_r5_core | |
97 | * | |
98 | * @dev: device of RPU instance | |
99 | * @np: device node of RPU instance | |
100 | * @tcm_bank_count: number TCM banks accessible to this RPU | |
101 | * @tcm_banks: array of each TCM bank data | |
6b291e80 TS |
102 | * @rproc: rproc handle |
103 | * @pm_domain_id: RPU CPU power domain id | |
5dfb28c2 | 104 | * @ipi: pointer to mailbox information |
6b291e80 TS |
105 | */ |
106 | struct zynqmp_r5_core { | |
107 | struct device *dev; | |
108 | struct device_node *np; | |
109 | int tcm_bank_count; | |
110 | struct mem_bank_data **tcm_banks; | |
6b291e80 TS |
111 | struct rproc *rproc; |
112 | u32 pm_domain_id; | |
5dfb28c2 | 113 | struct mbox_info *ipi; |
6b291e80 TS |
114 | }; |
115 | ||
116 | /** | |
117 | * struct zynqmp_r5_cluster | |
118 | * | |
119 | * @dev: r5f subsystem cluster device node | |
120 | * @mode: cluster mode of type zynqmp_r5_cluster_mode | |
121 | * @core_count: number of r5 cores used for this cluster mode | |
122 | * @r5_cores: Array of pointers pointing to r5 core | |
123 | */ | |
124 | struct zynqmp_r5_cluster { | |
125 | struct device *dev; | |
126 | enum zynqmp_r5_cluster_mode mode; | |
127 | int core_count; | |
128 | struct zynqmp_r5_core **r5_cores; | |
129 | }; | |
130 | ||
5dfb28c2 TS |
131 | /** |
132 | * event_notified_idr_cb() - callback for vq_interrupt per notifyid | |
133 | * @id: rproc->notify id | |
134 | * @ptr: pointer to idr private data | |
135 | * @data: data passed to idr_for_each callback | |
136 | * | |
137 | * Pass notification to remoteproc virtio | |
138 | * | |
139 | * Return: 0. having return is to satisfy the idr_for_each() function | |
140 | * pointer input argument requirement. | |
141 | **/ | |
142 | static int event_notified_idr_cb(int id, void *ptr, void *data) | |
143 | { | |
144 | struct rproc *rproc = data; | |
145 | ||
146 | if (rproc_vq_interrupt(rproc, id) == IRQ_NONE) | |
147 | dev_dbg(&rproc->dev, "data not found for vqid=%d\n", id); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | /** | |
153 | * handle_event_notified() - remoteproc notification work function | |
154 | * @work: pointer to the work structure | |
155 | * | |
156 | * It checks each registered remoteproc notify IDs. | |
157 | */ | |
158 | static void handle_event_notified(struct work_struct *work) | |
159 | { | |
160 | struct mbox_info *ipi; | |
161 | struct rproc *rproc; | |
162 | ||
163 | ipi = container_of(work, struct mbox_info, mbox_work); | |
164 | rproc = ipi->r5_core->rproc; | |
165 | ||
166 | /* | |
167 | * We only use IPI for interrupt. The RPU firmware side may or may | |
168 | * not write the notifyid when it trigger IPI. | |
169 | * And thus, we scan through all the registered notifyids and | |
170 | * find which one is valid to get the message. | |
171 | * Even if message from firmware is NULL, we attempt to get vqid | |
172 | */ | |
173 | idr_for_each(&rproc->notifyids, event_notified_idr_cb, rproc); | |
174 | } | |
175 | ||
176 | /** | |
177 | * zynqmp_r5_mb_rx_cb() - receive channel mailbox callback | |
178 | * @cl: mailbox client | |
179 | * @msg: message pointer | |
180 | * | |
181 | * Receive data from ipi buffer, ack interrupt and then | |
182 | * it will schedule the R5 notification work. | |
183 | */ | |
184 | static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg) | |
185 | { | |
186 | struct zynqmp_ipi_message *ipi_msg, *buf_msg; | |
187 | struct mbox_info *ipi; | |
188 | size_t len; | |
189 | ||
190 | ipi = container_of(cl, struct mbox_info, mbox_cl); | |
191 | ||
192 | /* copy data from ipi buffer to r5_core */ | |
193 | ipi_msg = (struct zynqmp_ipi_message *)msg; | |
194 | buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf; | |
195 | len = ipi_msg->len; | |
196 | if (len > IPI_BUF_LEN_MAX) { | |
197 | dev_warn(cl->dev, "msg size exceeded than %d\n", | |
198 | IPI_BUF_LEN_MAX); | |
199 | len = IPI_BUF_LEN_MAX; | |
200 | } | |
201 | buf_msg->len = len; | |
202 | memcpy(buf_msg->data, ipi_msg->data, len); | |
203 | ||
204 | /* received and processed interrupt ack */ | |
205 | if (mbox_send_message(ipi->rx_chan, NULL) < 0) | |
206 | dev_err(cl->dev, "ack failed to mbox rx_chan\n"); | |
207 | ||
208 | schedule_work(&ipi->mbox_work); | |
209 | } | |
210 | ||
211 | /** | |
212 | * zynqmp_r5_setup_mbox() - Setup mailboxes related properties | |
213 | * this is used for each individual R5 core | |
214 | * | |
215 | * @cdev: child node device | |
216 | * | |
217 | * Function to setup mailboxes related properties | |
218 | * return : NULL if failed else pointer to mbox_info | |
219 | */ | |
220 | static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) | |
221 | { | |
222 | struct mbox_client *mbox_cl; | |
223 | struct mbox_info *ipi; | |
224 | ||
225 | ipi = kzalloc(sizeof(*ipi), GFP_KERNEL); | |
226 | if (!ipi) | |
227 | return NULL; | |
228 | ||
229 | mbox_cl = &ipi->mbox_cl; | |
230 | mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb; | |
231 | mbox_cl->tx_block = false; | |
232 | mbox_cl->knows_txdone = false; | |
233 | mbox_cl->tx_done = NULL; | |
234 | mbox_cl->dev = cdev; | |
235 | ||
236 | /* Request TX and RX channels */ | |
237 | ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx"); | |
238 | if (IS_ERR(ipi->tx_chan)) { | |
239 | ipi->tx_chan = NULL; | |
240 | kfree(ipi); | |
241 | dev_warn(cdev, "mbox tx channel request failed\n"); | |
242 | return NULL; | |
243 | } | |
244 | ||
245 | ipi->rx_chan = mbox_request_channel_byname(mbox_cl, "rx"); | |
246 | if (IS_ERR(ipi->rx_chan)) { | |
247 | mbox_free_channel(ipi->tx_chan); | |
248 | ipi->rx_chan = NULL; | |
249 | ipi->tx_chan = NULL; | |
250 | kfree(ipi); | |
251 | dev_warn(cdev, "mbox rx channel request failed\n"); | |
252 | return NULL; | |
253 | } | |
254 | ||
255 | INIT_WORK(&ipi->mbox_work, handle_event_notified); | |
256 | ||
257 | return ipi; | |
258 | } | |
259 | ||
260 | static void zynqmp_r5_free_mbox(struct mbox_info *ipi) | |
261 | { | |
262 | if (!ipi) | |
263 | return; | |
264 | ||
265 | if (ipi->tx_chan) { | |
266 | mbox_free_channel(ipi->tx_chan); | |
267 | ipi->tx_chan = NULL; | |
268 | } | |
269 | ||
270 | if (ipi->rx_chan) { | |
271 | mbox_free_channel(ipi->rx_chan); | |
272 | ipi->rx_chan = NULL; | |
273 | } | |
274 | ||
275 | kfree(ipi); | |
276 | } | |
277 | ||
278 | /* | |
279 | * zynqmp_r5_core_kick() - kick a firmware if mbox is provided | |
280 | * @rproc: r5 core's corresponding rproc structure | |
281 | * @vqid: virtqueue ID | |
282 | */ | |
283 | static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) | |
284 | { | |
285 | struct zynqmp_r5_core *r5_core = rproc->priv; | |
286 | struct device *dev = r5_core->dev; | |
287 | struct zynqmp_ipi_message *mb_msg; | |
288 | struct mbox_info *ipi; | |
289 | int ret; | |
290 | ||
291 | ipi = r5_core->ipi; | |
292 | if (!ipi) | |
293 | return; | |
294 | ||
295 | mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf; | |
296 | memcpy(mb_msg->data, &vqid, sizeof(vqid)); | |
297 | mb_msg->len = sizeof(vqid); | |
298 | ret = mbox_send_message(ipi->tx_chan, mb_msg); | |
299 | if (ret < 0) | |
300 | dev_warn(dev, "failed to send message\n"); | |
301 | } | |
302 | ||
6b291e80 TS |
303 | /* |
304 | * zynqmp_r5_set_mode() | |
305 | * | |
306 | * set RPU cluster and TCM operation mode | |
307 | * | |
308 | * @r5_core: pointer to zynqmp_r5_core type object | |
309 | * @fw_reg_val: value expected by firmware to configure RPU cluster mode | |
310 | * @tcm_mode: value expected by fw to configure TCM mode (lockstep or split) | |
311 | * | |
312 | * Return: 0 for success and < 0 for failure | |
313 | */ | |
314 | static int zynqmp_r5_set_mode(struct zynqmp_r5_core *r5_core, | |
315 | enum rpu_oper_mode fw_reg_val, | |
316 | enum rpu_tcm_comb tcm_mode) | |
317 | { | |
318 | int ret; | |
319 | ||
320 | ret = zynqmp_pm_set_rpu_mode(r5_core->pm_domain_id, fw_reg_val); | |
321 | if (ret < 0) { | |
322 | dev_err(r5_core->dev, "failed to set RPU mode\n"); | |
323 | return ret; | |
324 | } | |
325 | ||
326 | ret = zynqmp_pm_set_tcm_config(r5_core->pm_domain_id, tcm_mode); | |
327 | if (ret < 0) | |
328 | dev_err(r5_core->dev, "failed to configure TCM\n"); | |
329 | ||
330 | return ret; | |
331 | } | |
332 | ||
333 | /* | |
334 | * zynqmp_r5_rproc_start() | |
335 | * @rproc: single R5 core's corresponding rproc instance | |
336 | * | |
337 | * Start R5 Core from designated boot address. | |
338 | * | |
339 | * return 0 on success, otherwise non-zero value on failure | |
340 | */ | |
341 | static int zynqmp_r5_rproc_start(struct rproc *rproc) | |
342 | { | |
343 | struct zynqmp_r5_core *r5_core = rproc->priv; | |
344 | enum rpu_boot_mem bootmem; | |
345 | int ret; | |
346 | ||
347 | /* | |
348 | * The exception vector pointers (EVP) refer to the base-address of | |
349 | * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector | |
350 | * starts at the base-address and subsequent vectors are on 4-byte | |
351 | * boundaries. | |
352 | * | |
353 | * Exception vectors can start either from 0x0000_0000 (LOVEC) or | |
354 | * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory) | |
355 | * | |
356 | * Usually firmware will put Exception vectors at LOVEC. | |
357 | * | |
358 | * It is not recommend that you change the exception vector. | |
359 | * Changing the EVP to HIVEC will result in increased interrupt latency | |
360 | * and jitter. Also, if the OCM is secured and the Cortex-R5F processor | |
361 | * is non-secured, then the Cortex-R5F processor cannot access the | |
362 | * HIVEC exception vectors in the OCM. | |
363 | */ | |
364 | bootmem = (rproc->bootaddr >= 0xFFFC0000) ? | |
365 | PM_RPU_BOOTMEM_HIVEC : PM_RPU_BOOTMEM_LOVEC; | |
366 | ||
367 | dev_dbg(r5_core->dev, "RPU boot addr 0x%llx from %s.", rproc->bootaddr, | |
368 | bootmem == PM_RPU_BOOTMEM_HIVEC ? "OCM" : "TCM"); | |
369 | ||
370 | ret = zynqmp_pm_request_wake(r5_core->pm_domain_id, 1, | |
371 | bootmem, ZYNQMP_PM_REQUEST_ACK_NO); | |
372 | if (ret) | |
373 | dev_err(r5_core->dev, | |
374 | "failed to start RPU = 0x%x\n", r5_core->pm_domain_id); | |
375 | return ret; | |
376 | } | |
377 | ||
378 | /* | |
379 | * zynqmp_r5_rproc_stop() | |
380 | * @rproc: single R5 core's corresponding rproc instance | |
381 | * | |
382 | * Power down R5 Core. | |
383 | * | |
384 | * return 0 on success, otherwise non-zero value on failure | |
385 | */ | |
386 | static int zynqmp_r5_rproc_stop(struct rproc *rproc) | |
387 | { | |
388 | struct zynqmp_r5_core *r5_core = rproc->priv; | |
389 | int ret; | |
390 | ||
391 | ret = zynqmp_pm_force_pwrdwn(r5_core->pm_domain_id, | |
392 | ZYNQMP_PM_REQUEST_ACK_BLOCKING); | |
393 | if (ret) | |
394 | dev_err(r5_core->dev, "failed to stop remoteproc RPU %d\n", ret); | |
395 | ||
396 | return ret; | |
397 | } | |
398 | ||
399 | /* | |
400 | * zynqmp_r5_mem_region_map() | |
401 | * @rproc: single R5 core's corresponding rproc instance | |
402 | * @mem: mem descriptor to map reserved memory-regions | |
403 | * | |
404 | * Callback to map va for memory-region's carveout. | |
405 | * | |
406 | * return 0 on success, otherwise non-zero value on failure | |
407 | */ | |
408 | static int zynqmp_r5_mem_region_map(struct rproc *rproc, | |
409 | struct rproc_mem_entry *mem) | |
410 | { | |
411 | void __iomem *va; | |
412 | ||
413 | va = ioremap_wc(mem->dma, mem->len); | |
414 | if (IS_ERR_OR_NULL(va)) | |
415 | return -ENOMEM; | |
416 | ||
417 | mem->va = (void *)va; | |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
422 | /* | |
423 | * zynqmp_r5_rproc_mem_unmap | |
424 | * @rproc: single R5 core's corresponding rproc instance | |
425 | * @mem: mem entry to unmap | |
426 | * | |
427 | * Unmap memory-region carveout | |
428 | * | |
429 | * return: always returns 0 | |
430 | */ | |
431 | static int zynqmp_r5_mem_region_unmap(struct rproc *rproc, | |
432 | struct rproc_mem_entry *mem) | |
433 | { | |
434 | iounmap((void __iomem *)mem->va); | |
435 | return 0; | |
436 | } | |
437 | ||
438 | /* | |
439 | * add_mem_regions_carveout() | |
440 | * @rproc: single R5 core's corresponding rproc instance | |
441 | * | |
442 | * Construct rproc mem carveouts from memory-region property nodes | |
443 | * | |
444 | * return 0 on success, otherwise non-zero value on failure | |
445 | */ | |
446 | static int add_mem_regions_carveout(struct rproc *rproc) | |
447 | { | |
448 | struct rproc_mem_entry *rproc_mem; | |
449 | struct zynqmp_r5_core *r5_core; | |
81c18e08 | 450 | struct of_phandle_iterator it; |
6b291e80 | 451 | struct reserved_mem *rmem; |
81c18e08 | 452 | int i = 0; |
6b291e80 | 453 | |
86660713 | 454 | r5_core = rproc->priv; |
6b291e80 | 455 | |
81c18e08 TS |
456 | /* Register associated reserved memory regions */ |
457 | of_phandle_iterator_init(&it, r5_core->np, "memory-region", NULL, 0); | |
6b291e80 | 458 | |
81c18e08 TS |
459 | while (of_phandle_iterator_next(&it) == 0) { |
460 | rmem = of_reserved_mem_lookup(it.node); | |
461 | if (!rmem) { | |
462 | of_node_put(it.node); | |
463 | dev_err(&rproc->dev, "unable to acquire memory-region\n"); | |
464 | return -EINVAL; | |
465 | } | |
466 | ||
467 | if (!strcmp(it.node->name, "vdev0buffer")) { | |
6b291e80 TS |
468 | /* Init reserved memory for vdev buffer */ |
469 | rproc_mem = rproc_of_resm_mem_entry_init(&rproc->dev, i, | |
470 | rmem->size, | |
471 | rmem->base, | |
81c18e08 | 472 | it.node->name); |
6b291e80 TS |
473 | } else { |
474 | /* Register associated reserved memory regions */ | |
475 | rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, | |
476 | (dma_addr_t)rmem->base, | |
477 | rmem->size, rmem->base, | |
478 | zynqmp_r5_mem_region_map, | |
479 | zynqmp_r5_mem_region_unmap, | |
81c18e08 | 480 | it.node->name); |
6b291e80 TS |
481 | } |
482 | ||
81c18e08 TS |
483 | if (!rproc_mem) { |
484 | of_node_put(it.node); | |
6b291e80 | 485 | return -ENOMEM; |
81c18e08 | 486 | } |
6b291e80 TS |
487 | |
488 | rproc_add_carveout(rproc, rproc_mem); | |
489 | ||
490 | dev_dbg(&rproc->dev, "reserved mem carveout %s addr=%llx, size=0x%llx", | |
81c18e08 TS |
491 | it.node->name, rmem->base, rmem->size); |
492 | i++; | |
6b291e80 TS |
493 | } |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
498 | /* | |
499 | * tcm_mem_unmap() | |
500 | * @rproc: single R5 core's corresponding rproc instance | |
501 | * @mem: tcm mem entry to unmap | |
502 | * | |
503 | * Unmap TCM banks when powering down R5 core. | |
504 | * | |
505 | * return always 0 | |
506 | */ | |
507 | static int tcm_mem_unmap(struct rproc *rproc, struct rproc_mem_entry *mem) | |
508 | { | |
509 | iounmap((void __iomem *)mem->va); | |
510 | ||
511 | return 0; | |
512 | } | |
513 | ||
514 | /* | |
515 | * tcm_mem_map() | |
516 | * @rproc: single R5 core's corresponding rproc instance | |
517 | * @mem: tcm memory entry descriptor | |
518 | * | |
519 | * Given TCM bank entry, this func setup virtual address for TCM bank | |
520 | * remoteproc carveout. It also takes care of va to da address translation | |
521 | * | |
522 | * return 0 on success, otherwise non-zero value on failure | |
523 | */ | |
524 | static int tcm_mem_map(struct rproc *rproc, | |
525 | struct rproc_mem_entry *mem) | |
526 | { | |
527 | void __iomem *va; | |
528 | ||
529 | va = ioremap_wc(mem->dma, mem->len); | |
530 | if (IS_ERR_OR_NULL(va)) | |
531 | return -ENOMEM; | |
532 | ||
533 | /* Update memory entry va */ | |
534 | mem->va = (void *)va; | |
535 | ||
536 | /* clear TCMs */ | |
537 | memset_io(va, 0, mem->len); | |
538 | ||
6b291e80 TS |
539 | return 0; |
540 | } | |
541 | ||
542 | /* | |
543 | * add_tcm_carveout_split_mode() | |
544 | * @rproc: single R5 core's corresponding rproc instance | |
545 | * | |
546 | * allocate and add remoteproc carveout for TCM memory in split mode | |
547 | * | |
548 | * return 0 on success, otherwise non-zero value on failure | |
549 | */ | |
550 | static int add_tcm_carveout_split_mode(struct rproc *rproc) | |
551 | { | |
552 | struct rproc_mem_entry *rproc_mem; | |
553 | struct zynqmp_r5_core *r5_core; | |
554 | int i, num_banks, ret; | |
555 | phys_addr_t bank_addr; | |
556 | struct device *dev; | |
557 | u32 pm_domain_id; | |
558 | size_t bank_size; | |
559 | char *bank_name; | |
6013727f | 560 | u32 da; |
6b291e80 | 561 | |
86660713 | 562 | r5_core = rproc->priv; |
6b291e80 TS |
563 | dev = r5_core->dev; |
564 | num_banks = r5_core->tcm_bank_count; | |
565 | ||
566 | /* | |
567 | * Power-on Each 64KB TCM, | |
568 | * register its address space, map and unmap functions | |
569 | * and add carveouts accordingly | |
570 | */ | |
571 | for (i = 0; i < num_banks; i++) { | |
572 | bank_addr = r5_core->tcm_banks[i]->addr; | |
6013727f | 573 | da = r5_core->tcm_banks[i]->da; |
6b291e80 TS |
574 | bank_name = r5_core->tcm_banks[i]->bank_name; |
575 | bank_size = r5_core->tcm_banks[i]->size; | |
576 | pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; | |
577 | ||
578 | ret = zynqmp_pm_request_node(pm_domain_id, | |
579 | ZYNQMP_PM_CAPABILITY_ACCESS, 0, | |
580 | ZYNQMP_PM_REQUEST_ACK_BLOCKING); | |
581 | if (ret < 0) { | |
582 | dev_err(dev, "failed to turn on TCM 0x%x", pm_domain_id); | |
583 | goto release_tcm_split; | |
584 | } | |
585 | ||
6013727f TS |
586 | dev_dbg(dev, "TCM carveout split mode %s addr=%llx, da=0x%x, size=0x%lx", |
587 | bank_name, bank_addr, da, bank_size); | |
6b291e80 TS |
588 | |
589 | rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr, | |
6013727f | 590 | bank_size, da, |
6b291e80 TS |
591 | tcm_mem_map, tcm_mem_unmap, |
592 | bank_name); | |
593 | if (!rproc_mem) { | |
594 | ret = -ENOMEM; | |
595 | zynqmp_pm_release_node(pm_domain_id); | |
596 | goto release_tcm_split; | |
597 | } | |
598 | ||
599 | rproc_add_carveout(rproc, rproc_mem); | |
600 | } | |
601 | ||
602 | return 0; | |
603 | ||
604 | release_tcm_split: | |
605 | /* If failed, Turn off all TCM banks turned on before */ | |
606 | for (i--; i >= 0; i--) { | |
607 | pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; | |
608 | zynqmp_pm_release_node(pm_domain_id); | |
609 | } | |
610 | return ret; | |
611 | } | |
612 | ||
613 | /* | |
614 | * add_tcm_carveout_lockstep_mode() | |
615 | * @rproc: single R5 core's corresponding rproc instance | |
616 | * | |
617 | * allocate and add remoteproc carveout for TCM memory in lockstep mode | |
618 | * | |
619 | * return 0 on success, otherwise non-zero value on failure | |
620 | */ | |
621 | static int add_tcm_carveout_lockstep_mode(struct rproc *rproc) | |
622 | { | |
623 | struct rproc_mem_entry *rproc_mem; | |
624 | struct zynqmp_r5_core *r5_core; | |
625 | int i, num_banks, ret; | |
626 | phys_addr_t bank_addr; | |
627 | size_t bank_size = 0; | |
628 | struct device *dev; | |
629 | u32 pm_domain_id; | |
630 | char *bank_name; | |
6013727f | 631 | u32 da; |
6b291e80 | 632 | |
86660713 | 633 | r5_core = rproc->priv; |
6b291e80 TS |
634 | dev = r5_core->dev; |
635 | ||
636 | /* Go through zynqmp banks for r5 node */ | |
637 | num_banks = r5_core->tcm_bank_count; | |
638 | ||
639 | /* | |
640 | * In lockstep mode, TCM is contiguous memory block | |
641 | * However, each TCM block still needs to be enabled individually. | |
9af45bbd TS |
642 | * So, Enable each TCM block individually. |
643 | * Although ATCM and BTCM is contiguous memory block, add two separate | |
644 | * carveouts for both. | |
6b291e80 | 645 | */ |
6b291e80 | 646 | for (i = 0; i < num_banks; i++) { |
6b291e80 TS |
647 | pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; |
648 | ||
649 | /* Turn on each TCM bank individually */ | |
650 | ret = zynqmp_pm_request_node(pm_domain_id, | |
651 | ZYNQMP_PM_CAPABILITY_ACCESS, 0, | |
652 | ZYNQMP_PM_REQUEST_ACK_BLOCKING); | |
653 | if (ret < 0) { | |
654 | dev_err(dev, "failed to turn on TCM 0x%x", pm_domain_id); | |
655 | goto release_tcm_lockstep; | |
656 | } | |
6b291e80 | 657 | |
9af45bbd TS |
658 | bank_size = r5_core->tcm_banks[i]->size; |
659 | if (bank_size == 0) | |
660 | continue; | |
661 | ||
662 | bank_addr = r5_core->tcm_banks[i]->addr; | |
6013727f | 663 | da = r5_core->tcm_banks[i]->da; |
9af45bbd TS |
664 | bank_name = r5_core->tcm_banks[i]->bank_name; |
665 | ||
666 | /* Register TCM address range, TCM map and unmap functions */ | |
667 | rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr, | |
6013727f | 668 | bank_size, da, |
9af45bbd TS |
669 | tcm_mem_map, tcm_mem_unmap, |
670 | bank_name); | |
671 | if (!rproc_mem) { | |
672 | ret = -ENOMEM; | |
673 | zynqmp_pm_release_node(pm_domain_id); | |
674 | goto release_tcm_lockstep; | |
675 | } | |
676 | ||
677 | /* If registration is success, add carveouts */ | |
678 | rproc_add_carveout(rproc, rproc_mem); | |
6b291e80 | 679 | |
6013727f TS |
680 | dev_dbg(dev, "TCM carveout lockstep mode %s addr=0x%llx, da=0x%x, size=0x%lx", |
681 | bank_name, bank_addr, da, bank_size); | |
9af45bbd | 682 | } |
6b291e80 TS |
683 | |
684 | return 0; | |
685 | ||
686 | release_tcm_lockstep: | |
687 | /* If failed, Turn off all TCM banks turned on before */ | |
688 | for (i--; i >= 0; i--) { | |
689 | pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; | |
690 | zynqmp_pm_release_node(pm_domain_id); | |
691 | } | |
692 | return ret; | |
693 | } | |
694 | ||
695 | /* | |
696 | * add_tcm_banks() | |
697 | * @rproc: single R5 core's corresponding rproc instance | |
698 | * | |
699 | * allocate and add remoteproc carveouts for TCM memory based on cluster mode | |
700 | * | |
701 | * return 0 on success, otherwise non-zero value on failure | |
702 | */ | |
703 | static int add_tcm_banks(struct rproc *rproc) | |
704 | { | |
705 | struct zynqmp_r5_cluster *cluster; | |
706 | struct zynqmp_r5_core *r5_core; | |
707 | struct device *dev; | |
708 | ||
86660713 | 709 | r5_core = rproc->priv; |
6b291e80 TS |
710 | if (!r5_core) |
711 | return -EINVAL; | |
712 | ||
713 | dev = r5_core->dev; | |
714 | ||
715 | cluster = dev_get_drvdata(dev->parent); | |
716 | if (!cluster) { | |
717 | dev_err(dev->parent, "Invalid driver data\n"); | |
718 | return -EINVAL; | |
719 | } | |
720 | ||
721 | /* | |
722 | * In lockstep mode TCM banks are one contiguous memory region of 256Kb | |
723 | * In split mode, each TCM bank is 64Kb and not contiguous. | |
724 | * We add memory carveouts accordingly. | |
725 | */ | |
726 | if (cluster->mode == SPLIT_MODE) | |
727 | return add_tcm_carveout_split_mode(rproc); | |
728 | else if (cluster->mode == LOCKSTEP_MODE) | |
729 | return add_tcm_carveout_lockstep_mode(rproc); | |
730 | ||
731 | return -EINVAL; | |
732 | } | |
733 | ||
734 | /* | |
735 | * zynqmp_r5_parse_fw() | |
736 | * @rproc: single R5 core's corresponding rproc instance | |
737 | * @fw: ptr to firmware to be loaded onto r5 core | |
738 | * | |
739 | * get resource table if available | |
740 | * | |
741 | * return 0 on success, otherwise non-zero value on failure | |
742 | */ | |
743 | static int zynqmp_r5_parse_fw(struct rproc *rproc, const struct firmware *fw) | |
744 | { | |
745 | int ret; | |
746 | ||
747 | ret = rproc_elf_load_rsc_table(rproc, fw); | |
748 | if (ret == -EINVAL) { | |
749 | /* | |
750 | * resource table only required for IPC. | |
751 | * if not present, this is not necessarily an error; | |
752 | * for example, loading r5 hello world application | |
753 | * so simply inform user and keep going. | |
754 | */ | |
755 | dev_info(&rproc->dev, "no resource table found.\n"); | |
756 | ret = 0; | |
757 | } | |
758 | return ret; | |
759 | } | |
760 | ||
761 | /** | |
762 | * zynqmp_r5_rproc_prepare() | |
763 | * adds carveouts for TCM bank and reserved memory regions | |
764 | * | |
765 | * @rproc: Device node of each rproc | |
766 | * | |
767 | * Return: 0 for success else < 0 error code | |
768 | */ | |
769 | static int zynqmp_r5_rproc_prepare(struct rproc *rproc) | |
770 | { | |
771 | int ret; | |
772 | ||
773 | ret = add_tcm_banks(rproc); | |
774 | if (ret) { | |
775 | dev_err(&rproc->dev, "failed to get TCM banks, err %d\n", ret); | |
776 | return ret; | |
777 | } | |
778 | ||
779 | ret = add_mem_regions_carveout(rproc); | |
780 | if (ret) { | |
781 | dev_err(&rproc->dev, "failed to get reserve mem regions %d\n", ret); | |
782 | return ret; | |
783 | } | |
784 | ||
785 | return 0; | |
786 | } | |
787 | ||
788 | /** | |
789 | * zynqmp_r5_rproc_unprepare() | |
790 | * Turns off TCM banks using power-domain id | |
791 | * | |
792 | * @rproc: Device node of each rproc | |
793 | * | |
794 | * Return: always 0 | |
795 | */ | |
796 | static int zynqmp_r5_rproc_unprepare(struct rproc *rproc) | |
797 | { | |
798 | struct zynqmp_r5_core *r5_core; | |
799 | u32 pm_domain_id; | |
800 | int i; | |
801 | ||
86660713 | 802 | r5_core = rproc->priv; |
6b291e80 TS |
803 | |
804 | for (i = 0; i < r5_core->tcm_bank_count; i++) { | |
805 | pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; | |
806 | if (zynqmp_pm_release_node(pm_domain_id)) | |
807 | dev_warn(r5_core->dev, | |
808 | "can't turn off TCM bank 0x%x", pm_domain_id); | |
809 | } | |
810 | ||
811 | return 0; | |
812 | } | |
813 | ||
814 | static const struct rproc_ops zynqmp_r5_rproc_ops = { | |
815 | .prepare = zynqmp_r5_rproc_prepare, | |
816 | .unprepare = zynqmp_r5_rproc_unprepare, | |
817 | .start = zynqmp_r5_rproc_start, | |
818 | .stop = zynqmp_r5_rproc_stop, | |
819 | .load = rproc_elf_load_segments, | |
820 | .parse_fw = zynqmp_r5_parse_fw, | |
821 | .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, | |
822 | .sanity_check = rproc_elf_sanity_check, | |
823 | .get_boot_addr = rproc_elf_get_boot_addr, | |
5dfb28c2 | 824 | .kick = zynqmp_r5_rproc_kick, |
6b291e80 TS |
825 | }; |
826 | ||
827 | /** | |
828 | * zynqmp_r5_add_rproc_core() | |
829 | * Allocate and add struct rproc object for each r5f core | |
830 | * This is called for each individual r5f core | |
831 | * | |
832 | * @cdev: Device node of each r5 core | |
833 | * | |
834 | * Return: zynqmp_r5_core object for success else error code pointer | |
835 | */ | |
836 | static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) | |
837 | { | |
838 | struct zynqmp_r5_core *r5_core; | |
839 | struct rproc *r5_rproc; | |
840 | int ret; | |
841 | ||
842 | /* Set up DMA mask */ | |
843 | ret = dma_set_coherent_mask(cdev, DMA_BIT_MASK(32)); | |
844 | if (ret) | |
845 | return ERR_PTR(ret); | |
846 | ||
847 | /* Allocate remoteproc instance */ | |
848 | r5_rproc = rproc_alloc(cdev, dev_name(cdev), | |
849 | &zynqmp_r5_rproc_ops, | |
850 | NULL, sizeof(struct zynqmp_r5_core)); | |
851 | if (!r5_rproc) { | |
852 | dev_err(cdev, "failed to allocate memory for rproc instance\n"); | |
853 | return ERR_PTR(-ENOMEM); | |
854 | } | |
855 | ||
856 | r5_rproc->auto_boot = false; | |
86660713 | 857 | r5_core = r5_rproc->priv; |
6b291e80 TS |
858 | r5_core->dev = cdev; |
859 | r5_core->np = dev_of_node(cdev); | |
860 | if (!r5_core->np) { | |
861 | dev_err(cdev, "can't get device node for r5 core\n"); | |
862 | ret = -EINVAL; | |
863 | goto free_rproc; | |
864 | } | |
865 | ||
866 | /* Add R5 remoteproc core */ | |
867 | ret = rproc_add(r5_rproc); | |
868 | if (ret) { | |
869 | dev_err(cdev, "failed to add r5 remoteproc\n"); | |
870 | goto free_rproc; | |
871 | } | |
872 | ||
873 | r5_core->rproc = r5_rproc; | |
874 | return r5_core; | |
875 | ||
876 | free_rproc: | |
877 | rproc_free(r5_rproc); | |
878 | return ERR_PTR(ret); | |
879 | } | |
880 | ||
881 | /** | |
882 | * zynqmp_r5_get_tcm_node() | |
883 | * Ideally this function should parse tcm node and store information | |
884 | * in r5_core instance. For now, Hardcoded TCM information is used. | |
885 | * This approach is used as TCM bindings for system-dt is being developed | |
886 | * | |
887 | * @cluster: pointer to zynqmp_r5_cluster type object | |
888 | * | |
889 | * Return: 0 for success and < 0 error code for failure. | |
890 | */ | |
891 | static int zynqmp_r5_get_tcm_node(struct zynqmp_r5_cluster *cluster) | |
892 | { | |
9af45bbd | 893 | const struct mem_bank_data *zynqmp_tcm_banks; |
6b291e80 TS |
894 | struct device *dev = cluster->dev; |
895 | struct zynqmp_r5_core *r5_core; | |
896 | int tcm_bank_count, tcm_node; | |
897 | int i, j; | |
898 | ||
9af45bbd TS |
899 | if (cluster->mode == SPLIT_MODE) { |
900 | zynqmp_tcm_banks = zynqmp_tcm_banks_split; | |
901 | tcm_bank_count = ARRAY_SIZE(zynqmp_tcm_banks_split); | |
902 | } else { | |
903 | zynqmp_tcm_banks = zynqmp_tcm_banks_lockstep; | |
904 | tcm_bank_count = ARRAY_SIZE(zynqmp_tcm_banks_lockstep); | |
905 | } | |
6b291e80 TS |
906 | |
907 | /* count per core tcm banks */ | |
908 | tcm_bank_count = tcm_bank_count / cluster->core_count; | |
909 | ||
910 | /* | |
911 | * r5 core 0 will use all of TCM banks in lockstep mode. | |
912 | * In split mode, r5 core0 will use 128k and r5 core1 will use another | |
913 | * 128k. Assign TCM banks to each core accordingly | |
914 | */ | |
915 | tcm_node = 0; | |
916 | for (i = 0; i < cluster->core_count; i++) { | |
917 | r5_core = cluster->r5_cores[i]; | |
918 | r5_core->tcm_banks = devm_kcalloc(dev, tcm_bank_count, | |
919 | sizeof(struct mem_bank_data *), | |
920 | GFP_KERNEL); | |
921 | if (!r5_core->tcm_banks) | |
922 | return -ENOMEM; | |
923 | ||
924 | for (j = 0; j < tcm_bank_count; j++) { | |
925 | /* | |
926 | * Use pre-defined TCM reg values. | |
927 | * Eventually this should be replaced by values | |
928 | * parsed from dts. | |
929 | */ | |
930 | r5_core->tcm_banks[j] = | |
931 | (struct mem_bank_data *)&zynqmp_tcm_banks[tcm_node]; | |
932 | tcm_node++; | |
933 | } | |
934 | ||
935 | r5_core->tcm_bank_count = tcm_bank_count; | |
936 | } | |
937 | ||
938 | return 0; | |
939 | } | |
940 | ||
6b291e80 TS |
941 | /* |
942 | * zynqmp_r5_core_init() | |
943 | * Create and initialize zynqmp_r5_core type object | |
944 | * | |
945 | * @cluster: pointer to zynqmp_r5_cluster type object | |
946 | * @fw_reg_val: value expected by firmware to configure RPU cluster mode | |
947 | * @tcm_mode: value expected by fw to configure TCM mode (lockstep or split) | |
948 | * | |
949 | * Return: 0 for success and error code for failure. | |
950 | */ | |
951 | static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, | |
952 | enum rpu_oper_mode fw_reg_val, | |
953 | enum rpu_tcm_comb tcm_mode) | |
954 | { | |
955 | struct device *dev = cluster->dev; | |
956 | struct zynqmp_r5_core *r5_core; | |
957 | int ret, i; | |
958 | ||
959 | ret = zynqmp_r5_get_tcm_node(cluster); | |
960 | if (ret < 0) { | |
961 | dev_err(dev, "can't get tcm node, err %d\n", ret); | |
962 | return ret; | |
963 | } | |
964 | ||
965 | for (i = 0; i < cluster->core_count; i++) { | |
966 | r5_core = cluster->r5_cores[i]; | |
967 | ||
6b291e80 TS |
968 | /* Initialize r5 cores with power-domains parsed from dts */ |
969 | ret = of_property_read_u32_index(r5_core->np, "power-domains", | |
970 | 1, &r5_core->pm_domain_id); | |
971 | if (ret) { | |
972 | dev_err(dev, "failed to get power-domains property\n"); | |
973 | return ret; | |
974 | } | |
975 | ||
976 | ret = zynqmp_r5_set_mode(r5_core, fw_reg_val, tcm_mode); | |
977 | if (ret) { | |
978 | dev_err(dev, "failed to set r5 cluster mode %d, err %d\n", | |
979 | cluster->mode, ret); | |
980 | return ret; | |
981 | } | |
982 | } | |
983 | ||
984 | return 0; | |
985 | } | |
986 | ||
987 | /* | |
988 | * zynqmp_r5_cluster_init() | |
989 | * Create and initialize zynqmp_r5_cluster type object | |
990 | * | |
991 | * @cluster: pointer to zynqmp_r5_cluster type object | |
992 | * | |
993 | * Return: 0 for success and error code for failure. | |
994 | */ | |
995 | static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) | |
996 | { | |
997 | enum zynqmp_r5_cluster_mode cluster_mode = LOCKSTEP_MODE; | |
998 | struct device *dev = cluster->dev; | |
999 | struct device_node *dev_node = dev_of_node(dev); | |
1000 | struct platform_device *child_pdev; | |
1001 | struct zynqmp_r5_core **r5_cores; | |
1002 | enum rpu_oper_mode fw_reg_val; | |
1003 | struct device **child_devs; | |
1004 | struct device_node *child; | |
1005 | enum rpu_tcm_comb tcm_mode; | |
1006 | int core_count, ret, i; | |
5dfb28c2 | 1007 | struct mbox_info *ipi; |
6b291e80 TS |
1008 | |
1009 | ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode); | |
1010 | ||
1011 | /* | |
1012 | * on success returns 0, if not defined then returns -EINVAL, | |
1013 | * In that case, default is LOCKSTEP mode. Other than that | |
1014 | * returns relative error code < 0. | |
1015 | */ | |
1016 | if (ret != -EINVAL && ret != 0) { | |
1017 | dev_err(dev, "Invalid xlnx,cluster-mode property\n"); | |
1018 | return ret; | |
1019 | } | |
1020 | ||
1021 | /* | |
1022 | * For now driver only supports split mode and lockstep mode. | |
1023 | * fail driver probe if either of that is not set in dts. | |
1024 | */ | |
1025 | if (cluster_mode == LOCKSTEP_MODE) { | |
1026 | tcm_mode = PM_RPU_TCM_COMB; | |
1027 | fw_reg_val = PM_RPU_MODE_LOCKSTEP; | |
1028 | } else if (cluster_mode == SPLIT_MODE) { | |
1029 | tcm_mode = PM_RPU_TCM_SPLIT; | |
1030 | fw_reg_val = PM_RPU_MODE_SPLIT; | |
1031 | } else { | |
1032 | dev_err(dev, "driver does not support cluster mode %d\n", cluster_mode); | |
1033 | return -EINVAL; | |
1034 | } | |
1035 | ||
1036 | /* | |
1037 | * Number of cores is decided by number of child nodes of | |
1038 | * r5f subsystem node in dts. If Split mode is used in dts | |
1039 | * 2 child nodes are expected. | |
1040 | * In lockstep mode if two child nodes are available, | |
1041 | * only use first child node and consider it as core0 | |
1042 | * and ignore core1 dt node. | |
1043 | */ | |
1044 | core_count = of_get_available_child_count(dev_node); | |
1045 | if (core_count == 0) { | |
1046 | dev_err(dev, "Invalid number of r5 cores %d", core_count); | |
1047 | return -EINVAL; | |
1048 | } else if (cluster_mode == SPLIT_MODE && core_count != 2) { | |
1049 | dev_err(dev, "Invalid number of r5 cores for split mode\n"); | |
1050 | return -EINVAL; | |
1051 | } else if (cluster_mode == LOCKSTEP_MODE && core_count == 2) { | |
1052 | dev_warn(dev, "Only r5 core0 will be used\n"); | |
1053 | core_count = 1; | |
1054 | } | |
1055 | ||
1056 | child_devs = kcalloc(core_count, sizeof(struct device *), GFP_KERNEL); | |
1057 | if (!child_devs) | |
1058 | return -ENOMEM; | |
1059 | ||
1060 | r5_cores = kcalloc(core_count, | |
1061 | sizeof(struct zynqmp_r5_core *), GFP_KERNEL); | |
1062 | if (!r5_cores) { | |
1063 | kfree(child_devs); | |
1064 | return -ENOMEM; | |
1065 | } | |
1066 | ||
1067 | i = 0; | |
1068 | for_each_available_child_of_node(dev_node, child) { | |
1069 | child_pdev = of_find_device_by_node(child); | |
1070 | if (!child_pdev) { | |
1071 | of_node_put(child); | |
1072 | ret = -ENODEV; | |
1073 | goto release_r5_cores; | |
1074 | } | |
1075 | ||
1076 | child_devs[i] = &child_pdev->dev; | |
1077 | ||
1078 | /* create and add remoteproc instance of type struct rproc */ | |
1079 | r5_cores[i] = zynqmp_r5_add_rproc_core(&child_pdev->dev); | |
1080 | if (IS_ERR(r5_cores[i])) { | |
1081 | of_node_put(child); | |
1082 | ret = PTR_ERR(r5_cores[i]); | |
1083 | r5_cores[i] = NULL; | |
1084 | goto release_r5_cores; | |
1085 | } | |
1086 | ||
5dfb28c2 TS |
1087 | /* |
1088 | * If mailbox nodes are disabled using "status" property then | |
1089 | * setting up mailbox channels will fail. | |
1090 | */ | |
1091 | ipi = zynqmp_r5_setup_mbox(&child_pdev->dev); | |
1092 | if (ipi) { | |
1093 | r5_cores[i]->ipi = ipi; | |
1094 | ipi->r5_core = r5_cores[i]; | |
1095 | } | |
1096 | ||
6b291e80 TS |
1097 | /* |
1098 | * If two child nodes are available in dts in lockstep mode, | |
1099 | * then ignore second child node. | |
1100 | */ | |
1101 | if (cluster_mode == LOCKSTEP_MODE) { | |
1102 | of_node_put(child); | |
1103 | break; | |
1104 | } | |
1105 | ||
1106 | i++; | |
1107 | } | |
1108 | ||
1109 | cluster->mode = cluster_mode; | |
1110 | cluster->core_count = core_count; | |
1111 | cluster->r5_cores = r5_cores; | |
1112 | ||
1113 | ret = zynqmp_r5_core_init(cluster, fw_reg_val, tcm_mode); | |
1114 | if (ret < 0) { | |
1115 | dev_err(dev, "failed to init r5 core err %d\n", ret); | |
1116 | cluster->core_count = 0; | |
1117 | cluster->r5_cores = NULL; | |
1118 | ||
1119 | /* | |
1120 | * at this point rproc resources for each core are allocated. | |
1121 | * adjust index to free resources in reverse order | |
1122 | */ | |
1123 | i = core_count - 1; | |
1124 | goto release_r5_cores; | |
1125 | } | |
1126 | ||
1127 | kfree(child_devs); | |
1128 | return 0; | |
1129 | ||
1130 | release_r5_cores: | |
1131 | while (i >= 0) { | |
1132 | put_device(child_devs[i]); | |
1133 | if (r5_cores[i]) { | |
5dfb28c2 | 1134 | zynqmp_r5_free_mbox(r5_cores[i]->ipi); |
6b291e80 TS |
1135 | of_reserved_mem_device_release(r5_cores[i]->dev); |
1136 | rproc_del(r5_cores[i]->rproc); | |
1137 | rproc_free(r5_cores[i]->rproc); | |
1138 | } | |
1139 | i--; | |
1140 | } | |
1141 | kfree(r5_cores); | |
1142 | kfree(child_devs); | |
1143 | return ret; | |
1144 | } | |
1145 | ||
1146 | static void zynqmp_r5_cluster_exit(void *data) | |
1147 | { | |
86660713 | 1148 | struct platform_device *pdev = data; |
6b291e80 TS |
1149 | struct zynqmp_r5_cluster *cluster; |
1150 | struct zynqmp_r5_core *r5_core; | |
1151 | int i; | |
1152 | ||
86660713 | 1153 | cluster = platform_get_drvdata(pdev); |
6b291e80 TS |
1154 | if (!cluster) |
1155 | return; | |
1156 | ||
1157 | for (i = 0; i < cluster->core_count; i++) { | |
1158 | r5_core = cluster->r5_cores[i]; | |
5dfb28c2 | 1159 | zynqmp_r5_free_mbox(r5_core->ipi); |
6b291e80 TS |
1160 | of_reserved_mem_device_release(r5_core->dev); |
1161 | put_device(r5_core->dev); | |
1162 | rproc_del(r5_core->rproc); | |
1163 | rproc_free(r5_core->rproc); | |
1164 | } | |
1165 | ||
1166 | kfree(cluster->r5_cores); | |
1167 | kfree(cluster); | |
1168 | platform_set_drvdata(pdev, NULL); | |
1169 | } | |
1170 | ||
1171 | /* | |
1172 | * zynqmp_r5_remoteproc_probe() | |
1173 | * parse device-tree, initialize hardware and allocate required resources | |
1174 | * and remoteproc ops | |
1175 | * | |
1176 | * @pdev: domain platform device for R5 cluster | |
1177 | * | |
1178 | * Return: 0 for success and < 0 for failure. | |
1179 | */ | |
1180 | static int zynqmp_r5_remoteproc_probe(struct platform_device *pdev) | |
1181 | { | |
1182 | struct zynqmp_r5_cluster *cluster; | |
1183 | struct device *dev = &pdev->dev; | |
1184 | int ret; | |
1185 | ||
1186 | cluster = kzalloc(sizeof(*cluster), GFP_KERNEL); | |
1187 | if (!cluster) | |
1188 | return -ENOMEM; | |
1189 | ||
1190 | cluster->dev = dev; | |
1191 | ||
1192 | ret = devm_of_platform_populate(dev); | |
1193 | if (ret) { | |
1194 | dev_err_probe(dev, ret, "failed to populate platform dev\n"); | |
1195 | kfree(cluster); | |
1196 | return ret; | |
1197 | } | |
1198 | ||
1199 | /* wire in so each core can be cleaned up at driver remove */ | |
1200 | platform_set_drvdata(pdev, cluster); | |
1201 | ||
1202 | ret = zynqmp_r5_cluster_init(cluster); | |
1203 | if (ret) { | |
1204 | kfree(cluster); | |
1205 | platform_set_drvdata(pdev, NULL); | |
1206 | dev_err_probe(dev, ret, "Invalid r5f subsystem device tree\n"); | |
1207 | return ret; | |
1208 | } | |
1209 | ||
1210 | ret = devm_add_action_or_reset(dev, zynqmp_r5_cluster_exit, pdev); | |
1211 | if (ret) | |
1212 | return ret; | |
1213 | ||
1214 | return 0; | |
1215 | } | |
1216 | ||
1217 | /* Match table for OF platform binding */ | |
1218 | static const struct of_device_id zynqmp_r5_remoteproc_match[] = { | |
1219 | { .compatible = "xlnx,zynqmp-r5fss", }, | |
1220 | { /* end of list */ }, | |
1221 | }; | |
1222 | MODULE_DEVICE_TABLE(of, zynqmp_r5_remoteproc_match); | |
1223 | ||
1224 | static struct platform_driver zynqmp_r5_remoteproc_driver = { | |
1225 | .probe = zynqmp_r5_remoteproc_probe, | |
1226 | .driver = { | |
1227 | .name = "zynqmp_r5_remoteproc", | |
1228 | .of_match_table = zynqmp_r5_remoteproc_match, | |
1229 | }, | |
1230 | }; | |
1231 | module_platform_driver(zynqmp_r5_remoteproc_driver); | |
1232 | ||
1233 | MODULE_DESCRIPTION("Xilinx R5F remote processor driver"); | |
1234 | MODULE_AUTHOR("Xilinx Inc."); | |
1235 | MODULE_LICENSE("GPL"); |