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