alder_lake_bios/Intel/AlderLake/AlderLakeChipsetPkg/8259InterruptControllerPei/8259.c

480 lines
14 KiB
C

/** @file
8259 PEIM initialize.
;******************************************************************************
;* Copyright (c) 2015, Insyde Software Corporation. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include <8259.h>
//
// Global Value
//
UINT8 mMasterBase = 0xff;
UINT8 mSlaveBase = 0xff;
UINT16 mProtectedModeMask = 0xffff;
UINT16 mProtectedModeEdgeLevel = 0x0000;
//
// Global for the Legacy 8259 PPI that is produced by this module
//
H2O_LEGACY_8259_PPI mInterrupt8259 = {
Interrupt8259SetVectorBase,
Interrupt8259GetMask,
Interrupt8259SetMask,
Interrupt8259SetMode,
Interrupt8259GetVector,
Interrupt8259EnableIrq,
Interrupt8259DisableIrq,
Interrupt8259GetInterruptLine,
Interrupt8259EndOfInterrupt
};
EFI_PEI_PPI_DESCRIPTOR mPpiList = {
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gH2OLegacy8259PpiGuid,
&mInterrupt8259
};
//
// Worker Functions
//
/**
Write to mask and edge/level triggered registers of master and slave PICs.
@param[in] Mask low byte for master PIC mask register,
high byte for slave PIC mask register.
@param[in] EdgeLevel low byte for master PIC edge/level triggered register,
high byte for slave PIC edge/level triggered register.
**/
VOID
Interrupt8259WriteMask (
IN UINT16 Mask,
IN UINT16 EdgeLevel
)
{
IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, (UINT8) Mask);
IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, (UINT8) (Mask >> 8));
IoWrite8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_MASTER, (UINT8) EdgeLevel);
IoWrite8 (LEGACY_8259_EDGE_LEVEL_TRIGGERED_REGISTER_SLAVE, (UINT8) (EdgeLevel >> 8));
}
//
// Legacy 8259 PPI Interface Functions
//
/**
Get the 8259 interrupt masks for Irq0 - Irq15. A different mask exists for
the legacy mode mask and the protected mode mask. The base address for the 8259
is different for legacy and protected mode, so two masks are required.
@param[in] This The PPI instance pointer.
@param[in] MasterBase The base vector for the Master PIC in the 8259 controller.
@param[in] SlaveBase The base vector for the Slave PIC in the 8259 controller.
@retval EFI_SUCCESS The new bases were programmed.
@retval EFI_DEVICE_ERROR A device error occured programming the vector bases.
**/
EFI_STATUS
EFIAPI
Interrupt8259SetVectorBase (
IN H2O_LEGACY_8259_PPI *This,
IN UINT8 MasterBase,
IN UINT8 SlaveBase
)
{
UINT8 Mask;
//
// Set vector base for slave PIC
//
if (SlaveBase != mSlaveBase) {
mSlaveBase = SlaveBase;
//
// Initialization sequence is needed for setting vector base.
//
//
// Preserve interrtup mask register before initialization sequence
// because it will be cleared during intialization
//
Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_SLAVE);
//
// ICW1: cascade mode, ICW4 write required
//
IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, 0x11);
//
// ICW2: new vector base (must be multiple of 8)
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, mSlaveBase);
//
// ICW3: slave indentification code must be 2
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x02);
//
// ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0x01);
//
// Restore interrupt mask register
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, Mask);
}
//
// Set vector base for master PIC
//
if (MasterBase != mMasterBase) {
mMasterBase = MasterBase;
//
// Initialization sequence is needed for setting vector base.
//
//
// Preserve interrtup mask register before initialization sequence
// because it will be cleared during intialization
//
Mask = IoRead8 (LEGACY_8259_MASK_REGISTER_MASTER);
//
// ICW1: cascade mode, ICW4 write required
//
IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, 0x11);
//
// ICW2: new vector base (must be multiple of 8)
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, mMasterBase);
//
// ICW3: slave PIC is cascaded on IRQ2
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x04);
//
// ICW4: fully nested mode, non-buffered mode, normal EOI, IA processor
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0x01);
//
// Restore interrupt mask register
//
IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, Mask);
}
IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
return EFI_SUCCESS;
}
/**
Get the 8259 interrupt masks for Irq0 - Irq15. A different mask exists for
the legacy mode mask and the protected mode mask. The base address for the 8259
is different for legacy and protected mode, so two masks are required.
@param[in] This The PPI instance pointer.
@param[out] LegacyMask Bit 0 is Irq0 - Bit 15 is Irq15.
@param[out] LegacyEdgeLevel Bit 0 is Irq0 - Bit 15 is Irq15.
@param[out] ProtectedMask Bit 0 is Irq0 - Bit 15 is Irq15.
@param[out] ProtectedEdgeLevel Bit 0 is Irq0 - Bit 15 is Irq15.
@retval EFI_SUCCESS 8259 status returned.
@retval EFI_DEVICE_ERROR Error reading 8259.
**/
EFI_STATUS
EFIAPI
Interrupt8259GetMask (
IN H2O_LEGACY_8259_PPI *This,
OUT UINT16 *LegacyMask OPTIONAL,
OUT UINT16 *LegacyEdgeLevel OPTIONAL,
OUT UINT16 *ProtectedMask OPTIONAL,
OUT UINT16 *ProtectedEdgeLevel OPTIONAL
)
{
return EFI_UNSUPPORTED;
}
/**
Set the 8259 interrupt masks for Irq0 - Irq15. A different mask exists for
the legacy mode mask and the protected mode mask. The base address for the 8259
is different for legacy and protected mode, so two masks are required.
Also set the edge/level masks.
@param[in] This The PPI instance pointer.
@param[in] LegacyMask Bit 0 is Irq0 - Bit 15 is Irq15.
@param[in] LegacyEdgeLevel Bit 0 is Irq0 - Bit 15 is Irq15.
@param[in] ProtectedMask Bit 0 is Irq0 - Bit 15 is Irq15.
@param[in] ProtectedEdgeLevel Bit 0 is Irq0 - Bit 15 is Irq15.
@retval EFI_SUCCESS 8259 status returned.
@retval EFI_DEVICE_ERROR Error writing 8259.
**/
EFI_STATUS
EFIAPI
Interrupt8259SetMask (
IN H2O_LEGACY_8259_PPI *This,
IN UINT16 *LegacyMask OPTIONAL,
IN UINT16 *LegacyEdgeLevel OPTIONAL,
IN UINT16 *ProtectedMask OPTIONAL,
IN UINT16 *ProtectedEdgeLevel OPTIONAL
)
{
return EFI_UNSUPPORTED;
}
/**
Set the 8259 mode of operation. The base address for the 8259 is different for
legacy and protected mode. The legacy mode requires the master 8259 to have a
master base of 0x08 and the slave base of 0x70. The protected mode base locations
are not defined. Interrupts must be masked by the caller before this function
is called. The interrupt mask from the current mode is saved. The interrupt
mask for the new mode is Mask, or if Mask does not exist the previously saved
mask is used.
@param[in] This The PPI instance pointer.
@param[in] Mode The mode of operation. i.e. the real mode or protected mode.
@param[in] Mask Optional interupt mask for the new mode.
@param[in] EdgeLevel Optional trigger mask for the new mode.
@retval EFI_SUCCESS 8259 programmed.
@retval EFI_DEVICE_ERROR Error writing to 8259.
**/
EFI_STATUS
EFIAPI
Interrupt8259SetMode (
IN H2O_LEGACY_8259_PPI *This,
IN EFI_8259_MODE Mode,
IN UINT16 *Mask OPTIONAL,
IN UINT16 *EdgeLevel OPTIONAL
)
{
return EFI_UNSUPPORTED;
}
/**
Convert from IRQ to processor interrupt vector number.
@param[in] This The PPI instance pointer.
@param[in] Irq 8259 IRQ0 - IRQ15.
@param[out] Vector The processor vector number that matches an Irq.
@retval EFI_SUCCESS The Vector matching Irq is returned.
@retval EFI_INVALID_PARAMETER The Irq not valid.
**/
EFI_STATUS
EFIAPI
Interrupt8259GetVector (
IN H2O_LEGACY_8259_PPI *This,
IN EFI_8259_IRQ Irq,
OUT UINT8 *Vector
)
{
if ((UINT32)Irq > Efi8259Irq15) {
return EFI_INVALID_PARAMETER;
}
if (Irq <= Efi8259Irq7) {
*Vector = (UINT8) (mMasterBase + Irq);
} else {
*Vector = (UINT8) (mSlaveBase + (Irq - Efi8259Irq8));
}
return EFI_SUCCESS;
}
/**
Enables the specified IRQ.
@param[in] This The PPI instance pointer.
@param[in] Irq IRQ0-IRQ15.
@param[in] LevelTriggered 0 = Edge triggered; 1 = Level triggered.
@retval EFI_SUCCESS The Irq was enabled on the 8259 PIC.
@retval EFI_INVALID_PARAMETER The Irq is not valid.
**/
EFI_STATUS
EFIAPI
Interrupt8259EnableIrq (
IN H2O_LEGACY_8259_PPI *This,
IN EFI_8259_IRQ Irq,
IN BOOLEAN LevelTriggered
)
{
if ((UINT32)Irq > Efi8259Irq15) {
return EFI_INVALID_PARAMETER;
}
mProtectedModeMask = (UINT16) (mProtectedModeMask & ~(1 << Irq));
if (LevelTriggered) {
mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel | (1 << Irq));
} else {
mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
}
Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
return EFI_SUCCESS;
}
/**
Disables the specified IRQ.
@param[in] This The PPI instance pointer.
@param[in] Irq IRQ0-IRQ15.
@retval EFI_SUCCESS The Irq was disabled on the 8259 PIC.
@retval EFI_INVALID_PARAMETER The Irq is not valid.
**/
EFI_STATUS
EFIAPI
Interrupt8259DisableIrq (
IN H2O_LEGACY_8259_PPI *This,
IN EFI_8259_IRQ Irq
)
{
if ((UINT32)Irq > Efi8259Irq15) {
return EFI_INVALID_PARAMETER;
}
mProtectedModeMask = (UINT16) (mProtectedModeMask | (1 << Irq));
mProtectedModeEdgeLevel = (UINT16) (mProtectedModeEdgeLevel & ~(1 << Irq));
Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
return EFI_SUCCESS;
}
/**
PciHandle represents a PCI config space of a PCI function. Vector
represents Interrupt Pin (from PCI config space) and it is the data
that is programmed into the Interrupt Line (from the PCI config space)
register.
@param[in] This The PPI instance pointer.
@param[in] PciHandle The PCI function to return the vector for.
@param[out] Vector The vector for the function it matches.
@retval EFI_SUCCESS A valid Vector was returned.
@retval EFI_INVALID_PARAMETER PciHandle not valid.
**/
EFI_STATUS
EFIAPI
Interrupt8259GetInterruptLine (
IN H2O_LEGACY_8259_PPI *This,
IN EFI_HANDLE PciHandle,
OUT UINT8 *Vector
)
{
return EFI_UNSUPPORTED;
}
/**
Issues the End of Interrupt (EOI) commands to PICs.
@param[in] This The PPI instance pointer.
@param[in] Irq The interrupt for which to issue the EOI command.
@retval EFI_SUCCESS The EOI command was issued.
@retval EFI_INVALID_PARAMETER The Irq is not valid.
**/
EFI_STATUS
EFIAPI
Interrupt8259EndOfInterrupt (
IN H2O_LEGACY_8259_PPI *This,
IN EFI_8259_IRQ Irq
)
{
if ((UINT32)Irq > Efi8259Irq15) {
return EFI_INVALID_PARAMETER;
}
if (Irq >= Efi8259Irq8) {
IoWrite8 (LEGACY_8259_CONTROL_REGISTER_SLAVE, LEGACY_8259_EOI);
}
IoWrite8 (LEGACY_8259_CONTROL_REGISTER_MASTER, LEGACY_8259_EOI);
return EFI_SUCCESS;
}
/**
Driver Entry point.
@param[in] FileHandle Handle of the file being invoked.
@param[in] PeiServices Describes the list of possible PEI Services.
@retval EFI_SUCCESS One or more of the drivers returned a success code.
@retval EFI_UNSUPPORTED UnSupport in S3 Mode.
@retval Other Error installing Legacy 8259 Protocol.
**/
EFI_STATUS
EFIAPI
Legacy8259PeimEntry (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
EFI_8259_IRQ Irq;
EFI_BOOT_MODE BootMode;
//
// Check bootmode, if S3 don't support
//
Status = (**PeiServices).GetBootMode (PeiServices, &BootMode);
if (BootMode == BOOT_ON_S3_RESUME) {
return EFI_UNSUPPORTED;
}
//
// Clear all pending interrupt
//
for (Irq = Efi8259Irq0; Irq <= Efi8259Irq15; Irq++) {
Interrupt8259EndOfInterrupt (&mInterrupt8259, Irq);
}
//
// Set the 8259 Master base to 0x68 and the 8259 Slave base to 0x70
//
Status = Interrupt8259SetVectorBase (&mInterrupt8259, PcdGet8(PcdH2OInterruptMasterBaseVector), PcdGet8(PcdH2OInterruptSlaveBaseVector));
//
// Set all 8259 interrupts to edge triggered and disabled
//
Interrupt8259WriteMask (mProtectedModeMask, mProtectedModeEdgeLevel);
//
// Install 8259 PPI
//
Status = (**PeiServices).InstallPpi (PeiServices, &mPpiList);
return Status;
}