Lines Matching +full:zynqmp +full:- +full:ipi +full:- +full:mailbox
1 // SPDX-License-Identifier: GPL-2.0
3 * ZynqMP R5 Remote Processor driver
7 #include <dt-bindings/power/xlnx-zynqmp-power.h>
8 #include <linux/dma-mapping.h>
9 #include <linux/firmware/xlnx-zynqmp.h>
12 #include <linux/mailbox/zynqmp-ipi-message.h>
22 /* IPI buffer MAX length */
25 /* RX mailbox client buffer max length */
30 * reflects possible values of xlnx,cluster-mode dt-property
34 LOCKSTEP_MODE = 1, /* cores execute same code in lockstep,clk-for-clk */
39 * struct mem_bank_data - Memory Bank description
43 * @pm_domain_id: Power-domains id of memory bank for firmware to turn on/off
56 * @rx_mc_buf: to copy data from mailbox rx channel
57 * @tx_mc_buf: to copy data to mailbox tx channel
58 * @r5_core: this mailbox's corresponding r5_core pointer
59 * @mbox_work: schedule work after receiving data from mailbox
60 * @mbox_cl: mailbox client
61 * @tx_chan: mailbox tx channel
62 * @rx_chan: mailbox rx channel
76 * accepted for system-dt specifications and upstreamed in linux kernel
94 * @ipi: pointer to mailbox information
103 struct mbox_info *ipi; member
122 * event_notified_idr_cb() - callback for vq_interrupt per notifyid
123 * @id: rproc->notify id
137 dev_dbg(&rproc->dev, "data not found for vqid=%d\n", id); in event_notified_idr_cb()
143 * handle_event_notified() - remoteproc notification work function
150 struct mbox_info *ipi; in handle_event_notified() local
153 ipi = container_of(work, struct mbox_info, mbox_work); in handle_event_notified()
154 rproc = ipi->r5_core->rproc; in handle_event_notified()
157 * We only use IPI for interrupt. The RPU firmware side may or may in handle_event_notified()
158 * not write the notifyid when it trigger IPI. in handle_event_notified()
163 idr_for_each(&rproc->notifyids, event_notified_idr_cb, rproc); in handle_event_notified()
167 * zynqmp_r5_mb_rx_cb() - receive channel mailbox callback
168 * @cl: mailbox client
171 * Receive data from ipi buffer, ack interrupt and then
177 struct mbox_info *ipi; in zynqmp_r5_mb_rx_cb() local
180 ipi = container_of(cl, struct mbox_info, mbox_cl); in zynqmp_r5_mb_rx_cb()
182 /* copy data from ipi buffer to r5_core */ in zynqmp_r5_mb_rx_cb()
184 buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf; in zynqmp_r5_mb_rx_cb()
185 len = ipi_msg->len; in zynqmp_r5_mb_rx_cb()
187 dev_warn(cl->dev, "msg size exceeded than %d\n", in zynqmp_r5_mb_rx_cb()
191 buf_msg->len = len; in zynqmp_r5_mb_rx_cb()
192 memcpy(buf_msg->data, ipi_msg->data, len); in zynqmp_r5_mb_rx_cb()
195 if (mbox_send_message(ipi->rx_chan, NULL) < 0) in zynqmp_r5_mb_rx_cb()
196 dev_err(cl->dev, "ack failed to mbox rx_chan\n"); in zynqmp_r5_mb_rx_cb()
198 schedule_work(&ipi->mbox_work); in zynqmp_r5_mb_rx_cb()
202 * zynqmp_r5_setup_mbox() - Setup mailboxes related properties
213 struct mbox_info *ipi; in zynqmp_r5_setup_mbox() local
215 ipi = kzalloc(sizeof(*ipi), GFP_KERNEL); in zynqmp_r5_setup_mbox()
216 if (!ipi) in zynqmp_r5_setup_mbox()
219 mbox_cl = &ipi->mbox_cl; in zynqmp_r5_setup_mbox()
220 mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb; in zynqmp_r5_setup_mbox()
221 mbox_cl->tx_block = false; in zynqmp_r5_setup_mbox()
222 mbox_cl->knows_txdone = false; in zynqmp_r5_setup_mbox()
223 mbox_cl->tx_done = NULL; in zynqmp_r5_setup_mbox()
224 mbox_cl->dev = cdev; in zynqmp_r5_setup_mbox()
227 ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx"); in zynqmp_r5_setup_mbox()
228 if (IS_ERR(ipi->tx_chan)) { in zynqmp_r5_setup_mbox()
229 ipi->tx_chan = NULL; in zynqmp_r5_setup_mbox()
230 kfree(ipi); in zynqmp_r5_setup_mbox()
235 ipi->rx_chan = mbox_request_channel_byname(mbox_cl, "rx"); in zynqmp_r5_setup_mbox()
236 if (IS_ERR(ipi->rx_chan)) { in zynqmp_r5_setup_mbox()
237 mbox_free_channel(ipi->tx_chan); in zynqmp_r5_setup_mbox()
238 ipi->rx_chan = NULL; in zynqmp_r5_setup_mbox()
239 ipi->tx_chan = NULL; in zynqmp_r5_setup_mbox()
240 kfree(ipi); in zynqmp_r5_setup_mbox()
245 INIT_WORK(&ipi->mbox_work, handle_event_notified); in zynqmp_r5_setup_mbox()
247 return ipi; in zynqmp_r5_setup_mbox()
250 static void zynqmp_r5_free_mbox(struct mbox_info *ipi) in zynqmp_r5_free_mbox() argument
252 if (!ipi) in zynqmp_r5_free_mbox()
255 if (ipi->tx_chan) { in zynqmp_r5_free_mbox()
256 mbox_free_channel(ipi->tx_chan); in zynqmp_r5_free_mbox()
257 ipi->tx_chan = NULL; in zynqmp_r5_free_mbox()
260 if (ipi->rx_chan) { in zynqmp_r5_free_mbox()
261 mbox_free_channel(ipi->rx_chan); in zynqmp_r5_free_mbox()
262 ipi->rx_chan = NULL; in zynqmp_r5_free_mbox()
265 kfree(ipi); in zynqmp_r5_free_mbox()
269 * zynqmp_r5_core_kick() - kick a firmware if mbox is provided
275 struct zynqmp_r5_core *r5_core = rproc->priv; in zynqmp_r5_rproc_kick()
276 struct device *dev = r5_core->dev; in zynqmp_r5_rproc_kick()
278 struct mbox_info *ipi; in zynqmp_r5_rproc_kick() local
281 ipi = r5_core->ipi; in zynqmp_r5_rproc_kick()
282 if (!ipi) in zynqmp_r5_rproc_kick()
285 mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf; in zynqmp_r5_rproc_kick()
286 memcpy(mb_msg->data, &vqid, sizeof(vqid)); in zynqmp_r5_rproc_kick()
287 mb_msg->len = sizeof(vqid); in zynqmp_r5_rproc_kick()
288 ret = mbox_send_message(ipi->tx_chan, mb_msg); in zynqmp_r5_rproc_kick()
310 ret = zynqmp_pm_set_rpu_mode(r5_core->pm_domain_id, fw_reg_val); in zynqmp_r5_set_mode()
312 dev_err(r5_core->dev, "failed to set RPU mode\n"); in zynqmp_r5_set_mode()
316 ret = zynqmp_pm_set_tcm_config(r5_core->pm_domain_id, tcm_mode); in zynqmp_r5_set_mode()
318 dev_err(r5_core->dev, "failed to configure TCM\n"); in zynqmp_r5_set_mode()
329 * return 0 on success, otherwise non-zero value on failure
333 struct zynqmp_r5_core *r5_core = rproc->priv; in zynqmp_r5_rproc_start()
338 * The exception vector pointers (EVP) refer to the base-address of in zynqmp_r5_rproc_start()
339 * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector in zynqmp_r5_rproc_start()
340 * starts at the base-address and subsequent vectors are on 4-byte in zynqmp_r5_rproc_start()
344 * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory) in zynqmp_r5_rproc_start()
350 * and jitter. Also, if the OCM is secured and the Cortex-R5F processor in zynqmp_r5_rproc_start()
351 * is non-secured, then the Cortex-R5F processor cannot access the in zynqmp_r5_rproc_start()
354 bootmem = (rproc->bootaddr >= 0xFFFC0000) ? in zynqmp_r5_rproc_start()
357 dev_dbg(r5_core->dev, "RPU boot addr 0x%llx from %s.", rproc->bootaddr, in zynqmp_r5_rproc_start()
360 ret = zynqmp_pm_request_wake(r5_core->pm_domain_id, 1, in zynqmp_r5_rproc_start()
363 dev_err(r5_core->dev, in zynqmp_r5_rproc_start()
364 "failed to start RPU = 0x%x\n", r5_core->pm_domain_id); in zynqmp_r5_rproc_start()
374 * return 0 on success, otherwise non-zero value on failure
378 struct zynqmp_r5_core *r5_core = rproc->priv; in zynqmp_r5_rproc_stop()
381 ret = zynqmp_pm_force_pwrdwn(r5_core->pm_domain_id, in zynqmp_r5_rproc_stop()
384 dev_err(r5_core->dev, "failed to stop remoteproc RPU %d\n", ret); in zynqmp_r5_rproc_stop()
392 * @mem: mem descriptor to map reserved memory-regions
394 * Callback to map va for memory-region's carveout.
396 * return 0 on success, otherwise non-zero value on failure
403 va = ioremap_wc(mem->dma, mem->len); in zynqmp_r5_mem_region_map()
405 return -ENOMEM; in zynqmp_r5_mem_region_map()
407 mem->va = (void *)va; in zynqmp_r5_mem_region_map()
417 * Unmap memory-region carveout
424 iounmap((void __iomem *)mem->va); in zynqmp_r5_mem_region_unmap()
432 * Construct rproc mem carveouts from memory-region property nodes
434 * return 0 on success, otherwise non-zero value on failure
444 r5_core = rproc->priv; in add_mem_regions_carveout()
447 of_phandle_iterator_init(&it, r5_core->np, "memory-region", NULL, 0); in add_mem_regions_carveout()
453 dev_err(&rproc->dev, "unable to acquire memory-region\n"); in add_mem_regions_carveout()
454 return -EINVAL; in add_mem_regions_carveout()
457 if (!strcmp(it.node->name, "vdev0buffer")) { in add_mem_regions_carveout()
459 rproc_mem = rproc_of_resm_mem_entry_init(&rproc->dev, i, in add_mem_regions_carveout()
460 rmem->size, in add_mem_regions_carveout()
461 rmem->base, in add_mem_regions_carveout()
462 it.node->name); in add_mem_regions_carveout()
465 rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, in add_mem_regions_carveout()
466 (dma_addr_t)rmem->base, in add_mem_regions_carveout()
467 rmem->size, rmem->base, in add_mem_regions_carveout()
470 it.node->name); in add_mem_regions_carveout()
475 return -ENOMEM; in add_mem_regions_carveout()
480 dev_dbg(&rproc->dev, "reserved mem carveout %s addr=%llx, size=0x%llx", in add_mem_regions_carveout()
481 it.node->name, rmem->base, rmem->size); in add_mem_regions_carveout()
499 iounmap((void __iomem *)mem->va); in tcm_mem_unmap()
512 * return 0 on success, otherwise non-zero value on failure
519 va = ioremap_wc(mem->dma, mem->len); in tcm_mem_map()
521 return -ENOMEM; in tcm_mem_map()
524 mem->va = (void *)va; in tcm_mem_map()
527 memset_io(va, 0, mem->len); in tcm_mem_map()
536 mem->da &= 0x000fffff; in tcm_mem_map()
545 if (mem->da == 0x90000 || mem->da == 0xB0000) in tcm_mem_map()
546 mem->da -= 0x90000; in tcm_mem_map()
549 if (mem->da != 0x0 && mem->da != 0x20000) { in tcm_mem_map()
550 dev_err(&rproc->dev, "invalid TCM address: %x\n", mem->da); in tcm_mem_map()
551 return -EINVAL; in tcm_mem_map()
562 * return 0 on success, otherwise non-zero value on failure
575 r5_core = rproc->priv; in add_tcm_carveout_split_mode()
576 dev = r5_core->dev; in add_tcm_carveout_split_mode()
577 num_banks = r5_core->tcm_bank_count; in add_tcm_carveout_split_mode()
580 * Power-on Each 64KB TCM, in add_tcm_carveout_split_mode()
585 bank_addr = r5_core->tcm_banks[i]->addr; in add_tcm_carveout_split_mode()
586 bank_name = r5_core->tcm_banks[i]->bank_name; in add_tcm_carveout_split_mode()
587 bank_size = r5_core->tcm_banks[i]->size; in add_tcm_carveout_split_mode()
588 pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; in add_tcm_carveout_split_mode()
606 ret = -ENOMEM; in add_tcm_carveout_split_mode()
618 for (i--; i >= 0; i--) { in add_tcm_carveout_split_mode()
619 pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; in add_tcm_carveout_split_mode()
631 * return 0 on success, otherwise non-zero value on failure
644 r5_core = rproc->priv; in add_tcm_carveout_lockstep_mode()
645 dev = r5_core->dev; in add_tcm_carveout_lockstep_mode()
647 /* Go through zynqmp banks for r5 node */ in add_tcm_carveout_lockstep_mode()
648 num_banks = r5_core->tcm_bank_count; in add_tcm_carveout_lockstep_mode()
656 bank_addr = r5_core->tcm_banks[0]->addr; in add_tcm_carveout_lockstep_mode()
657 bank_name = r5_core->tcm_banks[0]->bank_name; in add_tcm_carveout_lockstep_mode()
660 bank_size += r5_core->tcm_banks[i]->size; in add_tcm_carveout_lockstep_mode()
661 pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; in add_tcm_carveout_lockstep_mode()
682 ret = -ENOMEM; in add_tcm_carveout_lockstep_mode()
693 for (i--; i >= 0; i--) { in add_tcm_carveout_lockstep_mode()
694 pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; in add_tcm_carveout_lockstep_mode()
706 * return 0 on success, otherwise non-zero value on failure
714 r5_core = rproc->priv; in add_tcm_banks()
716 return -EINVAL; in add_tcm_banks()
718 dev = r5_core->dev; in add_tcm_banks()
720 cluster = dev_get_drvdata(dev->parent); in add_tcm_banks()
722 dev_err(dev->parent, "Invalid driver data\n"); in add_tcm_banks()
723 return -EINVAL; in add_tcm_banks()
731 if (cluster->mode == SPLIT_MODE) in add_tcm_banks()
733 else if (cluster->mode == LOCKSTEP_MODE) in add_tcm_banks()
736 return -EINVAL; in add_tcm_banks()
746 * return 0 on success, otherwise non-zero value on failure
753 if (ret == -EINVAL) { in zynqmp_r5_parse_fw()
760 dev_info(&rproc->dev, "no resource table found.\n"); in zynqmp_r5_parse_fw()
780 dev_err(&rproc->dev, "failed to get TCM banks, err %d\n", ret); in zynqmp_r5_rproc_prepare()
786 dev_err(&rproc->dev, "failed to get reserve mem regions %d\n", ret); in zynqmp_r5_rproc_prepare()
795 * Turns off TCM banks using power-domain id
807 r5_core = rproc->priv; in zynqmp_r5_rproc_unprepare()
809 for (i = 0; i < r5_core->tcm_bank_count; i++) { in zynqmp_r5_rproc_unprepare()
810 pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; in zynqmp_r5_rproc_unprepare()
812 dev_warn(r5_core->dev, in zynqmp_r5_rproc_unprepare()
858 return ERR_PTR(-ENOMEM); in zynqmp_r5_add_rproc_core()
861 r5_rproc->auto_boot = false; in zynqmp_r5_add_rproc_core()
862 r5_core = r5_rproc->priv; in zynqmp_r5_add_rproc_core()
863 r5_core->dev = cdev; in zynqmp_r5_add_rproc_core()
864 r5_core->np = dev_of_node(cdev); in zynqmp_r5_add_rproc_core()
865 if (!r5_core->np) { in zynqmp_r5_add_rproc_core()
867 ret = -EINVAL; in zynqmp_r5_add_rproc_core()
878 r5_core->rproc = r5_rproc; in zynqmp_r5_add_rproc_core()
890 * This approach is used as TCM bindings for system-dt is being developed
898 struct device *dev = cluster->dev; in zynqmp_r5_get_tcm_node()
906 tcm_bank_count = tcm_bank_count / cluster->core_count; in zynqmp_r5_get_tcm_node()
914 for (i = 0; i < cluster->core_count; i++) { in zynqmp_r5_get_tcm_node()
915 r5_core = cluster->r5_cores[i]; in zynqmp_r5_get_tcm_node()
916 r5_core->tcm_banks = devm_kcalloc(dev, tcm_bank_count, in zynqmp_r5_get_tcm_node()
919 if (!r5_core->tcm_banks) in zynqmp_r5_get_tcm_node()
920 return -ENOMEM; in zynqmp_r5_get_tcm_node()
924 * Use pre-defined TCM reg values. in zynqmp_r5_get_tcm_node()
928 r5_core->tcm_banks[j] = in zynqmp_r5_get_tcm_node()
933 r5_core->tcm_bank_count = tcm_bank_count; in zynqmp_r5_get_tcm_node()
953 struct device *dev = cluster->dev; in zynqmp_r5_core_init()
963 for (i = 0; i < cluster->core_count; i++) { in zynqmp_r5_core_init()
964 r5_core = cluster->r5_cores[i]; in zynqmp_r5_core_init()
966 /* Initialize r5 cores with power-domains parsed from dts */ in zynqmp_r5_core_init()
967 ret = of_property_read_u32_index(r5_core->np, "power-domains", in zynqmp_r5_core_init()
968 1, &r5_core->pm_domain_id); in zynqmp_r5_core_init()
970 dev_err(dev, "failed to get power-domains property\n"); in zynqmp_r5_core_init()
977 cluster->mode, ret); in zynqmp_r5_core_init()
996 struct device *dev = cluster->dev; in zynqmp_r5_cluster_init()
1005 struct mbox_info *ipi; in zynqmp_r5_cluster_init() local
1007 ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode); in zynqmp_r5_cluster_init()
1010 * on success returns 0, if not defined then returns -EINVAL, in zynqmp_r5_cluster_init()
1014 if (ret != -EINVAL && ret != 0) { in zynqmp_r5_cluster_init()
1015 dev_err(dev, "Invalid xlnx,cluster-mode property\n"); in zynqmp_r5_cluster_init()
1031 return -EINVAL; in zynqmp_r5_cluster_init()
1045 return -EINVAL; in zynqmp_r5_cluster_init()
1048 return -EINVAL; in zynqmp_r5_cluster_init()
1056 return -ENOMEM; in zynqmp_r5_cluster_init()
1062 return -ENOMEM; in zynqmp_r5_cluster_init()
1070 ret = -ENODEV; in zynqmp_r5_cluster_init()
1074 child_devs[i] = &child_pdev->dev; in zynqmp_r5_cluster_init()
1077 r5_cores[i] = zynqmp_r5_add_rproc_core(&child_pdev->dev); in zynqmp_r5_cluster_init()
1086 * If mailbox nodes are disabled using "status" property then in zynqmp_r5_cluster_init()
1087 * setting up mailbox channels will fail. in zynqmp_r5_cluster_init()
1089 ipi = zynqmp_r5_setup_mbox(&child_pdev->dev); in zynqmp_r5_cluster_init()
1090 if (ipi) { in zynqmp_r5_cluster_init()
1091 r5_cores[i]->ipi = ipi; in zynqmp_r5_cluster_init()
1092 ipi->r5_core = r5_cores[i]; in zynqmp_r5_cluster_init()
1107 cluster->mode = cluster_mode; in zynqmp_r5_cluster_init()
1108 cluster->core_count = core_count; in zynqmp_r5_cluster_init()
1109 cluster->r5_cores = r5_cores; in zynqmp_r5_cluster_init()
1114 cluster->core_count = 0; in zynqmp_r5_cluster_init()
1115 cluster->r5_cores = NULL; in zynqmp_r5_cluster_init()
1121 i = core_count - 1; in zynqmp_r5_cluster_init()
1132 zynqmp_r5_free_mbox(r5_cores[i]->ipi); in zynqmp_r5_cluster_init()
1133 of_reserved_mem_device_release(r5_cores[i]->dev); in zynqmp_r5_cluster_init()
1134 rproc_del(r5_cores[i]->rproc); in zynqmp_r5_cluster_init()
1135 rproc_free(r5_cores[i]->rproc); in zynqmp_r5_cluster_init()
1137 i--; in zynqmp_r5_cluster_init()
1155 for (i = 0; i < cluster->core_count; i++) { in zynqmp_r5_cluster_exit()
1156 r5_core = cluster->r5_cores[i]; in zynqmp_r5_cluster_exit()
1157 zynqmp_r5_free_mbox(r5_core->ipi); in zynqmp_r5_cluster_exit()
1158 of_reserved_mem_device_release(r5_core->dev); in zynqmp_r5_cluster_exit()
1159 put_device(r5_core->dev); in zynqmp_r5_cluster_exit()
1160 rproc_del(r5_core->rproc); in zynqmp_r5_cluster_exit()
1161 rproc_free(r5_core->rproc); in zynqmp_r5_cluster_exit()
1164 kfree(cluster->r5_cores); in zynqmp_r5_cluster_exit()
1171 * parse device-tree, initialize hardware and allocate required resources
1181 struct device *dev = &pdev->dev; in zynqmp_r5_remoteproc_probe()
1186 return -ENOMEM; in zynqmp_r5_remoteproc_probe()
1188 cluster->dev = dev; in zynqmp_r5_remoteproc_probe()
1217 { .compatible = "xlnx,zynqmp-r5fss", },