|
52 | 52 | #define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */
|
53 | 53 | #define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */
|
54 | 54 |
|
| 55 | +/* IPI Message Buffer Information */ |
| 56 | +#define RESP_OFFSET 0x20U |
| 57 | +#define DEST_OFFSET 0x40U |
| 58 | +#define IPI_BUF_SIZE 0x20U |
| 59 | +#define DST_BIT_POS 9U |
| 60 | +#define SRC_BITMASK GENMASK(11, 8) |
| 61 | + |
55 | 62 | /**
|
56 | 63 | * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel
|
57 | 64 | * @is_opened: indicate if the IPI channel is opened
|
@@ -169,9 +176,11 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data)
|
169 | 176 | if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) {
|
170 | 177 | if (mchan->is_opened) {
|
171 | 178 | msg = mchan->rx_buf;
|
172 |
| - msg->len = mchan->req_buf_size; |
173 |
| - memcpy_fromio(msg->data, mchan->req_buf, |
174 |
| - msg->len); |
| 179 | + if (msg) { |
| 180 | + msg->len = mchan->req_buf_size; |
| 181 | + memcpy_fromio(msg->data, mchan->req_buf, |
| 182 | + msg->len); |
| 183 | + } |
175 | 184 | mbox_chan_received_data(chan, (void *)msg);
|
176 | 185 | status = IRQ_HANDLED;
|
177 | 186 | }
|
@@ -281,26 +290,26 @@ static int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data)
|
281 | 290 |
|
282 | 291 | if (mchan->chan_type == IPI_MB_CHNL_TX) {
|
283 | 292 | /* Send request message */
|
284 |
| - if (msg && msg->len > mchan->req_buf_size) { |
| 293 | + if (msg && msg->len > mchan->req_buf_size && mchan->req_buf) { |
285 | 294 | dev_err(dev, "channel %d message length %u > max %lu\n",
|
286 | 295 | mchan->chan_type, (unsigned int)msg->len,
|
287 | 296 | mchan->req_buf_size);
|
288 | 297 | return -EINVAL;
|
289 | 298 | }
|
290 |
| - if (msg && msg->len) |
| 299 | + if (msg && msg->len && mchan->req_buf) |
291 | 300 | memcpy_toio(mchan->req_buf, msg->data, msg->len);
|
292 | 301 | /* Kick IPI mailbox to send message */
|
293 | 302 | arg0 = SMC_IPI_MAILBOX_NOTIFY;
|
294 | 303 | zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res);
|
295 | 304 | } else {
|
296 | 305 | /* Send response message */
|
297 |
| - if (msg && msg->len > mchan->resp_buf_size) { |
| 306 | + if (msg && msg->len > mchan->resp_buf_size && mchan->resp_buf) { |
298 | 307 | dev_err(dev, "channel %d message length %u > max %lu\n",
|
299 | 308 | mchan->chan_type, (unsigned int)msg->len,
|
300 | 309 | mchan->resp_buf_size);
|
301 | 310 | return -EINVAL;
|
302 | 311 | }
|
303 |
| - if (msg && msg->len) |
| 312 | + if (msg && msg->len && mchan->resp_buf) |
304 | 313 | memcpy_toio(mchan->resp_buf, msg->data, msg->len);
|
305 | 314 | arg0 = SMC_IPI_MAILBOX_ACK;
|
306 | 315 | zynqmp_ipi_fw_call(ipi_mbox, arg0, IPI_SMC_ACK_EIRQ_MASK,
|
@@ -636,6 +645,109 @@ static int zynqmp_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox,
|
636 | 645 | return 0;
|
637 | 646 | }
|
638 | 647 |
|
| 648 | +/** |
| 649 | + * versal_ipi_setup - Set up IPIs to support mixed usage of |
| 650 | + * Buffered and Bufferless IPIs. |
| 651 | + * |
| 652 | + * @ipi_mbox: pointer to IPI mailbox private data structure |
| 653 | + * @node: IPI mailbox device node |
| 654 | + * |
| 655 | + * Return: 0 for success, negative value for failure |
| 656 | + */ |
| 657 | +static int versal_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox, |
| 658 | + struct device_node *node) |
| 659 | +{ |
| 660 | + struct zynqmp_ipi_mchan *tx_mchan, *rx_mchan; |
| 661 | + struct resource host_res, remote_res; |
| 662 | + struct device_node *parent_node; |
| 663 | + int host_idx, remote_idx; |
| 664 | + struct device *mdev; |
| 665 | + |
| 666 | + tx_mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; |
| 667 | + rx_mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; |
| 668 | + parent_node = of_get_parent(node); |
| 669 | + mdev = &ipi_mbox->dev; |
| 670 | + |
| 671 | + host_idx = zynqmp_ipi_mbox_get_buf_res(parent_node, "msg", &host_res); |
| 672 | + remote_idx = zynqmp_ipi_mbox_get_buf_res(node, "msg", &remote_res); |
| 673 | + |
| 674 | + /* |
| 675 | + * Only set up buffers if both sides claim to have msg buffers. |
| 676 | + * This is because each buffered IPI's corresponding msg buffers |
| 677 | + * are reserved for use by other buffered IPI's. |
| 678 | + */ |
| 679 | + if (!host_idx && !remote_idx) { |
| 680 | + u32 host_src, host_dst, remote_src, remote_dst; |
| 681 | + u32 buff_sz; |
| 682 | + |
| 683 | + buff_sz = resource_size(&host_res); |
| 684 | + |
| 685 | + host_src = host_res.start & SRC_BITMASK; |
| 686 | + remote_src = remote_res.start & SRC_BITMASK; |
| 687 | + |
| 688 | + host_dst = (host_src >> DST_BIT_POS) * DEST_OFFSET; |
| 689 | + remote_dst = (remote_src >> DST_BIT_POS) * DEST_OFFSET; |
| 690 | + |
| 691 | + /* Validate that IPI IDs is within IPI Message buffer space. */ |
| 692 | + if (host_dst >= buff_sz || remote_dst >= buff_sz) { |
| 693 | + dev_err(mdev, |
| 694 | + "Invalid IPI Message buffer values: %x %x\n", |
| 695 | + host_dst, remote_dst); |
| 696 | + return -EINVAL; |
| 697 | + } |
| 698 | + |
| 699 | + tx_mchan->req_buf = devm_ioremap(mdev, |
| 700 | + host_res.start | remote_dst, |
| 701 | + IPI_BUF_SIZE); |
| 702 | + if (!tx_mchan->req_buf) { |
| 703 | + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); |
| 704 | + return -ENOMEM; |
| 705 | + } |
| 706 | + |
| 707 | + tx_mchan->resp_buf = devm_ioremap(mdev, |
| 708 | + (remote_res.start | host_dst) + |
| 709 | + RESP_OFFSET, IPI_BUF_SIZE); |
| 710 | + if (!tx_mchan->resp_buf) { |
| 711 | + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); |
| 712 | + return -ENOMEM; |
| 713 | + } |
| 714 | + |
| 715 | + rx_mchan->req_buf = devm_ioremap(mdev, |
| 716 | + remote_res.start | host_dst, |
| 717 | + IPI_BUF_SIZE); |
| 718 | + if (!rx_mchan->req_buf) { |
| 719 | + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); |
| 720 | + return -ENOMEM; |
| 721 | + } |
| 722 | + |
| 723 | + rx_mchan->resp_buf = devm_ioremap(mdev, |
| 724 | + (host_res.start | remote_dst) + |
| 725 | + RESP_OFFSET, IPI_BUF_SIZE); |
| 726 | + if (!rx_mchan->resp_buf) { |
| 727 | + dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); |
| 728 | + return -ENOMEM; |
| 729 | + } |
| 730 | + |
| 731 | + tx_mchan->resp_buf_size = IPI_BUF_SIZE; |
| 732 | + tx_mchan->req_buf_size = IPI_BUF_SIZE; |
| 733 | + tx_mchan->rx_buf = devm_kzalloc(mdev, IPI_BUF_SIZE + |
| 734 | + sizeof(struct zynqmp_ipi_message), |
| 735 | + GFP_KERNEL); |
| 736 | + if (!tx_mchan->rx_buf) |
| 737 | + return -ENOMEM; |
| 738 | + |
| 739 | + rx_mchan->resp_buf_size = IPI_BUF_SIZE; |
| 740 | + rx_mchan->req_buf_size = IPI_BUF_SIZE; |
| 741 | + rx_mchan->rx_buf = devm_kzalloc(mdev, IPI_BUF_SIZE + |
| 742 | + sizeof(struct zynqmp_ipi_message), |
| 743 | + GFP_KERNEL); |
| 744 | + if (!rx_mchan->rx_buf) |
| 745 | + return -ENOMEM; |
| 746 | + } |
| 747 | + |
| 748 | + return 0; |
| 749 | +} |
| 750 | + |
639 | 751 | /**
|
640 | 752 | * zynqmp_ipi_free_mboxes - Free IPI mailboxes devices
|
641 | 753 | *
|
@@ -743,6 +855,9 @@ static const struct of_device_id zynqmp_ipi_of_match[] = {
|
743 | 855 | { .compatible = "xlnx,zynqmp-ipi-mailbox",
|
744 | 856 | .data = &zynqmp_ipi_setup,
|
745 | 857 | },
|
| 858 | + { .compatible = "xlnx,versal-ipi-mailbox", |
| 859 | + .data = &versal_ipi_setup, |
| 860 | + }, |
746 | 861 | {},
|
747 | 862 | };
|
748 | 863 | MODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match);
|
|
0 commit comments