0001
0002
0003
0004
0005 #include <linux/bitops.h>
0006 #include <linux/clk.h>
0007 #include <linux/device.h>
0008 #include <linux/err.h>
0009 #include <linux/interrupt.h>
0010 #include <linux/io.h>
0011 #include <linux/kernel.h>
0012 #include <linux/mailbox_controller.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/of_irq.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/reset.h>
0018 #include <linux/spinlock.h>
0019
0020 #define NUM_CHANS 8
0021
0022 #define CTRL_REG(n) (0x0000 + 0x4 * ((n) / 4))
0023 #define CTRL_RX(n) BIT(0 + 8 * ((n) % 4))
0024 #define CTRL_TX(n) BIT(4 + 8 * ((n) % 4))
0025
0026 #define REMOTE_IRQ_EN_REG 0x0040
0027 #define REMOTE_IRQ_STAT_REG 0x0050
0028 #define LOCAL_IRQ_EN_REG 0x0060
0029 #define LOCAL_IRQ_STAT_REG 0x0070
0030
0031 #define RX_IRQ(n) BIT(0 + 2 * (n))
0032 #define RX_IRQ_MASK 0x5555
0033 #define TX_IRQ(n) BIT(1 + 2 * (n))
0034 #define TX_IRQ_MASK 0xaaaa
0035
0036 #define FIFO_STAT_REG(n) (0x0100 + 0x4 * (n))
0037 #define FIFO_STAT_MASK GENMASK(0, 0)
0038
0039 #define MSG_STAT_REG(n) (0x0140 + 0x4 * (n))
0040 #define MSG_STAT_MASK GENMASK(2, 0)
0041
0042 #define MSG_DATA_REG(n) (0x0180 + 0x4 * (n))
0043
0044 #define mbox_dbg(mbox, ...) dev_dbg((mbox)->controller.dev, __VA_ARGS__)
0045
0046 struct sun6i_msgbox {
0047 struct mbox_controller controller;
0048 struct clk *clk;
0049 spinlock_t lock;
0050 void __iomem *regs;
0051 };
0052
0053 static bool sun6i_msgbox_last_tx_done(struct mbox_chan *chan);
0054 static bool sun6i_msgbox_peek_data(struct mbox_chan *chan);
0055
0056 static inline int channel_number(struct mbox_chan *chan)
0057 {
0058 return chan - chan->mbox->chans;
0059 }
0060
0061 static inline struct sun6i_msgbox *to_sun6i_msgbox(struct mbox_chan *chan)
0062 {
0063 return chan->con_priv;
0064 }
0065
0066 static irqreturn_t sun6i_msgbox_irq(int irq, void *dev_id)
0067 {
0068 struct sun6i_msgbox *mbox = dev_id;
0069 uint32_t status;
0070 int n;
0071
0072
0073 status = readl(mbox->regs + LOCAL_IRQ_EN_REG) &
0074 readl(mbox->regs + LOCAL_IRQ_STAT_REG);
0075
0076 if (!(status & RX_IRQ_MASK))
0077 return IRQ_NONE;
0078
0079 for (n = 0; n < NUM_CHANS; ++n) {
0080 struct mbox_chan *chan = &mbox->controller.chans[n];
0081
0082 if (!(status & RX_IRQ(n)))
0083 continue;
0084
0085 while (sun6i_msgbox_peek_data(chan)) {
0086 uint32_t msg = readl(mbox->regs + MSG_DATA_REG(n));
0087
0088 mbox_dbg(mbox, "Channel %d received 0x%08x\n", n, msg);
0089 mbox_chan_received_data(chan, &msg);
0090 }
0091
0092
0093 writel(RX_IRQ(n), mbox->regs + LOCAL_IRQ_STAT_REG);
0094 }
0095
0096 return IRQ_HANDLED;
0097 }
0098
0099 static int sun6i_msgbox_send_data(struct mbox_chan *chan, void *data)
0100 {
0101 struct sun6i_msgbox *mbox = to_sun6i_msgbox(chan);
0102 int n = channel_number(chan);
0103 uint32_t msg = *(uint32_t *)data;
0104
0105
0106 if (WARN_ON_ONCE(!(readl(mbox->regs + CTRL_REG(n)) & CTRL_TX(n))))
0107 return 0;
0108
0109 writel(msg, mbox->regs + MSG_DATA_REG(n));
0110 mbox_dbg(mbox, "Channel %d sent 0x%08x\n", n, msg);
0111
0112 return 0;
0113 }
0114
0115 static int sun6i_msgbox_startup(struct mbox_chan *chan)
0116 {
0117 struct sun6i_msgbox *mbox = to_sun6i_msgbox(chan);
0118 int n = channel_number(chan);
0119
0120
0121 if (readl(mbox->regs + CTRL_REG(n)) & CTRL_RX(n)) {
0122
0123 while (sun6i_msgbox_peek_data(chan))
0124 readl(mbox->regs + MSG_DATA_REG(n));
0125 writel(RX_IRQ(n), mbox->regs + LOCAL_IRQ_STAT_REG);
0126
0127
0128 spin_lock(&mbox->lock);
0129 writel(readl(mbox->regs + LOCAL_IRQ_EN_REG) | RX_IRQ(n),
0130 mbox->regs + LOCAL_IRQ_EN_REG);
0131 spin_unlock(&mbox->lock);
0132 }
0133
0134 mbox_dbg(mbox, "Channel %d startup complete\n", n);
0135
0136 return 0;
0137 }
0138
0139 static void sun6i_msgbox_shutdown(struct mbox_chan *chan)
0140 {
0141 struct sun6i_msgbox *mbox = to_sun6i_msgbox(chan);
0142 int n = channel_number(chan);
0143
0144 if (readl(mbox->regs + CTRL_REG(n)) & CTRL_RX(n)) {
0145
0146 spin_lock(&mbox->lock);
0147 writel(readl(mbox->regs + LOCAL_IRQ_EN_REG) & ~RX_IRQ(n),
0148 mbox->regs + LOCAL_IRQ_EN_REG);
0149 spin_unlock(&mbox->lock);
0150
0151
0152 do {
0153 while (sun6i_msgbox_peek_data(chan))
0154 readl(mbox->regs + MSG_DATA_REG(n));
0155 writel(RX_IRQ(n), mbox->regs + LOCAL_IRQ_STAT_REG);
0156 } while (readl(mbox->regs + LOCAL_IRQ_STAT_REG) & RX_IRQ(n));
0157 }
0158
0159 mbox_dbg(mbox, "Channel %d shutdown complete\n", n);
0160 }
0161
0162 static bool sun6i_msgbox_last_tx_done(struct mbox_chan *chan)
0163 {
0164 struct sun6i_msgbox *mbox = to_sun6i_msgbox(chan);
0165 int n = channel_number(chan);
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 return !(readl(mbox->regs + REMOTE_IRQ_STAT_REG) & RX_IRQ(n));
0177 }
0178
0179 static bool sun6i_msgbox_peek_data(struct mbox_chan *chan)
0180 {
0181 struct sun6i_msgbox *mbox = to_sun6i_msgbox(chan);
0182 int n = channel_number(chan);
0183
0184 return readl(mbox->regs + MSG_STAT_REG(n)) & MSG_STAT_MASK;
0185 }
0186
0187 static const struct mbox_chan_ops sun6i_msgbox_chan_ops = {
0188 .send_data = sun6i_msgbox_send_data,
0189 .startup = sun6i_msgbox_startup,
0190 .shutdown = sun6i_msgbox_shutdown,
0191 .last_tx_done = sun6i_msgbox_last_tx_done,
0192 .peek_data = sun6i_msgbox_peek_data,
0193 };
0194
0195 static int sun6i_msgbox_probe(struct platform_device *pdev)
0196 {
0197 struct device *dev = &pdev->dev;
0198 struct mbox_chan *chans;
0199 struct reset_control *reset;
0200 struct sun6i_msgbox *mbox;
0201 int i, ret;
0202
0203 mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
0204 if (!mbox)
0205 return -ENOMEM;
0206
0207 chans = devm_kcalloc(dev, NUM_CHANS, sizeof(*chans), GFP_KERNEL);
0208 if (!chans)
0209 return -ENOMEM;
0210
0211 for (i = 0; i < NUM_CHANS; ++i)
0212 chans[i].con_priv = mbox;
0213
0214 mbox->clk = devm_clk_get(dev, NULL);
0215 if (IS_ERR(mbox->clk)) {
0216 ret = PTR_ERR(mbox->clk);
0217 dev_err(dev, "Failed to get clock: %d\n", ret);
0218 return ret;
0219 }
0220
0221 ret = clk_prepare_enable(mbox->clk);
0222 if (ret) {
0223 dev_err(dev, "Failed to enable clock: %d\n", ret);
0224 return ret;
0225 }
0226
0227 reset = devm_reset_control_get_exclusive(dev, NULL);
0228 if (IS_ERR(reset)) {
0229 ret = PTR_ERR(reset);
0230 dev_err(dev, "Failed to get reset control: %d\n", ret);
0231 goto err_disable_unprepare;
0232 }
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242 ret = reset_control_deassert(reset);
0243 if (ret) {
0244 dev_err(dev, "Failed to deassert reset: %d\n", ret);
0245 goto err_disable_unprepare;
0246 }
0247
0248 mbox->regs = devm_platform_ioremap_resource(pdev, 0);
0249 if (IS_ERR(mbox->regs)) {
0250 ret = PTR_ERR(mbox->regs);
0251 dev_err(dev, "Failed to map MMIO resource: %d\n", ret);
0252 goto err_disable_unprepare;
0253 }
0254
0255
0256 writel(0, mbox->regs + LOCAL_IRQ_EN_REG);
0257
0258 ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
0259 sun6i_msgbox_irq, 0, dev_name(dev), mbox);
0260 if (ret) {
0261 dev_err(dev, "Failed to register IRQ handler: %d\n", ret);
0262 goto err_disable_unprepare;
0263 }
0264
0265 mbox->controller.dev = dev;
0266 mbox->controller.ops = &sun6i_msgbox_chan_ops;
0267 mbox->controller.chans = chans;
0268 mbox->controller.num_chans = NUM_CHANS;
0269 mbox->controller.txdone_irq = false;
0270 mbox->controller.txdone_poll = true;
0271 mbox->controller.txpoll_period = 5;
0272
0273 spin_lock_init(&mbox->lock);
0274 platform_set_drvdata(pdev, mbox);
0275
0276 ret = mbox_controller_register(&mbox->controller);
0277 if (ret) {
0278 dev_err(dev, "Failed to register controller: %d\n", ret);
0279 goto err_disable_unprepare;
0280 }
0281
0282 return 0;
0283
0284 err_disable_unprepare:
0285 clk_disable_unprepare(mbox->clk);
0286
0287 return ret;
0288 }
0289
0290 static int sun6i_msgbox_remove(struct platform_device *pdev)
0291 {
0292 struct sun6i_msgbox *mbox = platform_get_drvdata(pdev);
0293
0294 mbox_controller_unregister(&mbox->controller);
0295
0296 clk_disable_unprepare(mbox->clk);
0297
0298 return 0;
0299 }
0300
0301 static const struct of_device_id sun6i_msgbox_of_match[] = {
0302 { .compatible = "allwinner,sun6i-a31-msgbox", },
0303 {},
0304 };
0305 MODULE_DEVICE_TABLE(of, sun6i_msgbox_of_match);
0306
0307 static struct platform_driver sun6i_msgbox_driver = {
0308 .driver = {
0309 .name = "sun6i-msgbox",
0310 .of_match_table = sun6i_msgbox_of_match,
0311 },
0312 .probe = sun6i_msgbox_probe,
0313 .remove = sun6i_msgbox_remove,
0314 };
0315 module_platform_driver(sun6i_msgbox_driver);
0316
0317 MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
0318 MODULE_DESCRIPTION("Allwinner sun6i/sun8i/sun9i/sun50i Message Box");
0319 MODULE_LICENSE("GPL v2");