Skip to content

Commit 2bb7005

Browse files
oleremJassiBrar
authored andcommitted
mailbox: Add support for i.MX messaging unit
The i.MX Messaging Unit is a two side block which allows applications implement communication over this sides. The MU includes the following features: - Messaging control by interrupts or by polling - Four general-purpose interrupt requests reflected to the other side - Three general-purpose flags reflected to the other side - Four receive registers with maskable interrupt - Four transmit registers with maskable interrupt Reviewed-by: Vladimir Zapolskiy <[email protected]> Reviewed-by: Dong Aisheng <[email protected]> Signed-off-by: Oleksij Rempel <[email protected]> Signed-off-by: Jassi Brar <[email protected]>
1 parent d6ef139 commit 2bb7005

File tree

3 files changed

+366
-0
lines changed

3 files changed

+366
-0
lines changed

drivers/mailbox/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ config ARM_MHU
1515
The controller has 3 mailbox channels, the last of which can be
1616
used in Secure mode only.
1717

18+
config IMX_MBOX
19+
tristate "i.MX Mailbox"
20+
depends on ARCH_MXC || COMPILE_TEST
21+
help
22+
Mailbox implementation for i.MX Messaging Unit (MU).
23+
1824
config PLATFORM_MHU
1925
tristate "Platform MHU Mailbox"
2026
depends on OF

drivers/mailbox/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ obj-$(CONFIG_MAILBOX_TEST) += mailbox-test.o
77

88
obj-$(CONFIG_ARM_MHU) += arm_mhu.o
99

10+
obj-$(CONFIG_IMX_MBOX) += imx-mailbox.o
11+
1012
obj-$(CONFIG_PLATFORM_MHU) += platform_mhu.o
1113

1214
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o

