162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xilinx Inter Processor Interrupt(IPI) Mailbox Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018 Xilinx, Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/arm-smccc.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/mailbox_controller.h> 1562306a36Sopenharmony_ci#include <linux/mailbox/zynqmp-ipi-message.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* IPI agent ID any */ 2262306a36Sopenharmony_ci#define IPI_ID_ANY 0xFFUL 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* indicate if ZynqMP IPI mailbox driver uses SMC calls or HVC calls */ 2562306a36Sopenharmony_ci#define USE_SMC 0 2662306a36Sopenharmony_ci#define USE_HVC 1 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Default IPI SMC function IDs */ 2962306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_OPEN 0x82001000U 3062306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_RELEASE 0x82001001U 3162306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_STATUS_ENQUIRY 0x82001002U 3262306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_NOTIFY 0x82001003U 3362306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_ACK 0x82001004U 3462306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_ENABLE_IRQ 0x82001005U 3562306a36Sopenharmony_ci#define SMC_IPI_MAILBOX_DISABLE_IRQ 0x82001006U 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* IPI SMC Macros */ 3862306a36Sopenharmony_ci#define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001UL /* Flag to indicate if 3962306a36Sopenharmony_ci * notification interrupt 4062306a36Sopenharmony_ci * to be disabled. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define IPI_SMC_ACK_EIRQ_MASK 0x00000001UL /* Flag to indicate if 4362306a36Sopenharmony_ci * notification interrupt 4462306a36Sopenharmony_ci * to be enabled. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* IPI mailbox status */ 4862306a36Sopenharmony_ci#define IPI_MB_STATUS_IDLE 0 4962306a36Sopenharmony_ci#define IPI_MB_STATUS_SEND_PENDING 1 5062306a36Sopenharmony_ci#define IPI_MB_STATUS_RECV_PENDING 2 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */ 5362306a36Sopenharmony_ci#define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel 5762306a36Sopenharmony_ci * @is_opened: indicate if the IPI channel is opened 5862306a36Sopenharmony_ci * @req_buf: local to remote request buffer start address 5962306a36Sopenharmony_ci * @resp_buf: local to remote response buffer start address 6062306a36Sopenharmony_ci * @req_buf_size: request buffer size 6162306a36Sopenharmony_ci * @resp_buf_size: response buffer size 6262306a36Sopenharmony_ci * @rx_buf: receive buffer to pass received message to client 6362306a36Sopenharmony_ci * @chan_type: channel type 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistruct zynqmp_ipi_mchan { 6662306a36Sopenharmony_ci int is_opened; 6762306a36Sopenharmony_ci void __iomem *req_buf; 6862306a36Sopenharmony_ci void __iomem *resp_buf; 6962306a36Sopenharmony_ci void *rx_buf; 7062306a36Sopenharmony_ci size_t req_buf_size; 7162306a36Sopenharmony_ci size_t resp_buf_size; 7262306a36Sopenharmony_ci unsigned int chan_type; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * struct zynqmp_ipi_mbox - Description of a ZynqMP IPI mailbox 7762306a36Sopenharmony_ci * platform data. 7862306a36Sopenharmony_ci * @pdata: pointer to the IPI private data 7962306a36Sopenharmony_ci * @dev: device pointer corresponding to the Xilinx ZynqMP 8062306a36Sopenharmony_ci * IPI mailbox 8162306a36Sopenharmony_ci * @remote_id: remote IPI agent ID 8262306a36Sopenharmony_ci * @mbox: mailbox Controller 8362306a36Sopenharmony_ci * @mchans: array for channels, tx channel and rx channel. 8462306a36Sopenharmony_ci * @irq: IPI agent interrupt ID 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistruct zynqmp_ipi_mbox { 8762306a36Sopenharmony_ci struct zynqmp_ipi_pdata *pdata; 8862306a36Sopenharmony_ci struct device dev; 8962306a36Sopenharmony_ci u32 remote_id; 9062306a36Sopenharmony_ci struct mbox_controller mbox; 9162306a36Sopenharmony_ci struct zynqmp_ipi_mchan mchans[2]; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * struct zynqmp_ipi_pdata - Description of z ZynqMP IPI agent platform data. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * @dev: device pointer corresponding to the Xilinx ZynqMP 9862306a36Sopenharmony_ci * IPI agent 9962306a36Sopenharmony_ci * @irq: IPI agent interrupt ID 10062306a36Sopenharmony_ci * @method: IPI SMC or HVC is going to be used 10162306a36Sopenharmony_ci * @local_id: local IPI agent ID 10262306a36Sopenharmony_ci * @num_mboxes: number of mailboxes of this IPI agent 10362306a36Sopenharmony_ci * @ipi_mboxes: IPI mailboxes of this IPI agent 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistruct zynqmp_ipi_pdata { 10662306a36Sopenharmony_ci struct device *dev; 10762306a36Sopenharmony_ci int irq; 10862306a36Sopenharmony_ci unsigned int method; 10962306a36Sopenharmony_ci u32 local_id; 11062306a36Sopenharmony_ci int num_mboxes; 11162306a36Sopenharmony_ci struct zynqmp_ipi_mbox ipi_mboxes[]; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct device_driver zynqmp_ipi_mbox_driver = { 11562306a36Sopenharmony_ci .owner = THIS_MODULE, 11662306a36Sopenharmony_ci .name = "zynqmp-ipi-mbox", 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void zynqmp_ipi_fw_call(struct zynqmp_ipi_mbox *ipi_mbox, 12062306a36Sopenharmony_ci unsigned long a0, unsigned long a3, 12162306a36Sopenharmony_ci struct arm_smccc_res *res) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct zynqmp_ipi_pdata *pdata = ipi_mbox->pdata; 12462306a36Sopenharmony_ci unsigned long a1, a2; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci a1 = pdata->local_id; 12762306a36Sopenharmony_ci a2 = ipi_mbox->remote_id; 12862306a36Sopenharmony_ci if (pdata->method == USE_SMC) 12962306a36Sopenharmony_ci arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, res); 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci arm_smccc_hvc(a0, a1, a2, a3, 0, 0, 0, 0, res); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * zynqmp_ipi_interrupt - Interrupt handler for IPI notification 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * @irq: Interrupt number 13862306a36Sopenharmony_ci * @data: ZynqMP IPI mailbox platform data. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Return: -EINVAL if there is no instance 14162306a36Sopenharmony_ci * IRQ_NONE if the interrupt is not ours. 14262306a36Sopenharmony_ci * IRQ_HANDLED if the rx interrupt was successfully handled. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct zynqmp_ipi_pdata *pdata = data; 14762306a36Sopenharmony_ci struct mbox_chan *chan; 14862306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox; 14962306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan; 15062306a36Sopenharmony_ci struct zynqmp_ipi_message *msg; 15162306a36Sopenharmony_ci u64 arg0, arg3; 15262306a36Sopenharmony_ci struct arm_smccc_res res; 15362306a36Sopenharmony_ci int ret, i, status = IRQ_NONE; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci (void)irq; 15662306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; 15762306a36Sopenharmony_ci arg3 = IPI_SMC_ENQUIRY_DIRQ_MASK; 15862306a36Sopenharmony_ci for (i = 0; i < pdata->num_mboxes; i++) { 15962306a36Sopenharmony_ci ipi_mbox = &pdata->ipi_mboxes[i]; 16062306a36Sopenharmony_ci mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; 16162306a36Sopenharmony_ci chan = &ipi_mbox->mbox.chans[IPI_MB_CHNL_RX]; 16262306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, arg3, &res); 16362306a36Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 16462306a36Sopenharmony_ci if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { 16562306a36Sopenharmony_ci if (mchan->is_opened) { 16662306a36Sopenharmony_ci msg = mchan->rx_buf; 16762306a36Sopenharmony_ci msg->len = mchan->req_buf_size; 16862306a36Sopenharmony_ci memcpy_fromio(msg->data, mchan->req_buf, 16962306a36Sopenharmony_ci msg->len); 17062306a36Sopenharmony_ci mbox_chan_received_data(chan, (void *)msg); 17162306a36Sopenharmony_ci status = IRQ_HANDLED; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci return status; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/** 17962306a36Sopenharmony_ci * zynqmp_ipi_peek_data - Peek to see if there are any rx messages. 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * @chan: Channel Pointer 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Return: 'true' if there is pending rx data, 'false' if there is none. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic bool zynqmp_ipi_peek_data(struct mbox_chan *chan) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct device *dev = chan->mbox->dev; 18862306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 18962306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 19062306a36Sopenharmony_ci int ret; 19162306a36Sopenharmony_ci u64 arg0; 19262306a36Sopenharmony_ci struct arm_smccc_res res; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (WARN_ON(!ipi_mbox)) { 19562306a36Sopenharmony_ci dev_err(dev, "no platform drv data??\n"); 19662306a36Sopenharmony_ci return false; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; 20062306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 20162306a36Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_TX) { 20462306a36Sopenharmony_ci /* TX channel, check if the message has been acked 20562306a36Sopenharmony_ci * by the remote, if yes, response is available. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) 20862306a36Sopenharmony_ci return false; 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci return true; 21162306a36Sopenharmony_ci } else if (ret > 0 && ret & IPI_MB_STATUS_RECV_PENDING) { 21262306a36Sopenharmony_ci /* RX channel, check if there is message arrived. */ 21362306a36Sopenharmony_ci return true; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci return false; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/** 21962306a36Sopenharmony_ci * zynqmp_ipi_last_tx_done - See if the last tx message is sent 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * @chan: Channel pointer 22262306a36Sopenharmony_ci * 22362306a36Sopenharmony_ci * Return: 'true' is no pending tx data, 'false' if there are any. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistatic bool zynqmp_ipi_last_tx_done(struct mbox_chan *chan) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct device *dev = chan->mbox->dev; 22862306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 22962306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci u64 arg0; 23262306a36Sopenharmony_ci struct arm_smccc_res res; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (WARN_ON(!ipi_mbox)) { 23562306a36Sopenharmony_ci dev_err(dev, "no platform drv data??\n"); 23662306a36Sopenharmony_ci return false; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_TX) { 24062306a36Sopenharmony_ci /* We only need to check if the message been taken 24162306a36Sopenharmony_ci * by the remote in the TX channel 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; 24462306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 24562306a36Sopenharmony_ci /* Check the SMC call status, a0 of the result */ 24662306a36Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 24762306a36Sopenharmony_ci if (ret < 0 || ret & IPI_MB_STATUS_SEND_PENDING) 24862306a36Sopenharmony_ci return false; 24962306a36Sopenharmony_ci return true; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci /* Always true for the response message in RX channel */ 25262306a36Sopenharmony_ci return true; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * zynqmp_ipi_send_data - Send data 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * @chan: Channel Pointer 25962306a36Sopenharmony_ci * @data: Message Pointer 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * Return: 0 if all goes good, else appropriate error messages. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic int zynqmp_ipi_send_data(struct mbox_chan *chan, void *data) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct device *dev = chan->mbox->dev; 26662306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 26762306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 26862306a36Sopenharmony_ci struct zynqmp_ipi_message *msg = data; 26962306a36Sopenharmony_ci u64 arg0; 27062306a36Sopenharmony_ci struct arm_smccc_res res; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (WARN_ON(!ipi_mbox)) { 27362306a36Sopenharmony_ci dev_err(dev, "no platform drv data??\n"); 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_TX) { 27862306a36Sopenharmony_ci /* Send request message */ 27962306a36Sopenharmony_ci if (msg && msg->len > mchan->req_buf_size) { 28062306a36Sopenharmony_ci dev_err(dev, "channel %d message length %u > max %lu\n", 28162306a36Sopenharmony_ci mchan->chan_type, (unsigned int)msg->len, 28262306a36Sopenharmony_ci mchan->req_buf_size); 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci if (msg && msg->len) 28662306a36Sopenharmony_ci memcpy_toio(mchan->req_buf, msg->data, msg->len); 28762306a36Sopenharmony_ci /* Kick IPI mailbox to send message */ 28862306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_NOTIFY; 28962306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci /* Send response message */ 29262306a36Sopenharmony_ci if (msg && msg->len > mchan->resp_buf_size) { 29362306a36Sopenharmony_ci dev_err(dev, "channel %d message length %u > max %lu\n", 29462306a36Sopenharmony_ci mchan->chan_type, (unsigned int)msg->len, 29562306a36Sopenharmony_ci mchan->resp_buf_size); 29662306a36Sopenharmony_ci return -EINVAL; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci if (msg && msg->len) 29962306a36Sopenharmony_ci memcpy_toio(mchan->resp_buf, msg->data, msg->len); 30062306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_ACK; 30162306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, IPI_SMC_ACK_EIRQ_MASK, 30262306a36Sopenharmony_ci &res); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * zynqmp_ipi_startup - Startup the IPI channel 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * @chan: Channel pointer 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * Return: 0 if all goes good, else return corresponding error message 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistatic int zynqmp_ipi_startup(struct mbox_chan *chan) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct device *dev = chan->mbox->dev; 31762306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 31862306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 31962306a36Sopenharmony_ci u64 arg0; 32062306a36Sopenharmony_ci struct arm_smccc_res res; 32162306a36Sopenharmony_ci int ret = 0; 32262306a36Sopenharmony_ci unsigned int nchan_type; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (mchan->is_opened) 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* If no channel has been opened, open the IPI mailbox */ 32862306a36Sopenharmony_ci nchan_type = (mchan->chan_type + 1) % 2; 32962306a36Sopenharmony_ci if (!ipi_mbox->mchans[nchan_type].is_opened) { 33062306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_OPEN; 33162306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 33262306a36Sopenharmony_ci /* Check the SMC call status, a0 of the result */ 33362306a36Sopenharmony_ci ret = (int)(res.a0 & 0xFFFFFFFF); 33462306a36Sopenharmony_ci if (ret < 0) { 33562306a36Sopenharmony_ci dev_err(dev, "SMC to open the IPI channel failed.\n"); 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci ret = 0; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* If it is RX channel, enable the IPI notification interrupt */ 34262306a36Sopenharmony_ci if (mchan->chan_type == IPI_MB_CHNL_RX) { 34362306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_ENABLE_IRQ; 34462306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci mchan->is_opened = 1; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/** 35262306a36Sopenharmony_ci * zynqmp_ipi_shutdown - Shutdown the IPI channel 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * @chan: Channel pointer 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_cistatic void zynqmp_ipi_shutdown(struct mbox_chan *chan) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct device *dev = chan->mbox->dev; 35962306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox = dev_get_drvdata(dev); 36062306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan = chan->con_priv; 36162306a36Sopenharmony_ci u64 arg0; 36262306a36Sopenharmony_ci struct arm_smccc_res res; 36362306a36Sopenharmony_ci unsigned int chan_type; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!mchan->is_opened) 36662306a36Sopenharmony_ci return; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* If it is RX channel, disable notification interrupt */ 36962306a36Sopenharmony_ci chan_type = mchan->chan_type; 37062306a36Sopenharmony_ci if (chan_type == IPI_MB_CHNL_RX) { 37162306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_DISABLE_IRQ; 37262306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci /* Release IPI mailbox if no other channel is opened */ 37562306a36Sopenharmony_ci chan_type = (chan_type + 1) % 2; 37662306a36Sopenharmony_ci if (!ipi_mbox->mchans[chan_type].is_opened) { 37762306a36Sopenharmony_ci arg0 = SMC_IPI_MAILBOX_RELEASE; 37862306a36Sopenharmony_ci zynqmp_ipi_fw_call(ipi_mbox, arg0, 0, &res); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci mchan->is_opened = 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* ZynqMP IPI mailbox operations */ 38562306a36Sopenharmony_cistatic const struct mbox_chan_ops zynqmp_ipi_chan_ops = { 38662306a36Sopenharmony_ci .startup = zynqmp_ipi_startup, 38762306a36Sopenharmony_ci .shutdown = zynqmp_ipi_shutdown, 38862306a36Sopenharmony_ci .peek_data = zynqmp_ipi_peek_data, 38962306a36Sopenharmony_ci .last_tx_done = zynqmp_ipi_last_tx_done, 39062306a36Sopenharmony_ci .send_data = zynqmp_ipi_send_data, 39162306a36Sopenharmony_ci}; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/** 39462306a36Sopenharmony_ci * zynqmp_ipi_of_xlate - Translate of phandle to IPI mailbox channel 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * @mbox: mailbox controller pointer 39762306a36Sopenharmony_ci * @p: phandle pointer 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Return: Mailbox channel, else return error pointer. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistatic struct mbox_chan *zynqmp_ipi_of_xlate(struct mbox_controller *mbox, 40262306a36Sopenharmony_ci const struct of_phandle_args *p) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct mbox_chan *chan; 40562306a36Sopenharmony_ci struct device *dev = mbox->dev; 40662306a36Sopenharmony_ci unsigned int chan_type; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Only supports TX and RX channels */ 40962306a36Sopenharmony_ci chan_type = p->args[0]; 41062306a36Sopenharmony_ci if (chan_type != IPI_MB_CHNL_TX && chan_type != IPI_MB_CHNL_RX) { 41162306a36Sopenharmony_ci dev_err(dev, "req chnl failure: invalid chnl type %u.\n", 41262306a36Sopenharmony_ci chan_type); 41362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci chan = &mbox->chans[chan_type]; 41662306a36Sopenharmony_ci return chan; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic const struct of_device_id zynqmp_ipi_of_match[] = { 42062306a36Sopenharmony_ci { .compatible = "xlnx,zynqmp-ipi-mailbox" }, 42162306a36Sopenharmony_ci {}, 42262306a36Sopenharmony_ci}; 42362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, zynqmp_ipi_of_match); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/** 42662306a36Sopenharmony_ci * zynqmp_ipi_mbox_get_buf_res - Get buffer resource from the IPI dev node 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * @node: IPI mbox device child node 42962306a36Sopenharmony_ci * @name: name of the IPI buffer 43062306a36Sopenharmony_ci * @res: pointer to where the resource information will be stored. 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * Return: 0 for success, negative value for failure 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_cistatic int zynqmp_ipi_mbox_get_buf_res(struct device_node *node, 43562306a36Sopenharmony_ci const char *name, 43662306a36Sopenharmony_ci struct resource *res) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int ret, index; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci index = of_property_match_string(node, "reg-names", name); 44162306a36Sopenharmony_ci if (index >= 0) { 44262306a36Sopenharmony_ci ret = of_address_to_resource(node, index, res); 44362306a36Sopenharmony_ci if (ret < 0) 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return -ENODEV; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/** 45162306a36Sopenharmony_ci * zynqmp_ipi_mbox_dev_release() - release the existence of a ipi mbox dev 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * @dev: the ipi mailbox device 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * This is to avoid the no device release() function kernel warning. 45662306a36Sopenharmony_ci * 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic void zynqmp_ipi_mbox_dev_release(struct device *dev) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci (void)dev; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * zynqmp_ipi_mbox_probe - probe IPI mailbox resource from device node 46562306a36Sopenharmony_ci * 46662306a36Sopenharmony_ci * @ipi_mbox: pointer to IPI mailbox private data structure 46762306a36Sopenharmony_ci * @node: IPI mailbox device node 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Return: 0 for success, negative value for failure 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_cistatic int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, 47262306a36Sopenharmony_ci struct device_node *node) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct zynqmp_ipi_mchan *mchan; 47562306a36Sopenharmony_ci struct mbox_chan *chans; 47662306a36Sopenharmony_ci struct mbox_controller *mbox; 47762306a36Sopenharmony_ci struct resource res; 47862306a36Sopenharmony_ci struct device *dev, *mdev; 47962306a36Sopenharmony_ci const char *name; 48062306a36Sopenharmony_ci int ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dev = ipi_mbox->pdata->dev; 48362306a36Sopenharmony_ci /* Initialize dev for IPI mailbox */ 48462306a36Sopenharmony_ci ipi_mbox->dev.parent = dev; 48562306a36Sopenharmony_ci ipi_mbox->dev.release = NULL; 48662306a36Sopenharmony_ci ipi_mbox->dev.of_node = node; 48762306a36Sopenharmony_ci dev_set_name(&ipi_mbox->dev, "%s", of_node_full_name(node)); 48862306a36Sopenharmony_ci dev_set_drvdata(&ipi_mbox->dev, ipi_mbox); 48962306a36Sopenharmony_ci ipi_mbox->dev.release = zynqmp_ipi_mbox_dev_release; 49062306a36Sopenharmony_ci ipi_mbox->dev.driver = &zynqmp_ipi_mbox_driver; 49162306a36Sopenharmony_ci ret = device_register(&ipi_mbox->dev); 49262306a36Sopenharmony_ci if (ret) { 49362306a36Sopenharmony_ci dev_err(dev, "Failed to register ipi mbox dev.\n"); 49462306a36Sopenharmony_ci put_device(&ipi_mbox->dev); 49562306a36Sopenharmony_ci return ret; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci mdev = &ipi_mbox->dev; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci mchan = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; 50062306a36Sopenharmony_ci name = "local_request_region"; 50162306a36Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 50262306a36Sopenharmony_ci if (!ret) { 50362306a36Sopenharmony_ci mchan->req_buf_size = resource_size(&res); 50462306a36Sopenharmony_ci mchan->req_buf = devm_ioremap(mdev, res.start, 50562306a36Sopenharmony_ci mchan->req_buf_size); 50662306a36Sopenharmony_ci if (!mchan->req_buf) { 50762306a36Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 50862306a36Sopenharmony_ci return -ENOMEM; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci } else if (ret != -ENODEV) { 51162306a36Sopenharmony_ci dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret); 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci name = "remote_response_region"; 51662306a36Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 51762306a36Sopenharmony_ci if (!ret) { 51862306a36Sopenharmony_ci mchan->resp_buf_size = resource_size(&res); 51962306a36Sopenharmony_ci mchan->resp_buf = devm_ioremap(mdev, res.start, 52062306a36Sopenharmony_ci mchan->resp_buf_size); 52162306a36Sopenharmony_ci if (!mchan->resp_buf) { 52262306a36Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 52362306a36Sopenharmony_ci return -ENOMEM; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci } else if (ret != -ENODEV) { 52662306a36Sopenharmony_ci dev_err(mdev, "Unmatched resource %s.\n", name); 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci mchan->rx_buf = devm_kzalloc(mdev, 53062306a36Sopenharmony_ci mchan->resp_buf_size + 53162306a36Sopenharmony_ci sizeof(struct zynqmp_ipi_message), 53262306a36Sopenharmony_ci GFP_KERNEL); 53362306a36Sopenharmony_ci if (!mchan->rx_buf) 53462306a36Sopenharmony_ci return -ENOMEM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci mchan = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; 53762306a36Sopenharmony_ci name = "remote_request_region"; 53862306a36Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 53962306a36Sopenharmony_ci if (!ret) { 54062306a36Sopenharmony_ci mchan->req_buf_size = resource_size(&res); 54162306a36Sopenharmony_ci mchan->req_buf = devm_ioremap(mdev, res.start, 54262306a36Sopenharmony_ci mchan->req_buf_size); 54362306a36Sopenharmony_ci if (!mchan->req_buf) { 54462306a36Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 54562306a36Sopenharmony_ci return -ENOMEM; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci } else if (ret != -ENODEV) { 54862306a36Sopenharmony_ci dev_err(mdev, "Unmatched resource %s.\n", name); 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci name = "local_response_region"; 55362306a36Sopenharmony_ci ret = zynqmp_ipi_mbox_get_buf_res(node, name, &res); 55462306a36Sopenharmony_ci if (!ret) { 55562306a36Sopenharmony_ci mchan->resp_buf_size = resource_size(&res); 55662306a36Sopenharmony_ci mchan->resp_buf = devm_ioremap(mdev, res.start, 55762306a36Sopenharmony_ci mchan->resp_buf_size); 55862306a36Sopenharmony_ci if (!mchan->resp_buf) { 55962306a36Sopenharmony_ci dev_err(mdev, "Unable to map IPI buffer I/O memory\n"); 56062306a36Sopenharmony_ci return -ENOMEM; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } else if (ret != -ENODEV) { 56362306a36Sopenharmony_ci dev_err(mdev, "Unmatched resource %s.\n", name); 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci mchan->rx_buf = devm_kzalloc(mdev, 56762306a36Sopenharmony_ci mchan->resp_buf_size + 56862306a36Sopenharmony_ci sizeof(struct zynqmp_ipi_message), 56962306a36Sopenharmony_ci GFP_KERNEL); 57062306a36Sopenharmony_ci if (!mchan->rx_buf) 57162306a36Sopenharmony_ci return -ENOMEM; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Get the IPI remote agent ID */ 57462306a36Sopenharmony_ci ret = of_property_read_u32(node, "xlnx,ipi-id", &ipi_mbox->remote_id); 57562306a36Sopenharmony_ci if (ret < 0) { 57662306a36Sopenharmony_ci dev_err(dev, "No IPI remote ID is specified.\n"); 57762306a36Sopenharmony_ci return ret; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci mbox = &ipi_mbox->mbox; 58162306a36Sopenharmony_ci mbox->dev = mdev; 58262306a36Sopenharmony_ci mbox->ops = &zynqmp_ipi_chan_ops; 58362306a36Sopenharmony_ci mbox->num_chans = 2; 58462306a36Sopenharmony_ci mbox->txdone_irq = false; 58562306a36Sopenharmony_ci mbox->txdone_poll = true; 58662306a36Sopenharmony_ci mbox->txpoll_period = 5; 58762306a36Sopenharmony_ci mbox->of_xlate = zynqmp_ipi_of_xlate; 58862306a36Sopenharmony_ci chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); 58962306a36Sopenharmony_ci if (!chans) 59062306a36Sopenharmony_ci return -ENOMEM; 59162306a36Sopenharmony_ci mbox->chans = chans; 59262306a36Sopenharmony_ci chans[IPI_MB_CHNL_TX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_TX]; 59362306a36Sopenharmony_ci chans[IPI_MB_CHNL_RX].con_priv = &ipi_mbox->mchans[IPI_MB_CHNL_RX]; 59462306a36Sopenharmony_ci ipi_mbox->mchans[IPI_MB_CHNL_TX].chan_type = IPI_MB_CHNL_TX; 59562306a36Sopenharmony_ci ipi_mbox->mchans[IPI_MB_CHNL_RX].chan_type = IPI_MB_CHNL_RX; 59662306a36Sopenharmony_ci ret = devm_mbox_controller_register(mdev, mbox); 59762306a36Sopenharmony_ci if (ret) 59862306a36Sopenharmony_ci dev_err(mdev, 59962306a36Sopenharmony_ci "Failed to register mbox_controller(%d)\n", ret); 60062306a36Sopenharmony_ci else 60162306a36Sopenharmony_ci dev_info(mdev, 60262306a36Sopenharmony_ci "Registered ZynqMP IPI mbox with TX/RX channels.\n"); 60362306a36Sopenharmony_ci return ret; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/** 60762306a36Sopenharmony_ci * zynqmp_ipi_free_mboxes - Free IPI mailboxes devices 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * @pdata: IPI private data 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_cistatic void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct zynqmp_ipi_mbox *ipi_mbox; 61462306a36Sopenharmony_ci int i; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci i = pdata->num_mboxes; 61762306a36Sopenharmony_ci for (; i >= 0; i--) { 61862306a36Sopenharmony_ci ipi_mbox = &pdata->ipi_mboxes[i]; 61962306a36Sopenharmony_ci if (ipi_mbox->dev.parent) { 62062306a36Sopenharmony_ci mbox_controller_unregister(&ipi_mbox->mbox); 62162306a36Sopenharmony_ci if (device_is_registered(&ipi_mbox->dev)) 62262306a36Sopenharmony_ci device_unregister(&ipi_mbox->dev); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int zynqmp_ipi_probe(struct platform_device *pdev) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 63062306a36Sopenharmony_ci struct device_node *nc, *np = pdev->dev.of_node; 63162306a36Sopenharmony_ci struct zynqmp_ipi_pdata *pdata; 63262306a36Sopenharmony_ci struct zynqmp_ipi_mbox *mbox; 63362306a36Sopenharmony_ci int num_mboxes, ret = -EINVAL; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci num_mboxes = of_get_available_child_count(np); 63662306a36Sopenharmony_ci if (num_mboxes == 0) { 63762306a36Sopenharmony_ci dev_err(dev, "mailbox nodes not available\n"); 63862306a36Sopenharmony_ci return -EINVAL; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci pdata = devm_kzalloc(dev, struct_size(pdata, ipi_mboxes, num_mboxes), 64262306a36Sopenharmony_ci GFP_KERNEL); 64362306a36Sopenharmony_ci if (!pdata) 64462306a36Sopenharmony_ci return -ENOMEM; 64562306a36Sopenharmony_ci pdata->dev = dev; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* Get the IPI local agents ID */ 64862306a36Sopenharmony_ci ret = of_property_read_u32(np, "xlnx,ipi-id", &pdata->local_id); 64962306a36Sopenharmony_ci if (ret < 0) { 65062306a36Sopenharmony_ci dev_err(dev, "No IPI local ID is specified.\n"); 65162306a36Sopenharmony_ci return ret; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci pdata->num_mboxes = num_mboxes; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci mbox = pdata->ipi_mboxes; 65762306a36Sopenharmony_ci for_each_available_child_of_node(np, nc) { 65862306a36Sopenharmony_ci mbox->pdata = pdata; 65962306a36Sopenharmony_ci ret = zynqmp_ipi_mbox_probe(mbox, nc); 66062306a36Sopenharmony_ci if (ret) { 66162306a36Sopenharmony_ci of_node_put(nc); 66262306a36Sopenharmony_ci dev_err(dev, "failed to probe subdev.\n"); 66362306a36Sopenharmony_ci ret = -EINVAL; 66462306a36Sopenharmony_ci goto free_mbox_dev; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci mbox++; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* IPI IRQ */ 67062306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 67162306a36Sopenharmony_ci if (ret < 0) 67262306a36Sopenharmony_ci goto free_mbox_dev; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci pdata->irq = ret; 67562306a36Sopenharmony_ci ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, 67662306a36Sopenharmony_ci IRQF_SHARED, dev_name(dev), pdata); 67762306a36Sopenharmony_ci if (ret) { 67862306a36Sopenharmony_ci dev_err(dev, "IRQ %d is not requested successfully.\n", 67962306a36Sopenharmony_ci pdata->irq); 68062306a36Sopenharmony_ci goto free_mbox_dev; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci platform_set_drvdata(pdev, pdata); 68462306a36Sopenharmony_ci return ret; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cifree_mbox_dev: 68762306a36Sopenharmony_ci zynqmp_ipi_free_mboxes(pdata); 68862306a36Sopenharmony_ci return ret; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int zynqmp_ipi_remove(struct platform_device *pdev) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct zynqmp_ipi_pdata *pdata; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci pdata = platform_get_drvdata(pdev); 69662306a36Sopenharmony_ci zynqmp_ipi_free_mboxes(pdata); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic struct platform_driver zynqmp_ipi_driver = { 70262306a36Sopenharmony_ci .probe = zynqmp_ipi_probe, 70362306a36Sopenharmony_ci .remove = zynqmp_ipi_remove, 70462306a36Sopenharmony_ci .driver = { 70562306a36Sopenharmony_ci .name = "zynqmp-ipi", 70662306a36Sopenharmony_ci .of_match_table = of_match_ptr(zynqmp_ipi_of_match), 70762306a36Sopenharmony_ci }, 70862306a36Sopenharmony_ci}; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int __init zynqmp_ipi_init(void) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci return platform_driver_register(&zynqmp_ipi_driver); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_cisubsys_initcall(zynqmp_ipi_init); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void __exit zynqmp_ipi_exit(void) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci platform_driver_unregister(&zynqmp_ipi_driver); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_cimodule_exit(zynqmp_ipi_exit); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 72362306a36Sopenharmony_ciMODULE_DESCRIPTION("Xilinx ZynqMP IPI Mailbox driver"); 72462306a36Sopenharmony_ciMODULE_AUTHOR("Xilinx Inc."); 725