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