1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-09-23     GuEe-GUI     first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 
14 #define DBG_TAG "mailbox.pic"
15 #define DBG_LVL DBG_INFO
16 #include <rtdbg.h>
17 
18 /*
19  * RT-Thread PIC Mailbox device driver
20  *
21  * The mailbox device(s) may be instantiated in one of three equivalent way:
22  *
23  * Device Tree node, eg.:
24  *
25  *      interrupt-controller@0 {
26  *          interrupt-controller;
27  *          #interrupt-cells = <1>;
28  *      };
29  *
30  *      pic_mailbox@10000 {
31  *          compatible = "rt-thread,pic-mailbox";
32  *          reg = <0x10000 0x100>;
33  *          position = <0>;
34  *          interrupts = <34>;
35  *          peer-interrupts = <35>;
36  *          uid = <0>;
37  *          #mbox-cells = <1>;
38  *      };
39  */
40 
41 #define MAILBOX_IMASK       0x00
42 #define MAILBOX_ISTATE      0x04
43 #define MAILBOX_MSG(n)      (0x08 + (n) * 4)
44 
45 struct pic_mbox
46 {
47     struct rt_mbox_controller parent;
48 
49     void *regs;
50     void *peer_regs;
51 
52     int position;
53     int chans_nr;
54     int irq;
55     int peer_hwirq;
56     struct rt_pic *pic;
57 
58     struct rt_spinlock lock;
59 };
60 
61 #define raw_to_pic_mbox(raw) rt_container_of(raw, struct pic_mbox, parent)
62 
pic_mbox_request(struct rt_mbox_chan * chan)63 static rt_err_t pic_mbox_request(struct rt_mbox_chan *chan)
64 {
65     int index = chan - chan->ctrl->chans;
66     struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl);
67 
68     HWREG32(pic_mbox->regs + MAILBOX_IMASK) &= ~RT_BIT(index);
69     HWREG32(pic_mbox->regs + MAILBOX_ISTATE) = 0;
70 
71     return RT_EOK;
72 }
73 
pic_mbox_release(struct rt_mbox_chan * chan)74 static void pic_mbox_release(struct rt_mbox_chan *chan)
75 {
76     int index = chan - chan->ctrl->chans;
77     struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl);
78 
79     HWREG32(pic_mbox->regs + MAILBOX_IMASK) |= RT_BIT(index);
80 }
81 
pic_mbox_send(struct rt_mbox_chan * chan,const void * data)82 static rt_err_t pic_mbox_send(struct rt_mbox_chan *chan, const void *data)
83 {
84     rt_ubase_t level;
85     int index = chan - chan->ctrl->chans;
86     struct pic_mbox *pic_mbox = raw_to_pic_mbox(chan->ctrl);
87 
88     while (HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) & RT_BIT(index))
89     {
90         rt_thread_yield();
91     }
92 
93     if (HWREG32(pic_mbox->peer_regs + MAILBOX_IMASK) & RT_BIT(index))
94     {
95         return -RT_ERROR;
96     }
97 
98     level = rt_spin_lock_irqsave(&pic_mbox->lock);
99 
100     HWREG32(pic_mbox->regs + MAILBOX_MSG(index)) = *(rt_uint32_t *)data;
101     HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) |= RT_BIT(index);
102     rt_hw_wmb();
103 
104     rt_pic_irq_set_state_raw(pic_mbox->pic, pic_mbox->peer_hwirq,
105             RT_IRQ_STATE_PENDING, RT_TRUE);
106 
107     rt_spin_unlock_irqrestore(&pic_mbox->lock, level);
108 
109     return RT_EOK;
110 }
111 
112 static const struct rt_mbox_controller_ops pic_mbox_ops =
113 {
114     .request = pic_mbox_request,
115     .release = pic_mbox_release,
116     .send = pic_mbox_send,
117 };
118 
pic_mbox_isr(int irqno,void * param)119 static void pic_mbox_isr(int irqno, void *param)
120 {
121     rt_uint32_t isr;
122     struct pic_mbox *pic_mbox = param;
123 
124     isr = HWREG32(pic_mbox->regs + MAILBOX_ISTATE);
125 
126     for (int idx = 0; idx < 32; ++idx)
127     {
128         rt_uint32_t msg;
129 
130         if (!(RT_BIT(idx) & isr))
131         {
132             continue;
133         }
134 
135         rt_hw_rmb();
136         msg = HWREG32(pic_mbox->peer_regs + MAILBOX_MSG(idx));
137 
138         rt_mbox_recv(&pic_mbox->parent.chans[idx], &msg);
139     }
140 
141     HWREG32(pic_mbox->regs + MAILBOX_ISTATE) &= ~isr;
142 }
143 
pic_mbox_free_resource(struct pic_mbox * pic_mbox)144 static void pic_mbox_free_resource(struct pic_mbox *pic_mbox)
145 {
146     if (pic_mbox->regs && pic_mbox->peer_regs)
147     {
148         if (pic_mbox->peer_regs > pic_mbox->regs)
149         {
150             rt_iounmap(pic_mbox->regs);
151         }
152         else
153         {
154             rt_iounmap(pic_mbox->peer_regs);
155         }
156     }
157 
158     rt_free(pic_mbox);
159 }
160 
pic_mbox_probe(struct rt_platform_device * pdev)161 static rt_err_t pic_mbox_probe(struct rt_platform_device *pdev)
162 {
163     rt_err_t err;
164     rt_uint64_t size;
165     rt_uint32_t value;
166     char dev_name[RT_NAME_MAX];
167     struct rt_ofw_node *pic_np;
168     struct rt_device *dev = &pdev->parent;
169     struct pic_mbox *pic_mbox = rt_calloc(1, sizeof(*pic_mbox));
170 
171     if (!pic_mbox)
172     {
173         return -RT_ENOMEM;
174     }
175 
176     if ((err = rt_dm_dev_get_address(dev, 0, RT_NULL, &size)))
177     {
178         goto _fail;
179     }
180 
181     if ((err = rt_dm_dev_prop_read_u32(dev, "position", &value)))
182     {
183         goto _fail;
184     }
185 
186     if (!value)
187     {
188         pic_mbox->regs = rt_dm_dev_iomap(dev, 0);
189 
190         if (!pic_mbox->regs)
191         {
192             goto _fail;
193         }
194 
195         pic_mbox->peer_regs = pic_mbox->regs + size / 2;
196 
197         /* Init by the captain */
198         HWREG32(pic_mbox->regs + MAILBOX_IMASK) = 0xffffffff;
199         HWREG32(pic_mbox->regs + MAILBOX_ISTATE) = 0;
200         HWREG32(pic_mbox->peer_regs + MAILBOX_IMASK) = 0xffffffff;
201         HWREG32(pic_mbox->peer_regs + MAILBOX_ISTATE) = 0;
202     }
203     else
204     {
205         pic_mbox->peer_regs = rt_dm_dev_iomap(dev, 0);
206 
207         if (!pic_mbox->peer_regs)
208         {
209             goto _fail;
210         }
211 
212         pic_mbox->regs = pic_mbox->peer_regs + size / 2;
213     }
214 
215     pic_mbox->irq = rt_dm_dev_get_irq(dev, 0);
216 
217     if (pic_mbox->irq < 0)
218     {
219         err = pic_mbox->irq;
220 
221         goto _fail;
222     }
223 
224     if ((err = rt_dm_dev_prop_read_u32(dev, "peer-interrupts", &value)))
225     {
226         goto _fail;
227     }
228     pic_mbox->peer_hwirq = value;
229 
230     if ((err = rt_dm_dev_prop_read_u32(dev, "uid", &value)))
231     {
232         goto _fail;
233     }
234 
235     if (!(pic_np = rt_ofw_find_irq_parent(dev->ofw_node, RT_NULL)))
236     {
237         goto _fail;
238     }
239     pic_mbox->pic = rt_ofw_data(pic_np);
240     rt_ofw_node_put(pic_np);
241 
242     rt_spin_lock_init(&pic_mbox->lock);
243 
244     pic_mbox->parent.dev = dev;
245     pic_mbox->parent.num_chans = 32;
246     pic_mbox->parent.ops = &pic_mbox_ops;
247 
248     if ((err = rt_mbox_controller_register(&pic_mbox->parent)))
249     {
250         goto _fail;
251     }
252 
253     rt_snprintf(dev_name, sizeof(dev_name), "pic-mbox%d", value);
254     rt_hw_interrupt_install(pic_mbox->irq, pic_mbox_isr, pic_mbox, dev_name);
255     rt_hw_interrupt_umask(pic_mbox->irq);
256 
257     return RT_EOK;
258 
259 _fail:
260     pic_mbox_free_resource(pic_mbox);
261 
262     return err;
263 }
264 
pic_mbox_remove(struct rt_platform_device * pdev)265 static rt_err_t pic_mbox_remove(struct rt_platform_device *pdev)
266 {
267     struct pic_mbox *pic_mbox = pdev->parent.user_data;
268 
269     rt_pic_detach_irq(pic_mbox->irq, pic_mbox);
270 
271     rt_mbox_controller_unregister(&pic_mbox->parent);
272 
273     pic_mbox_free_resource(pic_mbox);
274 
275     return RT_EOK;
276 }
277 
278 static const struct rt_ofw_node_id pic_mbox_ofw_ids[] =
279 {
280     { .compatible = "rt-thread,pic-mailbox" },
281     { /* sentinel */ }
282 };
283 
284 static struct rt_platform_driver pic_mbox_driver =
285 {
286     .name = "mailbox-pic",
287     .ids = pic_mbox_ofw_ids,
288 
289     .probe = pic_mbox_probe,
290     .remove = pic_mbox_remove,
291 };
292 RT_PLATFORM_DRIVER_EXPORT(pic_mbox_driver);
293