1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2025 MediaTek Corporation. All rights reserved. 4 * Author: Jjian Zhou <jjian.zhou.@mediatek.com> 5 */ 6 7 #include <linux/interrupt.h> 8 #include <linux/io.h> 9 #include <linux/kernel.h> 10 #include <linux/mailbox_controller.h> 11 #include <linux/mailbox/mtk-vcp-mailbox.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 17 struct mtk_vcp_mbox { 18 struct mbox_controller mbox; 19 void __iomem *base; 20 struct device *dev; 21 const struct mtk_vcp_mbox_cfg *cfg; 22 struct mtk_ipi_info ipi_recv; 23 struct mbox_chan chans; 24 }; 25 26 struct mtk_vcp_mbox_cfg { 27 u16 set_in; 28 u16 clr_out; 29 }; 30 31 static irqreturn_t mtk_vcp_mbox_irq_thread(int irq, void *data) 32 { 33 struct mtk_vcp_mbox *priv = data; 34 35 /* get irq status */ 36 priv->ipi_recv.irq_status = readl(priv->base + priv->cfg->clr_out); 37 38 __ioread32_copy(priv->ipi_recv.msg, priv->base, 39 MTK_VCP_MBOX_SLOT_MAX_SIZE / 4); 40 41 mbox_chan_received_data(&priv->chans, &priv->ipi_recv); 42 43 /* clear irq status */ 44 writel(priv->ipi_recv.irq_status, priv->base + priv->cfg->clr_out); 45 46 return IRQ_HANDLED; 47 } 48 49 static struct mbox_chan *mtk_vcp_mbox_xlate(struct mbox_controller *mbox, 50 const struct of_phandle_args *sp) 51 { 52 if (sp->args_count) 53 return NULL; 54 55 return &mbox->chans[0]; 56 } 57 58 static int mtk_vcp_mbox_send_data(struct mbox_chan *chan, void *data) 59 { 60 struct mtk_vcp_mbox *priv = chan->con_priv; 61 struct mtk_ipi_info *ipi_info = data; 62 u32 status; 63 64 if (!ipi_info->msg) { 65 dev_err(priv->dev, "msg buffer is NULL.\n"); 66 return -EINVAL; 67 } 68 69 status = readl(priv->base + priv->cfg->set_in); 70 if (status & BIT(ipi_info->index)) { 71 dev_warn(priv->dev, "mailbox IPI %d is busy.\n", ipi_info->id); 72 return -EBUSY; 73 } 74 75 if (ipi_info->slot_ofs + ipi_info->len > MTK_VCP_MBOX_SLOT_MAX_SIZE) 76 return -EINVAL; 77 __iowrite32_copy(priv->base + ipi_info->slot_ofs, ipi_info->msg, 78 ipi_info->len); 79 80 writel(BIT(ipi_info->index), priv->base + priv->cfg->set_in); 81 82 return 0; 83 } 84 85 static bool mtk_vcp_mbox_last_tx_done(struct mbox_chan *chan) 86 { 87 struct mtk_ipi_info *ipi_info = chan->active_req; 88 struct mtk_vcp_mbox *priv = chan->con_priv; 89 90 return !(readl(priv->base + priv->cfg->set_in) & BIT(ipi_info->index)); 91 } 92 93 static const struct mbox_chan_ops mtk_vcp_mbox_chan_ops = { 94 .send_data = mtk_vcp_mbox_send_data, 95 .last_tx_done = mtk_vcp_mbox_last_tx_done, 96 }; 97 98 static int mtk_vcp_mbox_probe(struct platform_device *pdev) 99 { 100 struct device *dev = &pdev->dev; 101 struct mtk_vcp_mbox *priv; 102 struct mbox_controller *mbox; 103 int ret, irq; 104 105 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 106 if (!priv) 107 return -ENOMEM; 108 109 priv->dev = dev; 110 priv->chans.con_priv = priv; 111 mbox = &priv->mbox; 112 mbox->dev = dev; 113 mbox->ops = &mtk_vcp_mbox_chan_ops; 114 mbox->txdone_irq = false; 115 mbox->txdone_poll = true; 116 mbox->of_xlate = mtk_vcp_mbox_xlate; 117 mbox->num_chans = 1; 118 mbox->chans = &priv->chans; 119 120 priv->ipi_recv.msg = devm_kzalloc(dev, MTK_VCP_MBOX_SLOT_MAX_SIZE, 121 GFP_KERNEL); 122 if (!priv->ipi_recv.msg) 123 return -ENOMEM; 124 125 priv->base = devm_platform_ioremap_resource(pdev, 0); 126 if (IS_ERR(priv->base)) 127 return PTR_ERR(priv->base); 128 129 priv->cfg = of_device_get_match_data(dev); 130 if (!priv->cfg) 131 return -EINVAL; 132 133 platform_set_drvdata(pdev, priv); 134 135 irq = platform_get_irq(pdev, 0); 136 if (irq < 0) 137 return irq; 138 139 ret = devm_request_threaded_irq(dev, irq, NULL, 140 mtk_vcp_mbox_irq_thread, IRQF_ONESHOT, 141 dev_name(dev), priv); 142 if (ret < 0) 143 return ret; 144 145 return devm_mbox_controller_register(dev, &priv->mbox); 146 } 147 148 static const struct mtk_vcp_mbox_cfg mt8196_cfg = { 149 .set_in = 0x100, 150 .clr_out = 0x10c, 151 }; 152 153 static const struct of_device_id mtk_vcp_mbox_of_match[] = { 154 { .compatible = "mediatek,mt8196-vcp-mbox", .data = &mt8196_cfg }, 155 {}, 156 }; 157 MODULE_DEVICE_TABLE(of, mtk_vcp_mbox_of_match); 158 159 static struct platform_driver mtk_vcp_mbox_driver = { 160 .probe = mtk_vcp_mbox_probe, 161 .driver = { 162 .name = "mtk_vcp_mbox", 163 .of_match_table = mtk_vcp_mbox_of_match, 164 }, 165 }; 166 module_platform_driver(mtk_vcp_mbox_driver); 167 168 MODULE_AUTHOR("Jjian Zhou <jjian.zhou@mediatek.com>"); 169 MODULE_DESCRIPTION("MTK VCP Mailbox Controller"); 170 MODULE_LICENSE("GPL"); 171