sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\nkernsmp\arm\ncirq.cpp sl@0: // sl@0: // sl@0: sl@0: /** sl@0: @file sl@0: @internalTechnology sl@0: */ sl@0: sl@0: #include "nk_priv.h" sl@0: #include "nk_plat.h" sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: #ifdef _DEBUG sl@0: #define DMEMDUMP(base,size) DbgMemDump((TLinAddr)base,size) sl@0: void DbgMemDump(TLinAddr aBase, TInt aSize) sl@0: { sl@0: TInt off; sl@0: const TUint8* p=(const TUint8*)aBase; sl@0: NKern::Lock(); sl@0: for (off=0; off>5] = mask; sl@0: arm_dsb(); sl@0: } sl@0: sl@0: void ArmGic::Disable(TInt aIndex) sl@0: { sl@0: TUint32 mask = 1u << (aIndex&31); sl@0: GIC_DIST.iEnableClear[aIndex>>5] = mask; sl@0: arm_dsb(); sl@0: } sl@0: sl@0: TBool ArmGic::IsEnabled(TInt aIndex) sl@0: { sl@0: TUint32 mask = 1u << (aIndex&31); sl@0: return GIC_DIST.iEnableSet[aIndex>>5] & mask; sl@0: } sl@0: sl@0: void ArmGic::SetPending(TInt aIndex) sl@0: { sl@0: TUint32 mask = 1u << (aIndex&31); sl@0: GIC_DIST.iPendingSet[aIndex>>5] = mask; sl@0: arm_dsb(); sl@0: } sl@0: sl@0: void ArmGic::ClearPending(TInt aIndex) sl@0: { sl@0: TUint32 mask = 1u << (aIndex&31); sl@0: GIC_DIST.iPendingClear[aIndex>>5] = mask; sl@0: arm_dsb(); sl@0: } sl@0: sl@0: TBool ArmGic::IsPending(TInt aIndex) sl@0: { sl@0: TUint32 mask = 1u << (aIndex&31); sl@0: return GIC_DIST.iPendingSet[aIndex>>5] & mask; sl@0: } sl@0: sl@0: TBool ArmGic::IsActive(TInt aIndex) sl@0: { sl@0: TUint32 mask = 1u << (aIndex&31); sl@0: return GIC_DIST.iActive[aIndex>>5] & mask; sl@0: } sl@0: sl@0: TUint32 ArmGic::Dest(TInt aIndex) sl@0: { sl@0: TUint32 reg = GIC_DIST.iTarget[aIndex>>2]; sl@0: reg >>= ((aIndex&3)<<3); sl@0: reg &= 0xff; sl@0: return reg; sl@0: } sl@0: sl@0: TUint32 ArmGic::ModifyDest(TInt aIndex, TUint32 aClear, TUint32 aSet) sl@0: { sl@0: aClear &= 0xff; sl@0: aSet &= 0xff; sl@0: TInt shift = (aIndex&3)<<3; sl@0: aClear <<= shift; sl@0: aSet <<= shift; sl@0: volatile TUint32& reg = GIC_DIST.iTarget[aIndex>>2]; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(ArmGicLock); sl@0: TUint32 old = reg; sl@0: reg = (old &~ aClear) | aSet; sl@0: arm_dsb(); sl@0: __SPIN_UNLOCK_IRQRESTORE(ArmGicLock, irq); sl@0: old >>= shift; sl@0: return old & 0xff; sl@0: } sl@0: sl@0: TUint32 ArmGic::Config(TInt aIndex) sl@0: { sl@0: TInt shift = (aIndex&15)<<1; sl@0: TUint32 x = GIC_DIST.iConfig[aIndex>>4]; sl@0: x >>= shift; sl@0: return x & 3; sl@0: } sl@0: sl@0: TUint32 ArmGic::ModifyConfig(TInt aIndex, TUint32 aClear, TUint32 aSet) sl@0: { sl@0: aClear &= 3; sl@0: aSet &= 3; sl@0: TInt shift = (aIndex&15)<<1; sl@0: aClear <<= shift; sl@0: aSet <<= shift; sl@0: volatile TUint32& reg = GIC_DIST.iConfig[aIndex>>4]; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(ArmGicLock); sl@0: TUint32 old = reg; sl@0: reg = (old &~ aClear) | aSet; sl@0: arm_dsb(); sl@0: __SPIN_UNLOCK_IRQRESTORE(ArmGicLock, irq); sl@0: old >>= shift; sl@0: return old & 3; sl@0: } sl@0: sl@0: TBool ArmGic::SetNonSecure(TInt aIndex, TBool aNonSecure) sl@0: { sl@0: TUint32 mask = 1u << (aIndex & 31); sl@0: volatile TUint32& reg = GIC_DIST.iIntSec[aIndex>>5]; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(ArmGicLock); sl@0: TUint32 old = reg; sl@0: reg = aNonSecure ? (old | mask) : (old &~ mask); sl@0: arm_dsb(); sl@0: __SPIN_UNLOCK_IRQRESTORE(ArmGicLock, irq); sl@0: return old & mask; sl@0: } sl@0: sl@0: TUint32 ArmGic::Priority(TInt aIndex) sl@0: { sl@0: TInt shift = (aIndex&3)<<3; sl@0: TUint32 x = GIC_DIST.iPriority[aIndex>>2]; sl@0: x >>= shift; sl@0: return x & 0xff; sl@0: } sl@0: sl@0: TUint32 ArmGic::SetPriority(TInt aIndex, TUint32 aPri) sl@0: { sl@0: aPri &= 0xff; sl@0: TInt shift = (aIndex&3)<<3; sl@0: TUint32 clear = 0xffu << shift; sl@0: aPri <<= shift; sl@0: volatile TUint32& reg = GIC_DIST.iPriority[aIndex>>2]; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(ArmGicLock); sl@0: TUint32 old = reg; sl@0: reg = (old &~ clear) | aPri; sl@0: arm_dsb(); sl@0: __SPIN_UNLOCK_IRQRESTORE(ArmGicLock, irq); sl@0: old >>= shift; sl@0: return old & 0xff; sl@0: } sl@0: sl@0: void ArmGic::Dump() sl@0: { sl@0: #ifdef KBOOT sl@0: __KTRACE_OPT(KBOOT,DEBUGPRINT("GIC iCtrl=%08x iType=%08x", GIC_DIST.iCtrl, GIC_DIST.iType)); sl@0: TInt n = ArmGic::NumLines; sl@0: TInt i; sl@0: for (i=0; iiEoiFn) sl@0: (*iX->iEoiFn)(this); sl@0: else sl@0: { sl@0: GIC_CPU_IFC.iEoi = iVector; sl@0: sl@0: #if defined(SMP_CRAZY_INTERRUPTS) && !defined(__STANDALONE_NANOKERNEL__) sl@0: // change the target CPU for the next Interrupt sl@0: if ((TInt)TheSuperPage().KernelConfigFlags() & EKernelConfigSMPCrazyInterrupts) sl@0: { sl@0: TInt cpu = NKern::CurrentCpu() + 1; sl@0: if(cpu >= NKern::NumberOfCpus()) sl@0: cpu = 0; sl@0: ArmGic::ModifyDest(iVector, 0xffu, 1u << cpu); sl@0: } sl@0: else sl@0: arm_dsb(); sl@0: sl@0: #else sl@0: arm_dsb(); sl@0: #endif sl@0: } sl@0: } sl@0: sl@0: void NIrq::HwEnable() sl@0: { sl@0: if (iX && iX->iEnableFn) sl@0: (*iX->iEnableFn)(this); sl@0: else sl@0: { sl@0: ArmGic::Enable(iVector); sl@0: } sl@0: } sl@0: sl@0: void NIrq::HwDisable() sl@0: { sl@0: if (iX && iX->iDisableFn) sl@0: (*iX->iDisableFn)(this); sl@0: else sl@0: { sl@0: ArmGic::Disable(iVector); sl@0: } sl@0: } sl@0: sl@0: void NIrq::HwSetCpu(TInt aCpu) sl@0: { sl@0: if (iX && iX->iSetCpuFn) sl@0: (*iX->iSetCpuFn)(this, 1u<iSetCpuFn) sl@0: (*iX->iSetCpuFn)(this, aMask); sl@0: else sl@0: { sl@0: ArmGic::ModifyDest(iVector, 0xffu, aMask); sl@0: } sl@0: } sl@0: sl@0: void NIrq::HwInit() sl@0: { sl@0: if (iX && iX->iInitFn) sl@0: (*iX->iInitFn)(this); sl@0: else sl@0: { sl@0: __KTRACE_OPT(KBOOT,DEBUGPRINT("NIrq %02x HwInit", iIndex)); sl@0: TUint32 clear = E_GicDistICfgEdge; sl@0: TUint32 set = 0; sl@0: if (!(iStaticFlags & ELevel)) sl@0: set = E_GicDistICfgEdge; sl@0: ArmGic::ModifyConfig(iVector, clear, set); sl@0: } sl@0: } sl@0: sl@0: TBool NIrq::HwPending() sl@0: { sl@0: if (iX && iX->iPendingFn) sl@0: return (*iX->iPendingFn)(this); sl@0: return ArmGic::IsPending(iVector) || ArmGic::IsActive(iVector); sl@0: } sl@0: sl@0: void NIrq::HwWaitCpus() sl@0: { sl@0: if (iX && iX->iWaitFn) sl@0: (*iX->iWaitFn)(this); sl@0: } sl@0: sl@0: void NIrq::HwInit0() sl@0: { sl@0: __KTRACE_OPT(KBOOT, DEBUGPRINT("NIrq::HwInit0")); sl@0: sl@0: // Need to set up addresses of GIC_DIST, GIC_CPU_IFC, SCU and LOCAL_TIMER sl@0: sl@0: GicDistributor& D = GIC_DIST; sl@0: GicCpuIfc& C = GIC_CPU_IFC; sl@0: D.iCtrl = 0; sl@0: C.iCtrl = 0; sl@0: arm_dsb(); sl@0: TUint32 type = D.iType; sl@0: __KTRACE_OPT(KBOOT, DEBUGPRINT("GIC iType = %08x", type)); sl@0: ArmGic::LSPI = (type & E_GicDistType_LSPIMask) >> E_GicDistType_LSPIShift; sl@0: ArmGic::Domains = (type & E_GicDistType_Domains) ? 2 : 1; sl@0: ArmGic::NumCpus = ((type & E_GicDistType_CPUNMask) >> E_GicDistType_CPUNShift) + 1; sl@0: ArmGic::NumLines = ((type & E_GicDistType_ITMask) + 1) << 5; sl@0: __KTRACE_OPT(KBOOT, DEBUGPRINT("GIC LSPI=%d Domains=%d NumCpus=%d NumLines=%d", sl@0: ArmGic::LSPI, ArmGic::Domains, ArmGic::NumCpus, ArmGic::NumLines)); sl@0: TInt i; sl@0: for (i=0; i<32; ++i) sl@0: D.iEnableClear[i] = 0xffffffffu; // disable all interrupts sl@0: arm_dsb(); sl@0: for (i=0; i<32; ++i) sl@0: D.iPendingClear[i] = 0xffffffffu; // clear any pending interrupts sl@0: arm_dsb(); sl@0: D.iPriority[0] = 0xffffffffu; sl@0: ArmGic::PriMask = D.iPriority[0] & 0xffu; sl@0: ArmGic::PriSpc = (~ArmGic::PriMask + 1) & 0xffu; sl@0: ArmGic::MinPri = ArmGic::PriMask - ArmGic::PriSpc; sl@0: __KTRACE_OPT(KBOOT, DEBUGPRINT("PriMask=%02x PriSpc=%02x MinPri=%02x", ArmGic::PriMask, ArmGic::PriSpc, ArmGic::MinPri)); sl@0: TUint32 x = ArmGic::MinPri; sl@0: x |= (x<<8); sl@0: x |= (x<<16); sl@0: for (i=0; i<256; ++i) sl@0: D.iPriority[i] = x; // set all interrupts to minimum active priority sl@0: x = 0x01010101u; sl@0: for (i=0; i<256; ++i) sl@0: D.iTarget[i] = x; // set all interrupts to target this CPU sl@0: x = 0xAAAAAAAAu; // config value for SW interrupts (rising edge, N-N) sl@0: D.iConfig[0] = x; // set config for 0-15 sl@0: x = 0x28000000u; // 31=0b00, 30=29=0b10 sl@0: D.iConfig[1] = x; // set config for 16-31 sl@0: x = 0xAAAAAAAAu; // config value for SW interrupts (rising edge, N-N) sl@0: for (i=2; i<64; ++i) sl@0: D.iConfig[i] = x; // set default value for other interrupts sl@0: arm_dsb(); sl@0: ArmGic::Dump(); sl@0: } sl@0: sl@0: void NIrq::HwInit1() sl@0: { sl@0: __KTRACE_OPT(KBOOT, DEBUGPRINT("NIrq::HwInit1")); sl@0: sl@0: // elevate priority of CRASH_IPI to highest level sl@0: ArmGic::SetPriority(CRASH_IPI_VECTOR, 0); sl@0: sl@0: GicDistributor& D = GIC_DIST; sl@0: GicCpuIfc& C = GIC_CPU_IFC; sl@0: C.iCtrl = 0; sl@0: C.iPriMask = ArmGic::PriMask; // unmask all interrupts sl@0: C.iBinaryPoint = 0; sl@0: arm_dsb(); sl@0: C.iCtrl = E_GicDistCtrl_Enable; // enable this CPU's interrupt controller interface sl@0: arm_dsb(); sl@0: D.iCtrl = E_GicDistCtrl_Enable; // enable the global interrupt distributor sl@0: arm_dsb(); sl@0: sl@0: // Enable timeslice timer interrupt sl@0: ArmLocalTimer& T = LOCAL_TIMER; sl@0: T.iTimerCtrl = 0; sl@0: T.iTimerIntStatus = E_ArmTmrIntStatus_Event; sl@0: ArmGic::ClearPending(TIMESLICE_VECTOR); sl@0: arm_dsb(); sl@0: ArmGic::Enable(TIMESLICE_VECTOR); sl@0: arm_dsb(); sl@0: T.iTimerLoad = KMaxTUint32; // timer wraps to 0xffffffff after reaching 0 sl@0: arm_dsb(); sl@0: T.iTimerCount = (TUint32)KMaxTInt32; // timer starts at 0x7fffffff (initial thread doesn't timeslice) sl@0: arm_dsb(); sl@0: sl@0: ArmGic::DumpCpuIfc(); sl@0: } sl@0: sl@0: void NIrq::HwInit2AP() sl@0: { sl@0: __KTRACE_OPT(KBOOT, DEBUGPRINT("NIrq::HwInit2AP")); sl@0: sl@0: // Must set up interrupts 0-31 separately for each CPU sl@0: GicDistributor& D = GIC_DIST; sl@0: TInt i; sl@0: TUint32 x = ArmGic::MinPri; sl@0: x |= (x<<8); sl@0: x |= (x<<16); sl@0: for (i=0; i<32; ++i) sl@0: D.iPriority[i] = x; // set all interrupts to minimum active priority sl@0: x = 0xAAAAAAAAu; // config value for SW interrupts (rising edge, N-N) sl@0: D.iConfig[0] = x; // set config for 0-15 sl@0: x = 0x28000000u; // 31=0b00, 30=29=0b10 sl@0: D.iConfig[1] = x; // set config for 16-31 sl@0: arm_dsb(); sl@0: ArmGic::Dump(); sl@0: sl@0: // elevate priority of CRASH_IPI to highest level sl@0: ArmGic::SetPriority(CRASH_IPI_VECTOR, 0); sl@0: sl@0: GicCpuIfc& C = GIC_CPU_IFC; sl@0: C.iCtrl = 0; sl@0: C.iPriMask = ArmGic::PriMask; // unmask all interrupts sl@0: C.iBinaryPoint = 0; sl@0: arm_dsb(); sl@0: C.iCtrl = E_GicDistCtrl_Enable; sl@0: arm_dsb(); sl@0: sl@0: // Enable timeslice timer interrupt sl@0: ArmLocalTimer& T = LOCAL_TIMER; sl@0: T.iTimerCtrl = 0; sl@0: T.iTimerIntStatus = E_ArmTmrIntStatus_Event; sl@0: ArmGic::ClearPending(TIMESLICE_VECTOR); sl@0: arm_dsb(); sl@0: ArmGic::Enable(TIMESLICE_VECTOR); sl@0: arm_dsb(); sl@0: T.iTimerLoad = KMaxTUint32; // timer wraps to 0xffffffff after reaching 0 sl@0: arm_dsb(); sl@0: T.iTimerCount = (TUint32)KMaxTInt32; // timer starts at 0x7fffffff (initial thread doesn't timeslice) sl@0: arm_dsb(); sl@0: sl@0: ArmGic::DumpCpuIfc(); sl@0: } sl@0: