Skip to content

Commit ca27fc2

Browse files
Baolin WangJassiBrar
authored andcommitted
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 <[email protected]> Signed-off-by: Jassi Brar <[email protected]>
1 parent fa75386 commit ca27fc2

File tree

3 files changed

+371
-0
lines changed

3 files changed

+371
-0
lines changed

drivers/mailbox/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,12 @@ config SUN6I_MSGBOX
236236
various Allwinner SoCs. This mailbox is used for communication
237237
between the application CPUs and the power management coprocessor.
238238

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+
239247
endif

drivers/mailbox/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,5 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
5050
obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
5151

5252
obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
53+
54+
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o

drivers/mailbox/sprd-mailbox.c

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
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 <[email protected]>");
360+
MODULE_DESCRIPTION("Spreadtrum mailbox driver");
361+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)