Skip to content

Commit ae524eb

Browse files
jammyaspeedJassi Brar
authored andcommitted
mailbox: aspeed: add mailbox driver for AST27XX series SoC
Add mailbox controller driver for AST27XX SoCs, which provides independent tx/rx mailbox between different processors. There are 4 channels for each tx/rx mailbox and each channel has an 32-byte FIFO. Signed-off-by: Jammy Huang <[email protected]> Reviewed-by: Andrew Jeffery <[email protected]> Signed-off-by: Jassi Brar <[email protected]>
1 parent 7d33dd2 commit ae524eb

File tree

3 files changed

+246
-0
lines changed

3 files changed

+246
-0
lines changed

drivers/mailbox/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ 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 AST2700_MBOX
40+
tristate "ASPEED AST2700 IPC driver"
41+
depends on ARCH_ASPEED || COMPILE_TEST
42+
help
43+
Mailbox driver implementation for ASPEED AST27XX SoCs. This driver
44+
can be used to send message between different processors in SoC.
45+
The driver provides mailbox support for sending interrupts to the
46+
clients. Say Y here if you want to build this driver.
47+
3948
config CV1800_MBOX
4049
tristate "cv1800 mailbox"
4150
depends on ARCH_SOPHGO || 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_AST2700_MBOX) += ast2700-mailbox.o
15+
1416
obj-$(CONFIG_CV1800_MBOX) += cv1800-mailbox.o
1517

1618
obj-$(CONFIG_EXYNOS_MBOX) += exynos-mailbox.o

