Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mailbox: sprd: Add Spreadtrum mailbox driver

The Spreadtrum mailbox controller supports 8 channels to communicate
with MCUs, and it contains 2 different parts: inbox and outbox, which
are used to send and receive messages by IRQ mode.

Signed-off-by: Baolin Wang <baolin.wang@unisoc.com>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>

authored by

Baolin Wang and committed by
Jassi Brar
ca27fc26 fa753865

+371
+8
drivers/mailbox/Kconfig
··· 236 236 various Allwinner SoCs. This mailbox is used for communication 237 237 between the application CPUs and the power management coprocessor. 238 238 239 + config SPRD_MBOX 240 + tristate "Spreadtrum Mailbox" 241 + depends on ARCH_SPRD || COMPILE_TEST 242 + help 243 + Mailbox driver implementation for the Spreadtrum platform. It is used 244 + to send message between application processors and MCU. Say Y here if 245 + you want to build the Spreatrum mailbox controller driver. 246 + 239 247 endif
+2
drivers/mailbox/Makefile
··· 50 50 obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o 51 51 52 52 obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o 53 + 54 + obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
+361
drivers/mailbox/sprd-mailbox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Spreadtrum mailbox driver 4 + * 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/platform_device.h> 15 + #include <linux/clk.h> 16 + 17 + #define SPRD_MBOX_ID 0x0 18 + #define SPRD_MBOX_MSG_LOW 0x4 19 + #define SPRD_MBOX_MSG_HIGH 0x8 20 + #define SPRD_MBOX_TRIGGER 0xc 21 + #define SPRD_MBOX_FIFO_RST 0x10 22 + #define SPRD_MBOX_FIFO_STS 0x14 23 + #define SPRD_MBOX_IRQ_STS 0x18 24 + #define SPRD_MBOX_IRQ_MSK 0x1c 25 + #define SPRD_MBOX_LOCK 0x20 26 + #define SPRD_MBOX_FIFO_DEPTH 0x24 27 + 28 + /* Bit and mask definiation for inbox's SPRD_MBOX_FIFO_STS register */ 29 + #define SPRD_INBOX_FIFO_DELIVER_MASK GENMASK(23, 16) 30 + #define SPRD_INBOX_FIFO_OVERLOW_MASK GENMASK(15, 8) 31 + #define SPRD_INBOX_FIFO_DELIVER_SHIFT 16 32 + #define SPRD_INBOX_FIFO_BUSY_MASK GENMASK(7, 0) 33 + 34 + /* Bit and mask definiation for SPRD_MBOX_IRQ_STS register */ 35 + #define SPRD_MBOX_IRQ_CLR BIT(0) 36 + 37 + /* Bit and mask definiation for outbox's SPRD_MBOX_FIFO_STS register */ 38 + #define SPRD_OUTBOX_FIFO_FULL BIT(0) 39 + #define SPRD_OUTBOX_FIFO_WR_SHIFT 16 40 + #define SPRD_OUTBOX_FIFO_RD_SHIFT 24 41 + #define SPRD_OUTBOX_FIFO_POS_MASK GENMASK(7, 0) 42 + 43 + /* Bit and mask definiation for inbox's SPRD_MBOX_IRQ_MSK register */ 44 + #define SPRD_INBOX_FIFO_BLOCK_IRQ BIT(0) 45 + #define SPRD_INBOX_FIFO_OVERFLOW_IRQ BIT(1) 46 + #define SPRD_INBOX_FIFO_DELIVER_IRQ BIT(2) 47 + #define SPRD_INBOX_FIFO_IRQ_MASK GENMASK(2, 0) 48 + 49 + /* Bit and mask definiation for outbox's SPRD_MBOX_IRQ_MSK register */ 50 + #define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ BIT(0) 51 + #define SPRD_OUTBOX_FIFO_IRQ_MASK GENMASK(4, 0) 52 + 53 + #define SPRD_MBOX_CHAN_MAX 8 54 + 55 + struct sprd_mbox_priv { 56 + struct mbox_controller mbox; 57 + struct device *dev; 58 + void __iomem *inbox_base; 59 + void __iomem *outbox_base; 60 + struct clk *clk; 61 + u32 outbox_fifo_depth; 62 + 63 + struct mbox_chan chan[SPRD_MBOX_CHAN_MAX]; 64 + }; 65 + 66 + static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox) 67 + { 68 + return container_of(mbox, struct sprd_mbox_priv, mbox); 69 + } 70 + 71 + static u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts) 72 + { 73 + u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) & 74 + SPRD_OUTBOX_FIFO_POS_MASK; 75 + u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) & 76 + SPRD_OUTBOX_FIFO_POS_MASK; 77 + u32 fifo_len; 78 + 79 + /* 80 + * If the read pointer is equal with write pointer, which means the fifo 81 + * is full or empty. 82 + */ 83 + if (wr_pos == rd_pos) { 84 + if (fifo_sts & SPRD_OUTBOX_FIFO_FULL) 85 + fifo_len = priv->outbox_fifo_depth; 86 + else 87 + fifo_len = 0; 88 + } else if (wr_pos > rd_pos) { 89 + fifo_len = wr_pos - rd_pos; 90 + } else { 91 + fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos; 92 + } 93 + 94 + return fifo_len; 95 + } 96 + 97 + static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data) 98 + { 99 + struct sprd_mbox_priv *priv = data; 100 + struct mbox_chan *chan; 101 + u32 fifo_sts, fifo_len, msg[2]; 102 + int i, id; 103 + 104 + fifo_sts = readl(priv->outbox_base + SPRD_MBOX_FIFO_STS); 105 + 106 + fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts); 107 + if (!fifo_len) { 108 + dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n"); 109 + return IRQ_NONE; 110 + } 111 + 112 + for (i = 0; i < fifo_len; i++) { 113 + msg[0] = readl(priv->outbox_base + SPRD_MBOX_MSG_LOW); 114 + msg[1] = readl(priv->outbox_base + SPRD_MBOX_MSG_HIGH); 115 + id = readl(priv->outbox_base + SPRD_MBOX_ID); 116 + 117 + chan = &priv->chan[id]; 118 + mbox_chan_received_data(chan, (void *)msg); 119 + 120 + /* Trigger to update outbox FIFO pointer */ 121 + writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER); 122 + } 123 + 124 + /* Clear irq status after reading all message. */ 125 + writel(SPRD_MBOX_IRQ_CLR, priv->outbox_base + SPRD_MBOX_IRQ_STS); 126 + 127 + return IRQ_HANDLED; 128 + } 129 + 130 + static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data) 131 + { 132 + struct sprd_mbox_priv *priv = data; 133 + struct mbox_chan *chan; 134 + u32 fifo_sts, send_sts, busy, id; 135 + 136 + fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS); 137 + 138 + /* Get the inbox data delivery status */ 139 + send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >> 140 + SPRD_INBOX_FIFO_DELIVER_SHIFT; 141 + if (!send_sts) { 142 + dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n"); 143 + return IRQ_NONE; 144 + } 145 + 146 + while (send_sts) { 147 + id = __ffs(send_sts); 148 + send_sts &= (send_sts - 1); 149 + 150 + chan = &priv->chan[id]; 151 + 152 + /* 153 + * Check if the message was fetched by remote traget, if yes, 154 + * that means the transmission has been completed. 155 + */ 156 + busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK; 157 + if (!(busy & BIT(id))) 158 + mbox_chan_txdone(chan, 0); 159 + } 160 + 161 + /* Clear FIFO delivery and overflow status */ 162 + writel(fifo_sts & 163 + (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK), 164 + priv->inbox_base + SPRD_MBOX_FIFO_RST); 165 + 166 + /* Clear irq status */ 167 + writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS); 168 + 169 + return IRQ_HANDLED; 170 + } 171 + 172 + static int sprd_mbox_send_data(struct mbox_chan *chan, void *msg) 173 + { 174 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 175 + unsigned long id = (unsigned long)chan->con_priv; 176 + u32 *data = msg; 177 + 178 + /* Write data into inbox FIFO, and only support 8 bytes every time */ 179 + writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW); 180 + writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH); 181 + 182 + /* Set target core id */ 183 + writel(id, priv->inbox_base + SPRD_MBOX_ID); 184 + 185 + /* Trigger remote request */ 186 + writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER); 187 + 188 + return 0; 189 + } 190 + 191 + static int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout) 192 + { 193 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 194 + unsigned long id = (unsigned long)chan->con_priv; 195 + u32 busy; 196 + 197 + timeout = jiffies + msecs_to_jiffies(timeout); 198 + 199 + while (time_before(jiffies, timeout)) { 200 + busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) & 201 + SPRD_INBOX_FIFO_BUSY_MASK; 202 + if (!(busy & BIT(id))) { 203 + mbox_chan_txdone(chan, 0); 204 + return 0; 205 + } 206 + 207 + udelay(1); 208 + } 209 + 210 + return -ETIME; 211 + } 212 + 213 + static int sprd_mbox_startup(struct mbox_chan *chan) 214 + { 215 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 216 + u32 val; 217 + 218 + /* Select outbox FIFO mode and reset the outbox FIFO status */ 219 + writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); 220 + 221 + /* Enable inbox FIFO overflow and delivery interrupt */ 222 + val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); 223 + val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); 224 + writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 225 + 226 + /* Enable outbox FIFO not empty interrupt */ 227 + val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); 228 + val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; 229 + writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 230 + 231 + return 0; 232 + } 233 + 234 + static void sprd_mbox_shutdown(struct mbox_chan *chan) 235 + { 236 + struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 237 + 238 + /* Disable inbox & outbox interrupt */ 239 + writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 240 + writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 241 + } 242 + 243 + static const struct mbox_chan_ops sprd_mbox_ops = { 244 + .send_data = sprd_mbox_send_data, 245 + .flush = sprd_mbox_flush, 246 + .startup = sprd_mbox_startup, 247 + .shutdown = sprd_mbox_shutdown, 248 + }; 249 + 250 + static void sprd_mbox_disable(void *data) 251 + { 252 + struct sprd_mbox_priv *priv = data; 253 + 254 + clk_disable_unprepare(priv->clk); 255 + } 256 + 257 + static int sprd_mbox_probe(struct platform_device *pdev) 258 + { 259 + struct device *dev = &pdev->dev; 260 + struct sprd_mbox_priv *priv; 261 + int ret, inbox_irq, outbox_irq; 262 + unsigned long id; 263 + 264 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 265 + if (!priv) 266 + return -ENOMEM; 267 + 268 + priv->dev = dev; 269 + 270 + /* 271 + * The Spreadtrum mailbox uses an inbox to send messages to the target 272 + * core, and uses an outbox to receive messages from other cores. 273 + * 274 + * Thus the mailbox controller supplies 2 different register addresses 275 + * and IRQ numbers for inbox and outbox. 276 + */ 277 + priv->inbox_base = devm_platform_ioremap_resource(pdev, 0); 278 + if (IS_ERR(priv->inbox_base)) 279 + return PTR_ERR(priv->inbox_base); 280 + 281 + priv->outbox_base = devm_platform_ioremap_resource(pdev, 1); 282 + if (IS_ERR(priv->outbox_base)) 283 + return PTR_ERR(priv->outbox_base); 284 + 285 + priv->clk = devm_clk_get(dev, "enable"); 286 + if (IS_ERR(priv->clk)) { 287 + dev_err(dev, "failed to get mailbox clock\n"); 288 + return PTR_ERR(priv->clk); 289 + } 290 + 291 + ret = clk_prepare_enable(priv->clk); 292 + if (ret) 293 + return ret; 294 + 295 + ret = devm_add_action_or_reset(dev, sprd_mbox_disable, priv); 296 + if (ret) { 297 + dev_err(dev, "failed to add mailbox disable action\n"); 298 + return ret; 299 + } 300 + 301 + inbox_irq = platform_get_irq(pdev, 0); 302 + if (inbox_irq < 0) 303 + return inbox_irq; 304 + 305 + ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr, 306 + IRQF_NO_SUSPEND, dev_name(dev), priv); 307 + if (ret) { 308 + dev_err(dev, "failed to request inbox IRQ: %d\n", ret); 309 + return ret; 310 + } 311 + 312 + outbox_irq = platform_get_irq(pdev, 1); 313 + if (outbox_irq < 0) 314 + return outbox_irq; 315 + 316 + ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr, 317 + IRQF_NO_SUSPEND, dev_name(dev), priv); 318 + if (ret) { 319 + dev_err(dev, "failed to request outbox IRQ: %d\n", ret); 320 + return ret; 321 + } 322 + 323 + /* Get the default outbox FIFO depth */ 324 + priv->outbox_fifo_depth = 325 + readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1; 326 + priv->mbox.dev = dev; 327 + priv->mbox.chans = &priv->chan[0]; 328 + priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX; 329 + priv->mbox.ops = &sprd_mbox_ops; 330 + priv->mbox.txdone_irq = true; 331 + 332 + for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++) 333 + priv->chan[id].con_priv = (void *)id; 334 + 335 + ret = devm_mbox_controller_register(dev, &priv->mbox); 336 + if (ret) { 337 + dev_err(dev, "failed to register mailbox: %d\n", ret); 338 + return ret; 339 + } 340 + 341 + return 0; 342 + } 343 + 344 + static const struct of_device_id sprd_mbox_of_match[] = { 345 + { .compatible = "sprd,sc9860-mailbox", }, 346 + { }, 347 + }; 348 + MODULE_DEVICE_TABLE(of, sprd_mbox_of_match); 349 + 350 + static struct platform_driver sprd_mbox_driver = { 351 + .driver = { 352 + .name = "sprd-mailbox", 353 + .of_match_table = sprd_mbox_of_match, 354 + }, 355 + .probe = sprd_mbox_probe, 356 + }; 357 + module_platform_driver(sprd_mbox_driver); 358 + 359 + MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>"); 360 + MODULE_DESCRIPTION("Spreadtrum mailbox driver"); 361 + MODULE_LICENSE("GPL v2");