/** * * Copyright (C) 2018, Marvell International Ltd. and its affiliates. * * SPDX-License-Identifier: BSD-2-Clause-Patent * * Glossary - abbreviations used in Marvell SampleAtReset library implementation: * ICU - Interrupt Consolidation Unit * AP - Application Processor hardware block (Armada 7k8k incorporates AP806) * CP - South Bridge hardware blocks (Armada 7k8k incorporates CP110) * **/ #include "IcuLib.h" STATIC EFI_EVENT mEfiExitBootServicesEvent; STATIC CONST ICU_IRQ IrqMapNonSecure[] = { {27, 0, IcuIrqTypeLevel}, /* SD/MMC */ {39, 1, IcuIrqTypeLevel}, /* PPv2 Irq */ {40, 2, IcuIrqTypeLevel}, /* PPv2 Irq */ {41, 3, IcuIrqTypeLevel}, /* PPv2 Irq */ {43, 4, IcuIrqTypeLevel}, /* PPv2 Irq */ {44, 5, IcuIrqTypeLevel}, /* PPv2 Irq */ {45, 6, IcuIrqTypeLevel}, /* PPv2 Irq */ {47, 7, IcuIrqTypeLevel}, /* PPv2 Irq */ {48, 8, IcuIrqTypeLevel}, /* PPv2 Irq */ {49, 9, IcuIrqTypeLevel}, /* PPv2 Irq */ {51, 10, IcuIrqTypeLevel}, /* PPv2 Irq */ {52, 11, IcuIrqTypeLevel}, /* PPv2 Irq */ {53, 12, IcuIrqTypeLevel}, /* PPv2 Irq */ {55, 13, IcuIrqTypeLevel}, /* PPv2 Irq */ {56, 14, IcuIrqTypeLevel}, /* PPv2 Irq */ {57, 15, IcuIrqTypeLevel}, /* PPv2 Irq */ {88, 16, IcuIrqTypeLevel}, /* EIP-197 ring-0 */ {105, 17, IcuIrqTypeLevel}, /* USB3 Host-1 Irq */ {106, 18, IcuIrqTypeLevel}, /* USB3 Host-0 Irq */ {107, 19, IcuIrqTypeLevel}, /* SATA Host-1 Irq */ {109, 19, IcuIrqTypeLevel}, /* SATA Host-0 Irq */ {122, 20, IcuIrqTypeLevel}, /* UART 0 Irq */ {123, 21, IcuIrqTypeLevel}, /* UART 1 Irq */ {124, 22, IcuIrqTypeLevel}, /* UART 2 Irq */ {125, 23, IcuIrqTypeLevel}, /* UART 3 Irq */ {127, 24, IcuIrqTypeLevel}, /* GOP-3 Irq */ {128, 25, IcuIrqTypeLevel}, /* GOP-2 Irq */ {129, 26, IcuIrqTypeLevel}, /* GOP-0 Irq */ }; /* * SEI - System Error Interrupts * Note: SPI ID 0-20 are reserved for North-Bridge */ STATIC ICU_IRQ IrqMapSei[] = { {11, 21, IcuIrqTypeLevel}, /* SEI error CP-2-CP */ {15, 22, IcuIrqTypeLevel}, /* PIDI-64 SOC */ {16, 23, IcuIrqTypeLevel}, /* D2D error Irq */ {17, 24, IcuIrqTypeLevel}, /* D2D Irq */ {18, 25, IcuIrqTypeLevel}, /* NAND error */ {19, 26, IcuIrqTypeLevel}, /* PCIx4 error */ {20, 27, IcuIrqTypeLevel}, /* PCIx1_0 error */ {21, 28, IcuIrqTypeLevel}, /* PCIx1_1 error */ {25, 29, IcuIrqTypeLevel}, /* SDIO reg error */ {75, 30, IcuIrqTypeLevel}, /* IOB error */ {94, 31, IcuIrqTypeLevel}, /* EIP150 error */ {97, 32, IcuIrqTypeLevel}, /* XOR-1 system error */ {99, 33, IcuIrqTypeLevel}, /* XOR-0 system error */ {108, 34, IcuIrqTypeLevel}, /* SATA-1 error */ {110, 35, IcuIrqTypeLevel}, /* SATA-0 error */ {114, 36, IcuIrqTypeLevel}, /* TDM-MC error */ {116, 37, IcuIrqTypeLevel}, /* DFX server Irq */ {117, 38, IcuIrqTypeLevel}, /* Device bus error */ {147, 39, IcuIrqTypeLevel}, /* Audio error */ {171, 40, IcuIrqTypeLevel}, /* PIDI Sync error */ }; /* REI - RAM Error Interrupts */ STATIC CONST ICU_IRQ IrqMapRei[] = { {12, 0, IcuIrqTypeLevel}, /* REI error CP-2-CP */ {26, 1, IcuIrqTypeLevel}, /* SDIO memory error */ {87, 2, IcuIrqTypeLevel}, /* EIP-197 ECC error */ {93, 3, IcuIrqTypeEdge}, /* EIP-150 RAM error */ {96, 4, IcuIrqTypeLevel}, /* XOR-1 memory Irq */ {98, 5, IcuIrqTypeLevel}, /* XOR-0 memory Irq */ {100, 6, IcuIrqTypeEdge}, /* USB3 device tx parity */ {101, 7, IcuIrqTypeEdge}, /* USB3 device rq parity */ {103, 8, IcuIrqTypeEdge}, /* USB3H-1 RAM error */ {104, 9, IcuIrqTypeEdge}, /* USB3H-0 RAM error */ }; STATIC CONST ICU_CONFIG IcuInitialConfig = { .NonSecure = { IrqMapNonSecure, ARRAY_SIZE (IrqMapNonSecure) }, .Sei = { IrqMapSei, ARRAY_SIZE (IrqMapSei) }, .Rei = { IrqMapRei, ARRAY_SIZE (IrqMapRei) }, }; STATIC VOID IcuClearIrq ( IN UINTN IcuBase, IN UINTN Nr ) { MmioWrite32 (IcuBase + ICU_INT_CFG (Nr), 0); } STATIC VOID IcuSetIrq ( IN UINTN IcuBase, IN CONST ICU_IRQ *Irq, IN UINTN SpiBase, IN ICU_GROUP Group ) { UINT32 IcuInt; IcuInt = (Irq->SpiId + SpiBase) | (1 << ICU_INT_ENABLE_OFFSET); IcuInt |= Irq->IrqType << ICU_IS_EDGE_OFFSET; IcuInt |= Group << ICU_GROUP_OFFSET; MmioWrite32 (IcuBase + ICU_INT_CFG (Irq->IcuId), IcuInt); } STATIC VOID IcuConfigure ( IN UINTN CpIndex, IN MV_SOC_ICU_DESC *IcuDesc, IN CONST ICU_CONFIG *Config ) { UINTN IcuBase, Index, SpiOffset, SpiBase; CONST ICU_IRQ *Irq; ICU_MSI *Msi; /* Get ICU registers base address */ IcuBase = ICU_REG_BASE (CpIndex); /* Get the base of the GIC SPI ID in the MSI message */ SpiBase = IcuDesc->IcuSpiBase; /* Get multiple CP110 instances SPI ID shift */ SpiOffset = ICU_SPI_OFFSET (CpIndex); /* Get MSI addresses per interrupt group */ Msi = IcuDesc->IcuMsi; /* Set the address for SET_SPI and CLR_SPI registers in AP */ for (Index = 0; Index < IcuGroupMax; Index++, Msi++) { MmioWrite32 (IcuBase + ICU_SET_SPI_AL (Msi->Group), Msi->SetSpiAddr & 0xFFFFFFFF); MmioWrite32 (IcuBase + ICU_SET_SPI_AH (Msi->Group), Msi->SetSpiAddr >> 32); MmioWrite32 (IcuBase + ICU_CLR_SPI_AL (Msi->Group), Msi->ClrSpiAddr & 0xFFFFFFFF); MmioWrite32 (IcuBase + ICU_CLR_SPI_AH (Msi->Group), Msi->ClrSpiAddr >> 32); } /* Mask all ICU interrupts */ for (Index = 0; Index < MAX_ICU_IRQS; Index++) { IcuClearIrq (IcuBase, Index); } /* Configure the ICU interrupt lines */ Irq = Config->NonSecure.Map; for (Index = 0; Index < Config->NonSecure.Size; Index++, Irq++) { IcuSetIrq (IcuBase, Irq, SpiBase + SpiOffset, IcuGroupNsr); } Irq = Config->Sei.Map; for (Index = 0; Index < Config->Sei.Size; Index++, Irq++) { IcuSetIrq (IcuBase, Irq, SpiBase, IcuGroupSei); } Irq = Config->Rei.Map; for (Index = 0; Index < Config->Rei.Size; Index++, Irq++) { IcuSetIrq (IcuBase, Irq, SpiBase, IcuGroupRei); } } STATIC VOID IcuClearGicSpi ( IN UINTN CpIndex, IN MV_SOC_ICU_DESC *IcuDesc ) { CONST ICU_CONFIG *Config; UINTN Index, SpiOffset, SpiBase; CONST ICU_IRQ *Irq; ICU_MSI *Msi; Config = &IcuInitialConfig; /* Get the base of the GIC SPI ID in the MSI message */ SpiBase = IcuDesc->IcuSpiBase; /* Get multiple CP110 instances SPI ID shift */ SpiOffset = ICU_SPI_OFFSET (CpIndex); /* Get MSI addresses per interrupt group */ Msi = IcuDesc->IcuMsi; /* Clear ICU-generated GIC SPI interrupts */ Irq = Config->NonSecure.Map; for (Index = 0; Index < Config->NonSecure.Size; Index++, Irq++) { MmioWrite32 (Msi->ClrSpiAddr, Irq->SpiId + SpiBase + SpiOffset); } } VOID EFIAPI IcuCleanUp ( IN EFI_EVENT Event, IN VOID *Context ) { MV_SOC_ICU_DESC *IcuDesc; UINTN CpCount, CpIndex; IcuDesc = Context; CpCount = FixedPcdGet8 (PcdMaxCpCount); for (CpIndex = 0; CpIndex < CpCount; CpIndex++) { IcuClearGicSpi (CpIndex, IcuDesc); } } EFI_STATUS EFIAPI ArmadaIcuInitialize ( ) { MV_SOC_ICU_DESC *IcuDesc; UINTN CpCount, CpIndex; EFI_STATUS Status; CpCount = FixedPcdGet8 (PcdMaxCpCount); ASSERT (CpCount <= ICU_MAX_SUPPORTED_UNITS); /* Obtain SoC description of the ICU */ Status = ArmadaSoCDescIcuGet (&IcuDesc); if (EFI_ERROR (Status)) { return Status; } /* Configure default ICU to GIC interrupt mapping for each CP110 */ for (CpIndex = 0; CpIndex < CpCount; CpIndex++) { IcuConfigure (CpIndex, IcuDesc, &IcuInitialConfig); } /* * In order to be immune to the OS capability of clearing ICU-generated * GIC interrupts, register ExitBootServices event, that will * make sure they remain disabled during OS boot. */ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, IcuCleanUp, IcuDesc, &mEfiExitBootServicesEvent); return Status; }