X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmRealViewEbPkg%2FInterruptDxe%2FInterruptDxe.c;fp=ArmRealViewEbPkg%2FInterruptDxe%2FInterruptDxe.c;h=b9e8256f824fd93f3fdbb8a8f16ae0602d709dfd;hp=0000000000000000000000000000000000000000;hb=afdfe8f02bdf8d563554bb620c001072862b064e;hpb=7ee525b2c1f8d3d6da5f788364fe5be36a979c9c diff --git a/ArmRealViewEbPkg/InterruptDxe/InterruptDxe.c b/ArmRealViewEbPkg/InterruptDxe/InterruptDxe.c new file mode 100755 index 0000000000..b9e8256f82 --- /dev/null +++ b/ArmRealViewEbPkg/InterruptDxe/InterruptDxe.c @@ -0,0 +1,484 @@ +/*++ + +Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
+Portions copyright (c) 2010, Apple Inc. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Gic.c + +Abstract: + + Driver implementing the GIC interrupt controller protocol + +--*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +// +// EB board definitions +// +#define EB_GIC1_CPU_INTF_BASE 0x10040000 +#define EB_GIC1_DIST_BASE 0x10041000 +#define EB_GIC2_CPU_INTF_BASE 0x10050000 +#define EB_GIC2_DIST_BASE 0x10051000 +#define EB_GIC3_CPU_INTF_BASE 0x10060000 +#define EB_GIC3_DIST_BASE 0x10061000 +#define EB_GIC4_CPU_INTF_BASE 0x10070000 +#define EB_GIC5_DIST_BASE 0x10071000 + +// number of interrupts sources supported by each GIC on the EB +#define EB_NUM_GIC_INTERRUPTS 96 + +// number of 32-bit registers needed to represent those interrupts as a bit +// (used for enable set, enable clear, pending set, pending clear, and active regs) +#define EB_NUM_GIC_REG_PER_INT_BITS (EB_NUM_GIC_INTERRUPTS / 32) + +// number of 32-bit registers needed to represent those interrupts as two bits +// (used for configuration reg) +#define EB_NUM_GIC_REG_PER_INT_CFG (EB_NUM_GIC_INTERRUPTS / 16) + +// number of 32-bit registers needed to represent interrupts as 8-bit priority field +// (used for priority regs) +#define EB_NUM_GIC_REG_PER_INT_BYTES (EB_NUM_GIC_INTERRUPTS / 4) + +#define GIC_DEFAULT_PRIORITY 0x80 + +// +// GIC definitions +// + +// Distributor +#define GIC_ICDDCR 0x000 // Distributor Control Register +#define GIC_ICDICTR 0x004 // Interrupt Controller Type Register +#define GIC_ICDIIDR 0x008 // Implementer Identification Register + +// each reg base below repeats for EB_NUM_GIC_REG_PER_INT_BITS (see GIC spec) +#define GIC_ICDISR 0x080 // Interrupt Security Registers +#define GIC_ICDISER 0x100 // Interrupt Set-Enable Registers +#define GIC_ICDICER 0x180 // Interrupt Clear-Enable Registers +#define GIC_ICDSPR 0x200 // Interrupt Set-Pending Registers +#define GIC_ICDCPR 0x280 // Interrupt Clear-Pending Registers +#define GIC_ICDABR 0x300 // Active Bit Registers + +// each reg base below repeats for EB_NUM_GIC_REG_PER_INT_BYTES +#define GIC_ICDIPR 0x400 // Interrupt Priority Registers + +// each reg base below repeats for EB_NUM_GIC_INTERRUPTS +#define GIC_ICDIPTR 0x800 // Interrupt Processor Target Registers +#define GIC_ICDICFR 0xC00 // Interrupt Configuration Registers + +// just one of these +#define GIC_ICDSGIR 0xF00 // Software Generated Interrupt Register + + +// Cpu interface +#define GIC_ICCICR 0x00 // CPU Interface Controler Register +#define GIC_ICCPMR 0x04 // Interrupt Priority Mask Register +#define GIC_ICCBPR 0x08 // Binary Point Register +#define GIC_ICCIAR 0x0C // Interrupt Acknowledge Register +#define GIC_ICCEIOR 0x10 // End Of Interrupt Register +#define GIC_ICCRPR 0x14 // Running Priority Register +#define GIC_ICCPIR 0x18 // Highest Pending Interrupt Register +#define GIC_ICCABPR 0x1C // Aliased Binary Point Register +#define GIC_ICCIDR 0xFC // Identification Register + +extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol; + +// +// Notifications +// +VOID *CpuProtocolNotificationToken = NULL; +EFI_EVENT CpuProtocolNotificationEvent = (EFI_EVENT)NULL; +EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL; + + +HARDWARE_INTERRUPT_HANDLER gRegisteredInterruptHandlers[EB_NUM_GIC_INTERRUPTS]; + +/** + Register Handler for the specified interrupt source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + @param Handler Callback for interrupt. NULL to unregister + + @retval EFI_SUCCESS Source was updated to support Handler. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +EFI_STATUS +EFIAPI +RegisterInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN HARDWARE_INTERRUPT_HANDLER Handler + ) +{ + if (Source > EB_NUM_GIC_INTERRUPTS) { + ASSERT(FALSE); + return EFI_UNSUPPORTED; + } + + if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) { + return EFI_ALREADY_STARTED; + } + + gRegisteredInterruptHandlers[Source] = Handler; + return This->EnableInterruptSource(This, Source); +} + + +/** + Enable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +EFI_STATUS +EFIAPI +EnableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + UINT32 RegOffset; + UINTN RegShift; + + if (Source > EB_NUM_GIC_INTERRUPTS) { + ASSERT(FALSE); + return EFI_UNSUPPORTED; + } + + // calculate enable register offset and bit position + RegOffset = Source / 32; + RegShift = Source % 32; + + // write set-enable register + MmioWrite32 (EB_GIC1_DIST_BASE+GIC_ICDISER+(4*RegOffset), 1 << RegShift); + + return EFI_SUCCESS; +} + + +/** + Disable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt disabled. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +EFI_STATUS +EFIAPI +DisableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + UINT32 RegOffset; + UINTN RegShift; + + if (Source > EB_NUM_GIC_INTERRUPTS) { + ASSERT(FALSE); + return EFI_UNSUPPORTED; + } + + // calculate enable register offset and bit position + RegOffset = Source / 32; + RegShift = Source % 32; + + // write set-enable register + MmioWrite32 (EB_GIC1_DIST_BASE+GIC_ICDICER+(4*RegOffset), 1 << RegShift); + + return EFI_SUCCESS; +} + + + +/** + Return current state of interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + @param InterruptState TRUE: source enabled, FALSE: source disabled. + + @retval EFI_SUCCESS InterruptState is valid + @retval EFI_DEVICE_ERROR InterruptState is not valid + +**/ +EFI_STATUS +EFIAPI +GetInterruptSourceState ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN BOOLEAN *InterruptState + ) +{ + UINT32 RegOffset; + UINTN RegShift; + + if (Source > EB_NUM_GIC_INTERRUPTS) { + ASSERT(FALSE); + return EFI_UNSUPPORTED; + } + + // calculate enable register offset and bit position + RegOffset = Source / 32; + RegShift = Source % 32; + + if ((MmioRead32 (EB_GIC1_DIST_BASE+GIC_ICDISER+(4*RegOffset)) & (1< EB_NUM_GIC_INTERRUPTS) { + ASSERT(FALSE); + return EFI_UNSUPPORTED; + } + + MmioWrite32 (EB_GIC1_CPU_INTF_BASE+GIC_ICCEIOR, Source); + return EFI_SUCCESS; +} + + +/** + EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + + @return None + +**/ +VOID +EFIAPI +IrqInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT32 GicInterrupt; + HARDWARE_INTERRUPT_HANDLER InterruptHandler; + + GicInterrupt = MmioRead32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCIAR); + if (GicInterrupt >= EB_NUM_GIC_INTERRUPTS) { + MmioWrite32 (EB_GIC1_CPU_INTF_BASE+GIC_ICCEIOR, GicInterrupt); + } + + InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt]; + if (InterruptHandler != NULL) { + // Call the registered interrupt handler. + InterruptHandler (GicInterrupt, SystemContext); + } else { + DEBUG ((EFI_D_ERROR, "Spurious GIC interrupt: %x\n", GicInterrupt)); + } + + EndOfInterrupt (&gHardwareInterruptProtocol, GicInterrupt); +} + + +// +// Making this global saves a few bytes in image size +// +EFI_HANDLE gHardwareInterruptHandle = NULL; + +// +// The protocol instance produced by this driver +// +EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = { + RegisterInterruptSource, + EnableInterruptSource, + DisableInterruptSource, + GetInterruptSourceState, + EndOfInterrupt +}; + + +/** + Shutdown our hardware + + DXE Core will disable interrupts and turn off the timer and disable interrupts + after all the event handlers have run. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +VOID +EFIAPI +ExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN i; + + for (i = 0; i < EB_NUM_GIC_INTERRUPTS; i++) { + DisableInterruptSource (&gHardwareInterruptProtocol, i); + } +} + + +// +// Notification routines +// +VOID +CpuProtocolInstalledNotification ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_CPU_ARCH_PROTOCOL *Cpu; + + // + // Get the cpu protocol that this driver requires. + // + Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu); + ASSERT_EFI_ERROR(Status); + + // + // Unregister the default exception handler. + // + Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, NULL); + ASSERT_EFI_ERROR(Status); + + // + // Register to receive interrupts + // + Status = Cpu->RegisterInterruptHandler(Cpu, EXCEPT_ARM_IRQ, IrqInterruptHandler); + ASSERT_EFI_ERROR(Status); +} + +/** + Initialize the state information for the CPU Architectural Protocol + + @param ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + +**/ +EFI_STATUS +InterruptDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN i; + UINT32 RegOffset; + UINTN RegShift; + + + // Make sure the Interrupt Controller Protocol is not already installed in the system. + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid); + + for (i = 0; i < EB_NUM_GIC_INTERRUPTS; i++) { + DisableInterruptSource (&gHardwareInterruptProtocol, i); + + // Set Priority + RegOffset = i / 4; + RegShift = (i % 4) * 8; + MmioAndThenOr32 ( + EB_GIC1_DIST_BASE+GIC_ICDIPR+(4*RegOffset), + ~(0xff << RegShift), + GIC_DEFAULT_PRIORITY << RegShift + ); + } + + // configure interrupts for cpu 0 + for (i = 0; i < EB_NUM_GIC_REG_PER_INT_BYTES; i++) { + MmioWrite32 (EB_GIC1_DIST_BASE + GIC_ICDIPTR + (i*4), 0x01010101); + } + + // set binary point reg to 0x7 (no preemption) + MmioWrite32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCBPR, 0x7); + + // set priority mask reg to 0xff to allow all priorities through + MmioWrite32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCPMR, 0xff); + + // enable gic cpu interface + MmioWrite32 (EB_GIC1_CPU_INTF_BASE + GIC_ICCICR, 0x1); + + // enable gic distributor + MmioWrite32 (EB_GIC1_DIST_BASE + GIC_ICCICR, 0x1); + + + ZeroMem (&gRegisteredInterruptHandlers, sizeof (gRegisteredInterruptHandlers)); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &gHardwareInterruptHandle, + &gHardwareInterruptProtocolGuid, &gHardwareInterruptProtocol, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // Set up to be notified when the Cpu protocol is installed. + Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CpuProtocolInstalledNotification, NULL, &CpuProtocolNotificationEvent); + ASSERT_EFI_ERROR (Status); + + Status = gBS->RegisterProtocolNotify (&gEfiCpuArchProtocolGuid, CpuProtocolNotificationEvent, (VOID *)&CpuProtocolNotificationToken); + ASSERT_EFI_ERROR (Status); + + // Register for an ExitBootServicesEvent + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent); + ASSERT_EFI_ERROR (Status); + + return Status; +} +