2 * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 #include <linux/clk.h>
15 #include <linux/interrupt.h>
17 #include <linux/kernel.h>
18 #include <linux/mailbox_controller.h>
19 #include <linux/module.h>
20 #include <linux/of_device.h>
21 #include <linux/platform_device.h>
23 #define MAILBOX_A2B_INTEN 0x00
24 #define MAILBOX_A2B_STATUS 0x04
25 #define MAILBOX_A2B_CMD(x) (0x08 + (x) * 8)
26 #define MAILBOX_A2B_DAT(x) (0x0c + (x) * 8)
28 #define MAILBOX_B2A_INTEN 0x28
29 #define MAILBOX_B2A_STATUS 0x2C
30 #define MAILBOX_B2A_CMD(x) (0x30 + (x) * 8)
31 #define MAILBOX_B2A_DAT(x) (0x34 + (x) * 8)
33 struct rockchip_mbox_msg
{
38 struct rockchip_mbox_data
{
42 struct rockchip_mbox_chan
{
45 struct rockchip_mbox_msg
*msg
;
46 struct rockchip_mbox
*mb
;
49 struct rockchip_mbox
{
50 struct mbox_controller mbox
;
52 void __iomem
*mbox_base
;
54 /* The maximum size of buf for each channel */
57 struct rockchip_mbox_chan
*chans
;
60 static int rockchip_mbox_send_data(struct mbox_chan
*chan
, void *data
)
62 struct rockchip_mbox
*mb
= dev_get_drvdata(chan
->mbox
->dev
);
63 struct rockchip_mbox_msg
*msg
= data
;
64 struct rockchip_mbox_chan
*chans
= mb
->chans
;
69 if (msg
->rx_size
> mb
->buf_size
) {
70 dev_err(mb
->mbox
.dev
, "Transmit size over buf size(%d)\n",
75 dev_dbg(mb
->mbox
.dev
, "Chan[%d]: A2B message, cmd 0x%08x\n",
76 chans
->idx
, msg
->cmd
);
78 mb
->chans
[chans
->idx
].msg
= msg
;
80 writel_relaxed(msg
->cmd
, mb
->mbox_base
+ MAILBOX_A2B_CMD(chans
->idx
));
81 writel_relaxed(msg
->rx_size
, mb
->mbox_base
+
82 MAILBOX_A2B_DAT(chans
->idx
));
87 static int rockchip_mbox_startup(struct mbox_chan
*chan
)
89 struct rockchip_mbox
*mb
= dev_get_drvdata(chan
->mbox
->dev
);
91 /* Enable all B2A interrupts */
92 writel_relaxed((1 << mb
->mbox
.num_chans
) - 1,
93 mb
->mbox_base
+ MAILBOX_B2A_INTEN
);
98 static void rockchip_mbox_shutdown(struct mbox_chan
*chan
)
100 struct rockchip_mbox
*mb
= dev_get_drvdata(chan
->mbox
->dev
);
101 struct rockchip_mbox_chan
*chans
= mb
->chans
;
103 /* Disable all B2A interrupts */
104 writel_relaxed(0, mb
->mbox_base
+ MAILBOX_B2A_INTEN
);
106 mb
->chans
[chans
->idx
].msg
= NULL
;
109 static const struct mbox_chan_ops rockchip_mbox_chan_ops
= {
110 .send_data
= rockchip_mbox_send_data
,
111 .startup
= rockchip_mbox_startup
,
112 .shutdown
= rockchip_mbox_shutdown
,
115 static irqreturn_t
rockchip_mbox_irq(int irq
, void *dev_id
)
118 struct rockchip_mbox
*mb
= (struct rockchip_mbox
*)dev_id
;
119 u32 status
= readl_relaxed(mb
->mbox_base
+ MAILBOX_B2A_STATUS
);
121 for (idx
= 0; idx
< mb
->mbox
.num_chans
; idx
++) {
122 if ((status
& (1 << idx
)) && (irq
== mb
->chans
[idx
].irq
)) {
123 /* Clear mbox interrupt */
124 writel_relaxed(1 << idx
,
125 mb
->mbox_base
+ MAILBOX_B2A_STATUS
);
126 return IRQ_WAKE_THREAD
;
133 static irqreturn_t
rockchip_mbox_isr(int irq
, void *dev_id
)
136 struct rockchip_mbox_msg
*msg
= NULL
;
137 struct rockchip_mbox
*mb
= (struct rockchip_mbox
*)dev_id
;
139 for (idx
= 0; idx
< mb
->mbox
.num_chans
; idx
++) {
140 if (irq
!= mb
->chans
[idx
].irq
)
143 msg
= mb
->chans
[idx
].msg
;
145 dev_err(mb
->mbox
.dev
,
146 "Chan[%d]: B2A message is NULL\n", idx
);
147 break; /* spurious */
150 mbox_chan_received_data(&mb
->mbox
.chans
[idx
], msg
);
151 mb
->chans
[idx
].msg
= NULL
;
153 dev_dbg(mb
->mbox
.dev
, "Chan[%d]: B2A message, cmd 0x%08x\n",
162 static const struct rockchip_mbox_data rk3368_drv_data
= {
166 static const struct of_device_id rockchip_mbox_of_match
[] = {
167 { .compatible
= "rockchip,rk3368-mailbox", .data
= &rk3368_drv_data
},
170 MODULE_DEVICE_TABLE(of
, rockchp_mbox_of_match
);
172 static int rockchip_mbox_probe(struct platform_device
*pdev
)
174 struct rockchip_mbox
*mb
;
175 const struct of_device_id
*match
;
176 const struct rockchip_mbox_data
*drv_data
;
177 struct resource
*res
;
180 if (!pdev
->dev
.of_node
)
183 match
= of_match_node(rockchip_mbox_of_match
, pdev
->dev
.of_node
);
184 drv_data
= (const struct rockchip_mbox_data
*)match
->data
;
186 mb
= devm_kzalloc(&pdev
->dev
, sizeof(*mb
), GFP_KERNEL
);
190 mb
->chans
= devm_kcalloc(&pdev
->dev
, drv_data
->num_chans
,
191 sizeof(*mb
->chans
), GFP_KERNEL
);
195 mb
->mbox
.chans
= devm_kcalloc(&pdev
->dev
, drv_data
->num_chans
,
196 sizeof(*mb
->mbox
.chans
), GFP_KERNEL
);
200 platform_set_drvdata(pdev
, mb
);
202 mb
->mbox
.dev
= &pdev
->dev
;
203 mb
->mbox
.num_chans
= drv_data
->num_chans
;
204 mb
->mbox
.ops
= &rockchip_mbox_chan_ops
;
205 mb
->mbox
.txdone_irq
= true;
207 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
211 mb
->mbox_base
= devm_ioremap_resource(&pdev
->dev
, res
);
212 if (IS_ERR(mb
->mbox_base
))
213 return PTR_ERR(mb
->mbox_base
);
215 /* Each channel has two buffers for A2B and B2A */
216 mb
->buf_size
= (size_t)resource_size(res
) / (drv_data
->num_chans
* 2);
218 mb
->pclk
= devm_clk_get(&pdev
->dev
, "pclk_mailbox");
219 if (IS_ERR(mb
->pclk
)) {
220 ret
= PTR_ERR(mb
->pclk
);
221 dev_err(&pdev
->dev
, "failed to get pclk_mailbox clock: %d\n",
226 ret
= clk_prepare_enable(mb
->pclk
);
228 dev_err(&pdev
->dev
, "failed to enable pclk: %d\n", ret
);
232 for (i
= 0; i
< mb
->mbox
.num_chans
; i
++) {
233 irq
= platform_get_irq(pdev
, i
);
237 ret
= devm_request_threaded_irq(&pdev
->dev
, irq
,
239 rockchip_mbox_isr
, IRQF_ONESHOT
,
240 dev_name(&pdev
->dev
), mb
);
244 mb
->chans
[i
].idx
= i
;
245 mb
->chans
[i
].irq
= irq
;
246 mb
->chans
[i
].mb
= mb
;
247 mb
->chans
[i
].msg
= NULL
;
250 ret
= mbox_controller_register(&mb
->mbox
);
252 dev_err(&pdev
->dev
, "Failed to register mailbox: %d\n", ret
);
257 static int rockchip_mbox_remove(struct platform_device
*pdev
)
259 struct rockchip_mbox
*mb
= platform_get_drvdata(pdev
);
264 mbox_controller_unregister(&mb
->mbox
);
269 static struct platform_driver rockchip_mbox_driver
= {
270 .probe
= rockchip_mbox_probe
,
271 .remove
= rockchip_mbox_remove
,
273 .name
= "rockchip-mailbox",
274 .of_match_table
= of_match_ptr(rockchip_mbox_of_match
),
278 module_platform_driver(rockchip_mbox_driver
);
280 MODULE_LICENSE("GPL v2");
281 MODULE_DESCRIPTION("Rockchip mailbox: communicate between CPU cores and MCU");
282 MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
283 MODULE_AUTHOR("Caesar Wang <wxt@rock-chips.com>");