1 // SPDX-License-Identifier: GPL-2.0
3 // Copyright (c) 2017-2019 Samuel Holland <samuel@sholland.org>
5 #include <linux/bitops.h>
7 #include <linux/device.h>
9 #include <linux/interrupt.h>
11 #include <linux/kernel.h>
12 #include <linux/mailbox_controller.h>
13 #include <linux/module.h>
15 #include <linux/of_irq.h>
16 #include <linux/platform_device.h>
17 #include <linux/reset.h>
18 #include <linux/spinlock.h>
22 #define CTRL_REG(n) (0x0000 + 0x4 * ((n) / 4))
23 #define CTRL_RX(n) BIT(0 + 8 * ((n) % 4))
24 #define CTRL_TX(n) BIT(4 + 8 * ((n) % 4))
26 #define REMOTE_IRQ_EN_REG 0x0040
27 #define REMOTE_IRQ_STAT_REG 0x0050
28 #define LOCAL_IRQ_EN_REG 0x0060
29 #define LOCAL_IRQ_STAT_REG 0x0070
31 #define RX_IRQ(n) BIT(0 + 2 * (n))
32 #define RX_IRQ_MASK 0x5555
33 #define TX_IRQ(n) BIT(1 + 2 * (n))
34 #define TX_IRQ_MASK 0xaaaa
36 #define FIFO_STAT_REG(n) (0x0100 + 0x4 * (n))
37 #define FIFO_STAT_MASK GENMASK(0, 0)
39 #define MSG_STAT_REG(n) (0x0140 + 0x4 * (n))
40 #define MSG_STAT_MASK GENMASK(2, 0)
42 #define MSG_DATA_REG(n) (0x0180 + 0x4 * (n))
44 #define mbox_dbg(mbox, ...) dev_dbg((mbox)->controller.dev, __VA_ARGS__)
47 struct mbox_controller controller
;
53 static bool sun6i_msgbox_last_tx_done(struct mbox_chan
*chan
);
54 static bool sun6i_msgbox_peek_data(struct mbox_chan
*chan
);
56 static inline int channel_number(struct mbox_chan
*chan
)
58 return chan
- chan
->mbox
->chans
;
61 static inline struct sun6i_msgbox
*to_sun6i_msgbox(struct mbox_chan
*chan
)
63 return chan
->con_priv
;
66 static irqreturn_t
sun6i_msgbox_irq(int irq
, void *dev_id
)
68 struct sun6i_msgbox
*mbox
= dev_id
;
72 /* Only examine channels that are currently enabled. */
73 status
= readl(mbox
->regs
+ LOCAL_IRQ_EN_REG
) &
74 readl(mbox
->regs
+ LOCAL_IRQ_STAT_REG
);
76 if (!(status
& RX_IRQ_MASK
))
79 for (n
= 0; n
< NUM_CHANS
; ++n
) {
80 struct mbox_chan
*chan
= &mbox
->controller
.chans
[n
];
82 if (!(status
& RX_IRQ(n
)))
85 while (sun6i_msgbox_peek_data(chan
)) {
86 uint32_t msg
= readl(mbox
->regs
+ MSG_DATA_REG(n
));
88 mbox_dbg(mbox
, "Channel %d received 0x%08x\n", n
, msg
);
89 mbox_chan_received_data(chan
, &msg
);
92 /* The IRQ can be cleared only once the FIFO is empty. */
93 writel(RX_IRQ(n
), mbox
->regs
+ LOCAL_IRQ_STAT_REG
);
99 static int sun6i_msgbox_send_data(struct mbox_chan
*chan
, void *data
)
101 struct sun6i_msgbox
*mbox
= to_sun6i_msgbox(chan
);
102 int n
= channel_number(chan
);
103 uint32_t msg
= *(uint32_t *)data
;
105 /* Using a channel backwards gets the hardware into a bad state. */
106 if (WARN_ON_ONCE(!(readl(mbox
->regs
+ CTRL_REG(n
)) & CTRL_TX(n
))))
109 writel(msg
, mbox
->regs
+ MSG_DATA_REG(n
));
110 mbox_dbg(mbox
, "Channel %d sent 0x%08x\n", n
, msg
);
115 static int sun6i_msgbox_startup(struct mbox_chan
*chan
)
117 struct sun6i_msgbox
*mbox
= to_sun6i_msgbox(chan
);
118 int n
= channel_number(chan
);
120 /* The coprocessor is responsible for setting channel directions. */
121 if (readl(mbox
->regs
+ CTRL_REG(n
)) & CTRL_RX(n
)) {
122 /* Flush the receive FIFO. */
123 while (sun6i_msgbox_peek_data(chan
))
124 readl(mbox
->regs
+ MSG_DATA_REG(n
));
125 writel(RX_IRQ(n
), mbox
->regs
+ LOCAL_IRQ_STAT_REG
);
127 /* Enable the receive IRQ. */
128 spin_lock(&mbox
->lock
);
129 writel(readl(mbox
->regs
+ LOCAL_IRQ_EN_REG
) | RX_IRQ(n
),
130 mbox
->regs
+ LOCAL_IRQ_EN_REG
);
131 spin_unlock(&mbox
->lock
);
134 mbox_dbg(mbox
, "Channel %d startup complete\n", n
);
139 static void sun6i_msgbox_shutdown(struct mbox_chan
*chan
)
141 struct sun6i_msgbox
*mbox
= to_sun6i_msgbox(chan
);
142 int n
= channel_number(chan
);
144 if (readl(mbox
->regs
+ CTRL_REG(n
)) & CTRL_RX(n
)) {
145 /* Disable the receive IRQ. */
146 spin_lock(&mbox
->lock
);
147 writel(readl(mbox
->regs
+ LOCAL_IRQ_EN_REG
) & ~RX_IRQ(n
),
148 mbox
->regs
+ LOCAL_IRQ_EN_REG
);
149 spin_unlock(&mbox
->lock
);
151 /* Attempt to flush the FIFO until the IRQ is cleared. */
153 while (sun6i_msgbox_peek_data(chan
))
154 readl(mbox
->regs
+ MSG_DATA_REG(n
));
155 writel(RX_IRQ(n
), mbox
->regs
+ LOCAL_IRQ_STAT_REG
);
156 } while (readl(mbox
->regs
+ LOCAL_IRQ_STAT_REG
) & RX_IRQ(n
));
159 mbox_dbg(mbox
, "Channel %d shutdown complete\n", n
);
162 static bool sun6i_msgbox_last_tx_done(struct mbox_chan
*chan
)
164 struct sun6i_msgbox
*mbox
= to_sun6i_msgbox(chan
);
165 int n
= channel_number(chan
);
168 * The hardware allows snooping on the remote user's IRQ statuses.
169 * We consider a message to be acknowledged only once the receive IRQ
170 * for that channel is cleared. Since the receive IRQ for a channel
171 * cannot be cleared until the FIFO for that channel is empty, this
172 * ensures that the message has actually been read. It also gives the
173 * recipient an opportunity to perform minimal processing before
174 * acknowledging the message.
176 return !(readl(mbox
->regs
+ REMOTE_IRQ_STAT_REG
) & RX_IRQ(n
));
179 static bool sun6i_msgbox_peek_data(struct mbox_chan
*chan
)
181 struct sun6i_msgbox
*mbox
= to_sun6i_msgbox(chan
);
182 int n
= channel_number(chan
);
184 return readl(mbox
->regs
+ MSG_STAT_REG(n
)) & MSG_STAT_MASK
;
187 static const struct mbox_chan_ops sun6i_msgbox_chan_ops
= {
188 .send_data
= sun6i_msgbox_send_data
,
189 .startup
= sun6i_msgbox_startup
,
190 .shutdown
= sun6i_msgbox_shutdown
,
191 .last_tx_done
= sun6i_msgbox_last_tx_done
,
192 .peek_data
= sun6i_msgbox_peek_data
,
195 static int sun6i_msgbox_probe(struct platform_device
*pdev
)
197 struct device
*dev
= &pdev
->dev
;
198 struct mbox_chan
*chans
;
199 struct reset_control
*reset
;
200 struct resource
*res
;
201 struct sun6i_msgbox
*mbox
;
204 mbox
= devm_kzalloc(dev
, sizeof(*mbox
), GFP_KERNEL
);
208 chans
= devm_kcalloc(dev
, NUM_CHANS
, sizeof(*chans
), GFP_KERNEL
);
212 for (i
= 0; i
< NUM_CHANS
; ++i
)
213 chans
[i
].con_priv
= mbox
;
215 mbox
->clk
= devm_clk_get(dev
, NULL
);
216 if (IS_ERR(mbox
->clk
)) {
217 ret
= PTR_ERR(mbox
->clk
);
218 dev_err(dev
, "Failed to get clock: %d\n", ret
);
222 ret
= clk_prepare_enable(mbox
->clk
);
224 dev_err(dev
, "Failed to enable clock: %d\n", ret
);
228 reset
= devm_reset_control_get_exclusive(dev
, NULL
);
230 ret
= PTR_ERR(reset
);
231 dev_err(dev
, "Failed to get reset control: %d\n", ret
);
232 goto err_disable_unprepare
;
236 * NOTE: We rely on platform firmware to preconfigure the channel
237 * directions, and we share this hardware block with other firmware
238 * that runs concurrently with Linux (e.g. a trusted monitor).
240 * Therefore, we do *not* assert the reset line if probing fails or
241 * when removing the device.
243 ret
= reset_control_deassert(reset
);
245 dev_err(dev
, "Failed to deassert reset: %d\n", ret
);
246 goto err_disable_unprepare
;
249 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
252 goto err_disable_unprepare
;
255 mbox
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
256 if (IS_ERR(mbox
->regs
)) {
257 ret
= PTR_ERR(mbox
->regs
);
258 dev_err(dev
, "Failed to map MMIO resource: %d\n", ret
);
259 goto err_disable_unprepare
;
262 /* Disable all IRQs for this end of the msgbox. */
263 writel(0, mbox
->regs
+ LOCAL_IRQ_EN_REG
);
265 ret
= devm_request_irq(dev
, irq_of_parse_and_map(dev
->of_node
, 0),
266 sun6i_msgbox_irq
, 0, dev_name(dev
), mbox
);
268 dev_err(dev
, "Failed to register IRQ handler: %d\n", ret
);
269 goto err_disable_unprepare
;
272 mbox
->controller
.dev
= dev
;
273 mbox
->controller
.ops
= &sun6i_msgbox_chan_ops
;
274 mbox
->controller
.chans
= chans
;
275 mbox
->controller
.num_chans
= NUM_CHANS
;
276 mbox
->controller
.txdone_irq
= false;
277 mbox
->controller
.txdone_poll
= true;
278 mbox
->controller
.txpoll_period
= 5;
280 spin_lock_init(&mbox
->lock
);
281 platform_set_drvdata(pdev
, mbox
);
283 ret
= mbox_controller_register(&mbox
->controller
);
285 dev_err(dev
, "Failed to register controller: %d\n", ret
);
286 goto err_disable_unprepare
;
291 err_disable_unprepare
:
292 clk_disable_unprepare(mbox
->clk
);
297 static int sun6i_msgbox_remove(struct platform_device
*pdev
)
299 struct sun6i_msgbox
*mbox
= platform_get_drvdata(pdev
);
301 mbox_controller_unregister(&mbox
->controller
);
302 /* See the comment in sun6i_msgbox_probe about the reset line. */
303 clk_disable_unprepare(mbox
->clk
);
308 static const struct of_device_id sun6i_msgbox_of_match
[] = {
309 { .compatible
= "allwinner,sun6i-a31-msgbox", },
312 MODULE_DEVICE_TABLE(of
, sun6i_msgbox_of_match
);
314 static struct platform_driver sun6i_msgbox_driver
= {
316 .name
= "sun6i-msgbox",
317 .of_match_table
= sun6i_msgbox_of_match
,
319 .probe
= sun6i_msgbox_probe
,
320 .remove
= sun6i_msgbox_remove
,
322 module_platform_driver(sun6i_msgbox_driver
);
324 MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
325 MODULE_DESCRIPTION("Allwinner sun6i/sun8i/sun9i/sun50i Message Box");
326 MODULE_LICENSE("GPL v2");