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