diff options
Diffstat (limited to 'drivers/remoteproc/xlnx_r5_remoteproc.c')
-rw-r--r-- | drivers/remoteproc/xlnx_r5_remoteproc.c | 324 |
1 files changed, 245 insertions, 79 deletions
diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 2db57d394155..feca6de68da2 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -8,16 +8,23 @@ #include <linux/dma-mapping.h> #include <linux/firmware/xlnx-zynqmp.h> #include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/mailbox/zynqmp-ipi-message.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> -#include <linux/slab.h> #include "remoteproc_internal.h" +/* IPI buffer MAX length */ +#define IPI_BUF_LEN_MAX 32U + +/* RX mailbox client buffer max length */ +#define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \ + sizeof(struct zynqmp_ipi_message)) /* * settings for RPU cluster mode which * reflects possible values of xlnx,cluster-mode dt-property @@ -43,6 +50,27 @@ struct mem_bank_data { char *bank_name; }; +/** + * struct mbox_info + * + * @rx_mc_buf: to copy data from mailbox rx channel + * @tx_mc_buf: to copy data to mailbox tx channel + * @r5_core: this mailbox's corresponding r5_core pointer + * @mbox_work: schedule work after receiving data from mailbox + * @mbox_cl: mailbox client + * @tx_chan: mailbox tx channel + * @rx_chan: mailbox rx channel + */ +struct mbox_info { + unsigned char rx_mc_buf[MBOX_CLIENT_BUF_MAX]; + unsigned char tx_mc_buf[MBOX_CLIENT_BUF_MAX]; + struct zynqmp_r5_core *r5_core; + struct work_struct mbox_work; + struct mbox_client mbox_cl; + struct mbox_chan *tx_chan; + struct mbox_chan *rx_chan; +}; + /* * Hardcoded TCM bank values. This will be removed once TCM bindings are * accepted for system-dt specifications and upstreamed in linux kernel @@ -61,20 +89,18 @@ static const struct mem_bank_data zynqmp_tcm_banks[] = { * @np: device node of RPU instance * @tcm_bank_count: number TCM banks accessible to this RPU * @tcm_banks: array of each TCM bank data - * @rmem_count: Number of reserved mem regions - * @rmem: reserved memory region nodes from device tree * @rproc: rproc handle * @pm_domain_id: RPU CPU power domain id + * @ipi: pointer to mailbox information */ struct zynqmp_r5_core { struct device *dev; struct device_node *np; int tcm_bank_count; struct mem_bank_data **tcm_banks; - int rmem_count; - struct reserved_mem **rmem; struct rproc *rproc; u32 pm_domain_id; + struct mbox_info *ipi; }; /** @@ -92,6 +118,178 @@ struct zynqmp_r5_cluster { struct zynqmp_r5_core **r5_cores; }; +/** + * event_notified_idr_cb() - callback for vq_interrupt per notifyid + * @id: rproc->notify id + * @ptr: pointer to idr private data + * @data: data passed to idr_for_each callback + * + * Pass notification to remoteproc virtio + * + * Return: 0. having return is to satisfy the idr_for_each() function + * pointer input argument requirement. + **/ +static int event_notified_idr_cb(int id, void *ptr, void *data) +{ + struct rproc *rproc = data; + + if (rproc_vq_interrupt(rproc, id) == IRQ_NONE) + dev_dbg(&rproc->dev, "data not found for vqid=%d\n", id); + + return 0; +} + +/** + * handle_event_notified() - remoteproc notification work function + * @work: pointer to the work structure + * + * It checks each registered remoteproc notify IDs. + */ +static void handle_event_notified(struct work_struct *work) +{ + struct mbox_info *ipi; + struct rproc *rproc; + + ipi = container_of(work, struct mbox_info, mbox_work); + rproc = ipi->r5_core->rproc; + + /* + * We only use IPI for interrupt. The RPU firmware side may or may + * not write the notifyid when it trigger IPI. + * And thus, we scan through all the registered notifyids and + * find which one is valid to get the message. + * Even if message from firmware is NULL, we attempt to get vqid + */ + idr_for_each(&rproc->notifyids, event_notified_idr_cb, rproc); +} + +/** + * zynqmp_r5_mb_rx_cb() - receive channel mailbox callback + * @cl: mailbox client + * @msg: message pointer + * + * Receive data from ipi buffer, ack interrupt and then + * it will schedule the R5 notification work. + */ +static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg) +{ + struct zynqmp_ipi_message *ipi_msg, *buf_msg; + struct mbox_info *ipi; + size_t len; + + ipi = container_of(cl, struct mbox_info, mbox_cl); + + /* copy data from ipi buffer to r5_core */ + ipi_msg = (struct zynqmp_ipi_message *)msg; + buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf; + len = ipi_msg->len; + if (len > IPI_BUF_LEN_MAX) { + dev_warn(cl->dev, "msg size exceeded than %d\n", + IPI_BUF_LEN_MAX); + len = IPI_BUF_LEN_MAX; + } + buf_msg->len = len; + memcpy(buf_msg->data, ipi_msg->data, len); + + /* received and processed interrupt ack */ + if (mbox_send_message(ipi->rx_chan, NULL) < 0) + dev_err(cl->dev, "ack failed to mbox rx_chan\n"); + + schedule_work(&ipi->mbox_work); +} + +/** + * zynqmp_r5_setup_mbox() - Setup mailboxes related properties + * this is used for each individual R5 core + * + * @cdev: child node device + * + * Function to setup mailboxes related properties + * return : NULL if failed else pointer to mbox_info + */ +static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) +{ + struct mbox_client *mbox_cl; + struct mbox_info *ipi; + + ipi = kzalloc(sizeof(*ipi), GFP_KERNEL); + if (!ipi) + return NULL; + + mbox_cl = &ipi->mbox_cl; + mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb; + mbox_cl->tx_block = false; + mbox_cl->knows_txdone = false; + mbox_cl->tx_done = NULL; + mbox_cl->dev = cdev; + + /* Request TX and RX channels */ + ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx"); + if (IS_ERR(ipi->tx_chan)) { + ipi->tx_chan = NULL; + kfree(ipi); + dev_warn(cdev, "mbox tx channel request failed\n"); + return NULL; + } + + ipi->rx_chan = mbox_request_channel_byname(mbox_cl, "rx"); + if (IS_ERR(ipi->rx_chan)) { + mbox_free_channel(ipi->tx_chan); + ipi->rx_chan = NULL; + ipi->tx_chan = NULL; + kfree(ipi); + dev_warn(cdev, "mbox rx channel request failed\n"); + return NULL; + } + + INIT_WORK(&ipi->mbox_work, handle_event_notified); + + return ipi; +} + +static void zynqmp_r5_free_mbox(struct mbox_info *ipi) +{ + if (!ipi) + return; + + if (ipi->tx_chan) { + mbox_free_channel(ipi->tx_chan); + ipi->tx_chan = NULL; + } + + if (ipi->rx_chan) { + mbox_free_channel(ipi->rx_chan); + ipi->rx_chan = NULL; + } + + kfree(ipi); +} + +/* + * zynqmp_r5_core_kick() - kick a firmware if mbox is provided + * @rproc: r5 core's corresponding rproc structure + * @vqid: virtqueue ID + */ +static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) +{ + struct zynqmp_r5_core *r5_core = rproc->priv; + struct device *dev = r5_core->dev; + struct zynqmp_ipi_message *mb_msg; + struct mbox_info *ipi; + int ret; + + ipi = r5_core->ipi; + if (!ipi) + return; + + mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf; + memcpy(mb_msg->data, &vqid, sizeof(vqid)); + mb_msg->len = sizeof(vqid); + ret = mbox_send_message(ipi->tx_chan, mb_msg); + if (ret < 0) + dev_warn(dev, "failed to send message\n"); +} + /* * zynqmp_r5_set_mode() * @@ -239,21 +437,29 @@ static int add_mem_regions_carveout(struct rproc *rproc) { struct rproc_mem_entry *rproc_mem; struct zynqmp_r5_core *r5_core; + struct of_phandle_iterator it; struct reserved_mem *rmem; - int i, num_mem_regions; + int i = 0; - r5_core = (struct zynqmp_r5_core *)rproc->priv; - num_mem_regions = r5_core->rmem_count; + r5_core = rproc->priv; - for (i = 0; i < num_mem_regions; i++) { - rmem = r5_core->rmem[i]; + /* Register associated reserved memory regions */ + of_phandle_iterator_init(&it, r5_core->np, "memory-region", NULL, 0); - if (!strncmp(rmem->name, "vdev0buffer", strlen("vdev0buffer"))) { + while (of_phandle_iterator_next(&it) == 0) { + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { + of_node_put(it.node); + dev_err(&rproc->dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + if (!strcmp(it.node->name, "vdev0buffer")) { /* Init reserved memory for vdev buffer */ rproc_mem = rproc_of_resm_mem_entry_init(&rproc->dev, i, rmem->size, rmem->base, - rmem->name); + it.node->name); } else { /* Register associated reserved memory regions */ rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, @@ -261,16 +467,19 @@ static int add_mem_regions_carveout(struct rproc *rproc) rmem->size, rmem->base, zynqmp_r5_mem_region_map, zynqmp_r5_mem_region_unmap, - rmem->name); + it.node->name); } - if (!rproc_mem) + if (!rproc_mem) { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, rproc_mem); dev_dbg(&rproc->dev, "reserved mem carveout %s addr=%llx, size=0x%llx", - rmem->name, rmem->base, rmem->size); + it.node->name, rmem->base, rmem->size); + i++; } return 0; @@ -363,7 +572,7 @@ static int add_tcm_carveout_split_mode(struct rproc *rproc) size_t bank_size; char *bank_name; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; dev = r5_core->dev; num_banks = r5_core->tcm_bank_count; @@ -432,7 +641,7 @@ static int add_tcm_carveout_lockstep_mode(struct rproc *rproc) u32 pm_domain_id; char *bank_name; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; dev = r5_core->dev; /* Go through zynqmp banks for r5 node */ @@ -502,7 +711,7 @@ static int add_tcm_banks(struct rproc *rproc) struct zynqmp_r5_core *r5_core; struct device *dev; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; if (!r5_core) return -EINVAL; @@ -595,7 +804,7 @@ static int zynqmp_r5_rproc_unprepare(struct rproc *rproc) u32 pm_domain_id; int i; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; for (i = 0; i < r5_core->tcm_bank_count; i++) { pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; @@ -617,6 +826,7 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = { .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, + .kick = zynqmp_r5_rproc_kick, }; /** @@ -649,7 +859,7 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) } r5_rproc->auto_boot = false; - r5_core = (struct zynqmp_r5_core *)r5_rproc->priv; + r5_core = r5_rproc->priv; r5_core->dev = cdev; r5_core->np = dev_of_node(cdev); if (!r5_core->np) { @@ -726,59 +936,6 @@ static int zynqmp_r5_get_tcm_node(struct zynqmp_r5_cluster *cluster) return 0; } -/** - * zynqmp_r5_get_mem_region_node() - * parse memory-region property and get reserved mem regions - * - * @r5_core: pointer to zynqmp_r5_core type object - * - * Return: 0 for success and error code for failure. - */ -static int zynqmp_r5_get_mem_region_node(struct zynqmp_r5_core *r5_core) -{ - struct device_node *np, *rmem_np; - struct reserved_mem **rmem; - int res_mem_count, i; - struct device *dev; - - dev = r5_core->dev; - np = r5_core->np; - - res_mem_count = of_property_count_elems_of_size(np, "memory-region", - sizeof(phandle)); - if (res_mem_count <= 0) { - dev_warn(dev, "failed to get memory-region property %d\n", - res_mem_count); - return 0; - } - - rmem = devm_kcalloc(dev, res_mem_count, - sizeof(struct reserved_mem *), GFP_KERNEL); - if (!rmem) - return -ENOMEM; - - for (i = 0; i < res_mem_count; i++) { - rmem_np = of_parse_phandle(np, "memory-region", i); - if (!rmem_np) - goto release_rmem; - - rmem[i] = of_reserved_mem_lookup(rmem_np); - if (!rmem[i]) { - of_node_put(rmem_np); - goto release_rmem; - } - - of_node_put(rmem_np); - } - - r5_core->rmem_count = res_mem_count; - r5_core->rmem = rmem; - return 0; - -release_rmem: - return -EINVAL; -} - /* * zynqmp_r5_core_init() * Create and initialize zynqmp_r5_core type object @@ -806,10 +963,6 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; - ret = zynqmp_r5_get_mem_region_node(r5_core); - if (ret) - dev_warn(dev, "memory-region prop failed %d\n", ret); - /* Initialize r5 cores with power-domains parsed from dts */ ret = of_property_read_u32_index(r5_core->np, "power-domains", 1, &r5_core->pm_domain_id); @@ -849,6 +1002,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) struct device_node *child; enum rpu_tcm_comb tcm_mode; int core_count, ret, i; + struct mbox_info *ipi; ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode); @@ -929,6 +1083,16 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) } /* + * If mailbox nodes are disabled using "status" property then + * setting up mailbox channels will fail. + */ + ipi = zynqmp_r5_setup_mbox(&child_pdev->dev); + if (ipi) { + r5_cores[i]->ipi = ipi; + ipi->r5_core = r5_cores[i]; + } + + /* * If two child nodes are available in dts in lockstep mode, * then ignore second child node. */ @@ -965,6 +1129,7 @@ release_r5_cores: while (i >= 0) { put_device(child_devs[i]); if (r5_cores[i]) { + zynqmp_r5_free_mbox(r5_cores[i]->ipi); of_reserved_mem_device_release(r5_cores[i]->dev); rproc_del(r5_cores[i]->rproc); rproc_free(r5_cores[i]->rproc); @@ -978,17 +1143,18 @@ release_r5_cores: static void zynqmp_r5_cluster_exit(void *data) { - struct platform_device *pdev = (struct platform_device *)data; + struct platform_device *pdev = data; struct zynqmp_r5_cluster *cluster; struct zynqmp_r5_core *r5_core; int i; - cluster = (struct zynqmp_r5_cluster *)platform_get_drvdata(pdev); + cluster = platform_get_drvdata(pdev); if (!cluster) return; for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; + zynqmp_r5_free_mbox(r5_core->ipi); of_reserved_mem_device_release(r5_core->dev); put_device(r5_core->dev); rproc_del(r5_core->rproc); |