drivers/mailbox/ast2700-mailbox.c

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright Aspeed Technology Inc. (C) 2025. All rights reserved
4+
*/
5+
6+
#include <linux/interrupt.h>
7+
#include <linux/io.h>
8+
#include <linux/iopoll.h>
9+
#include <linux/kernel.h>
10+
#include <linux/mailbox_controller.h>
11+
#include <linux/module.h>
12+
#include <linux/of.h>
13+
#include <linux/platform_device.h>
14+
#include <linux/slab.h>
15+
16+
/* Each bit in the register represents an IPC ID */
17+
#define IPCR_TX_TRIG 0x00
18+
#define IPCR_ENABLE 0x04
19+
#define IPCR_STATUS 0x08
20+
#define RX_IRQ(n) BIT(n)
21+
#define RX_IRQ_MASK 0xf
22+
#define IPCR_DATA 0x10
23+
24+
struct ast2700_mbox_data {
25+
u8 num_chans;
26+
u8 msg_size;
27+
};
28+
29+
struct ast2700_mbox {
30+
struct mbox_controller mbox;
31+
u8 msg_size;
32+
void __iomem *tx_regs;
33+
void __iomem *rx_regs;
34+
spinlock_t lock;
35+
};
36+
37+
static inline int ch_num(struct mbox_chan *chan)
38+
{
39+
return chan - chan->mbox->chans;
40+
}
41+
42+
static inline bool ast2700_mbox_tx_done(struct ast2700_mbox *mb, int idx)
43+
{
44+
return !(readl(mb->tx_regs + IPCR_STATUS) & BIT(idx));
45+
}
46+
47+
static irqreturn_t ast2700_mbox_irq(int irq, void *p)
48+
{
49+
struct ast2700_mbox *mb = p;
50+
void __iomem *data_reg;
51+
int num_words = mb->msg_size / sizeof(u32);
52+
u32 *word_data;
53+
u32 status;
54+
int n, i;
55+
56+
/* Only examine channels that are currently enabled. */
57+
status = readl(mb->rx_regs + IPCR_ENABLE) &
58+
readl(mb->rx_regs + IPCR_STATUS);
59+
60+
if (!(status & RX_IRQ_MASK))
61+
return IRQ_NONE;
62+
63+
for (n = 0; n < mb->mbox.num_chans; ++n) {
64+
struct mbox_chan *chan = &mb->mbox.chans[n];
65+
66+
if (!(status & RX_IRQ(n)))
67+
continue;
68+
69+
data_reg = mb->rx_regs + IPCR_DATA + mb->msg_size * n;
70+
word_data = chan->con_priv;
71+
/* Read the message data */
72+
for (i = 0; i < num_words; i++)
73+
word_data[i] = readl(data_reg + i * sizeof(u32));
74+
75+
mbox_chan_received_data(chan, chan->con_priv);
76+
77+
/* The IRQ can be cleared only once the FIFO is empty. */
78+
writel(RX_IRQ(n), mb->rx_regs + IPCR_STATUS);
79+
}
80+
81+
return IRQ_HANDLED;
82+
}
83+
84+
static int ast2700_mbox_send_data(struct mbox_chan *chan, void *data)
85+
{
86+
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
87+
int idx = ch_num(chan);
88+
void __iomem *data_reg = mb->tx_regs + IPCR_DATA + mb->msg_size * idx;
89+
u32 *word_data = data;
90+
int num_words = mb->msg_size / sizeof(u32);
91+
int i;
92+
93+
if (!(readl(mb->tx_regs + IPCR_ENABLE) & BIT(idx))) {
94+
dev_warn(mb->mbox.dev, "%s: Ch-%d not enabled yet\n", __func__, idx);
95+
return -ENODEV;
96+
}
97+
98+
if (!(ast2700_mbox_tx_done(mb, idx))) {
99+
dev_warn(mb->mbox.dev, "%s: Ch-%d last data has not finished\n", __func__, idx);
100+
return -EBUSY;
101+
}
102+
103+
/* Write the message data */
104+
for (i = 0 ; i < num_words; i++)
105+
writel(word_data[i], data_reg + i * sizeof(u32));
106+
107+
writel(BIT(idx), mb->tx_regs + IPCR_TX_TRIG);
108+
dev_dbg(mb->mbox.dev, "%s: Ch-%d sent\n", __func__, idx);
109+
110+
return 0;
111+
}
112+
113+
static int ast2700_mbox_startup(struct mbox_chan *chan)
114+
{
115+
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
116+
int idx = ch_num(chan);
117+
void __iomem *reg = mb->rx_regs + IPCR_ENABLE;
118+
unsigned long flags;
119+
120+
spin_lock_irqsave(&mb->lock, flags);
121+
writel(readl(reg) | BIT(idx), reg);
122+
spin_unlock_irqrestore(&mb->lock, flags);
123+
124+
return 0;
125+
}
126+
127+
static void ast2700_mbox_shutdown(struct mbox_chan *chan)
128+
{
129+
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
130+
int idx = ch_num(chan);
131+
void __iomem *reg = mb->rx_regs + IPCR_ENABLE;
132+
unsigned long flags;
133+
134+
spin_lock_irqsave(&mb->lock, flags);
135+
writel(readl(reg) & ~BIT(idx), reg);
136+
spin_unlock_irqrestore(&mb->lock, flags);
137+
}
138+
139+
static bool ast2700_mbox_last_tx_done(struct mbox_chan *chan)
140+
{
141+
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
142+
int idx = ch_num(chan);
143+
144+
return ast2700_mbox_tx_done(mb, idx);
145+
}
146+
147+
static const struct mbox_chan_ops ast2700_mbox_chan_ops = {
148+
.send_data = ast2700_mbox_send_data,
149+
.startup = ast2700_mbox_startup,
150+
.shutdown = ast2700_mbox_shutdown,
151+
.last_tx_done = ast2700_mbox_last_tx_done,
152+
};
153+
154+
static int ast2700_mbox_probe(struct platform_device *pdev)
155+
{
156+
struct ast2700_mbox *mb;
157+
const struct ast2700_mbox_data *dev_data;
158+
struct device *dev = &pdev->dev;
159+
int irq, ret;
160+
161+
if (!pdev->dev.of_node)
162+
return -ENODEV;
163+
164+
dev_data = device_get_match_data(&pdev->dev);
165+
166+
mb = devm_kzalloc(dev, sizeof(*mb), GFP_KERNEL);
167+
if (!mb)
168+
return -ENOMEM;
169+
170+
mb->mbox.chans = devm_kcalloc(&pdev->dev, dev_data->num_chans,
171+
sizeof(*mb->mbox.chans), GFP_KERNEL);
172+
if (!mb->mbox.chans)
173+
return -ENOMEM;
174+
175+
/* con_priv of each channel is used to store the message received */
176+
for (int i = 0; i < dev_data->num_chans; i++) {
177+
mb->mbox.chans[i].con_priv = devm_kcalloc(dev, dev_data->msg_size,
178+
sizeof(u8), GFP_KERNEL);
179+
if (!mb->mbox.chans[i].con_priv)
180+
return -ENOMEM;
181+
}
182+
183+
platform_set_drvdata(pdev, mb);
184+
185+
mb->tx_regs = devm_platform_ioremap_resource_byname(pdev, "tx");
186+
if (IS_ERR(mb->tx_regs))
187+
return PTR_ERR(mb->tx_regs);
188+
189+
mb->rx_regs = devm_platform_ioremap_resource_byname(pdev, "rx");
190+
if (IS_ERR(mb->rx_regs))
191+
return PTR_ERR(mb->rx_regs);
192+
193+
mb->msg_size = dev_data->msg_size;
194+
mb->mbox.dev = dev;
195+
mb->mbox.num_chans = dev_data->num_chans;
196+
mb->mbox.ops = &ast2700_mbox_chan_ops;
197+
mb->mbox.txdone_irq = false;
198+
mb->mbox.txdone_poll = true;
199+
mb->mbox.txpoll_period = 5;
200+
spin_lock_init(&mb->lock);
201+
202+
irq = platform_get_irq(pdev, 0);
203+
if (irq < 0)
204+
return irq;
205+
206+
ret = devm_request_irq(dev, irq, ast2700_mbox_irq, 0, dev_name(dev), mb);
207+
if (ret)
208+
return ret;
209+
210+
return devm_mbox_controller_register(dev, &mb->mbox);
211+
}
212+
213+
static const struct ast2700_mbox_data ast2700_dev_data = {
214+
.num_chans = 4,
215+
.msg_size = 0x20,
216+
};
217+
218+
static const struct of_device_id ast2700_mbox_of_match[] = {
219+
{ .compatible = "aspeed,ast2700-mailbox", .data = &ast2700_dev_data },
220+
{}
221+
};
222+
MODULE_DEVICE_TABLE(of, ast2700_mbox_of_match);
223+
224+
static struct platform_driver ast2700_mbox_driver = {
225+
.driver = {
226+
.name = "ast2700-mailbox",
227+
.of_match_table = ast2700_mbox_of_match,
228+
},
229+
.probe = ast2700_mbox_probe,
230+
};
231+
module_platform_driver(ast2700_mbox_driver);
232+
233+
MODULE_AUTHOR("Jammy Huang <[email protected]>");
234+
MODULE_DESCRIPTION("ASPEED AST2700 IPC driver");
235+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)