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 <rthw.h>
12 #include <rtthread.h>
13 
14 #define DBG_TAG "rtdm.mailbox"
15 #define DBG_LVL DBG_INFO
16 #include <rtdbg.h>
17 
18 #include <drivers/ofw.h>
19 #include <drivers/mailbox.h>
20 #include <drivers/platform.h>
21 #include <drivers/core/dm.h>
22 
23 static RT_DEFINE_SPINLOCK(mbox_ops_lock);
24 static rt_list_t mbox_nodes = RT_LIST_OBJECT_INIT(mbox_nodes);
25 
26 static void mbox_chan_timeout(void *param);
27 
rt_mbox_controller_register(struct rt_mbox_controller * ctrl)28 rt_err_t rt_mbox_controller_register(struct rt_mbox_controller *ctrl)
29 {
30     int len;
31     struct rt_mbox_chan *chan;
32     char timer_name[RT_NAME_MAX];
33 
34     if (!ctrl || !ctrl->dev || !ctrl->ops || !ctrl->num_chans)
35     {
36         return -RT_EINVAL;
37     }
38 
39     ctrl->chans = rt_calloc(ctrl->num_chans, sizeof(struct rt_mbox_chan));
40 
41     if (!ctrl->chans)
42     {
43         return -RT_ENOMEM;
44     }
45 
46     len = rt_snprintf(timer_name, sizeof(timer_name), "%s-",
47             rt_dm_dev_get_name(ctrl->dev));
48 
49     RT_ASSERT(len < sizeof(timer_name));
50 
51     chan = &ctrl->chans[0];
52 
53     for (int i = 0; i < ctrl->num_chans; ++i, ++chan)
54     {
55         chan->ctrl = ctrl;
56         rt_spin_lock_init(&chan->lock);
57 
58         rt_snprintf(&timer_name[len], sizeof(timer_name) - len, "%d", i);
59         rt_timer_init(&chan->timer, timer_name, mbox_chan_timeout, chan,
60                 0, RT_TIMER_FLAG_ONE_SHOT);
61     }
62 
63     rt_list_init(&ctrl->list);
64     rt_dm_dev_bind_fwdata(ctrl->dev, RT_NULL, ctrl);
65 
66     rt_spin_lock(&mbox_ops_lock);
67 
68     rt_list_insert_after(&mbox_nodes, &ctrl->list);
69 
70     rt_spin_unlock(&mbox_ops_lock);
71 
72     return RT_EOK;
73 }
74 
rt_mbox_controller_unregister(struct rt_mbox_controller * ctrl)75 rt_err_t rt_mbox_controller_unregister(struct rt_mbox_controller *ctrl)
76 {
77     struct rt_mbox_chan *chan;
78 
79     if (!ctrl)
80     {
81         return -RT_EINVAL;
82     }
83 
84     rt_spin_lock(&mbox_ops_lock);
85 
86     rt_dm_dev_unbind_fwdata(ctrl->dev, RT_NULL);
87     rt_list_remove(&ctrl->list);
88 
89     rt_spin_unlock(&mbox_ops_lock);
90 
91     chan = &ctrl->chans[0];
92 
93     for (int i = ctrl->num_chans - 1; i >= 0; --i, ++chan)
94     {
95         rt_mbox_release(&ctrl->chans[i]);
96     }
97 
98     rt_free(ctrl->chans);
99 
100     return RT_EOK;
101 }
102 
rt_mbox_send(struct rt_mbox_chan * chan,const void * data,rt_uint32_t timeout_ms)103 rt_err_t rt_mbox_send(struct rt_mbox_chan *chan, const void *data,
104         rt_uint32_t timeout_ms)
105 {
106     rt_err_t err;
107     rt_ubase_t level;
108     rt_bool_t timer_go = RT_FALSE;
109     struct rt_mbox_client *client;
110     struct rt_mbox_controller *ctrl;
111 
112     if (!chan || !data)
113     {
114         return -RT_EINVAL;
115     }
116 
117     ctrl = chan->ctrl;
118     client = chan->client;
119 
120     level = rt_spin_lock_irqsave(&chan->lock);
121 
122     if (client->tx_prepare)
123     {
124         client->tx_prepare(client, data);
125     }
126 
127     chan->complete = RT_FALSE;
128     err = ctrl->ops->send(chan, data);
129 
130     if (!err)
131     {
132         chan->data = (void *)data;
133 
134         if (timeout_ms != RT_WAITING_FOREVER)
135         {
136             rt_tick_t tick = rt_tick_from_millisecond(timeout_ms);
137 
138             rt_timer_control(&chan->timer, RT_TIMER_CTRL_SET_TIME, &tick);
139 
140             timer_go = RT_TRUE;
141         }
142     }
143     else
144     {
145         chan->complete = RT_TRUE;
146     }
147 
148     rt_spin_unlock_irqrestore(&chan->lock, level);
149 
150     if (timer_go)
151     {
152         rt_timer_start(&chan->timer);
153     }
154 
155     return err;
156 }
157 
rt_mbox_send_done(struct rt_mbox_chan * chan,rt_err_t err)158 void rt_mbox_send_done(struct rt_mbox_chan *chan, rt_err_t err)
159 {
160     void *data;
161     rt_ubase_t level;
162 
163     level = rt_spin_lock_irqsave(&chan->lock);
164 
165     data = chan->data;
166     chan->data = RT_NULL;
167 
168     rt_spin_unlock_irqrestore(&chan->lock, level);
169 
170     if (chan->client->tx_done)
171     {
172         chan->client->tx_done(chan->client, data, err);
173     }
174 
175     chan->complete = RT_TRUE;
176 }
177 
mbox_chan_timeout(void * param)178 static void mbox_chan_timeout(void *param)
179 {
180     rt_err_t err = RT_EOK;
181     struct rt_mbox_chan *chan = param;
182 
183     if (!chan->complete)
184     {
185         err = -RT_ETIMEOUT;
186     }
187 
188     rt_mbox_send_done(chan, err);
189 }
190 
rt_mbox_peek(struct rt_mbox_chan * chan)191 rt_bool_t rt_mbox_peek(struct rt_mbox_chan *chan)
192 {
193     if (chan && chan->ctrl->ops->peek)
194     {
195         return chan->ctrl->ops->peek(chan);
196     }
197 
198     return RT_FALSE;
199 }
200 
rt_mbox_recv(struct rt_mbox_chan * chan,void * data)201 rt_err_t rt_mbox_recv(struct rt_mbox_chan *chan, void *data)
202 {
203     if (!chan || !data)
204     {
205         return -RT_EINVAL;
206     }
207 
208     if (chan->client->rx_callback)
209     {
210         chan->client->rx_callback(chan->client, data);
211     }
212 
213     return RT_EOK;
214 }
215 
mbox_controller_ofw_parse_default(struct rt_mbox_controller * ctrl,struct rt_ofw_cell_args * args)216 static int mbox_controller_ofw_parse_default(struct rt_mbox_controller *ctrl,
217         struct rt_ofw_cell_args *args)
218 {
219     if (args->args_count != 1)
220     {
221         return -RT_EINVAL;
222     }
223 
224     return args->args[0];
225 }
226 
rt_mbox_request_by_index(struct rt_mbox_client * client,int index)227 struct rt_mbox_chan *rt_mbox_request_by_index(struct rt_mbox_client *client, int index)
228 {
229     rt_err_t err;
230     struct rt_ofw_cell_args args;
231     struct rt_ofw_node *np, *ctrl_np;
232     struct rt_mbox_controller *ctrl;
233     struct rt_mbox_chan *chan = RT_NULL;
234 
235     if (!client && index < 0)
236     {
237         return rt_err_ptr(-RT_EINVAL);
238     }
239 
240     np = client->dev->ofw_node;
241 
242     rt_spin_lock(&mbox_ops_lock);
243 
244     err = rt_ofw_parse_phandle_cells(np, "mboxes", "#mbox-cells", index, &args);
245 
246     if (err)
247     {
248         chan = rt_err_ptr(err);
249         goto _out_lock;
250     }
251 
252     ctrl_np = args.data;
253 
254     if (!rt_ofw_data(ctrl_np))
255     {
256         rt_spin_unlock(&mbox_ops_lock);
257 
258         rt_platform_ofw_request(ctrl_np);
259 
260         rt_spin_lock(&mbox_ops_lock);
261     }
262 
263     ctrl = rt_ofw_data(ctrl_np);
264     rt_ofw_node_put(ctrl_np);
265 
266     if (ctrl)
267     {
268         int index;
269 
270         if (ctrl->ops->ofw_parse)
271         {
272             index = ctrl->ops->ofw_parse(ctrl, &args);
273         }
274         else
275         {
276             index = mbox_controller_ofw_parse_default(ctrl, &args);
277         }
278 
279         if (index >= 0)
280         {
281             chan = &ctrl->chans[index];
282         }
283         else
284         {
285             LOG_E("Parse chan from %s error = %s",
286                     rt_dm_dev_get_name(ctrl->dev), rt_strerror(index));
287 
288             chan = rt_err_ptr(index);
289             goto _out_lock;
290         }
291 
292         if (ctrl->ops->request)
293         {
294             rt_err_t err = ctrl->ops->request(chan);
295 
296             if (err)
297             {
298                 LOG_E("Request chan[%d] from %s error = %s",
299                         index, rt_dm_dev_get_name(ctrl->dev), rt_strerror(err));
300 
301                 rt_mbox_release(chan);
302                 chan = rt_err_ptr(err);
303             }
304         }
305 
306         chan->client = client;
307     }
308     else
309     {
310         chan = rt_err_ptr(-RT_ENOSYS);
311     }
312 
313 _out_lock:
314     rt_spin_unlock(&mbox_ops_lock);
315 
316     return chan;
317 }
318 
rt_mbox_request_by_name(struct rt_mbox_client * client,char * name)319 struct rt_mbox_chan *rt_mbox_request_by_name(struct rt_mbox_client *client, char *name)
320 {
321     int index;
322     struct rt_ofw_node *np;
323 
324     if (!client || !name)
325     {
326         return rt_err_ptr(-RT_EINVAL);
327     }
328 
329     np = client->dev->ofw_node;
330     index = rt_ofw_prop_index_of_string(np, "mbox-names", name);
331 
332     if (index < 0)
333     {
334         return RT_NULL;
335     }
336 
337     return rt_mbox_request_by_index(client, index);
338 }
339 
rt_mbox_release(struct rt_mbox_chan * chan)340 rt_err_t rt_mbox_release(struct rt_mbox_chan *chan)
341 {
342     if (chan)
343     {
344         chan->ctrl->ops->release(chan);
345     }
346     else
347     {
348         return -RT_EINVAL;
349     }
350 
351     return RT_EOK;
352 }
353