/**
|
*
|
* 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;
|
}
|