1 /*
2 * Copyright (c) 2017-2018 Texas Instruments Incorporated - http://www.ti.com
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 */
33 /*
34 * ======== NotifyDriverMbx.c ========
35 */
36 #include <xdc/std.h>
37 #include <xdc/runtime/Assert.h>
38 #include <xdc/runtime/Startup.h>
40 #if defined(xdc_target__isaCompatible_v7R)
42 #include <ti/sysbios/family/arm/v7r/keystone3/Hwi.h>
44 #elif defined(xdc_target__isaCompatible_v8A)
46 #include <ti/sysbios/family/arm/gicv3/Hwi.h>
48 #else
49 #error Invalid target
50 #endif
52 #include <ti/sdo/ipc/_Notify.h>
53 #include <ti/sdo/ipc/family/am65xx/NotifySetup.h>
54 #include <ti/sdo/utils/_MultiProc.h>
56 #include "package/internal/NotifyDriverMbx.xdc.h"
58 /* Bit mask operations */
59 #define SET_BIT(num,pos) ((num) |= (1u << (pos)))
60 #define CLEAR_BIT(num,pos) ((num) &= ~(1u << (pos)))
61 #define TEST_BIT(num,pos) ((num) & (1u << (pos)))
63 /* register access methods */
64 #define REG16(A) (*(volatile UInt16 *)((uintptr_t)A))
65 #define REG32(A) (*(volatile UInt32 *)((uintptr_t)A))
67 #define MAILBOX_FIFOLENGTH 4
68 #define PROCID(idx) (NotifyDriverMbx_procIdTable[(idx)])
70 #define MBX_BASEADDR_IDX(idx) \
71 ((NotifyDriverMbx_mailboxTable[(idx)] >> 16) & 0xFFFF)
73 #define MAILBOX_ADDR(idx) \
74 (NotifyDriverMbx_mailboxBaseAddr[MBX_BASEADDR_IDX(idx)])
76 #define MBX_TABLE_IDX(src, dst) \
77 ((PROCID(src) * NotifyDriverMbx_NUM_CORES) + PROCID(dst))
79 #define SUBMBX_IDX(idx) (NotifyDriverMbx_mailboxTable[(idx)] & 0xFF)
81 #define MBX_USER_IDX(idx) ((NotifyDriverMbx_mailboxTable[(idx)] >> 8) & 0xFF)
83 #define MAILBOX_REG_VAL(m) (0x1 << (2 * m))
85 #define MAILBOX_MESSAGE(idx) \
86 (MAILBOX_ADDR(idx) + 0x40 + (0x4 * SUBMBX_IDX(idx)))
88 #define MAILBOX_STATUS(idx) \
89 (MAILBOX_ADDR(idx) + 0xC0 + (0x4 * SUBMBX_IDX(idx)))
91 #define MAILBOX_IRQSTATUS_CLR(idx) \
92 (MAILBOX_ADDR(idx) + 0x104 + (0x10 * MBX_USER_IDX(idx)))
94 #define MAILBOX_IRQENABLE_SET(idx) \
95 (MAILBOX_ADDR(idx) + 0x108 + (0x10 * MBX_USER_IDX(idx)))
97 #define MAILBOX_IRQENABLE_CLR(idx) \
98 (MAILBOX_ADDR(idx) + 0x10C + (0x10 * MBX_USER_IDX(idx)))
100 #define MAILBOX_EOI_REG(idx) (MAILBOX_ADDR(idx) + 0x140)
102 #define EVENT_GROUP_SIZE 32
104 /* empty the mailbox for the given index, clear its interrupt */
105 #define MAILBOX_INIT(idx) \
106 while (REG32(MAILBOX_STATUS(idx)) != 0) { \
107 REG32(MAILBOX_MESSAGE(idx)); \
108 } \
109 REG32(MAILBOX_IRQSTATUS_CLR(idx)) = MAILBOX_REG_VAL(SUBMBX_IDX(idx)); \
110 REG32(MAILBOX_EOI_REG(idx)) = MBX_USER_IDX(idx);
112 /*
113 *************************************************************************
114 * Module functions
115 *************************************************************************
116 */
118 /*
119 * ======== NotifyDriverMbx_Module_startup ========
120 */
121 Int NotifyDriverMbx_Module_startup(Int phase)
122 {
123 #if defined(xdc_target__isaCompatible_v7R)
125 /* nothing to do on this processor */
126 return (Startup_DONE);
128 #elif defined(xdc_target__isaCompatible_v8A)
130 return (Startup_DONE);
132 #else
133 #error Invalid target
134 #endif
135 }
137 /*
138 **************************************************************
139 * Instance functions
140 **************************************************************
141 */
143 /*
144 * ======== NotifyDriverMbx_Instance_init ========
145 */
146 Void NotifyDriverMbx_Instance_init(NotifyDriverMbx_Object *obj,
147 const NotifyDriverMbx_Params *params)
148 {
149 UInt key;
150 UInt16 selfVirtId;
151 UInt16 index;
153 obj->evtRegMask = 0;
154 obj->notifyHandle = NULL;
155 obj->remoteProcId = params->remoteProcId;
156 obj->remoteVirtId = PROCID(params->remoteProcId);
157 obj->cpuIntrNum = params->intVectorId;
159 /* disable global interrupts */
160 key = Hwi_disable();
162 /* clear inbound mailbox of all old messages */
163 selfVirtId = PROCID(MultiProc_self());
164 index = (obj->remoteVirtId * NotifyDriverMbx_NUM_CORES) + selfVirtId;
165 MAILBOX_INIT(index);
167 /* must use processor virtual ID to store driver handle in table */
168 NotifyDriverMbx_module->drvHandles[obj->remoteVirtId] = obj;
170 /* plug the cpu interrupt */
171 NotifySetup_plugHwi(params->remoteProcId, params->intVectorId,
172 NotifyDriverMbx_isr);
174 /* enable the mailbox interrupt from the remote core */
175 NotifyDriverMbx_enable(obj);
177 /* restore global interrupts */
178 Hwi_restore(key);
179 }
181 /*
182 * ======== NotifyDriverMbx_Instance_finalize ========
183 */
184 Void NotifyDriverMbx_Instance_finalize(NotifyDriverMbx_Object *obj)
185 {
187 /* disable the mailbox interrupt source */
188 NotifyDriverMbx_disable(obj);
190 /* unplug isr and unprogram the event dispatcher */
191 NotifySetup_unplugHwi(obj->remoteProcId, obj->cpuIntrNum);
193 /* must use processor virtual ID to remove driver handle from table */
194 NotifyDriverMbx_module->drvHandles[obj->remoteVirtId] = NULL;
195 }
197 /*
198 * ======== NotifyDriverMbx_registerEvent ========
199 */
200 Void NotifyDriverMbx_registerEvent(NotifyDriverMbx_Object *obj,
201 UInt32 eventId)
202 {
203 UInt hwiKey;
205 /*
206 * Disable interrupt line to ensure that isr doesn't
207 * preempt registerEvent and encounter corrupt state
208 */
209 hwiKey = Hwi_disable();
211 /* Set the 'registered' bit */
212 SET_BIT(obj->evtRegMask, eventId);
214 /* Restore the interrupt line */
215 Hwi_restore(hwiKey);
216 }
218 /*
219 * ======== NotifyDriverMbx_unregisterEvent ========
220 */
221 Void NotifyDriverMbx_unregisterEvent(NotifyDriverMbx_Object *obj,
222 UInt32 eventId)
223 {
224 UInt hwiKey;
226 /*
227 * Disable interrupt line to ensure that isr doesn't
228 * preempt registerEvent and encounter corrupt state
229 */
230 hwiKey = Hwi_disable();
232 /* Clear the registered bit */
233 CLEAR_BIT(obj->evtRegMask, eventId);
235 /* Restore the interrupt line */
236 Hwi_restore(hwiKey);
237 }
239 /*
240 * ======== NotifyDriverMbx_sendEvent ========
241 */
242 /*
243 * PUT_NOTIFICATION will spin waiting for enough room in the mailbox FIFO
244 * to store the number of messages needed for the notification ('numMsgs').
245 * If spinning is necesssary (i.e. if waitClear is TRUE and there isn't enough
246 * room in the FIFO) then PUT_NOTIFICATION will allow pre-emption while
247 * spinning.
248 *
249 * PUT_NOTIFICATION needs to prevent another local thread from writing to the
250 * same mailbox after the current thread has
251 * 1) determined that there is enough room to write the notification and
252 * 2) written the first of two messages to the mailbox.
253 * This is needed to respectively prevent
254 * 1) both threads from incorrectly assuming there is enough space in the FIFO
255 * for their own notifications
256 * 2) the interrupting thread from writing a notification between two
257 * two messages that need to be successivly written by the preempted thread.
258 * Therefore, the check for enough FIFO room and one/both mailbox write(s)
259 * should all occur atomically (i.e. with interrupts disabled)
260 */
261 #define PUT_NOTIFICATION(idx) \
262 key = Hwi_disable(); \
263 while(MAILBOX_FIFOLENGTH - REG32(MAILBOX_STATUS(idx)) < numMsgs) { \
264 Hwi_restore(key); \
265 if (!waitClear) { \
266 return (Notify_E_FAIL); \
267 } \
268 key = Hwi_disable(); \
269 }; \
270 REG32(MAILBOX_MESSAGE(idx)) = eventId + smallPayload; \
271 if (smallPayload == 0xFFFFFFE0) { \
272 REG32(MAILBOX_MESSAGE(idx)) = payload; \
273 } \
274 Hwi_restore(key);
276 Int NotifyDriverMbx_sendEvent(NotifyDriverMbx_Object *obj, UInt32 eventId,
277 UInt32 payload, Bool waitClear)
278 {
279 UInt16 selfVirtId = PROCID(MultiProc_self());
280 UInt16 index;
281 UInt key;
282 UInt numMsgs;
283 UInt32 smallPayload;
285 /* Decide if the payload is small enough to fit in the first mbx msg */
286 if (payload < 0x7FFFFFF) {
287 smallPayload = (payload << 5);
288 numMsgs = 1;
289 }
290 else {
291 smallPayload = 0xFFFFFFE0;
292 numMsgs = 2;
293 }
295 index = (selfVirtId * NotifyDriverMbx_NUM_CORES) + obj->remoteVirtId;
296 PUT_NOTIFICATION(index);
298 return (Notify_S_SUCCESS);
299 }
301 /*
302 * ======== NotifyDriverMbx_disable ========
303 */
304 Void NotifyDriverMbx_disable(NotifyDriverMbx_Object *obj)
305 {
306 UInt16 selfVirtId = PROCID(MultiProc_self());
307 UInt16 index;
309 index = (obj->remoteVirtId * NotifyDriverMbx_NUM_CORES) + selfVirtId;
310 REG32(MAILBOX_IRQENABLE_CLR(index)) = MAILBOX_REG_VAL(SUBMBX_IDX(index));
311 }
313 /*
314 * ======== NotifyDriverMbx_enable ========
315 */
316 Void NotifyDriverMbx_enable(NotifyDriverMbx_Object *obj)
317 {
318 UInt16 selfVirtId = PROCID(MultiProc_self());
319 UInt16 index;
321 index = (obj->remoteVirtId * NotifyDriverMbx_NUM_CORES) + selfVirtId;
322 REG32(MAILBOX_IRQENABLE_SET(index)) = MAILBOX_REG_VAL(SUBMBX_IDX(index));
323 }
325 /*
326 * ======== NotifyDriverMbx_disableEvent ========
327 */
328 Void NotifyDriverMbx_disableEvent(NotifyDriverMbx_Object *obj, UInt32 eventId)
329 {
330 /* NotifyDriverMbx_disableEvent not supported by this driver */
331 Assert_isTrue(FALSE, NotifyDriverMbx_A_notSupported);
332 }
334 /*
335 * ======== NotifyDriverMbx_enableEvent ========
336 */
337 Void NotifyDriverMbx_enableEvent(NotifyDriverMbx_Object *obj, UInt32 eventId)
338 {
339 /* NotifyDriverMbx_enableEvent not supported by this driver */
340 Assert_isTrue(FALSE, NotifyDriverMbx_A_notSupported);
341 }
343 /*
344 *************************************************************************
345 * Internal functions
346 *************************************************************************
347 */
349 /*
350 * ======== NotifyDriverMbx_isr ========
351 */
353 /* Read a message from the mailbox. The low 5 bits of the message
354 * contains the eventId. The high 27 bits of the message contains
355 * either:
356 * 1) The payload if the payload is less than 0x7FFFFFF
357 * 2) 0x7FFFFFF otherwise
358 * If the high 27 bits of the first message is 0x7FFFFFF, then the
359 * payload is in the next mailbox message.
360 *
361 * idx = mailbox table index
362 */
363 #define MESSAGE_DELIVERY(idx) \
364 msg = REG32(MAILBOX_MESSAGE(idx)); \
365 eventId = (UInt16)(msg & 0x1F); \
366 payload = msg >> 5; \
367 if (payload == 0x7FFFFFF) { \
368 while(REG32(MAILBOX_STATUS(idx)) == 0); \
369 payload = REG32(MAILBOX_MESSAGE(idx)); \
370 } \
371 REG32(MAILBOX_IRQSTATUS_CLR(idx)) = MAILBOX_REG_VAL(SUBMBX_IDX(idx)); \
372 obj = NotifyDriverMbx_module->drvHandles[srcVirtId]; \
373 Assert_isTrue(obj != NULL, ti_sdo_ipc_Notify_A_internal); \
374 if (TEST_BIT(obj->evtRegMask, eventId)) { \
375 ti_sdo_ipc_Notify_exec(obj->notifyHandle, eventId, payload); \
376 } \
377 REG32(MAILBOX_EOI_REG(idx)) = MBX_USER_IDX(idx);
379 Void NotifyDriverMbx_isr(UInt16 idx)
380 {
381 NotifyDriverMbx_Object *obj;
382 UInt32 msg, payload;
383 UInt16 eventId;
384 UInt16 srcVirtId;
386 srcVirtId = idx / NotifyDriverMbx_NUM_CORES;
387 MESSAGE_DELIVERY(idx)
388 }
390 /*
391 * ======== NotifyDriverMbx_setNotifyHandle ========
392 */
393 Void NotifyDriverMbx_setNotifyHandle(NotifyDriverMbx_Object *obj,
394 Ptr notifyHandle)
395 {
396 /* internally used, so no assert needed */
397 obj->notifyHandle = (ti_sdo_ipc_Notify_Handle)notifyHandle;
398 }