/**************************************************************************//** * @file sys.c * @brief N9H30 SYS driver source file * * @note * SPDX-License-Identifier: Apache-2.0 * Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ #include "N9H30.h" #include "nu_sys.h" /// @cond HIDDEN_SYMBOLS #define SYS_MIN_INT_SOURCE 1 #define SYS_MAX_INT_SOURCE 62 #define SYS_NUM_OF_AICREG 16 /* Global variables */ BOOL volatile _sys_bIsAICInitial = FALSE; /* declaration the function prototype */ extern void SYS_Interrupt_Shell(void); /* Interrupt Handler Table */ //typedef void (*sys_pvFunPtr)(); /* function pointer */ sys_pvFunPtr sysIrqHandlerTable[] = { 0, /* 0 */ SYS_Interrupt_Shell, /* 1 */ SYS_Interrupt_Shell, /* 2 */ SYS_Interrupt_Shell, /* 3 */ SYS_Interrupt_Shell, /* 4 */ SYS_Interrupt_Shell, /* 5 */ SYS_Interrupt_Shell, /* 6 */ SYS_Interrupt_Shell, /* 7 */ SYS_Interrupt_Shell, /* 8 */ SYS_Interrupt_Shell, /* 9 */ SYS_Interrupt_Shell, /* 10 */ SYS_Interrupt_Shell, /* 11 */ SYS_Interrupt_Shell, /* 12 */ SYS_Interrupt_Shell, /* 13 */ SYS_Interrupt_Shell, /* 14 */ SYS_Interrupt_Shell, /* 15 */ SYS_Interrupt_Shell, /* 16 */ SYS_Interrupt_Shell, /* 17 */ SYS_Interrupt_Shell, /* 18 */ SYS_Interrupt_Shell, /* 19 */ SYS_Interrupt_Shell, /* 20 */ SYS_Interrupt_Shell, /* 21 */ SYS_Interrupt_Shell, /* 22 */ SYS_Interrupt_Shell, /* 23 */ SYS_Interrupt_Shell, /* 24 */ SYS_Interrupt_Shell, /* 25 */ SYS_Interrupt_Shell, /* 26 */ SYS_Interrupt_Shell, /* 27 */ SYS_Interrupt_Shell, /* 28 */ SYS_Interrupt_Shell, /* 29 */ SYS_Interrupt_Shell, /* 30 */ SYS_Interrupt_Shell, /* 31 */ SYS_Interrupt_Shell, /* 32 */ SYS_Interrupt_Shell, /* 33 */ SYS_Interrupt_Shell, /* 34 */ SYS_Interrupt_Shell, /* 35 */ SYS_Interrupt_Shell, /* 36 */ SYS_Interrupt_Shell, /* 37 */ SYS_Interrupt_Shell, /* 38 */ SYS_Interrupt_Shell, /* 39 */ SYS_Interrupt_Shell, /* 40 */ SYS_Interrupt_Shell, /* 41 */ SYS_Interrupt_Shell, /* 42 */ SYS_Interrupt_Shell, /* 43 */ SYS_Interrupt_Shell, /* 44 */ SYS_Interrupt_Shell, /* 45 */ SYS_Interrupt_Shell, /* 46 */ SYS_Interrupt_Shell, /* 47 */ SYS_Interrupt_Shell, /* 48 */ SYS_Interrupt_Shell, /* 49 */ SYS_Interrupt_Shell, /* 50 */ SYS_Interrupt_Shell, /* 51 */ SYS_Interrupt_Shell, /* 52 */ SYS_Interrupt_Shell, /* 53 */ SYS_Interrupt_Shell, /* 54 */ SYS_Interrupt_Shell, /* 55 */ SYS_Interrupt_Shell, /* 56 */ SYS_Interrupt_Shell, /* 57 */ SYS_Interrupt_Shell, /* 58 */ SYS_Interrupt_Shell, /* 59 */ SYS_Interrupt_Shell, /* 60 */ SYS_Interrupt_Shell /* 61 */ }; sys_pvFunPtr sysFiqHandlerTable[] = { 0, SYS_Interrupt_Shell, /* 1 */ SYS_Interrupt_Shell, /* 2 */ SYS_Interrupt_Shell, /* 3 */ SYS_Interrupt_Shell, /* 4 */ SYS_Interrupt_Shell, /* 5 */ SYS_Interrupt_Shell, /* 6 */ SYS_Interrupt_Shell, /* 7 */ SYS_Interrupt_Shell, /* 8 */ SYS_Interrupt_Shell, /* 9 */ SYS_Interrupt_Shell, /* 10 */ SYS_Interrupt_Shell, /* 11 */ SYS_Interrupt_Shell, /* 12 */ SYS_Interrupt_Shell, /* 13 */ SYS_Interrupt_Shell, /* 14 */ SYS_Interrupt_Shell, /* 15 */ SYS_Interrupt_Shell, /* 16 */ SYS_Interrupt_Shell, /* 17 */ SYS_Interrupt_Shell, /* 18 */ SYS_Interrupt_Shell, /* 19 */ SYS_Interrupt_Shell, /* 20 */ SYS_Interrupt_Shell, /* 21 */ SYS_Interrupt_Shell, /* 22 */ SYS_Interrupt_Shell, /* 23 */ SYS_Interrupt_Shell, /* 24 */ SYS_Interrupt_Shell, /* 25 */ SYS_Interrupt_Shell, /* 26 */ SYS_Interrupt_Shell, /* 27 */ SYS_Interrupt_Shell, /* 28 */ SYS_Interrupt_Shell, /* 29 */ SYS_Interrupt_Shell, /* 30 */ SYS_Interrupt_Shell, /* 31 */ SYS_Interrupt_Shell, /* 32 */ SYS_Interrupt_Shell, /* 33 */ SYS_Interrupt_Shell, /* 34 */ SYS_Interrupt_Shell, /* 35 */ SYS_Interrupt_Shell, /* 36 */ SYS_Interrupt_Shell, /* 37 */ SYS_Interrupt_Shell, /* 38 */ SYS_Interrupt_Shell, /* 39 */ SYS_Interrupt_Shell, /* 40 */ SYS_Interrupt_Shell, /* 41 */ SYS_Interrupt_Shell, /* 42 */ SYS_Interrupt_Shell, /* 43 */ SYS_Interrupt_Shell, /* 44 */ SYS_Interrupt_Shell, /* 45 */ SYS_Interrupt_Shell, /* 46 */ SYS_Interrupt_Shell, /* 47 */ SYS_Interrupt_Shell, /* 48 */ SYS_Interrupt_Shell, /* 49 */ SYS_Interrupt_Shell, /* 50 */ SYS_Interrupt_Shell, /* 51 */ SYS_Interrupt_Shell, /* 52 */ SYS_Interrupt_Shell, /* 53 */ SYS_Interrupt_Shell, /* 54 */ SYS_Interrupt_Shell, /* 55 */ SYS_Interrupt_Shell, /* 56 */ SYS_Interrupt_Shell, /* 57 */ SYS_Interrupt_Shell, /* 58 */ SYS_Interrupt_Shell, /* 59 */ SYS_Interrupt_Shell, /* 60 */ SYS_Interrupt_Shell /* 61 */ }; /* Interrupt Handler */ #if defined ( __GNUC__ ) && !(__CC_ARM) static void __attribute__((interrupt("IRQ"))) sysIrqHandler(void) #else __irq void sysIrqHandler() #endif { UINT32 volatile _mIPER, _mISNR; _mIPER = (inpw(REG_AIC_IPER) >> 2) & 0x3f; _mISNR = inpw(REG_AIC_ISNR); if (_mIPER != 0) { if (_mISNR != 0) (*sysIrqHandlerTable[_mIPER])(); outpw(REG_AIC_EOSCR, 1); } } #if defined ( __GNUC__ ) && !(__CC_ARM) static void __attribute__((interrupt("FIQ"))) sysFiqHandler(void) #else __irq void sysFiqHandler() #endif { UINT32 volatile _mIPER, _mISNR; _mIPER = (inpw(REG_AIC_IPER) >> 2) & 0x3f; _mISNR = inpw(REG_AIC_ISNR); if (_mIPER != 0) { if (_mISNR != 0) (*sysFiqHandlerTable[_mIPER])(); outpw(REG_AIC_EOSCR, 1); } } void SYS_Interrupt_Shell() { //sysprintf("ISR not found! ISNR=%d\n", inpw(REG_AIC_ISNR)); } void sysInitializeAIC() { *(unsigned int volatile *)0x38 = (unsigned int)sysIrqHandler; *(unsigned int volatile *)0x3C = (unsigned int)sysFiqHandler; } /// @endcond HIDDEN_SYMBOLS /* Interrupt library functions */ /** * @brief system AIC - disable interrupt * * @param[in] eIntNo Select interrupt source. \ref IRQn_Type * * @return 0 */ INT32 sysDisableInterrupt(IRQn_Type eIntNo) { if ((eIntNo > SYS_MAX_INT_SOURCE) || (eIntNo < SYS_MIN_INT_SOURCE)) return 1; if (eIntNo < 32) outpw(REG_AIC_MDCR, (1 << eIntNo)); else outpw(REG_AIC_MDCRH, (1 << (eIntNo - 32))); return 0; } /** * @brief system AIC - enable interrupt * * @param[in] eIntNo Select interrupt source. \ref IRQn_Type * * @return 0 */ INT32 sysEnableInterrupt(IRQn_Type eIntNo) { if ((eIntNo > SYS_MAX_INT_SOURCE) || (eIntNo < SYS_MIN_INT_SOURCE)) return 1; if (eIntNo < 32) outpw(REG_AIC_MECR, (1 << eIntNo)); else outpw(REG_AIC_MECRH, (1 << (eIntNo - 32))); return 0; } /** * @brief system AIC - install exception handler * * @param[in] nExceptType exception type. ( \ref SYS_SWI / \ref SYS_D_ABORT / \ref SYS_I_ABORT / \ref SYS_UNDEFINE) * @param[in] pvNewHandler own exception handler * * @return old handler */ PVOID sysInstallExceptionHandler(INT32 nExceptType, PVOID pvNewHandler) { PVOID _mOldVect = NULL; switch (nExceptType) { case SYS_SWI: _mOldVect = *(PVOID volatile *)0x28; *(PVOID volatile *)0x28 = pvNewHandler; break; case SYS_D_ABORT: _mOldVect = *(PVOID volatile *)0x30; *(PVOID volatile *)0x30 = pvNewHandler; break; case SYS_I_ABORT: _mOldVect = *(PVOID volatile *)0x2C; *(PVOID volatile *)0x2C = pvNewHandler; break; case SYS_UNDEFINE: _mOldVect = *(PVOID volatile *)0x24; *(PVOID volatile *)0x24 = pvNewHandler; break; default: ; } return _mOldVect; } /** * @brief system AIC - install FIQ handler * * @param[in] pvNewISR own fiq handler * * @return old handler */ PVOID sysInstallFiqHandler(PVOID pvNewISR) { PVOID _mOldVect; _mOldVect = *(PVOID volatile *)0x3C; *(PVOID volatile *)0x3C = pvNewISR; return _mOldVect; } /** * @brief system AIC - install IRQ handler * * @param[in] pvNewISR own irq handler * * @return old handler */ PVOID sysInstallIrqHandler(PVOID pvNewISR) { PVOID _mOldVect; _mOldVect = *(PVOID volatile *)0x38; *(PVOID volatile *)0x38 = pvNewISR; return _mOldVect; } /** * @brief system AIC - install Own IRQ service routine * * @param[in] nIntTypeLevel Interrupt Level. ( \ref FIQ_LEVEL_0 / \ref IRQ_LEVEL_1 / \ref IRQ_LEVEL_2 / \ref IRQ_LEVEL_3 / * \ref IRQ_LEVEL_4 / \ref IRQ_LEVEL_5 / \ref IRQ_LEVEL_6 / \ref IRQ_LEVEL_7 ) * @param[in] eIntNo Interrupt number. \ref IRQn_Type * @param[in] pvNewISR own irq handler * * @return old handler */ PVOID sysInstallISR(INT32 nIntTypeLevel, IRQn_Type eIntNo, PVOID pvNewISR) { PVOID _mOldVect; UINT32 _mRegAddr/*, _mRegValue*/; INT shift; if (!_sys_bIsAICInitial) { sysInitializeAIC(); _sys_bIsAICInitial = TRUE; } _mRegAddr = REG_AIC_SCR1 + ((eIntNo / 4) * 4); shift = (eIntNo % 4) * 8; nIntTypeLevel &= 0xff; outpw(_mRegAddr, (inpw(_mRegAddr) & ~(0x07 << shift)) | (nIntTypeLevel << shift)); if ((nIntTypeLevel & 0x7) == FIQ_LEVEL_0) { _mOldVect = (PVOID) sysFiqHandlerTable[eIntNo]; sysFiqHandlerTable[eIntNo] = (sys_pvFunPtr)pvNewISR; } else { _mOldVect = (PVOID) sysIrqHandlerTable[eIntNo]; sysIrqHandlerTable[eIntNo] = (sys_pvFunPtr)pvNewISR; } return _mOldVect; } INT32 sysSetGlobalInterrupt(INT32 nIntState) { switch (nIntState) { case ENABLE_ALL_INTERRUPTS: outpw(REG_AIC_MECR, 0xFFFFFFFF); outpw(REG_AIC_MECRH, 0xFFFFFFFF); break; case DISABLE_ALL_INTERRUPTS: outpw(REG_AIC_MDCR, 0xFFFFFFFF); outpw(REG_AIC_MDCRH, 0xFFFFFFFF); break; default: ; } return 0; } /** * @brief system AIC - Change interrupt level * * @param[in] eIntNo Interrupt number. \ref IRQn_Type * @param[in] uIntLevel Interrupt Level. ( \ref FIQ_LEVEL_0 / \ref IRQ_LEVEL_1 / \ref IRQ_LEVEL_2 / \ref IRQ_LEVEL_3 / * \ref IRQ_LEVEL_4 / \ref IRQ_LEVEL_5 / \ref IRQ_LEVEL_6 / \ref IRQ_LEVEL_7 ) * * @return 0 */ INT32 sysSetInterruptPriorityLevel(IRQn_Type eIntNo, UINT32 uIntLevel) { UINT32 _mRegAddr; INT shift; if ((eIntNo > SYS_MAX_INT_SOURCE) || (eIntNo < SYS_MIN_INT_SOURCE)) return 1; _mRegAddr = REG_AIC_SCR1 + ((eIntNo / 4) * 4); shift = (eIntNo % 4) * 8; uIntLevel &= 0x7; outpw(_mRegAddr, (inpw(_mRegAddr) & ~(0x07 << shift)) | (uIntLevel << shift)); return 0; } INT32 sysSetInterruptType(IRQn_Type eIntNo, UINT32 uIntSourceType) { UINT32 _mRegAddr; INT shift; if ((eIntNo > SYS_MAX_INT_SOURCE) || (eIntNo < SYS_MIN_INT_SOURCE)) return 1; _mRegAddr = REG_AIC_SCR1 + ((eIntNo / 4) * 4); shift = (eIntNo % 4) * 8; uIntSourceType &= 0xC0; outpw(_mRegAddr, (inpw(_mRegAddr) & ~(0xC0 << shift)) | (uIntSourceType << shift)); return 0; } /** * @brief system AIC - Set CP15 Interrupt Type * * @param[in] nIntState Interrupt state. ( \ref ENABLE_IRQ / \ref ENABLE_FIQ / \ref ENABLE_FIQ_IRQ / * \ref DISABLE_IRQ / \ref DISABLE_FIQ / \ref DISABLE_FIQ_IRQ) * * @return 0 */ INT32 sysSetLocalInterrupt(INT32 nIntState) { #if defined ( __GNUC__ ) && !(__CC_ARM) # else INT32 temp; #endif switch (nIntState) { case ENABLE_IRQ: case ENABLE_FIQ: case ENABLE_FIQ_IRQ: #if defined ( __GNUC__ ) && !(__CC_ARM) asm ( "mrs r0, CPSR \n" "bic r0, r0, #0x80 \n" "msr CPSR_c, r0 \n" ); #else __asm { MRS temp, CPSR AND temp, temp, nIntState MSR CPSR_c, temp } #endif break; case DISABLE_IRQ: case DISABLE_FIQ: case DISABLE_FIQ_IRQ: #if defined ( __GNUC__ ) && !(__CC_ARM) asm ( "MRS r0, CPSR \n" "ORR r0, r0, #0x80 \n" "MSR CPSR_c, r0 \n" ); #else __asm { MRS temp, CPSR ORR temp, temp, nIntState MSR CPSR_c, temp } #endif break; default: ; } return 0; } UINT32 sysGetInterruptEnableStatus(void) { return (inpw(REG_AIC_IMR)); } UINT32 sysGetInterruptEnableStatusH(void) { return (inpw(REG_AIC_IMRH)); } /// @cond HIDDEN_SYMBOLS BOOL sysGetIBitState() { INT32 temp; #if defined ( __GNUC__ ) && !(__CC_ARM) asm ( "MRS %0, CPSR \n" :"=r"(temp) : : ); #else __asm { MRS temp, CPSR } #endif if (temp & 0x80) return FALSE; else return TRUE; } INT32 sysGetPLL(UINT32 reg) { UINT32 N, M, P; N = ((inpw(reg) & 0x007F) >> 0) + 1; M = ((inpw(reg) & 0x1F80) >> 7) + 1; P = ((inpw(reg) & 0xE000) >> 13) + 1; return (12 * N / (M * P)); /* 12MHz HXT */ } /// @endcond HIDDEN_SYMBOLS /** * @brief system Timer - install WDT interrupt handler * * @param[in] clk clock source. \ref CLK_Type * * @return MHz */ UINT32 sysGetClock(CLK_Type clk) { UINT32 src, divS, divN, reg, div; switch (clk) { case SYS_UPLL: return sysGetPLL(REG_CLK_UPLLCON); case SYS_APLL: return sysGetPLL(REG_CLK_APLLCON); case SYS_SYSTEM: { reg = inpw(REG_CLK_DIVCTL0); switch (reg & 0x18) { case 0x0: src = 12; /* HXT */ break; case 0x10: src = sysGetPLL(REG_CLK_APLLCON); break; case 0x18: src = sysGetPLL(REG_CLK_UPLLCON); break; default: return 0; } divS = (reg & 0x7) + 1; divN = ((reg & 0xf00) >> 8) + 1; return (src / divS / divN); } case SYS_HCLK1: { reg = inpw(REG_CLK_DIVCTL0); switch (reg & 0x18) { case 0x0: src = 12; /* HXT */ break; case 0x10: src = sysGetPLL(REG_CLK_APLLCON); break; case 0x18: src = sysGetPLL(REG_CLK_UPLLCON); break; default: return 0; } divS = (reg & 0x7) + 1; divN = ((reg & 0xf00) >> 8) + 1; return (src / divS / divN / 2); } case SYS_HCLK234: { reg = inpw(REG_CLK_DIVCTL0); switch (reg & 0x18) { case 0x0: src = 12; /* HXT */ break; case 0x10: src = sysGetPLL(REG_CLK_APLLCON); break; case 0x18: src = sysGetPLL(REG_CLK_UPLLCON); break; default: return 0; } divS = (reg & 0x7) + 1; divN = ((reg & 0xf00) >> 8) + 1; div = ((reg & 0xf00000) >> 20) + 1; return (src / divS / divN / 2 / div); } case SYS_PCLK: { reg = inpw(REG_CLK_DIVCTL0); switch (reg & 0x18) { case 0x0: src = 12; /* HXT */ break; case 0x10: src = sysGetPLL(REG_CLK_APLLCON); break; case 0x18: src = sysGetPLL(REG_CLK_UPLLCON); break; default: return 0; } divS = (reg & 0x7) + 1; divN = ((reg & 0xf00) >> 8) + 1; div = ((reg & 0xf000000) >> 24) + 1; return (src / divS / divN / 2 / div); } case SYS_CPU: { reg = inpw(REG_CLK_DIVCTL0); switch (reg & 0x18) { case 0x0: src = 12; /* HXT */ break; case 0x10: src = sysGetPLL(REG_CLK_APLLCON); break; case 0x18: src = sysGetPLL(REG_CLK_UPLLCON); break; default: return 0; } divS = (reg & 0x7) + 1; divN = ((reg & 0xf00) >> 8) + 1; div = ((reg & 0xf0000) >> 16) + 1; return (src / divS / divN / div); } default: ; } return 0; } /*** (C) COPYRIGHT 2018 Nuvoton Technology Corp. ***/