18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Xilinx Inter Processor Interrupt(IPI) Mailbox Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Xilinx, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 158c2ecf20Sopenharmony_ci#include <linux/mailbox/zynqmp-ipi-message.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* IPI agent ID any */ 248c2ecf20Sopenharmony_ci#define IPI_ID_ANY 0xFFUL 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* indicate if ZynqMP IPI mailbox driver uses SMC calls or HVC calls */ 278c2ecf20Sopenharmony_ci#define USE_SMC 0 288c2ecf20Sopenharmony_ci#define USE_HVC 1 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Default IPI SMC function IDs */ 318c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_OPEN 0x82001000U 328c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_RELEASE 0x82001001U 338c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_STATUS_ENQUIRY 0x82001002U 348c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_NOTIFY 0x82001003U 358c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_ACK 0x82001004U 368c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_ENABLE_IRQ 0x82001005U 378c2ecf20Sopenharmony_ci#define SMC_IPI_MAILBOX_DISABLE_IRQ 0x82001006U 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* IPI SMC Macros */ 408c2ecf20Sopenharmony_ci#define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001UL /* Flag to indicate if 418c2ecf20Sopenharmony_ci * notification interrupt 428c2ecf20Sopenharmony_ci * to be disabled. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define IPI_SMC_ACK_EIRQ_MASK 0x00000001UL /* Flag to indicate if 458c2ecf20Sopenharmony_ci * notification interrupt 468c2ecf20Sopenharmony_ci * to be enabled. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* IPI mailbox status */ 508c2ecf20Sopenharmony_ci#define IPI_MB_STATUS_IDLE 0 518c2ecf20Sopenharmony_ci#define IPI_MB_STATUS_SEND_PENDING 1 528c2ecf20Sopenharmony_ci#define IPI_MB_STATUS_RECV_PENDING 2 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */ 558c2ecf20Sopenharmony_ci#define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel 598c2ecf20Sopenharmony_ci * @is_opened: indicate if the IPI channel is opened 608c2ecf20Sopenharmony_ci * @req_buf: local to remote request buffer start address 618c2ecf20Sopenharmony_ci * @resp_buf: local to remote response buffer start address 628c2ecf20Sopenharmony_ci * @req_buf_size: request buffer size 638c2ecf20Sopenharmony_ci * @resp_buf_size: response buffer size 648c2ecf20Sopenharmony_ci * @rx_buf: receive buffer to pass received message to client 658c2ecf20Sopenharmony_ci * @chan_type: channel type 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistruct zynqmp_ipi_mchan { 688c2ecf20Sopenharmony_ci int is_opened; 698c2ecf20Sopenharmony_ci void __iomem *req_buf; 708c2ecf20Sopenharmony_ci void __iomem *resp_buf; 718c2ecf20Sopenharmony_ci void *rx_buf; 728c2ecf20Sopenharmony_ci size_t req_buf_size; 738c2ecf20Sopenharmony_ci size_t resp_buf_size; 748c2ecf20Sopenharmony_ci unsigned int chan_type; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/** 788c2ecf20Sopenharmony_ci * struct zynqmp_ipi_mbox - Description of a ZynqMP IPI mailbox 798c2ecf20Sopenharmony_ci * platform data. 808c2ecf20Sopenharmony_ci * @pdata: pointer to the IPI private data 818c2ecf20Sopenharmony_ci * @dev: device pointer corresponding to the Xilinx ZynqMP 828c2ecf20Sopenharmony_ci * IPI mailbox 838c2ecf20Sopenharmony_ci * @remote_id: remote IPI agent ID 848c2ecf20Sopenharmony_ci * @mbox: mailbox Controller 858c2ecf20Sopenharmony_ci * @mchans: array for channels, tx channel and rx channel. 868c2ecf20Sopenharmony_ci * @irq: IPI agent interrupt ID 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistruct zynqmp_ipi_mbox { 898c2ecf20Sopenharmony_ci struct zynqmp_ipi_pdata *pdata; 908c2ecf20Sopenharmony_ci struct device dev; 918c2ecf20Sopenharmony_ci u32 remote_id; 928c2ecf20Sopenharmony_ci struct mbox_controller mbox; 938c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan mchans[2]; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/** 978c2ecf20Sopenharmony_ci * struct zynqmp_ipi_pdata - Description of z ZynqMP IPI agent platform data. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * @dev: device pointer corresponding to the Xilinx ZynqMP 1008c2ecf20Sopenharmony_ci * IPI agent 1018c2ecf20Sopenharmony_ci * @irq: IPI agent interrupt ID 1028c2ecf20Sopenharmony_ci * @method: IPI SMC or HVC is going to be used 1038c2ecf20Sopenharmony_ci * @local_id: local IPI agent ID 1048c2ecf20Sopenharmony_ci * @num_mboxes: number of mailboxes of this IPI agent 1058c2ecf20Sopenharmony_ci * @ipi_mboxes: IPI mailboxes of this IPI agent 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistruct zynqmp_ipi_pdata { 1088c2ecf20Sopenharmony_ci struct device *dev; 1098c2ecf20Sopenharmony_ci int irq; 1108c2ecf20Sopenharmony_ci unsigned int method; 1118c2ecf20Sopenharmony_ci u32 local_id; 1128c2ecf20Sopenharmony_ci int num_mboxes; 1138c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox ipi_mboxes[]; 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic struct device_driver zynqmp_ipi_mbox_driver = { 1178c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1188c2ecf20Sopenharmony_ci .name = "zynqmp-ipi-mbox", 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void zynqmp_ipi_fw_call(struct zynqmp_ipi_mbox *ipi_mbox, 1228c2ecf20Sopenharmony_ci unsigned long a0, unsigned long a3, 1238c2ecf20Sopenharmony_ci struct arm_smccc_res *res) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct zynqmp_ipi_pdata *pdata = ipi_mbox->pdata; 1268c2ecf20Sopenharmony_ci unsigned long a1, a2; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci a1 = pdata->local_id; 1298c2ecf20Sopenharmony_ci a2 = ipi_mbox->remote_id; 1308c2ecf20Sopenharmony_ci if (pdata->method == USE_SMC) 1318c2ecf20Sopenharmony_ci arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, res); 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci arm_smccc_hvc(a0, a1, a2, a3, 0, 0, 0, 0, res); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/** 1378c2ecf20Sopenharmony_ci * zynqmp_ipi_interrupt - Interrupt handler for IPI notification 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * @irq: Interrupt number 1408c2ecf20Sopenharmony_ci * @data: ZynqMP IPI mailbox platform data. 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * Return: -EINVAL if there is no instance 1438c2ecf20Sopenharmony_ci * IRQ_NONE if the interrupt is not ours. 1448c2ecf20Sopenharmony_ci * IRQ_HANDLED if the rx interrupt was successfully handled. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct zynqmp_ipi_pdata *pdata = data; 1498c2ecf20Sopenharmony_ci struct mbox_chan *chan; 1508c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox; 1518c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan; 1528c2ecf20Sopenharmony_ci struct zynqmp_ipi_message *msg; 1538c2ecf20Sopenharmony_ci u64 arg0, arg3; 1548c2ecf20Sopenharmony_ci struct arm_smccc_res res; 1558c2ecf20Sopenharmony_ci int ret, i, status = IRQ_NONE; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci (void)irq; 1588c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; 1598c2ecf20Sopenharmony_ci arg3 = IPI_SMC_ENQUIRY_DIRQ_MASK; 1608c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_mboxes; i++) { 1618c2ecf20Sopenharmony_ci ipi_mbox = &pdata->ipi_mboxes[i]; 1628c2ecf20Sopenharmony_ci mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; 1638c2ecf20Sopenharmony_ci chan = &ipi_mbox->mbox.chans[IPI_MB_CHNL_RX]; 1648c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, arg3, &res); 1658c2ecf20Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 1668c2ecf20Sopenharmony_ci if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { 1678c2ecf20Sopenharmony_ci if (mchan->is_opened) { 1688c2ecf20Sopenharmony_ci msg = mchan->rx_buf; 1698c2ecf20Sopenharmony_ci msg->len = mchan->req_buf_size; 1708c2ecf20Sopenharmony_ci memcpy_fromio(msg->data, mchan->req_buf, 1718c2ecf20Sopenharmony_ci msg->len); 1728c2ecf20Sopenharmony_ci mbox_chan_received_data(chan, (void *)msg); 1738c2ecf20Sopenharmony_ci status = IRQ_HANDLED; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci return status; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * zynqmp_ipi_peek_data - Peek to see if there are any rx messages. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * @chan: Channel Pointer 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * Return: 'true' if there is pending rx data, 'false' if there is none. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistatic bool zynqmp_ipi_peek_data(struct mbox_chan *chan) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct device *dev = chan->mbox->dev; 1908c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 1918c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci u64 arg0; 1948c2ecf20Sopenharmony_ci struct arm_smccc_res res; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (WARN_ON(!ipi_mbox)) { 1978c2ecf20Sopenharmony_ci dev_err(dev, "no platform drv data??\n"); 1988c2ecf20Sopenharmony_ci return false; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; 2028c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 2038c2ecf20Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_TX) { 2068c2ecf20Sopenharmony_ci /* TX channel, check if the message has been acked 2078c2ecf20Sopenharmony_ci * by the remote, if yes, response is available. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) 2108c2ecf20Sopenharmony_ci return false; 2118c2ecf20Sopenharmony_ci else 2128c2ecf20Sopenharmony_ci return true; 2138c2ecf20Sopenharmony_ci } else if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { 2148c2ecf20Sopenharmony_ci /* RX channel, check if there is message arrived. */ 2158c2ecf20Sopenharmony_ci return true; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci return false; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * zynqmp_ipi_last_tx_done - See if the last tx message is sent 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * @chan: Channel pointer 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * Return: 'true' is no pending tx data, 'false' if there are any. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic bool zynqmp_ipi_last_tx_done(struct mbox_chan *chan) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct device *dev = chan->mbox->dev; 2308c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 2318c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 2328c2ecf20Sopenharmony_ci int ret; 2338c2ecf20Sopenharmony_ci u64 arg0; 2348c2ecf20Sopenharmony_ci struct arm_smccc_res res; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (WARN_ON(!ipi_mbox)) { 2378c2ecf20Sopenharmony_ci dev_err(dev, "no platform drv data??\n"); 2388c2ecf20Sopenharmony_ci return false; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_TX) { 2428c2ecf20Sopenharmony_ci /* We only need to check if the message been taken 2438c2ecf20Sopenharmony_ci * by the remote in the TX channel 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; 2468c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 2478c2ecf20Sopenharmony_ci /* Check the SMC call status, a0 of the result */ 2488c2ecf20Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 2498c2ecf20Sopenharmony_ci if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) 2508c2ecf20Sopenharmony_ci return false; 2518c2ecf20Sopenharmony_ci return true; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci /* Always true for the response message in RX channel */ 2548c2ecf20Sopenharmony_ci return true; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/** 2588c2ecf20Sopenharmony_ci * zynqmp_ipi_send_data - Send data 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * @chan: Channel Pointer 2618c2ecf20Sopenharmony_ci * @data: Message Pointer 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * Return: 0 if all goes good, else appropriate error messages. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_cistatic int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct device *dev = chan->mbox->dev; 2688c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 2698c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 2708c2ecf20Sopenharmony_ci struct zynqmp_ipi_message *msg = data; 2718c2ecf20Sopenharmony_ci u64 arg0; 2728c2ecf20Sopenharmony_ci struct arm_smccc_res res; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (WARN_ON(!ipi_mbox)) { 2758c2ecf20Sopenharmony_ci dev_err(dev, "no platform drv data??\n"); 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_TX) { 2808c2ecf20Sopenharmony_ci /* Send request message */ 2818c2ecf20Sopenharmony_ci if (msg && msg->len > mchan->req_buf_size) { 2828c2ecf20Sopenharmony_ci dev_err(dev, "channel %d message length %u > max %lu\n", 2838c2ecf20Sopenharmony_ci mchan->chan_type, (unsigned int)msg->len, 2848c2ecf20Sopenharmony_ci mchan->req_buf_size); 2858c2ecf20Sopenharmony_ci return -EINVAL; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci if (msg && msg->len) 2888c2ecf20Sopenharmony_ci memcpy_toio(mchan->req_buf, msg->data, msg->len); 2898c2ecf20Sopenharmony_ci /* Kick IPI mailbox to send message */ 2908c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_NOTIFY; 2918c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci /* Send response message */ 2948c2ecf20Sopenharmony_ci if (msg && msg->len > mchan->resp_buf_size) { 2958c2ecf20Sopenharmony_ci dev_err(dev, "channel %d message length %u > max %lu\n", 2968c2ecf20Sopenharmony_ci mchan->chan_type, (unsigned int)msg->len, 2978c2ecf20Sopenharmony_ci mchan->resp_buf_size); 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci if (msg && msg->len) 3018c2ecf20Sopenharmony_ci memcpy_toio(mchan->resp_buf, msg->data, msg->len); 3028c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_ACK; 3038c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, IPI_SMC_ACK_EIRQ_MASK, 3048c2ecf20Sopenharmony_ci &res); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/** 3108c2ecf20Sopenharmony_ci * zynqmp_ipi_startup - Startup the IPI channel 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * @chan: Channel pointer 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * Return: 0 if all goes good, else return corresponding error message 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_cistatic int zynqmp_ipi_startup(struct mbox_chan *chan) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct device *dev = chan->mbox->dev; 3198c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 3208c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 3218c2ecf20Sopenharmony_ci u64 arg0; 3228c2ecf20Sopenharmony_ci struct arm_smccc_res res; 3238c2ecf20Sopenharmony_ci int ret = 0; 3248c2ecf20Sopenharmony_ci unsigned int nchan_type; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (mchan->is_opened) 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* If no channel has been opened, open the IPI mailbox */ 3308c2ecf20Sopenharmony_ci nchan_type = (mchan->chan_type + 1) % 2; 3318c2ecf20Sopenharmony_ci if (!ipi_mbox->mchans[nchan_type].is_opened) { 3328c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_OPEN; 3338c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 3348c2ecf20Sopenharmony_ci /* Check the SMC call status, a0 of the result */ 3358c2ecf20Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 3368c2ecf20Sopenharmony_ci if (ret < 0) { 3378c2ecf20Sopenharmony_ci dev_err(dev, "SMC to open the IPI channel failed.\n"); 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci ret = 0; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* If it is RX channel, enable the IPI notification interrupt */ 3448c2ecf20Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_RX) { 3458c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_ENABLE_IRQ; 3468c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci mchan->is_opened = 1; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/** 3548c2ecf20Sopenharmony_ci * zynqmp_ipi_shutdown - Shutdown the IPI channel 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * @chan: Channel pointer 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic void zynqmp_ipi_shutdown(struct mbox_chan *chan) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct device *dev = chan->mbox->dev; 3618c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 3628c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 3638c2ecf20Sopenharmony_ci u64 arg0; 3648c2ecf20Sopenharmony_ci struct arm_smccc_res res; 3658c2ecf20Sopenharmony_ci unsigned int chan_type; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!mchan->is_opened) 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* If it is RX channel, disable notification interrupt */ 3718c2ecf20Sopenharmony_ci chan_type = mchan->chan_type; 3728c2ecf20Sopenharmony_ci if (chan_type == IPI_MB_CHNL_RX) { 3738c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_DISABLE_IRQ; 3748c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci /* Release IPI mailbox if no other channel is opened */ 3778c2ecf20Sopenharmony_ci chan_type = (chan_type + 1) % 2; 3788c2ecf20Sopenharmony_ci if (!ipi_mbox->mchans[chan_type].is_opened) { 3798c2ecf20Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_RELEASE; 3808c2ecf20Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci mchan->is_opened = 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* ZynqMP IPI mailbox operations */ 3878c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops zynqmp_ipi_chan_ops = { 3888c2ecf20Sopenharmony_ci .startup = zynqmp_ipi_startup, 3898c2ecf20Sopenharmony_ci .shutdown = zynqmp_ipi_shutdown, 3908c2ecf20Sopenharmony_ci .peek_data = zynqmp_ipi_peek_data, 3918c2ecf20Sopenharmony_ci .last_tx_done = zynqmp_ipi_last_tx_done, 3928c2ecf20Sopenharmony_ci .send_data = zynqmp_ipi_send_data, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/** 3968c2ecf20Sopenharmony_ci * zynqmp_ipi_of_xlate - Translate of phandle to IPI mailbox channel 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * @mbox: mailbox controller pointer 3998c2ecf20Sopenharmony_ci * @p: phandle pointer 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * Return: Mailbox channel, else return error pointer. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic struct mbox_chan *zynqmp_ipi_of_xlate(struct mbox_controller *mbox, 4048c2ecf20Sopenharmony_ci const struct of_phandle_args *p) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct mbox_chan *chan; 4078c2ecf20Sopenharmony_ci struct device *dev = mbox->dev; 4088c2ecf20Sopenharmony_ci unsigned int chan_type; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Only supports TX and RX channels */ 4118c2ecf20Sopenharmony_ci chan_type = p->args[0]; 4128c2ecf20Sopenharmony_ci if (chan_type != IPI_MB_CHNL_TX && chan_type != IPI_MB_CHNL_RX) { 4138c2ecf20Sopenharmony_ci dev_err(dev, "req chnl failure: invalid chnl type %u.\n", 4148c2ecf20Sopenharmony_ci chan_type); 4158c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci chan = &mbox->chans[chan_type]; 4188c2ecf20Sopenharmony_ci return chan; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const struct of_device_id zynqmp_ipi_of_match[] = { 4228c2ecf20Sopenharmony_ci { .compatible = "xlnx,zynqmp-ipi-mailbox" }, 4238c2ecf20Sopenharmony_ci {}, 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * zynqmp_ipi_mbox_get_buf_res - Get buffer resource from the IPI dev node 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * @node: IPI mbox device child node 4318c2ecf20Sopenharmony_ci * @name: name of the IPI buffer 4328c2ecf20Sopenharmony_ci * @res: pointer to where the resource information will be stored. 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Return: 0 for success, negative value for failure 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic int zynqmp_ipi_mbox_get_buf_res(struct device_node *node, 4378c2ecf20Sopenharmony_ci const char *name, 4388c2ecf20Sopenharmony_ci struct resource *res) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci int ret, index; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci index = of_property_match_string(node, "reg-names", name); 4438c2ecf20Sopenharmony_ci if (index >= 0) { 4448c2ecf20Sopenharmony_ci ret = of_address_to_resource(node, index, res); 4458c2ecf20Sopenharmony_ci if (ret < 0) 4468c2ecf20Sopenharmony_ci return -EINVAL; 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci return -ENODEV; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * zynqmp_ipi_mbox_dev_release() - release the existence of a ipi mbox dev 4548c2ecf20Sopenharmony_ci * 4558c2ecf20Sopenharmony_ci * @dev: the ipi mailbox device 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * This is to avoid the no device release() function kernel warning. 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic void zynqmp_ipi_mbox_dev_release(struct device *dev) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci (void)dev; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/** 4668c2ecf20Sopenharmony_ci * zynqmp_ipi_mbox_probe - probe IPI mailbox resource from device node 4678c2ecf20Sopenharmony_ci * 4688c2ecf20Sopenharmony_ci * @ipi_mbox: pointer to IPI mailbox private data structure 4698c2ecf20Sopenharmony_ci * @node: IPI mailbox device node 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * Return: 0 for success, negative value for failure 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, 4748c2ecf20Sopenharmony_ci struct device_node *node) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct zynqmp_ipi_mchan *mchan; 4778c2ecf20Sopenharmony_ci struct mbox_chan *chans; 4788c2ecf20Sopenharmony_ci struct mbox_controller *mbox; 4798c2ecf20Sopenharmony_ci struct resource res; 4808c2ecf20Sopenharmony_ci struct device *dev, *mdev; 4818c2ecf20Sopenharmony_ci const char *name; 4828c2ecf20Sopenharmony_ci int ret; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dev = ipi_mbox->pdata->dev; 4858c2ecf20Sopenharmony_ci /* Initialize dev for IPI mailbox */ 4868c2ecf20Sopenharmony_ci ipi_mbox->dev.parent = dev; 4878c2ecf20Sopenharmony_ci ipi_mbox->dev.release = NULL; 4888c2ecf20Sopenharmony_ci ipi_mbox->dev.of_node = node; 4898c2ecf20Sopenharmony_ci dev_set_name(&ipi_mbox->dev, "%s", of_node_full_name(node)); 4908c2ecf20Sopenharmony_ci dev_set_drvdata(&ipi_mbox->dev, ipi_mbox); 4918c2ecf20Sopenharmony_ci ipi_mbox->dev.release = zynqmp_ipi_mbox_dev_release; 4928c2ecf20Sopenharmony_ci ipi_mbox->dev.driver = &zynqmp_ipi_mbox_driver; 4938c2ecf20Sopenharmony_ci ret = device_register(&ipi_mbox->dev); 4948c2ecf20Sopenharmony_ci if (ret) { 4958c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register ipi mbox dev.\n"); 4968c2ecf20Sopenharmony_ci put_device(&ipi_mbox->dev); 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci mdev = &ipi_mbox->dev; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; 5028c2ecf20Sopenharmony_ci name = "local_request_region"; 5038c2ecf20Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 5048c2ecf20Sopenharmony_ci if (!ret) { 5058c2ecf20Sopenharmony_ci mchan->req_buf_size = resource_size(&res); 5068c2ecf20Sopenharmony_ci mchan->req_buf = devm_ioremap(mdev, res.start, 5078c2ecf20Sopenharmony_ci mchan->req_buf_size); 5088c2ecf20Sopenharmony_ci if (!mchan->req_buf) { 5098c2ecf20Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 5108c2ecf20Sopenharmony_ci return -ENOMEM; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci } else if (ret != -ENODEV) { 5138c2ecf20Sopenharmony_ci dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci name = "remote_response_region"; 5188c2ecf20Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 5198c2ecf20Sopenharmony_ci if (!ret) { 5208c2ecf20Sopenharmony_ci mchan->resp_buf_size = resource_size(&res); 5218c2ecf20Sopenharmony_ci mchan->resp_buf = devm_ioremap(mdev, res.start, 5228c2ecf20Sopenharmony_ci mchan->resp_buf_size); 5238c2ecf20Sopenharmony_ci if (!mchan->resp_buf) { 5248c2ecf20Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 5258c2ecf20Sopenharmony_ci return -ENOMEM; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci } else if (ret != -ENODEV) { 5288c2ecf20Sopenharmony_ci dev_err(mdev, "Unmatched resource %s.\n", name); 5298c2ecf20Sopenharmony_ci return ret; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci mchan->rx_buf = devm_kzalloc(mdev, 5328c2ecf20Sopenharmony_ci mchan->resp_buf_size + 5338c2ecf20Sopenharmony_ci sizeof(struct zynqmp_ipi_message), 5348c2ecf20Sopenharmony_ci GFP_KERNEL); 5358c2ecf20Sopenharmony_ci if (!mchan->rx_buf) 5368c2ecf20Sopenharmony_ci return -ENOMEM; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; 5398c2ecf20Sopenharmony_ci name = "remote_request_region"; 5408c2ecf20Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 5418c2ecf20Sopenharmony_ci if (!ret) { 5428c2ecf20Sopenharmony_ci mchan->req_buf_size = resource_size(&res); 5438c2ecf20Sopenharmony_ci mchan->req_buf = devm_ioremap(mdev, res.start, 5448c2ecf20Sopenharmony_ci mchan->req_buf_size); 5458c2ecf20Sopenharmony_ci if (!mchan->req_buf) { 5468c2ecf20Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 5478c2ecf20Sopenharmony_ci return -ENOMEM; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } else if (ret != -ENODEV) { 5508c2ecf20Sopenharmony_ci dev_err(mdev, "Unmatched resource %s.\n", name); 5518c2ecf20Sopenharmony_ci return ret; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci name = "local_response_region"; 5558c2ecf20Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 5568c2ecf20Sopenharmony_ci if (!ret) { 5578c2ecf20Sopenharmony_ci mchan->resp_buf_size = resource_size(&res); 5588c2ecf20Sopenharmony_ci mchan->resp_buf = devm_ioremap(mdev, res.start, 5598c2ecf20Sopenharmony_ci mchan->resp_buf_size); 5608c2ecf20Sopenharmony_ci if (!mchan->resp_buf) { 5618c2ecf20Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 5628c2ecf20Sopenharmony_ci return -ENOMEM; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci } else if (ret != -ENODEV) { 5658c2ecf20Sopenharmony_ci dev_err(mdev, "Unmatched resource %s.\n", name); 5668c2ecf20Sopenharmony_ci return ret; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci mchan->rx_buf = devm_kzalloc(mdev, 5698c2ecf20Sopenharmony_ci mchan->resp_buf_size + 5708c2ecf20Sopenharmony_ci sizeof(struct zynqmp_ipi_message), 5718c2ecf20Sopenharmony_ci GFP_KERNEL); 5728c2ecf20Sopenharmony_ci if (!mchan->rx_buf) 5738c2ecf20Sopenharmony_ci return -ENOMEM; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Get the IPI remote agent ID */ 5768c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id); 5778c2ecf20Sopenharmony_ci if (ret < 0) { 5788c2ecf20Sopenharmony_ci dev_err(dev, "No IPI remote ID is specified.\n"); 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci mbox = &ipi_mbox->mbox; 5838c2ecf20Sopenharmony_ci mbox->dev = mdev; 5848c2ecf20Sopenharmony_ci mbox->ops = &zynqmp_ipi_chan_ops; 5858c2ecf20Sopenharmony_ci mbox->num_chans = 2; 5868c2ecf20Sopenharmony_ci mbox->txdone_irq = false; 5878c2ecf20Sopenharmony_ci mbox->txdone_poll = true; 5888c2ecf20Sopenharmony_ci mbox->txpoll_period = 5; 5898c2ecf20Sopenharmony_ci mbox->of_xlate = zynqmp_ipi_of_xlate; 5908c2ecf20Sopenharmony_ci chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); 5918c2ecf20Sopenharmony_ci if (!chans) 5928c2ecf20Sopenharmony_ci return -ENOMEM; 5938c2ecf20Sopenharmony_ci mbox->chans = chans; 5948c2ecf20Sopenharmony_ci chans[IPI_MB_CHNL_TX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; 5958c2ecf20Sopenharmony_ci chans[IPI_MB_CHNL_RX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; 5968c2ecf20Sopenharmony_ci ipi_mbox->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX; 5978c2ecf20Sopenharmony_ci ipi_mbox->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX; 5988c2ecf20Sopenharmony_ci ret = devm_mbox_controller_register(mdev, mbox); 5998c2ecf20Sopenharmony_ci if (ret) 6008c2ecf20Sopenharmony_ci dev_err(mdev, 6018c2ecf20Sopenharmony_ci "Failed to register mbox_controller(%d)\n", ret); 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci dev_info(mdev, 6048c2ecf20Sopenharmony_ci "Registered ZynqMP IPI mbox with TX/RX channels.\n"); 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/** 6098c2ecf20Sopenharmony_ci * zynqmp_ipi_free_mboxes - Free IPI mailboxes devices 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * @pdata: IPI private data 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_cistatic void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox; 6168c2ecf20Sopenharmony_ci int i; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci i = pdata->num_mboxes; 6198c2ecf20Sopenharmony_ci for (; i >= 0; i--) { 6208c2ecf20Sopenharmony_ci ipi_mbox = &pdata->ipi_mboxes[i]; 6218c2ecf20Sopenharmony_ci if (ipi_mbox->dev.parent) { 6228c2ecf20Sopenharmony_ci mbox_controller_unregister(&ipi_mbox->mbox); 6238c2ecf20Sopenharmony_ci if (device_is_registered(&ipi_mbox->dev)) 6248c2ecf20Sopenharmony_ci device_unregister(&ipi_mbox->dev); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int zynqmp_ipi_probe(struct platform_device *pdev) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6328c2ecf20Sopenharmony_ci struct device_node *nc, *np = pdev->dev.of_node; 6338c2ecf20Sopenharmony_ci struct zynqmp_ipi_pdata *pdata; 6348c2ecf20Sopenharmony_ci struct zynqmp_ipi_mbox *mbox; 6358c2ecf20Sopenharmony_ci int num_mboxes, ret = -EINVAL; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci num_mboxes = of_get_available_child_count(np); 6388c2ecf20Sopenharmony_ci if (num_mboxes == 0) { 6398c2ecf20Sopenharmony_ci dev_err(dev, "mailbox nodes not available\n"); 6408c2ecf20Sopenharmony_ci return -EINVAL; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, struct_size(pdata, ipi_mboxes, num_mboxes), 6448c2ecf20Sopenharmony_ci GFP_KERNEL); 6458c2ecf20Sopenharmony_ci if (!pdata) 6468c2ecf20Sopenharmony_ci return -ENOMEM; 6478c2ecf20Sopenharmony_ci pdata->dev = dev; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Get the IPI local agents ID */ 6508c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "xlnx,ipi-id", &pdata->local_id); 6518c2ecf20Sopenharmony_ci if (ret < 0) { 6528c2ecf20Sopenharmony_ci dev_err(dev, "No IPI local ID is specified.\n"); 6538c2ecf20Sopenharmony_ci return ret; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci pdata->num_mboxes = num_mboxes; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci mbox = pdata->ipi_mboxes; 6598c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, nc) { 6608c2ecf20Sopenharmony_ci mbox->pdata = pdata; 6618c2ecf20Sopenharmony_ci ret = zynqmp_ipi_mbox_probe(mbox, nc); 6628c2ecf20Sopenharmony_ci if (ret) { 6638c2ecf20Sopenharmony_ci dev_err(dev, "failed to probe subdev.\n"); 6648c2ecf20Sopenharmony_ci ret = -EINVAL; 6658c2ecf20Sopenharmony_ci goto free_mbox_dev; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci mbox++; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* IPI IRQ */ 6718c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 6728c2ecf20Sopenharmony_ci if (ret < 0) 6738c2ecf20Sopenharmony_ci goto free_mbox_dev; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci pdata->irq = ret; 6768c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, 6778c2ecf20Sopenharmony_ci IRQF_SHARED, dev_name(dev), pdata); 6788c2ecf20Sopenharmony_ci if (ret) { 6798c2ecf20Sopenharmony_ci dev_err(dev, "IRQ %d is not requested successfully.\n", 6808c2ecf20Sopenharmony_ci pdata->irq); 6818c2ecf20Sopenharmony_ci goto free_mbox_dev; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pdata); 6858c2ecf20Sopenharmony_ci return ret; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cifree_mbox_dev: 6888c2ecf20Sopenharmony_ci zynqmp_ipi_free_mboxes(pdata); 6898c2ecf20Sopenharmony_ci return ret; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int zynqmp_ipi_remove(struct platform_device *pdev) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct zynqmp_ipi_pdata *pdata; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci pdata = platform_get_drvdata(pdev); 6978c2ecf20Sopenharmony_ci zynqmp_ipi_free_mboxes(pdata); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic struct platform_driver zynqmp_ipi_driver = { 7038c2ecf20Sopenharmony_ci .probe = zynqmp_ipi_probe, 7048c2ecf20Sopenharmony_ci .remove = zynqmp_ipi_remove, 7058c2ecf20Sopenharmony_ci .driver = { 7068c2ecf20Sopenharmony_ci .name = "zynqmp-ipi", 7078c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(zynqmp_ipi_of_match), 7088c2ecf20Sopenharmony_ci }, 7098c2ecf20Sopenharmony_ci}; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int __init zynqmp_ipi_init(void) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci return platform_driver_register(&zynqmp_ipi_driver); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_cisubsys_initcall(zynqmp_ipi_init); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic void __exit zynqmp_ipi_exit(void) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci platform_driver_unregister(&zynqmp_ipi_driver); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_cimodule_exit(zynqmp_ipi_exit); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx ZynqMP IPI Mailbox driver"); 7258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx Inc."); 726