drivers/mailbox/imx-mailbox.c

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2018 Pengutronix, Oleksij Rempel <[email protected]>
4+
*/
5+
6+
#include <linux/clk.h>
7+
#include <linux/interrupt.h>
8+
#include <linux/io.h>
9+
#include <linux/kernel.h>
10+
#include <linux/mailbox_controller.h>
11+
#include <linux/module.h>
12+
#include <linux/of_device.h>
13+
#include <linux/slab.h>
14+
15+
/* Transmit Register */
16+
#define IMX_MU_xTRn(x) (0x00 + 4 * (x))
17+
/* Receive Register */
18+
#define IMX_MU_xRRn(x) (0x10 + 4 * (x))
19+
/* Status Register */
20+
#define IMX_MU_xSR 0x20
21+
#define IMX_MU_xSR_GIPn(x) BIT(28 + (3 - (x)))
22+
#define IMX_MU_xSR_RFn(x) BIT(24 + (3 - (x)))
23+
#define IMX_MU_xSR_TEn(x) BIT(20 + (3 - (x)))
24+
#define IMX_MU_xSR_BRDIP BIT(9)
25+
26+
/* Control Register */
27+
#define IMX_MU_xCR 0x24
28+
/* General Purpose Interrupt Enable */
29+
#define IMX_MU_xCR_GIEn(x) BIT(28 + (3 - (x)))
30+
/* Receive Interrupt Enable */
31+
#define IMX_MU_xCR_RIEn(x) BIT(24 + (3 - (x)))
32+
/* Transmit Interrupt Enable */
33+
#define IMX_MU_xCR_TIEn(x) BIT(20 + (3 - (x)))
34+
/* General Purpose Interrupt Request */
35+
#define IMX_MU_xCR_GIRn(x) BIT(16 + (3 - (x)))
36+
37+
#define IMX_MU_CHANS 16
38+
#define IMX_MU_CHAN_NAME_SIZE 20
39+
40+
enum imx_mu_chan_type {
41+
IMX_MU_TYPE_TX, /* Tx */
42+
IMX_MU_TYPE_RX, /* Rx */
43+
IMX_MU_TYPE_TXDB, /* Tx doorbell */
44+
IMX_MU_TYPE_RXDB, /* Rx doorbell */
45+
};
46+
47+
struct imx_mu_con_priv {
48+
unsigned int idx;
49+
char irq_desc[IMX_MU_CHAN_NAME_SIZE];
50+
enum imx_mu_chan_type type;
51+
struct mbox_chan *chan;
52+
struct tasklet_struct txdb_tasklet;
53+
};
54+
55+
struct imx_mu_priv {
56+
struct device *dev;
57+
void __iomem *base;
58+
spinlock_t xcr_lock; /* control register lock */
59+
60+
struct mbox_controller mbox;
61+
struct mbox_chan mbox_chans[IMX_MU_CHANS];
62+
63+
struct imx_mu_con_priv con_priv[IMX_MU_CHANS];
64+
struct clk *clk;
65+
int irq;
66+
67+
bool side_b;
68+
};
69+
70+
static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox)
71+
{
72+
return container_of(mbox, struct imx_mu_priv, mbox);
73+
}
74+
75+
static void imx_mu_write(struct imx_mu_priv *priv, u32 val, u32 offs)
76+
{
77+
iowrite32(val, priv->base + offs);
78+
}
79+
80+
static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs)
81+
{
82+
return ioread32(priv->base + offs);
83+
}
84+
85+
static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, u32 set, u32 clr)
86+
{
87+
unsigned long flags;
88+
u32 val;
89+
90+
spin_lock_irqsave(&priv->xcr_lock, flags);
91+
val = imx_mu_read(priv, IMX_MU_xCR);
92+
val &= ~clr;
93+
val |= set;
94+
imx_mu_write(priv, val, IMX_MU_xCR);
95+
spin_unlock_irqrestore(&priv->xcr_lock, flags);
96+
97+
return val;
98+
}
99+
100+
static void imx_mu_txdb_tasklet(unsigned long data)
101+
{
102+
struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data;
103+
104+
mbox_chan_txdone(cp->chan, 0);
105+
}
106+
107+
static irqreturn_t imx_mu_isr(int irq, void *p)
108+
{
109+
struct mbox_chan *chan = p;
110+
struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
111+
struct imx_mu_con_priv *cp = chan->con_priv;
112+
u32 val, ctrl, dat;
113+
114+
ctrl = imx_mu_read(priv, IMX_MU_xCR);
115+
val = imx_mu_read(priv, IMX_MU_xSR);
116+
117+
switch (cp->type) {
118+
case IMX_MU_TYPE_TX:
119+
val &= IMX_MU_xSR_TEn(cp->idx) &
120+
(ctrl & IMX_MU_xCR_TIEn(cp->idx));
121+
break;
122+
case IMX_MU_TYPE_RX:
123+
val &= IMX_MU_xSR_RFn(cp->idx) &
124+
(ctrl & IMX_MU_xCR_RIEn(cp->idx));
125+
break;
126+
case IMX_MU_TYPE_RXDB:
127+
val &= IMX_MU_xSR_GIPn(cp->idx) &
128+
(ctrl & IMX_MU_xCR_GIEn(cp->idx));
129+
break;
130+
default:
131+
break;
132+
}
133+
134+
if (!val)
135+
return IRQ_NONE;
136+
137+
if (val == IMX_MU_xSR_TEn(cp->idx)) {
138+
imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx));
139+
mbox_chan_txdone(chan, 0);
140+
} else if (val == IMX_MU_xSR_RFn(cp->idx)) {
141+
dat = imx_mu_read(priv, IMX_MU_xRRn(cp->idx));
142+
mbox_chan_received_data(chan, (void *)&dat);
143+
} else if (val == IMX_MU_xSR_GIPn(cp->idx)) {
144+
imx_mu_write(priv, IMX_MU_xSR_GIPn(cp->idx), IMX_MU_xSR);
145+
mbox_chan_received_data(chan, NULL);
146+
} else {
147+
dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
148+
return IRQ_NONE;
149+
}
150+
151+
return IRQ_HANDLED;
152+
}
153+
154+
static int imx_mu_send_data(struct mbox_chan *chan, void *data)
155+
{
156+
struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
157+
struct imx_mu_con_priv *cp = chan->con_priv;
158+
u32 *arg = data;
159+
160+
switch (cp->type) {
161+
case IMX_MU_TYPE_TX:
162+
imx_mu_write(priv, *arg, IMX_MU_xTRn(cp->idx));
163+
imx_mu_xcr_rmw(priv, IMX_MU_xCR_TIEn(cp->idx), 0);
164+
break;
165+
case IMX_MU_TYPE_TXDB:
166+
imx_mu_xcr_rmw(priv, IMX_MU_xCR_GIRn(cp->idx), 0);
167+
tasklet_schedule(&cp->txdb_tasklet);
168+
break;
169+
default:
170+
dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
171+
return -EINVAL;
172+
}
173+
174+
return 0;
175+
}
176+
177+
static int imx_mu_startup(struct mbox_chan *chan)
178+
{
179+
struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
180+
struct imx_mu_con_priv *cp = chan->con_priv;
181+
int ret;
182+
183+
if (cp->type == IMX_MU_TYPE_TXDB) {
184+
/* Tx doorbell don't have ACK support */
185+
tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
186+
(unsigned long)cp);
187+
return 0;
188+
}
189+
190+
ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED, cp->irq_desc,
191+
chan);
192+
if (ret) {
193+
dev_err(priv->dev,
194+
"Unable to acquire IRQ %d\n", priv->irq);
195+
return ret;
196+
}
197+
198+
switch (cp->type) {
199+
case IMX_MU_TYPE_RX:
200+
imx_mu_xcr_rmw(priv, IMX_MU_xCR_RIEn(cp->idx), 0);
201+
break;
202+
case IMX_MU_TYPE_RXDB:
203+
imx_mu_xcr_rmw(priv, IMX_MU_xCR_GIEn(cp->idx), 0);
204+
break;
205+
default:
206+
break;
207+
}
208+
209+
return 0;
210+
}
211+
212+
static void imx_mu_shutdown(struct mbox_chan *chan)
213+
{
214+
struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
215+
struct imx_mu_con_priv *cp = chan->con_priv;
216+
217+
if (cp->type == IMX_MU_TYPE_TXDB)
218+
tasklet_kill(&cp->txdb_tasklet);
219+
220+
imx_mu_xcr_rmw(priv, 0,
221+
IMX_MU_xCR_TIEn(cp->idx) | IMX_MU_xCR_RIEn(cp->idx));
222+
223+
free_irq(priv->irq, chan);
224+
}
225+
226+
static const struct mbox_chan_ops imx_mu_ops = {
227+
.send_data = imx_mu_send_data,
228+
.startup = imx_mu_startup,
229+
.shutdown = imx_mu_shutdown,
230+
};
231+
232+
static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox,
233+
const struct of_phandle_args *sp)
234+
{
235+
u32 type, idx, chan;
236+
237+
if (sp->args_count != 2) {
238+
dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
239+
return ERR_PTR(-EINVAL);
240+
}
241+
242+
type = sp->args[0]; /* channel type */
243+
idx = sp->args[1]; /* index */
244+
chan = type * 4 + idx;
245+
246+
if (chan >= mbox->num_chans) {
247+
dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", chan, type, idx);
248+
return ERR_PTR(-EINVAL);
249+
}
250+
251+
return &mbox->chans[chan];
252+
}
253+
254+
static void imx_mu_init_generic(struct imx_mu_priv *priv)
255+
{
256+
if (priv->side_b)
257+
return;
258+
259+
/* Set default MU configuration */
260+
imx_mu_write(priv, 0, IMX_MU_xCR);
261+
}
262+
263+
static int imx_mu_probe(struct platform_device *pdev)
264+
{
265+
struct device *dev = &pdev->dev;
266+
struct device_node *np = dev->of_node;
267+
struct resource *iomem;
268+
struct imx_mu_priv *priv;
269+
unsigned int i;
270+
int ret;
271+
272+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
273+
if (!priv)
274+
return -ENOMEM;
275+
276+
priv->dev = dev;
277+
278+
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
279+
priv->base = devm_ioremap_resource(&pdev->dev, iomem);
280+
if (IS_ERR(priv->base))
281+
return PTR_ERR(priv->base);
282+
283+
priv->irq = platform_get_irq(pdev, 0);
284+
if (priv->irq < 0)
285+
return priv->irq;
286+
287+
priv->clk = devm_clk_get(dev, NULL);
288+
if (IS_ERR(priv->clk)) {
289+
if (PTR_ERR(priv->clk) != -ENOENT)
290+
return PTR_ERR(priv->clk);
291+
292+
priv->clk = NULL;
293+
}
294+
295+
ret = clk_prepare_enable(priv->clk);
296+
if (ret) {
297+
dev_err(dev, "Failed to enable clock\n");
298+
return ret;
299+
}
300+
301+
for (i = 0; i < IMX_MU_CHANS; i++) {
302+
struct imx_mu_con_priv *cp = &priv->con_priv[i];
303+
304+
cp->idx = i % 4;
305+
cp->type = i >> 2;
306+
cp->chan = &priv->mbox_chans[i];
307+
priv->mbox_chans[i].con_priv = cp;
308+
snprintf(cp->irq_desc, sizeof(cp->irq_desc),
309+
"imx_mu_chan[%i-%i]", cp->type, cp->idx);
310+
}
311+
312+
priv->side_b = of_property_read_bool(np, "fsl,mu-side-b");
313+
314+
spin_lock_init(&priv->xcr_lock);
315+
316+
priv->mbox.dev = dev;
317+
priv->mbox.ops = &imx_mu_ops;
318+
priv->mbox.chans = priv->mbox_chans;
319+
priv->mbox.num_chans = IMX_MU_CHANS;
320+
priv->mbox.of_xlate = imx_mu_xlate;
321+
priv->mbox.txdone_irq = true;
322+
323+
platform_set_drvdata(pdev, priv);
324+
325+
imx_mu_init_generic(priv);
326+
327+
return mbox_controller_register(&priv->mbox);
328+
}
329+
330+
static int imx_mu_remove(struct platform_device *pdev)
331+
{
332+
struct imx_mu_priv *priv = platform_get_drvdata(pdev);
333+
334+
mbox_controller_unregister(&priv->mbox);
335+
clk_disable_unprepare(priv->clk);
336+
337+
return 0;
338+
}
339+
340+
static const struct of_device_id imx_mu_dt_ids[] = {
341+
{ .compatible = "fsl,imx6sx-mu" },
342+
{ },
343+
};
344+
MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
345+
346+
static struct platform_driver imx_mu_driver = {
347+
.probe = imx_mu_probe,
348+
.remove = imx_mu_remove,
349+
.driver = {
350+
.name = "imx_mu",
351+
.of_match_table = imx_mu_dt_ids,
352+
},
353+
};
354+
module_platform_driver(imx_mu_driver);
355+
356+
MODULE_AUTHOR("Oleksij Rempel <[email protected]>");
357+
MODULE_DESCRIPTION("Message Unit driver for i.MX");
358+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)