1 /*
2 * Copyright (c) 2024, Texas Instruments Incorporated
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/__assert.h>
9 #include <ti/drivers/dpl/HwiP.h>
10
11 #include <inc/hw_types.h>
12 #include <inc/hw_ints.h>
13
14 #include <driverlib/interrupt.h>
15
16 /*
17 * IRQ_CONNECT requires we know the ISR signature and argument
18 * at build time; whereas SimpleLink plugs the interrupts
19 * at run time, so we create an ISR shim, and register that.
20 * The callback argument doesn't change after the ISR is registered.
21 */
22 struct sl_isr_args
23 {
24 HwiP_Fxn cb;
25 uintptr_t arg;
26 };
27
sl_isr(const void * isr_arg)28 static void sl_isr(const void *isr_arg)
29 {
30 HwiP_Fxn cb = ((struct sl_isr_args *)isr_arg)->cb;
31 uintptr_t arg = ((struct sl_isr_args *)isr_arg)->arg;
32
33 /* Call the SimpleLink ISR Handler: */
34 if (cb)
35 {
36 cb(arg);
37 }
38 }
39
40 typedef struct _HwiP_Obj
41 {
42 uint32_t intNum;
43 struct sl_isr_args *cb;
44 } HwiP_Obj;
45
46 /* interrupt reserved for SwiP */
47 int HwiP_swiPIntNum = INT_CPUIRQ1;
48
49 static struct sl_isr_args sl_IRQ01_cb = {NULL, 0};
50 static struct sl_isr_args sl_IRQ03_cb = {NULL, 0};
51 static struct sl_isr_args s1_IRQ04_cb = {NULL, 0};
52 static struct sl_isr_args sl_IRQ16_cb = {NULL, 0};
53 static struct sl_isr_args s1_LRFD_IRQ0_cb = {NULL, 0};
54 static struct sl_isr_args s1_LRFD_IRQ1_cb = {NULL, 0};
55
56 /*
57 * ======== HwiP_construct ========
58 */
HwiP_construct(HwiP_Struct * handle,int interruptNum,HwiP_Fxn hwiFxn,HwiP_Params * params)59 HwiP_Handle HwiP_construct(HwiP_Struct *handle, int interruptNum, HwiP_Fxn hwiFxn, HwiP_Params *params)
60 {
61 HwiP_Obj *obj = (HwiP_Obj *)handle;
62 uintptr_t arg = 0;
63 uint8_t priority = INT_PRI_LEVEL3; /* default to lowest priority */
64
65 if (handle == NULL)
66 {
67 return NULL;
68 }
69
70 if (params)
71 {
72 priority = params->priority & 0xFF;
73 arg = params->arg;
74 }
75
76 /*
77 * Currently only support INT_CPUIRQ1 (SwiP), INT_CPUIRQ3 (Oscillator ISR),
78 * INT_CPUIRQ16 (Batmon ISR), INT_CPUIRQ4 (RCL Scheduler ISR), INT_LRFD_IRQ0
79 * (RCL Command Handler ISR) and INT_LRFD_IRQ1 (RCL Dispatcher ISR)
80 */
81 __ASSERT((INT_CPUIRQ1 == interruptNum) || (INT_CPUIRQ3 == interruptNum) || (INT_CPUIRQ4 == interruptNum) ||
82 (INT_CPUIRQ16 == interruptNum) || (INT_LRFD_IRQ0 == interruptNum) || (INT_LRFD_IRQ1 == interruptNum),
83 "Unexpected interruptNum: %d\r\n",
84 interruptNum);
85
86 /*
87 * Priority expected is either:
88 * INT_PRI_LEVEL0 to INT_PRI_LEVEL3,
89 * or ~0 or 255 (meaning lowest priority)
90 * ~0 and 255 are meant to be the same as INT_PRI_LEVEL3.
91 */
92 __ASSERT((INT_PRI_LEVEL0 == priority) || (INT_PRI_LEVEL3 == priority) || (INT_PRI_LEVEL2 == priority) ||
93 (INT_PRI_LEVEL1 == priority) || (0xFF == priority),
94 "Unexpected priority level, got: 0x%x\r\n",
95 (unsigned int)priority);
96
97 if (0xFF == priority)
98 {
99 priority = INT_PRI_LEVEL3;
100 }
101
102 /* The priority for IRQ_CONNECT is encoded in the top 2 bits */
103 priority = (priority >> 6);
104
105 switch (interruptNum)
106 {
107 case INT_CPUIRQ1:
108 sl_IRQ01_cb.cb = hwiFxn;
109 sl_IRQ01_cb.arg = arg;
110 obj->cb = &sl_IRQ01_cb;
111 irq_connect_dynamic(INT_CPUIRQ1 - 16, priority, sl_isr, &sl_IRQ01_cb, 0);
112 break;
113 case INT_CPUIRQ3:
114 sl_IRQ03_cb.cb = hwiFxn;
115 sl_IRQ03_cb.arg = arg;
116 obj->cb = &sl_IRQ03_cb;
117 irq_connect_dynamic(INT_CPUIRQ3 - 16, priority, sl_isr, &sl_IRQ03_cb, 0);
118 break;
119 case INT_CPUIRQ4:
120 s1_IRQ04_cb.cb = hwiFxn;
121 s1_IRQ04_cb.arg = arg;
122 obj->cb = &s1_IRQ04_cb;
123 irq_connect_dynamic(INT_CPUIRQ4 - 16, priority, sl_isr, &s1_IRQ04_cb, 0);
124 break;
125 case INT_CPUIRQ16:
126 sl_IRQ16_cb.cb = hwiFxn;
127 sl_IRQ16_cb.arg = arg;
128 obj->cb = &sl_IRQ16_cb;
129 irq_connect_dynamic(INT_CPUIRQ16 - 16, priority, sl_isr, &sl_IRQ16_cb, 0);
130 break;
131 case INT_LRFD_IRQ0:
132 s1_LRFD_IRQ0_cb.cb = hwiFxn;
133 s1_LRFD_IRQ0_cb.arg = arg;
134 obj->cb = &s1_LRFD_IRQ0_cb;
135 irq_connect_dynamic(INT_LRFD_IRQ0 - 16, priority, sl_isr, &s1_LRFD_IRQ0_cb, 0);
136 break;
137 case INT_LRFD_IRQ1:
138 s1_LRFD_IRQ1_cb.cb = hwiFxn;
139 s1_LRFD_IRQ1_cb.arg = arg;
140 obj->cb = &s1_LRFD_IRQ1_cb;
141 irq_connect_dynamic(INT_LRFD_IRQ1 - 16, priority, sl_isr, &s1_LRFD_IRQ1_cb, 0);
142 break;
143 default:
144 return (NULL);
145 }
146 irq_enable(interruptNum - 16);
147
148 obj->intNum = interruptNum;
149
150 return (HwiP_Handle)handle;
151 }
152
HwiP_Params_init(HwiP_Params * params)153 void HwiP_Params_init(HwiP_Params *params)
154 {
155 params->arg = 0;
156 params->priority = ~0;
157 }
158
159 /* Zephyr has no functions for clearing an interrupt, so use driverlib: */
HwiP_clearInterrupt(int interruptNum)160 void HwiP_clearInterrupt(int interruptNum)
161 {
162 IntClearPend((uint32_t)interruptNum);
163 }
164
HwiP_enableInterrupt(int interruptNum)165 void HwiP_enableInterrupt(int interruptNum)
166 {
167 irq_enable(interruptNum - 16);
168 }
169
HwiP_disableInterrupt(int interruptNum)170 void HwiP_disableInterrupt(int interruptNum)
171 {
172 irq_disable(interruptNum - 16);
173 }
174
HwiP_disable(void)175 uintptr_t HwiP_disable(void)
176 {
177 uintptr_t key;
178
179 key = irq_lock();
180
181 return (key);
182 }
183
HwiP_restore(uintptr_t key)184 void HwiP_restore(uintptr_t key)
185 {
186 irq_unlock(key);
187 }
188
HwiP_post(int interruptNum)189 void HwiP_post(int interruptNum)
190 {
191 IntSetPend((uint32_t)interruptNum);
192 }
193
HwiP_setFunc(HwiP_Handle hwiP,HwiP_Fxn fxn,uintptr_t arg)194 void HwiP_setFunc(HwiP_Handle hwiP, HwiP_Fxn fxn, uintptr_t arg)
195 {
196 HwiP_Obj *obj = (HwiP_Obj *)hwiP;
197
198 uintptr_t key = HwiP_disable();
199
200 obj->cb->cb = fxn;
201 obj->cb->arg = arg;
202
203 HwiP_restore(key);
204 }
205
HwiP_destruct(HwiP_Struct * hwiP)206 void HwiP_destruct(HwiP_Struct *hwiP)
207 {
208 HwiP_Obj *obj = (HwiP_Obj *)hwiP->data;
209
210 int interruptNum = obj->intNum;
211
212 irq_disable(interruptNum - 16);
213
214 obj->cb->cb = NULL;
215 obj->cb->arg = (uintptr_t)NULL;
216 obj->cb = NULL;
217 }
218
HwiP_setPriority(int interruptNum,uint32_t priority)219 void HwiP_setPriority(int interruptNum, uint32_t priority)
220 {
221 /*
222 * On CM0+, dynamically changing priorities is not allowed. In order to
223 * change priority of an interrupt, after it already has been enabled, the
224 * following must be done:
225 * - Disable the interrupt whose priority needs to be updated
226 * - Set the priority to be at the desired priority level
227 * - Re-enable the interrupt.
228 *
229 * These steps are all handled by IntSetPriority().
230 *
231 * HwiP_disable/enable here serves the purpose of deferring the handling of
232 * user interrupts until the priority of interruptNum has been changed. This
233 * prevents another interrupt from changing the global state potentially
234 * altering the outcome of this function.
235 */
236
237 uintptr_t key = HwiP_disable();
238 IntSetPriority((uint32_t)interruptNum, (uint8_t)priority);
239 HwiP_restore(key);
240 }
241
242 /*
243 * ======== HwiP_inISR ========
244 */
HwiP_inISR(void)245 bool HwiP_inISR(void)
246 {
247 bool stat;
248
249 if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0)
250 {
251 /* Not currently in an ISR */
252 stat = false;
253 }
254 else
255 {
256 stat = true;
257 }
258
259 return (stat);
260 }
261
262 /*
263 * ======== HwiP_inSwi ========
264 */
HwiP_inSwi(void)265 bool HwiP_inSwi(void)
266 {
267 uint32_t intNum = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk;
268 if (intNum == HwiP_swiPIntNum)
269 {
270 /* Currently in a Swi */
271 return (true);
272 }
273
274 return (false);
275 }