1 /**************************************************************************//**
2 *
3 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Change Logs:
8 * Date            Author       Notes
9 * 2022-10-5       Wayne        First version
10 *
11 ******************************************************************************/
12 
13 #include <rtconfig.h>
14 
15 #if defined(BSP_USING_HWSEM)
16 
17 #include <rthw.h>
18 #include "drv_hwsem.h"
19 #include "drv_sys.h"
20 #include "drv_common.h"
21 #include "nu_bitutil.h"
22 
23 /* Private define ---------------------------------------------------------------*/
24 enum
25 {
26     HWSEM_START = -1,
27 #if defined(BSP_USING_HWSEM0)
28     HWSEM0_IDX,
29 #endif
30     HWSEM_END
31 };
32 
33 /* Private typedef --------------------------------------------------------------*/
34 
35 struct nu_mutex_priv
36 {
37     struct nu_mutex parent;
38 
39     rt_thread_t owner;
40     uint8_t key;
41     uint8_t hold;
42     struct rt_completion completion;
43     void *user_data;
44 };
45 typedef struct nu_mutex_priv *nu_mutex_priv_t;
46 
47 struct nu_hwsem
48 {
49     struct rt_device  dev;
50     char            *name;
51     HWSEM_T         *base;
52     IRQn_Type       irqn;
53     uint32_t        rstidx;
54 
55     struct nu_mutex_priv mutex[evHWSEM_CNT];
56 };
57 typedef struct nu_hwsem *nu_hwsem_t;
58 
59 static struct nu_hwsem nu_hwsem_arr [] =
60 {
61 #if defined(BSP_USING_HWSEM0)
62     {
63         .name   = "hwsem0",
64         .base   = HWSEM0,
65         .irqn   = HWSEM0_IRQn,
66         .rstidx = HWSEM0_RST,
67     },
68 #endif
69 }; /* nu_hwsem */
70 
71 /**
72  * All HWSEM interrupt service routine
73  */
nu_hwsem_isr(int vector,void * param)74 static void nu_hwsem_isr(int vector, void *param)
75 {
76     nu_hwsem_t psNuHwSem = (nu_hwsem_t)param;
77     rt_int32_t irqidx;
78 
79     volatile uint32_t vu32Intsts = psNuHwSem->base->INTSTS_CORE;
80 
81     while ((irqidx = nu_ctz(vu32Intsts)) < evHWSEM_CNT)
82     {
83         nu_mutex_priv_t priv = (nu_mutex_priv_t)&psNuHwSem->mutex[irqidx];
84         uint32_t u32IsrBitMask =  1 << irqidx ;
85 
86         HWSEM_CLR_INT_FLAG(psNuHwSem->base, irqidx);
87 
88         /* Unlocked, Signal waiter. */
89         rt_completion_done(&priv->completion);
90 
91         /* Clear sent bit */
92         vu32Intsts &= ~(u32IsrBitMask);
93     }
94 }
95 
nu_mutex_init(struct rt_device * device,E_HWSEM_ID id)96 nu_mutex_t nu_mutex_init(struct rt_device *device, E_HWSEM_ID id)
97 {
98     if (id < evHWSEM_CNT)
99     {
100         nu_hwsem_t psNuHwSem = (nu_hwsem_t)device->user_data;
101         nu_mutex_t mutex = (nu_mutex_t)&psNuHwSem->mutex[id];
102         nu_mutex_priv_t priv = (nu_mutex_priv_t)mutex;
103 
104         if (!priv->owner)
105         {
106             priv->owner = rt_thread_self();
107         }
108         else
109         {
110             goto exit_nu_mutex_init;
111         }
112 
113         return mutex;
114     }
115 
116 exit_nu_mutex_init:
117 
118     return RT_NULL;
119 }
120 
nu_mutex_deinit(struct rt_device * device,E_HWSEM_ID id)121 void nu_mutex_deinit(struct rt_device *device, E_HWSEM_ID id)
122 {
123     if (id < evHWSEM_CNT)
124     {
125         nu_hwsem_t psNuHwSem = (nu_hwsem_t)device->user_data;
126         nu_mutex_t mutex = (nu_mutex_t)&psNuHwSem->mutex[id];
127         nu_mutex_priv_t priv = (nu_mutex_priv_t)mutex;
128 
129         if (priv->owner == rt_thread_self())
130         {
131             priv->owner = RT_NULL;
132         }
133 
134     }
135 
136 }
137 
nu_mutex_take(nu_mutex_t mutex,rt_int32_t timeout)138 rt_err_t nu_mutex_take(nu_mutex_t mutex, rt_int32_t timeout)
139 {
140     rt_err_t ret = RT_EOK;
141     nu_mutex_priv_t priv = (nu_mutex_priv_t)mutex;
142     nu_hwsem_t dev = (nu_hwsem_t)priv->user_data;
143 
144     uint8_t u8PrivKey = priv->key;
145 
146 #ifdef RT_USING_SMP
147     u8PrivKey |= (rt_hw_cpu_id() << 6);
148 #endif /* RT_USING_SMP */
149 
150     if (priv->owner != rt_thread_self())
151     {
152         return -RT_ERROR;
153     }
154 
155     rt_completion_init(&priv->completion);
156 
157     while (1)
158     {
159         if (HWSEM_IS_LOCKED(dev->base, mutex->id) != HWSEM_NOLOCK)
160         {
161             /* LOCKED */
162             if (HWSEM_GET_KEY(dev->base, mutex->id) != u8PrivKey)
163             {
164                 /* Enable interrupt */
165                 HWSEM_ENABLE_INT(dev->base, mutex->id);
166 
167                 /* owner is NOT me. */
168                 if (rt_completion_wait(&priv->completion, timeout) != RT_EOK)
169                 {
170                     ret = -RT_EBUSY;
171                     break;
172                 }
173                 else
174                 {
175                     /* Got notification, do lock. */
176                 }
177             }
178             else
179             {
180                 /* owner is me. */
181                 priv->hold++;
182                 break;
183             }
184         }
185         else
186         {
187             /* NOLOCK, To lock */
188             HWSEM_LOCK(dev->base, mutex->id, u8PrivKey);
189             if (HWSEM_GET_KEY(dev->base, mutex->id) == u8PrivKey)
190             {
191                 /* owner is me. */
192                 priv->hold = 1;
193 
194                 /* Disable interrupt */
195                 HWSEM_DISABLE_INT(dev->base, mutex->id);
196 
197                 break;
198             }
199             else
200             {
201                 /* Failed to lock, owner is not me. wait notification. */
202             }
203         }
204 
205     } //while(1)
206 
207     return ret;
208 }
209 RTM_EXPORT(nu_mutex_take);
210 
nu_mutex_release(nu_mutex_t mutex)211 rt_err_t nu_mutex_release(nu_mutex_t mutex)
212 {
213     rt_err_t ret = RT_EOK;
214     nu_mutex_priv_t priv = (nu_mutex_priv_t)mutex;
215     nu_hwsem_t dev = (nu_hwsem_t)priv->user_data;
216 
217     uint8_t u8PrivKey = priv->key;
218 
219     if (priv->owner != rt_thread_self())
220     {
221         return -RT_ERROR;
222     }
223 
224 #ifdef RT_USING_SMP
225     u8PrivKey |= (rt_hw_cpu_id() << 6);
226 #endif /* RT_USING_SMP */
227 
228     if (HWSEM_IS_LOCKED(dev->base, mutex->id) != 0 &&
229             HWSEM_GET_KEY(dev->base, mutex->id) == u8PrivKey)
230     {
231         priv->hold--;
232         if (priv->hold == 0)
233         {
234             /* Unlocked */
235             HWSEM_UNLOCK(dev->base, mutex->id, u8PrivKey);
236         }
237     }
238     else
239     {
240         ret = -RT_ERROR;
241     }
242 
243     return ret;
244 }
245 RTM_EXPORT(nu_mutex_release);
246 
hwsem_register(struct rt_device * device,const char * name,void * user_data)247 static rt_err_t hwsem_register(struct rt_device *device, const char *name, void *user_data)
248 {
249     RT_ASSERT(device);
250 
251     device->type        = RT_Device_Class_Miscellaneous;
252     device->rx_indicate = RT_NULL;
253     device->tx_complete = RT_NULL;
254 
255 #ifdef RT_USING_DEVICE_OPS
256     device->ops         = RT_NULL;
257 #else
258     device->init        = RT_NULL;
259     device->open        = RT_NULL;
260     device->close       = RT_NULL;
261     device->read        = RT_NULL;
262     device->write       = RT_NULL;
263     device->control     = RT_NULL;
264 #endif
265     device->user_data   = user_data;
266 
267     return rt_device_register(device, name, RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_STANDALONE);
268 }
269 
270 /**
271  * Hardware Sem Initialization
272  */
rt_hw_hwsem_init(void)273 int rt_hw_hwsem_init(void)
274 {
275     int i, j;
276     rt_err_t ret = RT_EOK;
277 
278     for (i = (HWSEM_START + 1); i < HWSEM_END; i++)
279     {
280 #if !defined(USE_MA35D1_SUBM)
281         /* Reset this module */
282         nu_sys_ip_reset(nu_hwsem_arr[i].rstidx);
283 #endif
284         for (j = 0; j < evHWSEM_CNT; j++)
285         {
286             nu_hwsem_arr[i].mutex[j].parent.id = (E_HWSEM_ID)j;
287             nu_hwsem_arr[i].mutex[j].user_data = (void *)&nu_hwsem_arr[i];
288             nu_hwsem_arr[i].mutex[j].key = (HWSEM_LOCK_BY_OWNER << 4) | j;   // CoreID + SemID
289             nu_hwsem_arr[i].mutex[j].hold = 0;
290             nu_hwsem_arr[i].mutex[j].owner = RT_NULL;
291 
292             if (HWSEM_IS_LOCKED(nu_hwsem_arr[i].base, j) == HWSEM_LOCK_BY_OWNER)
293                 HWSEM_UNLOCK(nu_hwsem_arr[i].base, j, nu_hwsem_arr[i].mutex[j].key);
294 
295             /* Disable interrupt */
296             HWSEM_DISABLE_INT(nu_hwsem_arr[i].base, j);
297         }
298 
299         rt_hw_interrupt_install(nu_hwsem_arr[i].irqn, nu_hwsem_isr, &nu_hwsem_arr[i], nu_hwsem_arr[i].name);
300         rt_hw_interrupt_umask(nu_hwsem_arr[i].irqn);
301 
302         ret = hwsem_register(&nu_hwsem_arr[i].dev, (const char *)nu_hwsem_arr[i].name, (void *)&nu_hwsem_arr[i]);
303         RT_ASSERT(ret == RT_EOK);
304     }
305 
306     return 0;
307 }
308 INIT_BOARD_EXPORT(rt_hw_hwsem_init);
309 
310 #endif //#if defined(BSP_USING_UART)
311