Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/interrupt.h>
0008 #include <linux/io.h>
0009 #include <linux/kernel.h>
0010 #include <linux/mailbox_controller.h>
0011 #include <linux/module.h>
0012 #include <linux/of_device.h>
0013 #include <linux/platform_device.h>
0014 
0015 #define MAILBOX_A2B_INTEN       0x00
0016 #define MAILBOX_A2B_STATUS      0x04
0017 #define MAILBOX_A2B_CMD(x)      (0x08 + (x) * 8)
0018 #define MAILBOX_A2B_DAT(x)      (0x0c + (x) * 8)
0019 
0020 #define MAILBOX_B2A_INTEN       0x28
0021 #define MAILBOX_B2A_STATUS      0x2C
0022 #define MAILBOX_B2A_CMD(x)      (0x30 + (x) * 8)
0023 #define MAILBOX_B2A_DAT(x)      (0x34 + (x) * 8)
0024 
0025 struct rockchip_mbox_msg {
0026     u32 cmd;
0027     int rx_size;
0028 };
0029 
0030 struct rockchip_mbox_data {
0031     int num_chans;
0032 };
0033 
0034 struct rockchip_mbox_chan {
0035     int idx;
0036     int irq;
0037     struct rockchip_mbox_msg *msg;
0038     struct rockchip_mbox *mb;
0039 };
0040 
0041 struct rockchip_mbox {
0042     struct mbox_controller mbox;
0043     struct clk *pclk;
0044     void __iomem *mbox_base;
0045 
0046     /* The maximum size of buf for each channel */
0047     u32 buf_size;
0048 
0049     struct rockchip_mbox_chan *chans;
0050 };
0051 
0052 static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data)
0053 {
0054     struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
0055     struct rockchip_mbox_msg *msg = data;
0056     struct rockchip_mbox_chan *chans = mb->chans;
0057 
0058     if (!msg)
0059         return -EINVAL;
0060 
0061     if (msg->rx_size > mb->buf_size) {
0062         dev_err(mb->mbox.dev, "Transmit size over buf size(%d)\n",
0063             mb->buf_size);
0064         return -EINVAL;
0065     }
0066 
0067     dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x\n",
0068         chans->idx, msg->cmd);
0069 
0070     mb->chans[chans->idx].msg = msg;
0071 
0072     writel_relaxed(msg->cmd, mb->mbox_base + MAILBOX_A2B_CMD(chans->idx));
0073     writel_relaxed(msg->rx_size, mb->mbox_base +
0074                MAILBOX_A2B_DAT(chans->idx));
0075 
0076     return 0;
0077 }
0078 
0079 static int rockchip_mbox_startup(struct mbox_chan *chan)
0080 {
0081     struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
0082 
0083     /* Enable all B2A interrupts */
0084     writel_relaxed((1 << mb->mbox.num_chans) - 1,
0085                mb->mbox_base + MAILBOX_B2A_INTEN);
0086 
0087     return 0;
0088 }
0089 
0090 static void rockchip_mbox_shutdown(struct mbox_chan *chan)
0091 {
0092     struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
0093     struct rockchip_mbox_chan *chans = mb->chans;
0094 
0095     /* Disable all B2A interrupts */
0096     writel_relaxed(0, mb->mbox_base + MAILBOX_B2A_INTEN);
0097 
0098     mb->chans[chans->idx].msg = NULL;
0099 }
0100 
0101 static const struct mbox_chan_ops rockchip_mbox_chan_ops = {
0102     .send_data  = rockchip_mbox_send_data,
0103     .startup    = rockchip_mbox_startup,
0104     .shutdown   = rockchip_mbox_shutdown,
0105 };
0106 
0107 static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id)
0108 {
0109     int idx;
0110     struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id;
0111     u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS);
0112 
0113     for (idx = 0; idx < mb->mbox.num_chans; idx++) {
0114         if ((status & (1 << idx)) && (irq == mb->chans[idx].irq)) {
0115             /* Clear mbox interrupt */
0116             writel_relaxed(1 << idx,
0117                        mb->mbox_base + MAILBOX_B2A_STATUS);
0118             return IRQ_WAKE_THREAD;
0119         }
0120     }
0121 
0122     return IRQ_NONE;
0123 }
0124 
0125 static irqreturn_t rockchip_mbox_isr(int irq, void *dev_id)
0126 {
0127     int idx;
0128     struct rockchip_mbox_msg *msg = NULL;
0129     struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id;
0130 
0131     for (idx = 0; idx < mb->mbox.num_chans; idx++) {
0132         if (irq != mb->chans[idx].irq)
0133             continue;
0134 
0135         msg = mb->chans[idx].msg;
0136         if (!msg) {
0137             dev_err(mb->mbox.dev,
0138                 "Chan[%d]: B2A message is NULL\n", idx);
0139             break; /* spurious */
0140         }
0141 
0142         mbox_chan_received_data(&mb->mbox.chans[idx], msg);
0143         mb->chans[idx].msg = NULL;
0144 
0145         dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n",
0146             idx, msg->cmd);
0147 
0148         break;
0149     }
0150 
0151     return IRQ_HANDLED;
0152 }
0153 
0154 static const struct rockchip_mbox_data rk3368_drv_data = {
0155     .num_chans = 4,
0156 };
0157 
0158 static const struct of_device_id rockchip_mbox_of_match[] = {
0159     { .compatible = "rockchip,rk3368-mailbox", .data = &rk3368_drv_data},
0160     { },
0161 };
0162 MODULE_DEVICE_TABLE(of, rockchp_mbox_of_match);
0163 
0164 static int rockchip_mbox_probe(struct platform_device *pdev)
0165 {
0166     struct rockchip_mbox *mb;
0167     const struct of_device_id *match;
0168     const struct rockchip_mbox_data *drv_data;
0169     struct resource *res;
0170     int ret, irq, i;
0171 
0172     if (!pdev->dev.of_node)
0173         return -ENODEV;
0174 
0175     match = of_match_node(rockchip_mbox_of_match, pdev->dev.of_node);
0176     drv_data = (const struct rockchip_mbox_data *)match->data;
0177 
0178     mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
0179     if (!mb)
0180         return -ENOMEM;
0181 
0182     mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
0183                  sizeof(*mb->chans), GFP_KERNEL);
0184     if (!mb->chans)
0185         return -ENOMEM;
0186 
0187     mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
0188                       sizeof(*mb->mbox.chans), GFP_KERNEL);
0189     if (!mb->mbox.chans)
0190         return -ENOMEM;
0191 
0192     platform_set_drvdata(pdev, mb);
0193 
0194     mb->mbox.dev = &pdev->dev;
0195     mb->mbox.num_chans = drv_data->num_chans;
0196     mb->mbox.ops = &rockchip_mbox_chan_ops;
0197     mb->mbox.txdone_irq = true;
0198 
0199     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0200     if (!res)
0201         return -ENODEV;
0202 
0203     mb->mbox_base = devm_ioremap_resource(&pdev->dev, res);
0204     if (IS_ERR(mb->mbox_base))
0205         return PTR_ERR(mb->mbox_base);
0206 
0207     /* Each channel has two buffers for A2B and B2A */
0208     mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2);
0209 
0210     mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox");
0211     if (IS_ERR(mb->pclk)) {
0212         ret = PTR_ERR(mb->pclk);
0213         dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n",
0214             ret);
0215         return ret;
0216     }
0217 
0218     ret = clk_prepare_enable(mb->pclk);
0219     if (ret) {
0220         dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret);
0221         return ret;
0222     }
0223 
0224     for (i = 0; i < mb->mbox.num_chans; i++) {
0225         irq = platform_get_irq(pdev, i);
0226         if (irq < 0)
0227             return irq;
0228 
0229         ret = devm_request_threaded_irq(&pdev->dev, irq,
0230                         rockchip_mbox_irq,
0231                         rockchip_mbox_isr, IRQF_ONESHOT,
0232                         dev_name(&pdev->dev), mb);
0233         if (ret < 0)
0234             return ret;
0235 
0236         mb->chans[i].idx = i;
0237         mb->chans[i].irq = irq;
0238         mb->chans[i].mb = mb;
0239         mb->chans[i].msg = NULL;
0240     }
0241 
0242     ret = devm_mbox_controller_register(&pdev->dev, &mb->mbox);
0243     if (ret < 0)
0244         dev_err(&pdev->dev, "Failed to register mailbox: %d\n", ret);
0245 
0246     return ret;
0247 }
0248 
0249 static struct platform_driver rockchip_mbox_driver = {
0250     .probe  = rockchip_mbox_probe,
0251     .driver = {
0252         .name = "rockchip-mailbox",
0253         .of_match_table = of_match_ptr(rockchip_mbox_of_match),
0254     },
0255 };
0256 
0257 module_platform_driver(rockchip_mbox_driver);
0258 
0259 MODULE_LICENSE("GPL v2");
0260 MODULE_DESCRIPTION("Rockchip mailbox: communicate between CPU cores and MCU");
0261 MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
0262 MODULE_AUTHOR("Caesar Wang <wxt@rock-chips.com>");