123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769 |
- /*++
- Copyright (c) 2012 Minoca Corp. All Rights Reserved
- Module Name:
- cpintr.c
- Abstract:
- This module implements interrupt controller support for the Integrator/CP
- board.
- Author:
- Evan Green 22-Aug-2012
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- //
- // Include kernel.h, but be cautious about which APIs are used. Most of the
- // system depends on the hardware modules. Limit use to HL, RTL and AR routines.
- //
- #include <minoca/kernel/kernel.h>
- #include "integcp.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the number of soft priority levels implemented in the interrupt
- // controller.
- //
- #define INTEGRATORCP_INTERRUPT_PRIORITY_COUNT 16
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // This macro reads from the Integrator/CP interrupt controller. The parameter
- // should be CP_INTERRUPT_REGISTER value.
- //
- #define READ_INTERRUPT_REGISTER(_Register) \
- HlReadRegister32((PULONG)HlCpInterruptController + (_Register))
- //
- // This macro writes to the Integrator/CP interrupt controller. _Register
- // should be CP_INTERRUPT_REGISTER value, and _Value should be a ULONG.
- //
- #define WRITE_INTERRUPT_REGISTER(_Register, _Value) \
- HlWriteRegister32((PULONG)HlCpInterruptController + (_Register), (_Value))
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- HlpCpInterruptInitializeIoUnit (
- PVOID Context
- );
- INTERRUPT_CAUSE
- HlpCpInterruptBegin (
- PVOID Context,
- PINTERRUPT_LINE FiringLine,
- PULONG MagicCandy
- );
- VOID
- HlpCpInterruptEndOfInterrupt (
- PVOID Context,
- ULONG MagicCandy
- );
- KSTATUS
- HlpCpInterruptRequestInterrupt (
- PVOID Context,
- PINTERRUPT_LINE Line,
- ULONG Vector,
- PINTERRUPT_HARDWARE_TARGET Target
- );
- KSTATUS
- HlpCpInterruptSetLineState (
- PVOID Context,
- PINTERRUPT_LINE Line,
- PINTERRUPT_LINE_STATE State,
- PVOID ResourceData,
- UINTN ResourceDataSize
- );
- VOID
- HlpCpInterruptMaskLine (
- PVOID Context,
- PINTERRUPT_LINE Line,
- BOOL Enable
- );
- KSTATUS
- HlpCpInterruptDescribeLines (
- VOID
- );
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // Define the offsets to interrupt controller registers, in ULONGs.
- //
- typedef enum _CP_INTERRUPT_REGISTER {
- CpInterruptIrqStatus = 0x0,
- CpInterruptIrqRawStatus = 0x1,
- CpInterruptIrqEnable = 0x2,
- CpInterruptIrqDisable = 0x3,
- CpInterruptSoftwareInterruptSet = 0x4,
- CpInterruptSoftwareInterruptClear = 0x5,
- CpInterruptFiqStatus = 0x8,
- CpInterruptFiqRawStatus = 0x9,
- CpInterruptFiqEnable = 0xA,
- CpInterruptFiqDisable = 0xB,
- } CP_INTERRUPT_REGISTER, *PCP_INTERRUPT_REGISTER;
- /*++
- Structure Description:
- This structure describes the Integrator/CP private interrupt controller
- state.
- Members:
- PhysicalAddress - Stores the physical address of this controller.
- LinePriority - Stores the priority for each interrupt line.
- CurrentPriority - Stores the current priority of the interrupt controller.
- Masks - Stores the mask of interrupts to disable when an interrupt of each
- priority fires.
- EnabledMask - Stores the mask of interrupts enabled at any priority.
- --*/
- typedef struct _INTEGRATORCP_INTERRUPT_DATA {
- PHYSICAL_ADDRESS PhysicalAddress;
- UCHAR LinePriority[INTEGRATORCP_INTERRUPT_LINE_COUNT];
- UCHAR CurrentPriority;
- ULONG Masks[INTEGRATORCP_INTERRUPT_PRIORITY_COUNT];
- ULONG EnabledMask;
- } INTEGRATORCP_INTERRUPT_DATA, *PINTEGRATORCP_INTERRUPT_DATA;
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store the virtual address of the mapped interrupt controller.
- //
- PVOID HlCpInterruptController = NULL;
- //
- // Store a pointer to the Integrator/CP ACPI table, if found.
- //
- PINTEGRATORCP_TABLE HlCpIntegratorTable = NULL;
- //
- // Define the interrupt function table template.
- //
- INTERRUPT_FUNCTION_TABLE HlCpInterruptFunctionTable = {
- HlpCpInterruptInitializeIoUnit,
- HlpCpInterruptSetLineState,
- HlpCpInterruptMaskLine,
- HlpCpInterruptBegin,
- NULL,
- HlpCpInterruptEndOfInterrupt,
- HlpCpInterruptRequestInterrupt,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- VOID
- HlpCpInterruptModuleEntry (
- VOID
- )
- /*++
- Routine Description:
- This routine is the entry point for the Integrator/CP Interrupt hardware
- module. Its role is to detect and report the prescense of an Integrator/CP
- interrupt controller.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- PINTEGRATORCP_TABLE IntegratorTable;
- PINTEGRATORCP_INTERRUPT_DATA InterruptData;
- INTERRUPT_CONTROLLER_DESCRIPTION NewController;
- KSTATUS Status;
- //
- // Attempt to find the Integrator/CP ACPI table.
- //
- IntegratorTable = HlGetAcpiTable(INTEGRATORCP_SIGNATURE, NULL);
- if (IntegratorTable == NULL) {
- goto IntegratorCpInterruptModuleEntryEnd;
- }
- HlCpIntegratorTable = IntegratorTable;
- //
- // Zero out the controller description.
- //
- RtlZeroMemory(&NewController, sizeof(INTERRUPT_CONTROLLER_DESCRIPTION));
- //
- // Allocate context needed for this Interrupt Controller.
- //
- InterruptData = HlAllocateMemory(sizeof(INTEGRATORCP_INTERRUPT_DATA),
- INTEGRATOR_ALLOCATION_TAG,
- FALSE,
- NULL);
- if (InterruptData == NULL) {
- goto IntegratorCpInterruptModuleEntryEnd;
- }
- RtlZeroMemory(InterruptData, sizeof(INTEGRATORCP_INTERRUPT_DATA));
- InterruptData->PhysicalAddress =
- IntegratorTable->InterruptControllerPhysicalAddress;
- //
- // Initialize the new controller structure.
- //
- NewController.TableVersion = INTERRUPT_CONTROLLER_DESCRIPTION_VERSION;
- RtlCopyMemory(&(NewController.FunctionTable),
- &HlCpInterruptFunctionTable,
- sizeof(INTERRUPT_FUNCTION_TABLE));
- NewController.Context = InterruptData;
- NewController.Identifier = 0;
- NewController.ProcessorCount = 0;
- NewController.PriorityCount = INTEGRATORCP_INTERRUPT_PRIORITY_COUNT;
- //
- // Register the controller with the system.
- //
- Status = HlRegisterHardware(HardwareModuleInterruptController,
- &NewController);
- if (!KSUCCESS(Status)) {
- goto IntegratorCpInterruptModuleEntryEnd;
- }
- IntegratorCpInterruptModuleEntryEnd:
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- HlpCpInterruptInitializeIoUnit (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine initializes an interrupt controller. It's responsible for
- masking all interrupt lines on the controller and setting the current
- priority to the lowest (allow all interrupts). Once completed successfully,
- it is expected that interrupts can be enabled at the processor core with
- no interrupts occurring.
- Arguments:
- Context - Supplies the pointer to the controller's context, provided by the
- hardware module upon initialization.
- Return Value:
- STATUS_SUCCESS on success.
- Other status codes on failure.
- --*/
- {
- PINTEGRATORCP_INTERRUPT_DATA InterruptData;
- KSTATUS Status;
- InterruptData = (PINTEGRATORCP_INTERRUPT_DATA)Context;
- if (HlCpInterruptController == NULL) {
- HlCpInterruptController = HlMapPhysicalAddress(
- InterruptData->PhysicalAddress,
- INTEGRATORCP_INTERRUPT_CONTROLLER_SIZE,
- TRUE);
- if (HlCpInterruptController == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CpInterruptInitializeIoUnitEnd;
- }
- //
- // Describe the interrupt lines on this controller.
- //
- Status = HlpCpInterruptDescribeLines();
- if (!KSUCCESS(Status)) {
- goto CpInterruptInitializeIoUnitEnd;
- }
- }
- //
- // Disable all FIQ and IRQ lines.
- //
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqDisable, 0xFFFFFFFF);
- WRITE_INTERRUPT_REGISTER(CpInterruptFiqDisable, 0xFFFFFFFF);
- InterruptData->CurrentPriority = 0;
- InterruptData->EnabledMask = 0;
- Status = STATUS_SUCCESS;
- CpInterruptInitializeIoUnitEnd:
- return Status;
- }
- INTERRUPT_CAUSE
- HlpCpInterruptBegin (
- PVOID Context,
- PINTERRUPT_LINE FiringLine,
- PULONG MagicCandy
- )
- /*++
- Routine Description:
- This routine is called when an interrupt fires. Its role is to determine
- if an interrupt has fired on the given controller, accept it, and determine
- which line fired if any. This routine will always be called with interrupts
- disabled at the processor core.
- Arguments:
- Context - Supplies the pointer to the controller's context, provided by the
- hardware module upon initialization.
- FiringLine - Supplies a pointer where the interrupt hardware module will
- fill in which line fired, if applicable.
- MagicCandy - Supplies a pointer where the interrupt hardware module can
- store 32 bits of private information regarding this interrupt. This
- information will be returned to it when the End Of Interrupt routine
- is called.
- Return Value:
- Returns an interrupt cause indicating whether or not an interrupt line,
- spurious interrupt, or no interrupt fired on this controller.
- --*/
- {
- ULONG Index;
- PINTEGRATORCP_INTERRUPT_DATA InterruptData;
- ULONG Mask;
- UCHAR Priority;
- ULONG Status;
- InterruptData = (PINTEGRATORCP_INTERRUPT_DATA)Context;
- Status = READ_INTERRUPT_REGISTER(CpInterruptIrqStatus);
- if (Status == 0) {
- return InterruptCauseNoInterruptHere;
- }
- //
- // Find the first firing index.
- //
- Index = 0;
- while ((Status & 0x1) == 0) {
- Status = Status >> 1;
- Index += 1;
- }
- //
- // Disable all interrupts at or below this run level.
- //
- Priority = InterruptData->LinePriority[Index];
- Mask = InterruptData->Masks[Priority];
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqDisable, Mask);
- //
- // Save the previous priority to know what to restore to when this
- // interrupt ends.
- //
- *MagicCandy = InterruptData->CurrentPriority;
- InterruptData->CurrentPriority = Priority;
- //
- // Return the interrupting line's information.
- //
- FiringLine->Type = InterruptLineControllerSpecified;
- FiringLine->U.Local.Controller = 0;
- FiringLine->U.Local.Line = Index;
- return InterruptCauseLineFired;
- }
- VOID
- HlpCpInterruptEndOfInterrupt (
- PVOID Context,
- ULONG MagicCandy
- )
- /*++
- Routine Description:
- This routine is called after an interrupt has fired and been serviced. Its
- role is to tell the interrupt controller that processing has completed.
- This routine will always be called with interrupts disabled at the
- processor core.
- Arguments:
- Context - Supplies the pointer to the controller's context, provided by the
- hardware module upon initialization.
- MagicCandy - Supplies the magic candy that that the interrupt hardware
- module stored when the interrupt began.
- Return Value:
- None.
- --*/
- {
- PINTEGRATORCP_INTERRUPT_DATA InterruptData;
- ULONG Mask;
- //
- // Re-enable interrupts at the previous priority level before this
- // interrupt fired. The enabled mask prevents enabling interrupts that
- // weren't enabled before.
- //
- InterruptData = (PINTEGRATORCP_INTERRUPT_DATA)Context;
- InterruptData->CurrentPriority = MagicCandy;
- Mask = ~InterruptData->Masks[MagicCandy] & InterruptData->EnabledMask;
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqEnable, Mask);
- return;
- }
- KSTATUS
- HlpCpInterruptRequestInterrupt (
- PVOID Context,
- PINTERRUPT_LINE Line,
- ULONG Vector,
- PINTERRUPT_HARDWARE_TARGET Target
- )
- /*++
- Routine Description:
- This routine requests a hardware interrupt on the given line.
- Arguments:
- Context - Supplies the pointer to the controller's context, provided by the
- hardware module upon initialization.
- Line - Supplies a pointer to the interrupt line to spark.
- Vector - Supplies the vector to generate the interrupt on (for vectored
- architectures only).
- Target - Supplies a pointer to the set of processors to target.
- Return Value:
- STATUS_SUCCESS on success.
- Error code on failure.
- --*/
- {
- //
- // This feature will be implemented when it is required (probably by
- // power management).
- //
- return STATUS_NOT_IMPLEMENTED;
- }
- KSTATUS
- HlpCpInterruptSetLineState (
- PVOID Context,
- PINTERRUPT_LINE Line,
- PINTERRUPT_LINE_STATE State,
- PVOID ResourceData,
- UINTN ResourceDataSize
- )
- /*++
- Routine Description:
- This routine enables or disables and configures an interrupt line.
- Arguments:
- Context - Supplies the pointer to the controller's context, provided by the
- hardware module upon initialization.
- Line - Supplies a pointer to the line to set up. This will always be a
- controller specified line.
- State - Supplies a pointer to the new configuration of the line.
- ResourceData - Supplies an optional pointer to the device specific resource
- data for the interrupt line.
- ResourceDataSize - Supplies the size of the resource data, in bytes.
- Return Value:
- Status code.
- --*/
- {
- ULONG BitMask;
- ULONG Index;
- PINTEGRATORCP_INTERRUPT_DATA InterruptData;
- ULONG LocalLine;
- UCHAR Priority;
- KSTATUS Status;
- InterruptData = (PINTEGRATORCP_INTERRUPT_DATA)Context;
- LocalLine = Line->U.Local.Line;
- if ((Line->Type != InterruptLineControllerSpecified) ||
- (Line->U.Local.Controller != 0) ||
- (LocalLine >= INTEGRATORCP_INTERRUPT_LINE_COUNT)) {
- Status = STATUS_INVALID_PARAMETER;
- goto CpInterruptSetLineStateEnd;
- }
- if ((State->Output.Type != InterruptLineControllerSpecified) ||
- (State->Output.U.Local.Controller != INTERRUPT_CPU_IDENTIFIER) ||
- (State->Output.U.Local.Line != INTERRUPT_CPU_IRQ_PIN)) {
- Status = STATUS_INVALID_PARAMETER;
- goto CpInterruptSetLineStateEnd;
- }
- //
- // Calculate the bit to flip and flip it.
- //
- BitMask = 1 << LocalLine;
- if ((State->Flags & INTERRUPT_LINE_STATE_FLAG_ENABLED) != 0) {
- Priority = State->HardwarePriority;
- InterruptData->LinePriority[LocalLine] = Priority;
- InterruptData->EnabledMask |= BitMask;
- //
- // This interrupt gets masked at and above its priority level.
- //
- for (Index = Priority;
- Index < INTEGRATORCP_INTERRUPT_PRIORITY_COUNT;
- Index += 1) {
- InterruptData->Masks[Index] |= BitMask;
- }
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqEnable, BitMask);
- } else {
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqDisable, BitMask);
- InterruptData->EnabledMask &= ~BitMask;
- //
- // Remove this interrupt from the masks.
- //
- for (Index = 0;
- Index < INTEGRATORCP_INTERRUPT_PRIORITY_COUNT;
- Index += 1) {
- InterruptData->Masks[Index] &= ~BitMask;
- }
- }
- Status = STATUS_SUCCESS;
- CpInterruptSetLineStateEnd:
- return Status;
- }
- VOID
- HlpCpInterruptMaskLine (
- PVOID Context,
- PINTERRUPT_LINE Line,
- BOOL Enable
- )
- /*++
- Routine Description:
- This routine masks or unmasks an interrupt line, leaving the rest of the
- line state intact.
- Arguments:
- Context - Supplies the pointer to the controller's context, provided by the
- hardware module upon initialization.
- Line - Supplies a pointer to the line to maek or unmask. This will always
- be a controller specified line.
- Enable - Supplies a boolean indicating whether to mask the interrupt,
- preventing interrupts from coming through (FALSE), or enable the line
- and allow interrupts to come through (TRUE).
- Return Value:
- None.
- --*/
- {
- ULONG BitMask;
- //
- // Calculate the bit to flip and flip it.
- //
- BitMask = 1 << Line->U.Local.Line;
- if (Enable != FALSE) {
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqEnable, BitMask);
- } else {
- WRITE_INTERRUPT_REGISTER(CpInterruptIrqDisable, BitMask);
- }
- return;
- }
- KSTATUS
- HlpCpInterruptDescribeLines (
- VOID
- )
- /*++
- Routine Description:
- This routine describes all interrupt lines to the system.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- INTERRUPT_LINES_DESCRIPTION Lines;
- KSTATUS Status;
- RtlZeroMemory(&Lines, sizeof(INTERRUPT_LINES_DESCRIPTION));
- Lines.Version = INTERRUPT_LINES_DESCRIPTION_VERSION;
- //
- // Describe the normal lines on the Integrator/CP.
- //
- Lines.Type = InterruptLinesStandardPin;
- Lines.Controller = 0;
- Lines.LineStart = 0;
- Lines.LineEnd = INTEGRATORCP_INTERRUPT_LINE_COUNT;
- Lines.Gsi = HlCpIntegratorTable->InterruptControllerGsiBase;
- Status = HlRegisterHardware(HardwareModuleInterruptLines, &Lines);
- if (!KSUCCESS(Status)) {
- goto CpInterruptDescribeLinesEnd;
- }
- //
- // Register the output lines.
- //
- Lines.Type = InterruptLinesOutput;
- Lines.OutputControllerIdentifier = INTERRUPT_CPU_IDENTIFIER;
- Lines.LineStart = INTERRUPT_ARM_MIN_CPU_LINE;
- Lines.LineEnd = INTERRUPT_ARM_MAX_CPU_LINE;
- Status = HlRegisterHardware(HardwareModuleInterruptLines, &Lines);
- if (!KSUCCESS(Status)) {
- goto CpInterruptDescribeLinesEnd;
- }
- CpInterruptDescribeLinesEnd:
- return Status;
- }
|