1 /** @file
2   Handle OMAP35xx interrupt controller
3 
4   Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 #include <PiDxe.h>
16 
17 #include <Library/BaseLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/UefiLib.h>
22 #include <Library/PcdLib.h>
23 #include <Library/IoLib.h>
24 #include <Library/ArmLib.h>
25 
26 #include <Protocol/Cpu.h>
27 #include <Protocol/HardwareInterrupt.h>
28 
29 #include <Omap3530/Omap3530.h>
30 
31 //
32 // Notifications
33 //
34 EFI_EVENT EfiExitBootServicesEvent      = (EFI_EVENT)NULL;
35 
36 
37 HARDWARE_INTERRUPT_HANDLER  gRegisteredInterruptHandlers[INT_NROF_VECTORS];
38 
39 /**
40   Shutdown our hardware
41 
42   DXE Core will disable interrupts and turn off the timer and disable interrupts
43   after all the event handlers have run.
44 
45   @param[in]  Event   The Event that is being processed
46   @param[in]  Context Event Context
47 **/
48 VOID
49 EFIAPI
ExitBootServicesEvent(IN EFI_EVENT Event,IN VOID * Context)50 ExitBootServicesEvent (
51   IN EFI_EVENT  Event,
52   IN VOID       *Context
53   )
54 {
55   // Disable all interrupts
56   MmioWrite32 (INTCPS_MIR(0), 0xFFFFFFFF);
57   MmioWrite32 (INTCPS_MIR(1), 0xFFFFFFFF);
58   MmioWrite32 (INTCPS_MIR(2), 0xFFFFFFFF);
59   MmioWrite32 (INTCPS_CONTROL, INTCPS_CONTROL_NEWIRQAGR);
60 
61   // Add code here to disable all FIQs as debugger may have turned one on
62 }
63 
64 /**
65   Register Handler for the specified interrupt source.
66 
67   @param This     Instance pointer for this protocol
68   @param Source   Hardware source of the interrupt
69   @param Handler  Callback for interrupt. NULL to unregister
70 
71   @retval EFI_SUCCESS Source was updated to support Handler.
72   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
73 
74 **/
75 EFI_STATUS
76 EFIAPI
RegisterInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source,IN HARDWARE_INTERRUPT_HANDLER Handler)77 RegisterInterruptSource (
78   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
79   IN HARDWARE_INTERRUPT_SOURCE          Source,
80   IN HARDWARE_INTERRUPT_HANDLER         Handler
81   )
82 {
83   if (Source > MAX_VECTOR) {
84     ASSERT(FALSE);
85     return EFI_UNSUPPORTED;
86   }
87 
88   if ((MmioRead32 (INTCPS_ILR(Source)) & INTCPS_ILR_FIQ) == INTCPS_ILR_FIQ) {
89     // This vector has been programmed as FIQ so we can't use it for IRQ
90     // EFI does not use FIQ, but the debugger can use it to check for
91     // ctrl-c. So this ASSERT means you have a conflict with the debug agent
92     ASSERT (FALSE);
93     return EFI_UNSUPPORTED;
94   }
95 
96   if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) {
97     return EFI_INVALID_PARAMETER;
98   }
99 
100   if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) {
101     return EFI_ALREADY_STARTED;
102   }
103 
104   gRegisteredInterruptHandlers[Source] = Handler;
105   return This->EnableInterruptSource(This, Source);
106 }
107 
108 
109 /**
110   Enable interrupt source Source.
111 
112   @param This     Instance pointer for this protocol
113   @param Source   Hardware source of the interrupt
114 
115   @retval EFI_SUCCESS       Source interrupt enabled.
116   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
117 
118 **/
119 EFI_STATUS
120 EFIAPI
EnableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)121 EnableInterruptSource (
122   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
123   IN HARDWARE_INTERRUPT_SOURCE          Source
124   )
125 {
126   UINTN Bank;
127   UINTN Bit;
128 
129   if (Source > MAX_VECTOR) {
130     ASSERT(FALSE);
131     return EFI_UNSUPPORTED;
132   }
133 
134   Bank = Source / 32;
135   Bit  = 1UL << (Source % 32);
136 
137   MmioWrite32 (INTCPS_MIR_CLEAR(Bank), Bit);
138 
139   return EFI_SUCCESS;
140 }
141 
142 
143 /**
144   Disable interrupt source Source.
145 
146   @param This     Instance pointer for this protocol
147   @param Source   Hardware source of the interrupt
148 
149   @retval EFI_SUCCESS       Source interrupt disabled.
150   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
151 
152 **/
153 EFI_STATUS
154 EFIAPI
DisableInterruptSource(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)155 DisableInterruptSource (
156   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
157   IN HARDWARE_INTERRUPT_SOURCE          Source
158   )
159 {
160   UINTN Bank;
161   UINTN Bit;
162 
163   if (Source > MAX_VECTOR) {
164     ASSERT(FALSE);
165     return EFI_UNSUPPORTED;
166   }
167 
168   Bank = Source / 32;
169   Bit  = 1UL << (Source % 32);
170 
171   MmioWrite32 (INTCPS_MIR_SET(Bank), Bit);
172 
173   return EFI_SUCCESS;
174 }
175 
176 
177 
178 /**
179   Return current state of interrupt source Source.
180 
181   @param This     Instance pointer for this protocol
182   @param Source   Hardware source of the interrupt
183   @param InterruptState  TRUE: source enabled, FALSE: source disabled.
184 
185   @retval EFI_SUCCESS       InterruptState is valid
186   @retval EFI_DEVICE_ERROR  InterruptState is not valid
187 
188 **/
189 EFI_STATUS
190 EFIAPI
GetInterruptSourceState(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source,IN BOOLEAN * InterruptState)191 GetInterruptSourceState (
192   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
193   IN HARDWARE_INTERRUPT_SOURCE          Source,
194   IN BOOLEAN                            *InterruptState
195   )
196 {
197   UINTN Bank;
198   UINTN Bit;
199 
200   if (InterruptState == NULL) {
201     return EFI_INVALID_PARAMETER;
202   }
203 
204   if (Source > MAX_VECTOR) {
205     ASSERT(FALSE);
206     return EFI_UNSUPPORTED;
207   }
208 
209   Bank = Source / 32;
210   Bit  = 1UL << (Source % 32);
211 
212   if ((MmioRead32(INTCPS_MIR(Bank)) & Bit) == Bit) {
213     *InterruptState = FALSE;
214   } else {
215     *InterruptState = TRUE;
216   }
217 
218   return EFI_SUCCESS;
219 }
220 
221 /**
222   Signal to the hardware that the End Of Intrrupt state
223   has been reached.
224 
225   @param This     Instance pointer for this protocol
226   @param Source   Hardware source of the interrupt
227 
228   @retval EFI_SUCCESS       Source interrupt EOI'ed.
229   @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
230 
231 **/
232 EFI_STATUS
233 EFIAPI
EndOfInterrupt(IN EFI_HARDWARE_INTERRUPT_PROTOCOL * This,IN HARDWARE_INTERRUPT_SOURCE Source)234 EndOfInterrupt (
235   IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
236   IN HARDWARE_INTERRUPT_SOURCE          Source
237   )
238 {
239   MmioWrite32 (INTCPS_CONTROL, INTCPS_CONTROL_NEWIRQAGR);
240   ArmDataSynchronizationBarrier ();
241   return EFI_SUCCESS;
242 }
243 
244 
245 /**
246   EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
247 
248   @param  InterruptType    Defines the type of interrupt or exception that
249                            occurred on the processor.This parameter is processor architecture specific.
250   @param  SystemContext    A pointer to the processor context when
251                            the interrupt occurred on the processor.
252 
253   @return None
254 
255 **/
256 VOID
257 EFIAPI
IrqInterruptHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_SYSTEM_CONTEXT SystemContext)258 IrqInterruptHandler (
259   IN EFI_EXCEPTION_TYPE           InterruptType,
260   IN EFI_SYSTEM_CONTEXT           SystemContext
261   )
262 {
263   UINT32                     Vector;
264   HARDWARE_INTERRUPT_HANDLER InterruptHandler;
265 
266   Vector = MmioRead32 (INTCPS_SIR_IRQ) & INTCPS_SIR_IRQ_MASK;
267 
268   // Needed to prevent infinite nesting when Time Driver lowers TPL
269   MmioWrite32 (INTCPS_CONTROL, INTCPS_CONTROL_NEWIRQAGR);
270   ArmDataSynchronizationBarrier ();
271 
272   InterruptHandler = gRegisteredInterruptHandlers[Vector];
273   if (InterruptHandler != NULL) {
274     // Call the registered interrupt handler.
275     InterruptHandler (Vector, SystemContext);
276   }
277 
278   // Needed to clear after running the handler
279   MmioWrite32 (INTCPS_CONTROL, INTCPS_CONTROL_NEWIRQAGR);
280   ArmDataSynchronizationBarrier ();
281 }
282 
283 //
284 // Making this global saves a few bytes in image size
285 //
286 EFI_HANDLE  gHardwareInterruptHandle = NULL;
287 
288 //
289 // The protocol instance produced by this driver
290 //
291 EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
292   RegisterInterruptSource,
293   EnableInterruptSource,
294   DisableInterruptSource,
295   GetInterruptSourceState,
296   EndOfInterrupt
297 };
298 
299 /**
300   Initialize the state information for the CPU Architectural Protocol
301 
302   @param  ImageHandle   of the loaded driver
303   @param  SystemTable   Pointer to the System Table
304 
305   @retval EFI_SUCCESS           Protocol registered
306   @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
307   @retval EFI_DEVICE_ERROR      Hardware problems
308 
309 **/
310 EFI_STATUS
InterruptDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)311 InterruptDxeInitialize (
312   IN EFI_HANDLE         ImageHandle,
313   IN EFI_SYSTEM_TABLE   *SystemTable
314   )
315 {
316   EFI_STATUS  Status;
317   EFI_CPU_ARCH_PROTOCOL   *Cpu;
318 
319   // Make sure the Interrupt Controller Protocol is not already installed in the system.
320   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
321 
322   // Make sure all interrupts are disabled by default.
323   MmioWrite32 (INTCPS_MIR(0), 0xFFFFFFFF);
324   MmioWrite32 (INTCPS_MIR(1), 0xFFFFFFFF);
325   MmioWrite32 (INTCPS_MIR(2), 0xFFFFFFFF);
326   MmioOr32 (INTCPS_CONTROL, INTCPS_CONTROL_NEWIRQAGR);
327 
328   Status = gBS->InstallMultipleProtocolInterfaces(&gHardwareInterruptHandle,
329                                                   &gHardwareInterruptProtocolGuid,   &gHardwareInterruptProtocol,
330                                                   NULL);
331   ASSERT_EFI_ERROR(Status);
332 
333   //
334   // Get the CPU protocol that this driver requires.
335   //
336   Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
337   ASSERT_EFI_ERROR(Status);
338 
339   //
340   // Unregister the default exception handler.
341   //
342   Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL);
343   ASSERT_EFI_ERROR(Status);
344 
345   //
346   // Register to receive interrupts
347   //
348   Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler);
349   ASSERT_EFI_ERROR(Status);
350 
351   // Register for an ExitBootServicesEvent
352   Status = gBS->CreateEvent(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
353   ASSERT_EFI_ERROR(Status);
354 
355   return Status;
356 }
357 
358