1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
// SPDX-License-Identifier: GPL-2.0
/*
* mailbox driver for StarFive JH7110 SoC
*
* Copyright (c) 2021 StarFive Technology Co., Ltd.
* Author: Shanlong Li <shanlong.li@starfivetech.com>
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mailbox_controller.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/pm_runtime.h>
#include "mailbox.h"
#define MBOX_CHAN_MAX 4
#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x10))
#define MBOX_IRQ_REG 0x00
#define MBOX_SET_REG 0x04
#define MBOX_CLR_REG 0x08
#define MBOX_CMD_REG 0x0c
#define MBC_PEND_SMRY 0x100
typedef enum {
MAILBOX_CORE_U7 = 0,
MAILBOX_CORE_HIFI4,
MAILBOX_CORE_E2,
MAILBOX_CORE_RSVD0,
MAILBOX_CORE_NUM,
} mailbox_core_t;
struct mailbox_irq_name_c{
int id;
char name[16];
};
static const struct mailbox_irq_name_c irq_peer_name[MBOX_CHAN_MAX] = {
{MAILBOX_CORE_U7, "u74_core"},
{MAILBOX_CORE_HIFI4, "hifi4_core"},
{MAILBOX_CORE_E2, "e24_core"},
{MAILBOX_CORE_RSVD0, "" },
};
/**
* starfive mailbox channel information
*
* A channel can be used for TX or RX, it can trigger remote
* processor interrupt to notify remote processor and can receive
* interrupt if has incoming message.
*
* @dst_irq: Interrupt vector for remote processor
* @core_id: id for remote processor
*/
struct starfive_chan_info {
unsigned int dst_irq;
mailbox_core_t core_id;
};
/**
* starfive mailbox controller data
*
* Mailbox controller includes 4 channels and can allocate
* channel for message transferring.
*
* @dev: Device to which it is attached
* @base: Base address of the register mapping region
* @chan: Representation of channels in mailbox controller
* @mchan: Representation of channel info
* @controller: Representation of a communication channel controller
*/
struct starfive_mbox {
struct device *dev;
void __iomem *base;
struct mbox_chan chan[MBOX_CHAN_MAX];
struct starfive_chan_info mchan[MBOX_CHAN_MAX];
struct mbox_controller controller;
struct clk *clk;
struct reset_control *rst_rresetn;
};
static struct starfive_mbox *to_starfive_mbox(struct mbox_controller *mbox)
{
return container_of(mbox, struct starfive_mbox, controller);
}
static struct mbox_chan *
starfive_of_mbox_index_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
struct starfive_mbox *sbox;
int ind = sp->args[0];
int core_id = sp->args[1];
if (ind >= mbox->num_chans || core_id >= MAILBOX_CORE_NUM)
return ERR_PTR(-EINVAL);
sbox = to_starfive_mbox(mbox);
sbox->mchan[ind].core_id = core_id;
return &mbox->chans[ind];
}
static irqreturn_t starfive_rx_irq_handler(int irq, void *p)
{
struct mbox_chan *chan = p;
unsigned long ch = (unsigned long)chan->con_priv;
struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
void __iomem *base = MBOX_BASE(mbox, ch);
u32 val;
val = readl(base + MBOX_CMD_REG);
if (!val)
return IRQ_NONE;
mbox_chan_received_data(chan, (void *)&val);
writel(0, base + MBOX_CLR_REG);
return IRQ_HANDLED;
}
static int starfive_mbox_check_state(struct mbox_chan *chan)
{
unsigned long ch = (unsigned long)chan->con_priv;
struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
unsigned long irq_flag = IRQF_SHARED;
long ret = 0;
pm_runtime_get_sync(mbox->dev);
/* MAILBOX should be with IRQF_NO_SUSPEND set */
if (!mbox->dev->pm_domain)
irq_flag |= IRQF_NO_SUSPEND;
/* Mailbox is idle so directly bail out */
if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch))
return -EBUSY;
if (mbox->mchan[ch].dst_irq > 0) {
dev_dbg(mbox->dev, "%s: host IRQ = %d, ch:%ld", __func__, mbox->mchan[ch].dst_irq, ch);
ret = devm_request_irq(mbox->dev, mbox->mchan[ch].dst_irq, starfive_rx_irq_handler,
irq_flag, irq_peer_name[ch].name, chan);
if (ret < 0)
dev_err(mbox->dev, "request_irq %d failed\n", mbox->mchan[ch].dst_irq);
}
return ret;
}
static int starfive_mbox_startup(struct mbox_chan *chan)
{
return starfive_mbox_check_state(chan);
}
static void starfive_mbox_shutdown(struct mbox_chan *chan)
{
struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
unsigned long ch = (unsigned long)chan->con_priv;
void __iomem *base = MBOX_BASE(mbox, ch);
writel(0x0, base + MBOX_IRQ_REG);
writel(0x0, base + MBOX_CLR_REG);
if (mbox->mchan[ch].dst_irq > 0)
devm_free_irq(mbox->dev, mbox->mchan[ch].dst_irq, chan);
pm_runtime_put_sync(mbox->dev);
}
static int starfive_mbox_send_data(struct mbox_chan *chan, void *msg)
{
unsigned long ch = (unsigned long)chan->con_priv;
struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox);
struct starfive_chan_info *mchan = &mbox->mchan[ch];
void __iomem *base = MBOX_BASE(mbox, ch);
u32 *buf = msg;
/* Ensure channel is released */
if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) {
pr_debug("%s:%d. busy\n", __func__, __LINE__);
return -EBUSY;
}
/* Clear mask for destination interrupt */
writel(BIT(mchan->core_id), base + MBOX_IRQ_REG);
/* Fill message data */
writel(*buf, base + MBOX_SET_REG);
return 0;
}
static struct mbox_chan_ops starfive_mbox_ops = {
.startup = starfive_mbox_startup,
.send_data = starfive_mbox_send_data,
.shutdown = starfive_mbox_shutdown,
};
static const struct of_device_id starfive_mbox_of_match[] = {
{ .compatible = "starfive,mail_box",},
{},
};
MODULE_DEVICE_TABLE(of, starfive_mbox_of_match);
void starfive_mailbox_init(struct starfive_mbox *mbox)
{
mbox->clk = devm_clk_get_optional(mbox->dev, "clk_apb");
if (IS_ERR(mbox->clk)) {
dev_err(mbox->dev, "failed to get mailbox\n");
return;
}
mbox->rst_rresetn = devm_reset_control_get_exclusive(mbox->dev, "mbx_rre");
if (IS_ERR(mbox->rst_rresetn)) {
dev_err(mbox->dev, "failed to get mailbox reset\n");
return;
}
clk_prepare_enable(mbox->clk);
reset_control_deassert(mbox->rst_rresetn);
}
static int starfive_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct starfive_mbox *mbox;
struct mbox_chan *chan;
struct resource *res;
unsigned long ch;
int err;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
if (!mbox)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mbox->base = devm_ioremap_resource(dev, res);
mbox->dev = dev;
if (IS_ERR(mbox->base))
return PTR_ERR(mbox->base);
starfive_mailbox_init(mbox);
mbox->controller.dev = dev;
mbox->controller.chans = mbox->chan;
mbox->controller.num_chans = MBOX_CHAN_MAX;
mbox->controller.ops = &starfive_mbox_ops;
mbox->controller.of_xlate = starfive_of_mbox_index_xlate;
mbox->controller.txdone_irq = true;
mbox->controller.txdone_poll = false;
/* Initialize mailbox channel data */
chan = mbox->chan;
for (ch = 0; ch < MBOX_CHAN_MAX; ch++) {
mbox->mchan[ch].dst_irq = 0;
mbox->mchan[ch].core_id = (mailbox_core_t)ch;
chan[ch].con_priv = (void *)ch;
}
mbox->mchan[MAILBOX_CORE_HIFI4].dst_irq = platform_get_irq(pdev, 0);
mbox->mchan[MAILBOX_CORE_E2].dst_irq = platform_get_irq(pdev, 1);
err = mbox_controller_register(&mbox->controller);
if (err) {
dev_err(dev, "Failed to register mailbox %d\n", err);
return err;
}
platform_set_drvdata(pdev, mbox);
dev_info(dev, "Mailbox enabled\n");
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
}
static int starfive_mbox_remove(struct platform_device *pdev)
{
struct starfive_mbox *mbox = platform_get_drvdata(pdev);
mbox_controller_unregister(&mbox->controller);
devm_clk_put(mbox->dev, mbox->clk);
pm_runtime_disable(mbox->dev);
return 0;
}
static int __maybe_unused starfive_mbox_suspend(struct device *dev)
{
struct starfive_mbox *mbox = dev_get_drvdata(dev);
clk_disable_unprepare(mbox->clk);
return 0;
}
static int __maybe_unused starfive_mbox_resume(struct device *dev)
{
struct starfive_mbox *mbox = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(mbox->clk);
if (ret)
dev_err(dev, "failed to enable clock\n");
return ret;
}
static const struct dev_pm_ops starfive_mbox_pm_ops = {
.suspend = starfive_mbox_suspend,
.resume = starfive_mbox_resume,
SET_RUNTIME_PM_OPS(starfive_mbox_suspend, starfive_mbox_resume, NULL)
};
static struct platform_driver starfive_mbox_driver = {
.probe = starfive_mbox_probe,
.remove = starfive_mbox_remove,
.driver = {
.name = "mailbox",
.of_match_table = starfive_mbox_of_match,
.pm = &starfive_mbox_pm_ops,
},
};
static int __init starfive_mbox_init(void)
{
return platform_driver_register(&starfive_mbox_driver);
}
core_initcall(starfive_mbox_init);
static void __exit starfive_mbox_exit(void)
{
platform_driver_unregister(&starfive_mbox_driver);
}
module_exit(starfive_mbox_exit);
MODULE_DESCRIPTION("StarFive Mailbox Controller driver");
MODULE_AUTHOR("Shanlong Li <shanlong.li@starfivetech.com>");
MODULE_LICENSE("GPL");
|