Skip to content

Commit 529015a

Browse files
Frankd35Jassi Brar
authored andcommitted
mailbox: sophgo: add mailbox driver for CV18XX series SoC
Add mailbox controller driver for CV18XX SoCs, which provides 8 channels and each channel has an 8-byte FIFO. Signed-off-by: Yuntao Dai <[email protected]> Signed-off-by: Junhui Liu <[email protected]> Signed-off-by: Jassi Brar <[email protected]>
1 parent fca8d64 commit 529015a

File tree

3 files changed

+232
-0
lines changed

3 files changed

+232
-0
lines changed

drivers/mailbox/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ config ARM_MHU_V3
3636
that provides different means of transports: supported extensions
3737
will be discovered and possibly managed at probe-time.
3838

39+
config CV1800_MBOX
40+
tristate "cv1800 mailbox"
41+
depends on ARCH_SOPHGO || COMPILE_TEST
42+
help
43+
Mailbox driver implementation for Sophgo CV18XX SoCs. This driver
44+
can be used to send message between different processors in SoC. Any
45+
processer can write data in a channel, and set co-responding register
46+
to raise interrupt to notice another processor, and it is allowed to
47+
send data to itself.
48+
3949
config EXYNOS_MBOX
4050
tristate "Exynos Mailbox"
4151
depends on ARCH_EXYNOS || COMPILE_TEST

drivers/mailbox/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o
1111

1212
obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o
1313

14+
obj-$(CONFIG_CV1800_MBOX) += cv1800-mailbox.o
15+
1416
obj-$(CONFIG_EXYNOS_MBOX) += exynos-mailbox.o
1517

1618
obj-$(CONFIG_IMX_MBOX) += imx-mailbox.o

