1 /*
2 * Copyright (c) 2012-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 * ======== InterruptDsp.c ========
34 * Mailbox based interrupt manager
35 */
37 #include <xdc/std.h>
38 #include <xdc/runtime/Assert.h>
39 #include <xdc/runtime/Error.h>
41 #include <ti/sysbios/family/c64p/Hwi.h>
43 #include <ti/sdo/utils/_MultiProc.h>
44 #include <ti/sdo/ipc/_Ipc.h>
45 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
47 #include "package/internal/InterruptDsp.xdc.h"
49 /* Register access method. */
50 #define REG16(A) (*(volatile UInt16 *) (A))
51 #define REG32(A) (*(volatile UInt32 *) (A))
53 /* Assigned mailboxes */
54 #define DSP_TO_ARP32 0
55 #define ARP32_TO_DSP 1
57 #define MAILBOX_REG_VAL(M) (0x1 << (2 * M))
59 #define MAILBOX_MESSAGE(M) (InterruptDsp_mailboxBaseAddr + 0x40 + (0x4 * M))
60 #define MAILBOX_STATUS(M) (InterruptDsp_mailboxBaseAddr + 0xC0 + (0x4 * M))
62 #define MBX_INTR_TO_ARP32 0
63 #define MBX_INTR_TO_DSP 1
65 #define MAILBOX_IRQSTATUS_CLR(INTR_NUM) (InterruptDsp_mailboxBaseAddr + 0x104 + ((INTR_NUM) * 0x10))
67 #define MAILBOX_IRQENABLE_SET(INTR_NUM) (InterruptDsp_mailboxBaseAddr + 0x108 + ((INTR_NUM) * 0x10))
69 #define MAILBOX_IRQENABLE_CLR(INTR_NUM) (InterruptDsp_mailboxBaseAddr + 0x10C + ((INTR_NUM) * 0x10))
71 #define MAILBOX_EOI_REG (InterruptDsp_mailboxBaseAddr + 0x140)
73 /*
74 *************************************************************************
75 * Module functions
76 *************************************************************************
77 */
79 /*!
80 * ======== InterruptDsp_intEnable ========
81 * Enable remote processor interrupt
82 */
83 Void InterruptDsp_intEnable(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
84 {
85 REG32(MAILBOX_IRQENABLE_SET(MBX_INTR_TO_DSP)) = MAILBOX_REG_VAL(ARP32_TO_DSP);
86 }
88 /*!
89 * ======== InterruptDsp_intDisable ========
90 * Disables remote processor interrupt
91 */
92 Void InterruptDsp_intDisable(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
93 {
94 REG32(MAILBOX_IRQENABLE_CLR(MBX_INTR_TO_DSP)) = MAILBOX_REG_VAL(ARP32_TO_DSP);
95 }
97 /*!
98 * ======== InterruptDsp_intRegister ========
99 */
100 Void InterruptDsp_intRegister(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo,
101 Fxn func, UArg arg)
102 {
103 UInt key;
104 Hwi_Params hwiAttrs;
105 Error_Block eb;
106 InterruptDsp_FxnTable *table;
108 Assert_isTrue(intInfo->intVectorId <= 15, ti_sdo_ipc_Ipc_A_internal);
110 /* init error block */
111 Error_init(&eb);
113 /* Disable global interrupts */
114 key = Hwi_disable();
116 table = &(InterruptDsp_module->fxnTable);
117 table->func = func;
118 table->arg = arg;
120 InterruptDsp_intClear(remoteProcId, intInfo);
122 /* Make sure the interrupt only gets plugged once */
123 InterruptDsp_module->numPlugged++;
124 if (InterruptDsp_module->numPlugged == 1) {
125 /* Register interrupt to remote processor */
126 Hwi_Params_init(&hwiAttrs);
127 hwiAttrs.arg = arg;
128 hwiAttrs.eventId = 90;
130 Hwi_create(intInfo->intVectorId,
131 (Hwi_FuncPtr)InterruptDsp_intShmStub,
132 &hwiAttrs,
133 &eb);
135 /* enable the interrupt vector */
136 Hwi_enableInterrupt(intInfo->intVectorId);
137 }
139 /* Enable the mailbox interrupt to the DSP */
140 InterruptDsp_intEnable(remoteProcId, intInfo);
142 /* Restore global interrupts */
143 Hwi_restore(key);
144 }
146 /*!
147 * ======== InterruptDsp_intUnregister ========
148 */
149 Void InterruptDsp_intUnregister(UInt16 remoteProcId,
150 IInterrupt_IntInfo *intInfo)
151 {
152 Hwi_Handle hwiHandle;
153 InterruptDsp_FxnTable *table;
155 /* Disable the mailbox interrupt source */
156 InterruptDsp_intDisable(remoteProcId, intInfo);
158 InterruptDsp_module->numPlugged--;
159 if (InterruptDsp_module->numPlugged == 0) {
160 /* Delete the Hwi */
161 hwiHandle = Hwi_getHandle(intInfo->intVectorId);
162 Hwi_delete(&hwiHandle);
163 }
165 /* Clear the FxnTable entry for the remote processor */
166 table = &(InterruptDsp_module->fxnTable);
167 table->func = NULL;
168 table->arg = 0;
169 }
171 /*!
172 * ======== InterruptDsp_intSend ========
173 * Send interrupt to the remote processor
174 */
175 Void InterruptDsp_intSend(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo,
176 UArg arg)
177 {
178 UInt key;
180 /*
181 * Before writing to a mailbox, check whehter it already contains a message
182 * If so, then don't write to the mailbox since we want one and only one
183 * message per interrupt. Disable interrupts between reading
184 * the MSGSTATUS_X register and writing to the mailbox to protect from
185 * another thread doing an intSend at the same time
186 *
187 * Note regarding possible race condition between local 'intSend' and
188 * remote 'intClear':
189 * It is possible that we we read the MAILBOX_MSGSTATUS_X register during
190 * the remote side's intClear. Therefore, we might choose _not_ to send
191 * write to the mailbox even though the mailbox is about to be cleared a
192 * few cycles later. In this case, the interrupt will be lost.
193 * This is OK, however. intClear should always be called by the Notify
194 * driver _before_ shared memory is read, so the event will be picked up
195 * anyway by the previous interrupt that caused intClear to be called.
196 */
197 key = Hwi_disable();
199 if (REG32(MAILBOX_STATUS(DSP_TO_ARP32)) == 0) {
200 /* write the mailbox message to arp32 */
201 REG32(MAILBOX_MESSAGE(DSP_TO_ARP32)) = arg;
202 }
204 /* restore interrupts */
205 Hwi_restore(key);
206 }
208 /*!
209 * ======== InterruptDsp_intPost ========
210 */
211 Void InterruptDsp_intPost(UInt16 srcProcId, IInterrupt_IntInfo *intInfo,
212 UArg arg)
213 {
214 UInt key;
216 /* disable interrupts */
217 key = Hwi_disable();
219 if (REG32(MAILBOX_STATUS(ARP32_TO_DSP)) == 0) {
220 /* write the mailbox message to dsp */
221 REG32(MAILBOX_MESSAGE(ARP32_TO_DSP)) = arg;
222 }
224 /* restore interrupts */
225 Hwi_restore(key);
226 }
228 /*!
229 * ======== InterruptDsp_intClear ========
230 * Clear interrupt
231 */
232 UInt InterruptDsp_intClear(UInt16 remoteProcId, IInterrupt_IntInfo *intInfo)
233 {
234 UInt arg;
236 arg = REG32(MAILBOX_MESSAGE(ARP32_TO_DSP));
238 /* clear the dsp mailbox */
239 REG32(MAILBOX_IRQSTATUS_CLR(MBX_INTR_TO_DSP)) = MAILBOX_REG_VAL(ARP32_TO_DSP);
241 /* Write to EOI (End Of Interrupt) register */
242 REG32(MAILBOX_EOI_REG) = 0x1;
244 return (arg);
245 }
247 /*
248 *************************************************************************
249 * Internals functions
250 *************************************************************************
251 */
253 /*!
254 * ======== InterruptDsp_intShmStub ========
255 */
256 Void InterruptDsp_intShmStub(UArg arg)
257 {
258 InterruptDsp_FxnTable *table;
260 if (((REG32(MAILBOX_IRQENABLE_SET(MBX_INTR_TO_DSP)) &
261 MAILBOX_REG_VAL(ARP32_TO_DSP)) &&
262 REG32(MAILBOX_STATUS(ARP32_TO_DSP))) != 0) {
263 /* call function with arg */
264 table = &(InterruptDsp_module->fxnTable);
265 (table->func)(table->arg);
266 }
267 }