414 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Spreadtrum mailbox driver4 *5 * Copyright (c) 2020 Spreadtrum Communications Inc.6 */7 8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/interrupt.h>11#include <linux/io.h>12#include <linux/mailbox_controller.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/clk.h>17 18#define SPRD_MBOX_ID 0x019#define SPRD_MBOX_MSG_LOW 0x420#define SPRD_MBOX_MSG_HIGH 0x821#define SPRD_MBOX_TRIGGER 0xc22#define SPRD_MBOX_FIFO_RST 0x1023#define SPRD_MBOX_FIFO_STS 0x1424#define SPRD_MBOX_IRQ_STS 0x1825#define SPRD_MBOX_IRQ_MSK 0x1c26#define SPRD_MBOX_LOCK 0x2027#define SPRD_MBOX_FIFO_DEPTH 0x2428 29/* Bit and mask definition for inbox's SPRD_MBOX_FIFO_STS register */30#define SPRD_INBOX_FIFO_DELIVER_MASK GENMASK(23, 16)31#define SPRD_INBOX_FIFO_OVERLOW_MASK GENMASK(15, 8)32#define SPRD_INBOX_FIFO_DELIVER_SHIFT 1633#define SPRD_INBOX_FIFO_BUSY_MASK GENMASK(7, 0)34 35/* Bit and mask definition for SPRD_MBOX_IRQ_STS register */36#define SPRD_MBOX_IRQ_CLR BIT(0)37 38/* Bit and mask definition for outbox's SPRD_MBOX_FIFO_STS register */39#define SPRD_OUTBOX_FIFO_FULL BIT(2)40#define SPRD_OUTBOX_FIFO_WR_SHIFT 1641#define SPRD_OUTBOX_FIFO_RD_SHIFT 2442#define SPRD_OUTBOX_FIFO_POS_MASK GENMASK(7, 0)43 44/* Bit and mask definition for inbox's SPRD_MBOX_IRQ_MSK register */45#define SPRD_INBOX_FIFO_BLOCK_IRQ BIT(0)46#define SPRD_INBOX_FIFO_OVERFLOW_IRQ BIT(1)47#define SPRD_INBOX_FIFO_DELIVER_IRQ BIT(2)48#define SPRD_INBOX_FIFO_IRQ_MASK GENMASK(2, 0)49 50/* Bit and mask definition for outbox's SPRD_MBOX_IRQ_MSK register */51#define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ BIT(0)52#define SPRD_OUTBOX_FIFO_IRQ_MASK GENMASK(4, 0)53 54#define SPRD_OUTBOX_BASE_SPAN 0x100055#define SPRD_MBOX_CHAN_MAX 856#define SPRD_SUPP_INBOX_ID_SC9863A 757 58struct sprd_mbox_priv {59 struct mbox_controller mbox;60 struct device *dev;61 void __iomem *inbox_base;62 void __iomem *outbox_base;63 /* Base register address for supplementary outbox */64 void __iomem *supp_base;65 u32 outbox_fifo_depth;66 67 struct mutex lock;68 u32 refcnt;69 struct mbox_chan chan[SPRD_MBOX_CHAN_MAX];70};71 72static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox)73{74 return container_of(mbox, struct sprd_mbox_priv, mbox);75}76 77static u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts)78{79 u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) &80 SPRD_OUTBOX_FIFO_POS_MASK;81 u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) &82 SPRD_OUTBOX_FIFO_POS_MASK;83 u32 fifo_len;84 85 /*86 * If the read pointer is equal with write pointer, which means the fifo87 * is full or empty.88 */89 if (wr_pos == rd_pos) {90 if (fifo_sts & SPRD_OUTBOX_FIFO_FULL)91 fifo_len = priv->outbox_fifo_depth;92 else93 fifo_len = 0;94 } else if (wr_pos > rd_pos) {95 fifo_len = wr_pos - rd_pos;96 } else {97 fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos;98 }99 100 return fifo_len;101}102 103static irqreturn_t do_outbox_isr(void __iomem *base, struct sprd_mbox_priv *priv)104{105 struct mbox_chan *chan;106 u32 fifo_sts, fifo_len, msg[2];107 int i, id;108 109 fifo_sts = readl(base + SPRD_MBOX_FIFO_STS);110 111 fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts);112 if (!fifo_len) {113 dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n");114 return IRQ_NONE;115 }116 117 for (i = 0; i < fifo_len; i++) {118 msg[0] = readl(base + SPRD_MBOX_MSG_LOW);119 msg[1] = readl(base + SPRD_MBOX_MSG_HIGH);120 id = readl(base + SPRD_MBOX_ID);121 122 chan = &priv->chan[id];123 if (chan->cl)124 mbox_chan_received_data(chan, (void *)msg);125 else126 dev_warn_ratelimited(priv->dev,127 "message's been dropped at ch[%d]\n", id);128 129 /* Trigger to update outbox FIFO pointer */130 writel(0x1, base + SPRD_MBOX_TRIGGER);131 }132 133 /* Clear irq status after reading all message. */134 writel(SPRD_MBOX_IRQ_CLR, base + SPRD_MBOX_IRQ_STS);135 136 return IRQ_HANDLED;137}138 139static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data)140{141 struct sprd_mbox_priv *priv = data;142 143 return do_outbox_isr(priv->outbox_base, priv);144}145 146static irqreturn_t sprd_mbox_supp_isr(int irq, void *data)147{148 struct sprd_mbox_priv *priv = data;149 150 return do_outbox_isr(priv->supp_base, priv);151}152 153static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data)154{155 struct sprd_mbox_priv *priv = data;156 struct mbox_chan *chan;157 u32 fifo_sts, send_sts, busy, id;158 159 fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS);160 161 /* Get the inbox data delivery status */162 send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >>163 SPRD_INBOX_FIFO_DELIVER_SHIFT;164 if (!send_sts) {165 dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n");166 return IRQ_NONE;167 }168 169 while (send_sts) {170 id = __ffs(send_sts);171 send_sts &= (send_sts - 1);172 173 chan = &priv->chan[id];174 175 /*176 * Check if the message was fetched by remote target, if yes,177 * that means the transmission has been completed.178 */179 busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK;180 if (!(busy & BIT(id)))181 mbox_chan_txdone(chan, 0);182 }183 184 /* Clear FIFO delivery and overflow status */185 writel(fifo_sts &186 (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK),187 priv->inbox_base + SPRD_MBOX_FIFO_RST);188 189 /* Clear irq status */190 writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS);191 192 return IRQ_HANDLED;193}194 195static int sprd_mbox_send_data(struct mbox_chan *chan, void *msg)196{197 struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);198 unsigned long id = (unsigned long)chan->con_priv;199 u32 *data = msg;200 201 /* Write data into inbox FIFO, and only support 8 bytes every time */202 writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW);203 writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH);204 205 /* Set target core id */206 writel(id, priv->inbox_base + SPRD_MBOX_ID);207 208 /* Trigger remote request */209 writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER);210 211 return 0;212}213 214static int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout)215{216 struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);217 unsigned long id = (unsigned long)chan->con_priv;218 u32 busy;219 220 timeout = jiffies + msecs_to_jiffies(timeout);221 222 while (time_before(jiffies, timeout)) {223 busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) &224 SPRD_INBOX_FIFO_BUSY_MASK;225 if (!(busy & BIT(id))) {226 mbox_chan_txdone(chan, 0);227 return 0;228 }229 230 udelay(1);231 }232 233 return -ETIME;234}235 236static int sprd_mbox_startup(struct mbox_chan *chan)237{238 struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);239 u32 val;240 241 mutex_lock(&priv->lock);242 if (priv->refcnt++ == 0) {243 /* Select outbox FIFO mode and reset the outbox FIFO status */244 writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST);245 246 /* Enable inbox FIFO overflow and delivery interrupt */247 val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK);248 val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ);249 writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK);250 251 /* Enable outbox FIFO not empty interrupt */252 val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK);253 val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;254 writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK);255 256 /* Enable supplementary outbox as the fundamental one */257 if (priv->supp_base) {258 writel(0x0, priv->supp_base + SPRD_MBOX_FIFO_RST);259 val = readl(priv->supp_base + SPRD_MBOX_IRQ_MSK);260 val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;261 writel(val, priv->supp_base + SPRD_MBOX_IRQ_MSK);262 }263 }264 mutex_unlock(&priv->lock);265 266 return 0;267}268 269static void sprd_mbox_shutdown(struct mbox_chan *chan)270{271 struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);272 273 mutex_lock(&priv->lock);274 if (--priv->refcnt == 0) {275 /* Disable inbox & outbox interrupt */276 writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK);277 writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK);278 279 if (priv->supp_base)280 writel(SPRD_OUTBOX_FIFO_IRQ_MASK,281 priv->supp_base + SPRD_MBOX_IRQ_MSK);282 }283 mutex_unlock(&priv->lock);284}285 286static const struct mbox_chan_ops sprd_mbox_ops = {287 .send_data = sprd_mbox_send_data,288 .flush = sprd_mbox_flush,289 .startup = sprd_mbox_startup,290 .shutdown = sprd_mbox_shutdown,291};292 293static int sprd_mbox_probe(struct platform_device *pdev)294{295 struct device *dev = &pdev->dev;296 struct sprd_mbox_priv *priv;297 int ret, inbox_irq, outbox_irq, supp_irq;298 unsigned long id, supp;299 struct clk *clk;300 301 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);302 if (!priv)303 return -ENOMEM;304 305 priv->dev = dev;306 mutex_init(&priv->lock);307 308 /*309 * Unisoc mailbox uses an inbox to send messages to the target310 * core, and uses (an) outbox(es) to receive messages from other311 * cores.312 *313 * Thus in general the mailbox controller supplies 2 different314 * register addresses and IRQ numbers for inbox and outbox.315 *316 * If necessary, a supplementary inbox could be enabled optionally317 * with an independent FIFO and an extra interrupt.318 */319 priv->inbox_base = devm_platform_ioremap_resource(pdev, 0);320 if (IS_ERR(priv->inbox_base))321 return PTR_ERR(priv->inbox_base);322 323 priv->outbox_base = devm_platform_ioremap_resource(pdev, 1);324 if (IS_ERR(priv->outbox_base))325 return PTR_ERR(priv->outbox_base);326 327 clk = devm_clk_get_enabled(dev, "enable");328 if (IS_ERR(clk)) {329 dev_err(dev, "failed to get mailbox clock\n");330 return PTR_ERR(clk);331 }332 333 inbox_irq = platform_get_irq_byname(pdev, "inbox");334 if (inbox_irq < 0)335 return inbox_irq;336 337 ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr,338 IRQF_NO_SUSPEND, dev_name(dev), priv);339 if (ret) {340 dev_err(dev, "failed to request inbox IRQ: %d\n", ret);341 return ret;342 }343 344 outbox_irq = platform_get_irq_byname(pdev, "outbox");345 if (outbox_irq < 0)346 return outbox_irq;347 348 ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr,349 IRQF_NO_SUSPEND, dev_name(dev), priv);350 if (ret) {351 dev_err(dev, "failed to request outbox IRQ: %d\n", ret);352 return ret;353 }354 355 /* Supplementary outbox IRQ is optional */356 supp_irq = platform_get_irq_byname(pdev, "supp-outbox");357 if (supp_irq > 0) {358 ret = devm_request_irq(dev, supp_irq, sprd_mbox_supp_isr,359 IRQF_NO_SUSPEND, dev_name(dev), priv);360 if (ret) {361 dev_err(dev, "failed to request outbox IRQ: %d\n", ret);362 return ret;363 }364 365 supp = (unsigned long) of_device_get_match_data(dev);366 if (!supp) {367 dev_err(dev, "no supplementary outbox specified\n");368 return -ENODEV;369 }370 priv->supp_base = priv->outbox_base + (SPRD_OUTBOX_BASE_SPAN * supp);371 }372 373 /* Get the default outbox FIFO depth */374 priv->outbox_fifo_depth =375 readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1;376 priv->mbox.dev = dev;377 priv->mbox.chans = &priv->chan[0];378 priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX;379 priv->mbox.ops = &sprd_mbox_ops;380 priv->mbox.txdone_irq = true;381 382 for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++)383 priv->chan[id].con_priv = (void *)id;384 385 ret = devm_mbox_controller_register(dev, &priv->mbox);386 if (ret) {387 dev_err(dev, "failed to register mailbox: %d\n", ret);388 return ret;389 }390 391 return 0;392}393 394static const struct of_device_id sprd_mbox_of_match[] = {395 { .compatible = "sprd,sc9860-mailbox" },396 { .compatible = "sprd,sc9863a-mailbox",397 .data = (void *)SPRD_SUPP_INBOX_ID_SC9863A },398 { },399};400MODULE_DEVICE_TABLE(of, sprd_mbox_of_match);401 402static struct platform_driver sprd_mbox_driver = {403 .driver = {404 .name = "sprd-mailbox",405 .of_match_table = sprd_mbox_of_match,406 },407 .probe = sprd_mbox_probe,408};409module_platform_driver(sprd_mbox_driver);410 411MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>");412MODULE_DESCRIPTION("Spreadtrum mailbox driver");413MODULE_LICENSE("GPL v2");414