drivers/mailbox/cv1800-mailbox.c

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2024 Sophgo Technology Inc.
4+
* Copyright (C) 2024 Yuntao Dai <[email protected]>
5+
* Copyright (C) 2025 Junhui Liu <[email protected]>
6+
*/
7+
8+
#include <linux/bits.h>
9+
#include <linux/device.h>
10+
#include <linux/err.h>
11+
#include <linux/interrupt.h>
12+
#include <linux/io.h>
13+
#include <linux/kfifo.h>
14+
#include <linux/mailbox_client.h>
15+
#include <linux/mailbox_controller.h>
16+
#include <linux/module.h>
17+
#include <linux/platform_device.h>
18+
#include <linux/slab.h>
19+
20+
#define RECV_CPU 1
21+
22+
#define MAILBOX_MAX_CHAN 8
23+
#define MAILBOX_MSG_LEN 8
24+
25+
#define MBOX_EN_REG(cpu) (cpu << 2)
26+
#define MBOX_DONE_REG(cpu) ((cpu << 2) + 2)
27+
#define MBOX_SET_CLR_REG(cpu) (0x10 + (cpu << 4))
28+
#define MBOX_SET_INT_REG(cpu) (0x18 + (cpu << 4))
29+
#define MBOX_SET_REG 0x60
30+
31+
#define MAILBOX_CONTEXT_OFFSET 0x0400
32+
#define MAILBOX_CONTEXT_SIZE 0x0040
33+
34+
#define MBOX_CONTEXT_BASE_INDEX(base, index) \
35+
((u64 __iomem *)(base + MAILBOX_CONTEXT_OFFSET) + index)
36+
37+
/**
38+
* struct cv1800_mbox_chan_priv - cv1800 mailbox channel private data
39+
* @idx: index of channel
40+
* @cpu: send to which processor
41+
*/
42+
struct cv1800_mbox_chan_priv {
43+
int idx;
44+
int cpu;
45+
};
46+
47+
struct cv1800_mbox {
48+
struct mbox_controller mbox;
49+
struct cv1800_mbox_chan_priv priv[MAILBOX_MAX_CHAN];
50+
struct mbox_chan chans[MAILBOX_MAX_CHAN];
51+
u64 __iomem *content[MAILBOX_MAX_CHAN];
52+
void __iomem *mbox_base;
53+
int recvid;
54+
};
55+
56+
static irqreturn_t cv1800_mbox_isr(int irq, void *dev_id)
57+
{
58+
struct cv1800_mbox *mbox = (struct cv1800_mbox *)dev_id;
59+
size_t i;
60+
u64 msg;
61+
int ret = IRQ_NONE;
62+
63+
for (i = 0; i < MAILBOX_MAX_CHAN; i++) {
64+
if (mbox->content[i] && mbox->chans[i].cl) {
65+
memcpy_fromio(&msg, mbox->content[i], MAILBOX_MSG_LEN);
66+
mbox->content[i] = NULL;
67+
mbox_chan_received_data(&mbox->chans[i], (void *)&msg);
68+
ret = IRQ_HANDLED;
69+
}
70+
}
71+
72+
return ret;
73+
}
74+
75+
static irqreturn_t cv1800_mbox_irq(int irq, void *dev_id)
76+
{
77+
struct cv1800_mbox *mbox = (struct cv1800_mbox *)dev_id;
78+
u8 set, valid;
79+
size_t i;
80+
int ret = IRQ_NONE;
81+
82+
set = readb(mbox->mbox_base + MBOX_SET_INT_REG(RECV_CPU));
83+
84+
if (!set)
85+
return ret;
86+
87+
for (i = 0; i < MAILBOX_MAX_CHAN; i++) {
88+
valid = set & BIT(i);
89+
if (valid) {
90+
mbox->content[i] =
91+
MBOX_CONTEXT_BASE_INDEX(mbox->mbox_base, i);
92+
writeb(valid, mbox->mbox_base +
93+
MBOX_SET_CLR_REG(RECV_CPU));
94+
writeb(~valid, mbox->mbox_base + MBOX_EN_REG(RECV_CPU));
95+
ret = IRQ_WAKE_THREAD;
96+
}
97+
}
98+
99+
return ret;
100+
}
101+
102+
static int cv1800_mbox_send_data(struct mbox_chan *chan, void *data)
103+
{
104+
struct cv1800_mbox_chan_priv *priv =
105+
(struct cv1800_mbox_chan_priv *)chan->con_priv;
106+
struct cv1800_mbox *mbox = dev_get_drvdata(chan->mbox->dev);
107+
int idx = priv->idx;
108+
int cpu = priv->cpu;
109+
u8 en, valid;
110+
111+
memcpy_toio(MBOX_CONTEXT_BASE_INDEX(mbox->mbox_base, idx),
112+
data, MAILBOX_MSG_LEN);
113+
114+
valid = BIT(idx);
115+
writeb(valid, mbox->mbox_base + MBOX_SET_CLR_REG(cpu));
116+
en = readb(mbox->mbox_base + MBOX_EN_REG(cpu));
117+
writeb(en | valid, mbox->mbox_base + MBOX_EN_REG(cpu));
118+
writeb(valid, mbox->mbox_base + MBOX_SET_REG);
119+
120+
return 0;
121+
}
122+
123+
static bool cv1800_last_tx_done(struct mbox_chan *chan)
124+
{
125+
struct cv1800_mbox_chan_priv *priv =
126+
(struct cv1800_mbox_chan_priv *)chan->con_priv;
127+
struct cv1800_mbox *mbox = dev_get_drvdata(chan->mbox->dev);
128+
u8 en;
129+
130+
en = readb(mbox->mbox_base + MBOX_EN_REG(priv->cpu));
131+
132+
return !(en & BIT(priv->idx));
133+
}
134+
135+
static const struct mbox_chan_ops cv1800_mbox_chan_ops = {
136+
.send_data = cv1800_mbox_send_data,
137+
.last_tx_done = cv1800_last_tx_done,
138+
};
139+
140+
static struct mbox_chan *cv1800_mbox_xlate(struct mbox_controller *mbox,
141+
const struct of_phandle_args *spec)
142+
{
143+
struct cv1800_mbox_chan_priv *priv;
144+
145+
int idx = spec->args[0];
146+
int cpu = spec->args[1];
147+
148+
if (idx >= mbox->num_chans)
149+
return ERR_PTR(-EINVAL);
150+
151+
priv = mbox->chans[idx].con_priv;
152+
priv->cpu = cpu;
153+
154+
return &mbox->chans[idx];
155+
}
156+
157+
static const struct of_device_id cv1800_mbox_of_match[] = {
158+
{ .compatible = "sophgo,cv1800b-mailbox", },
159+
{},
160+
};
161+
MODULE_DEVICE_TABLE(of, cv1800_mbox_of_match);
162+
163+
static int cv1800_mbox_probe(struct platform_device *pdev)
164+
{
165+
struct device *dev = &pdev->dev;
166+
struct cv1800_mbox *mb;
167+
int irq, idx, err;
168+
169+
mb = devm_kzalloc(dev, sizeof(*mb), GFP_KERNEL);
170+
if (!mb)
171+
return -ENOMEM;
172+
173+
mb->mbox_base = devm_platform_ioremap_resource(pdev, 0);
174+
if (IS_ERR(mb->mbox_base))
175+
return dev_err_probe(dev, PTR_ERR(mb->mbox_base),
176+
"Failed to map resource\n");
177+
178+
mb->mbox.dev = dev;
179+
mb->mbox.chans = mb->chans;
180+
mb->mbox.txdone_poll = true;
181+
mb->mbox.ops = &cv1800_mbox_chan_ops;
182+
mb->mbox.num_chans = MAILBOX_MAX_CHAN;
183+
mb->mbox.of_xlate = cv1800_mbox_xlate;
184+
185+
irq = platform_get_irq(pdev, 0);
186+
if (irq < 0)
187+
return irq;
188+
189+
err = devm_request_threaded_irq(dev, irq, cv1800_mbox_irq,
190+
cv1800_mbox_isr, IRQF_ONESHOT,
191+
dev_name(&pdev->dev), mb);
192+
if (err < 0)
193+
return dev_err_probe(dev, err, "Failed to register irq\n");
194+
195+
for (idx = 0; idx < MAILBOX_MAX_CHAN; idx++) {
196+
mb->priv[idx].idx = idx;
197+
mb->mbox.chans[idx].con_priv = &mb->priv[idx];
198+
}
199+
200+
platform_set_drvdata(pdev, mb);
201+
202+
err = devm_mbox_controller_register(dev, &mb->mbox);
203+
if (err)
204+
return dev_err_probe(dev, err, "Failed to register mailbox\n");
205+
206+
return 0;
207+
}
208+
209+
static struct platform_driver cv1800_mbox_driver = {
210+
.driver = {
211+
.name = "cv1800-mbox",
212+
.of_match_table = cv1800_mbox_of_match,
213+
},
214+
.probe = cv1800_mbox_probe,
215+
};
216+
217+
module_platform_driver(cv1800_mbox_driver);
218+
219+
MODULE_DESCRIPTION("cv1800 mailbox driver");
220+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)