1 /*
2 * Copyright (c) 2013, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /*
33 * ======== Interrupt.c ========
34 * C647x based interrupt manager.
35 */
37 #include <xdc/std.h>
38 #include <xdc/runtime/Assert.h>
39 #include <xdc/runtime/Log.h>
40 #include <xdc/runtime/Diags.h>
41 #include <xdc/runtime/Startup.h>
43 #include <ti/sysbios/family/c64p/Hwi.h>
45 #include <ti/ipc/MultiProc.h>
46 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
48 #include "Interrupt.h"
50 #include "package/internal/Interrupt.xdc.h"
52 extern volatile cregister Uns DNUM;
54 Fxn userFxn = NULL;
55 Void Interrupt_isr(UArg arg);
57 /* Shift to source bit id for CORES 0-3 */
58 #define MAP_TO_BITPOS(intId) \
59 (intId == Interrupt_SRCS_BITPOS_CORE0 ? (intId + DNUM) : intId)
61 /*
62 * Map remoteProcId to CORE ID [0-3]
63 * NOTE: This assumes that HOST is at MultiProcId == 0, and CORE0 at 1i
64 */
65 #define MAP_RPROCID_TO_COREID(rProcId) (rProcId-1)
67 #define MAP_RPROCID_TO_SRCC(rProcId, intId) \
68 (intId == Interrupt_SRCS_BITPOS_CORE0 ? \
69 (intId + (rProcId-1)) : intId)
70 /*
71 *************************************************************************
72 * Module functions
73 *************************************************************************
74 */
76 /*
77 * ======== Interrupt_Module_startup ========
78 */
79 Int Interrupt_Module_startup(Int phase)
80 {
81 volatile UInt32 *kick0 = (volatile UInt32 *)Interrupt_KICK0;
82 volatile UInt32 *kick1 = (volatile UInt32 *)Interrupt_KICK1;
83 UInt16 procId = MultiProc_self();
85 /*
86 * If this assert fails, the MultiProc config has changed to break
87 * an assumption in Linux rpmsg driver, that HOST is listed first in
88 * MultiProc ID configuration.
89 */
90 Assert_isTrue(0 == MultiProc_getId("HOST"), NULL);
92 /*
93 * Wait for Startup to be done (if MultiProc id not yet set) because a
94 * user fxn should set it
95 */
96 if (!Startup_Module_startupDone() && procId == MultiProc_INVALIDID) {
97 return (Startup_NOTDONE);
98 }
100 if (!Interrupt_enableKick) {
101 /* Do not unlock the kick registers */
102 return (Startup_DONE);
103 }
105 /*
106 * Only write to the KICK registers if:
107 * - This core is the SR0 owner
108 * - There is no SR0 and this core has procId '1' (IPC 3.x: this case).
109 */
110 /* TODO: What if CORE0 is not started, but the others are? */
111 if (DNUM == 0) {
112 if (Interrupt_KICK0 && Interrupt_KICK1){
113 /* unlock the KICK mechanism in the Bootcfg MMRs if defined */
114 kick0[0] = 0x83e70b13; /* must be written with this value */
115 kick1[0] = 0x95a4f1e0; /* must be written with this value */
116 }
117 }
119 return (Startup_DONE);
120 }
122 /*!
123 * ======== Interrupt_intEnable ========
124 * Enable interrupt
125 */
126 Void Interrupt_intEnable(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
127 {
128 Hwi_enableInterrupt(intInfo->intVectorId);
129 }
131 /*!
132 * ======== Interrupt_intDisable ========
133 * Disables interrupts
134 */
135 Void Interrupt_intDisable(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
136 {
137 Hwi_disableInterrupt(intInfo->intVectorId);
138 }
140 Void Interrupt_intClearAll()
141 {
142 volatile UInt32 *ipcgr = (volatile UInt32 *)Interrupt_IPCGR0;
143 volatile UInt32 *ipcar = (volatile UInt32 *)Interrupt_IPCAR0;
144 UInt val = ipcgr[DNUM]; /* Interrupt source bits */
146 ipcar[DNUM] = val;
147 }
150 /*
151 * ======== Interrupt_intRegister ========
152 * Register ISR for remote processor interrupt
153 */
154 Void Interrupt_intRegister(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo,
155 Fxn func, UArg arg)
156 {
157 Hwi_Params hwiAttrs;
158 Interrupt_FxnTable *table;
160 UInt pos;
161 Assert_isTrue(intInfo != NULL, NULL);
163 pos = MAP_RPROCID_TO_SRCC(remoteProcId, intInfo->localIntId);
165 Log_print2(Diags_USER1, "Interrupt_intRegister: pos: %d, func: 0x%x\n",
166 (IArg)pos, (IArg)func);
168 /* setup the function table with client function and arg to call: */
169 table = &(Interrupt_module->fxnTable[pos]);
170 table->func = func;
171 table->arg = arg;
173 /* Make sure the interrupt only gets plugged once */
174 Interrupt_module->numPlugged++;
175 if (Interrupt_module->numPlugged == 1) {
176 /* Clear all pending interrupts */
177 Interrupt_intClearAll();
179 /* Register interrupt to remote processor */
180 Hwi_Params_init(&hwiAttrs);
181 hwiAttrs.maskSetting = Hwi_MaskingOption_SELF;
182 hwiAttrs.arg = arg;
183 hwiAttrs.eventId = Interrupt_INTERDSPINT;
185 Hwi_create(intInfo->intVectorId,
186 (Hwi_FuncPtr)Interrupt_isr, &hwiAttrs, NULL);
188 Hwi_enableInterrupt(intInfo->intVectorId);
189 }
190 }
192 Void Interrupt_intUnregister(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
193 {
194 Hwi_Handle hwiHandle;
195 Interrupt_FxnTable *table;
196 UInt pos;
198 Assert_isTrue(intInfo != NULL, NULL);
200 Interrupt_module->numPlugged--;
201 if (Interrupt_module->numPlugged == 0) {
202 /* No need to disable interrupt: Hwi_delete takes care of this */
203 hwiHandle = Hwi_getHandle(intInfo->intVectorId);
204 Hwi_delete(&hwiHandle);
205 }
207 /* Unset the function table */
208 pos = MAP_RPROCID_TO_SRCC(remoteProcId, intInfo->localIntId);
210 table = &(Interrupt_module->fxnTable[pos]);
211 table->func = NULL;
212 table->arg = 0;
213 }
215 /*!
216 * ======== Interrupt_intSend ========
217 * Send interrupt to the remote processor, identifying this core as source.
218 * If CORE0 BIT POS, we add DNUM to identify this core as the source;
219 * Otherwise, we just use the localIntId as the source bit position.
220 */
221 Void Interrupt_intSend(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo,
222 UArg arg)
223 {
224 UInt32 val;
225 volatile UInt32 *ipcgr = (volatile UInt32 *)Interrupt_IPCGR0;
226 volatile UInt32 *ipcgrh = (volatile UInt32 *)Interrupt_IPCGRH;
227 UInt pos;
229 Assert_isTrue((intInfo != NULL), NULL);
230 pos = MAP_TO_BITPOS(intInfo->localIntId);
232 /*
233 * bit 0 is set to generate the interrupt.
234 * bits 4-7 are set to specify the interrupt generation source.
235 * The convention is that bit 4 (SRCS0) is used for core 0,
236 * bit 5 (SRCS1) for core 1, etc... .
237 */
238 val = (1 << pos) | 1;
239 Log_print3(Diags_USER1,
240 "Interrupt_intSend: setting bit %d in SRCS as 0x%x to for rprocId #%d",
241 (IArg)pos, (IArg)val, (IArg)remoteProcId);
243 if (remoteProcId == MultiProc_getId("HOST"))
244 {
245 /* Interrupt is to be generated on the Host processor. Go through
246 * IPCGRH register
247 */
248 *ipcgrh = val;
249 }
250 else
251 {
252 /* Interrupt is to be generated on another DSP. */
253 ipcgr[MAP_RPROCID_TO_COREID(remoteProcId)] = val;
254 }
255 }
257 /*
258 * ======== Interrupt_intClear ========
259 * Acknowledge interrupt by clearing the corresponding source bit.
260 *
261 * intInfo->localIntId encodes the Source bit position to be cleared.
262 * If this corresponds to Core0, adjust using remoteProcId to get true
263 * SRCS bit position for the DSP core.
264 *
265 * Otherwise, the localIntId is used directly as the bit position.
266 *
267 * Only callers setting remoteProcId == HOST id care about return value.
268 */
269 UInt Interrupt_intClear(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
270 {
271 volatile UInt32 *ipcgr = (volatile UInt32 *)Interrupt_IPCGR0;
272 volatile UInt32 *ipcar = (volatile UInt32 *)Interrupt_IPCAR0;
273 UInt payload = Interrupt_INVALIDPAYLOAD;
274 UInt val = ipcgr[DNUM]; /* Interrupt source bits */
275 UInt pos;
277 Assert_isTrue((intInfo != NULL), NULL);
278 pos = MAP_RPROCID_TO_SRCC(remoteProcId, intInfo->localIntId);
279 ipcar[DNUM] = (1 << pos);
281 Log_print2(Diags_USER1, "Interrupt_intClear: ipcgr: 0x%x, cleared: 0x%x\n",
282 val, (1 << pos));
284 if (remoteProcId == MultiProc_getId("HOST")) {
285 payload = ((val & (UInt)(1 << Interrupt_SRCS_BITPOS_HOST)) ? val :
286 Interrupt_INVALIDPAYLOAD);
287 }
289 return (payload);
290 }
292 /*
293 * ======== Interrupt_checkAndClear ========
294 * Return 1 if the interrupt was set (if so, we clear it);
295 * Otherwise, returns 0.
296 */
297 UInt Interrupt_checkAndClear(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
298 {
299 volatile UInt32 *ipcgr = (volatile UInt32 *)Interrupt_IPCGR0;
300 volatile UInt32 *ipcar = (volatile UInt32 *)Interrupt_IPCAR0;
301 UInt val = ipcgr[DNUM]; /* Interrupt source bits */
302 UInt pos;
304 Assert_isTrue((intInfo != NULL), NULL);
306 pos = MAP_TO_BITPOS(intInfo->localIntId);
307 if (val & (1 << pos)) {
308 ipcar[DNUM] = (1 << pos);
309 return(1);
310 }
312 return(0);
313 }
315 /*
316 * ======== Interrupt_isr ========
317 */
318 Void Interrupt_isr(UArg arg)
319 {
320 Int i;
321 Interrupt_FxnTable *table;
322 volatile UInt32 *ipcgr = (volatile UInt32 *)Interrupt_IPCGR0;
323 volatile UInt32 *ipcar = (volatile UInt32 *)Interrupt_IPCAR0;
324 UInt val = ipcgr[DNUM]; /* Interrupt source bits */
326 Log_print1(Diags_USER1,
327 "Interrupt_isr: Interrupt(s) received: 0x%x", (IArg)val);
329 for (i = Interrupt_SRCS_BITPOS_CORE0; i < 32; i++) {
330 if (val & (1 << i)) {
331 /* Clear the specific interrupt: */
332 ipcar[DNUM] = (1 << i);
334 /* Call the client function: */
335 table = &(Interrupt_module->fxnTable[i]);
336 if (table->func != NULL) {
337 Log_print1(Diags_USER1,
338 "Interrupt_isr: source id bit: %d", i);
339 (table->func)(table->arg);
340 }
341 }
342 }
343 }