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 }