From 29bec9418183bc9581f0f5b287f8264d53c7ca44 Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 24 Jun 2026 19:47:10 -0700 Subject: [PATCH] sa1100: add initial interrupt controller implementation --- CMakeLists.txt | 1 + WindCore/series7/interrupt_controller.cpp | 485 ++++++++++++++++++++++ WindCore/series7/interrupt_controller.h | 118 ++++++ 3 files changed, 604 insertions(+) create mode 100644 WindCore/series7/interrupt_controller.cpp create mode 100644 WindCore/series7/interrupt_controller.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 586305b..e1335ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(WIND_CORE_SOURCES WindCore/series5mx/etna.cpp WindCore/series5mx/windermere.cpp WindCore/series7/gpio_controller.cpp + WindCore/series7/interrupt_controller.cpp WindCore/series7/lcd_controller.cpp WindCore/series7/memory_controller.cpp WindCore/series7/os_timer.cpp diff --git a/WindCore/series7/interrupt_controller.cpp b/WindCore/series7/interrupt_controller.cpp new file mode 100644 index 0000000..9af74db --- /dev/null +++ b/WindCore/series7/interrupt_controller.cpp @@ -0,0 +1,485 @@ +// ARMware - an ARM emulator +// Copyright (C) <2007> Wei Hu +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "interrupt_controller.h" + +#include "logger.h" + +namespace SA1100 { + +IntController::IntController(const LCDController* lcdController, + const OsTimer* osTimer, + const GPIOController* gpioController, + const Uart* uart, const RTC* rtc) + : mOsTimer(osTimer), mLCDController(lcdController), + mGPIOController(gpioController), mUart(uart), mRTC(rtc) { + initRegisters(); +} + +void IntController::initRegisters() { + mICIP = 0; + mICMR = 0; + mICLR = 0; + mICCR = 0; + mICFP = 0; + mICPR = 0; +} + +void IntController::reset() { + mICIP = 0; + + // :NOTE: Wei 2004-Apr-25: + // + // Although the SA-1110 Developer's Manual doesn't specify whether ICFP will + // be initialized to 0 when reset. However, SA-1110 Developer's Manual does + // specify ICIP will be initialized to 0 when reset. Thus I _assume_ this is a + // bug in the SA-1110 Developer's Manual, and will do the initialization by my + // self. + + // :NOTE: Wei 2004-Jul-20; + // + // It is really a bug. + // According to the Intel-StrongARM SA-1110 Microprocessor Specification + // Update (December 2001), p.37: + // + // Changed the reset values for the Interrupt Controller IRQ and FIQ Pending + // Registers (ICIP and ICFP) from undefined to 0. + mICFP = 0; + + mICCR = 0; + // mICMR = ~0; + + // :NOTE: Wei 2004-Jun-06: + // + // Althought there is no words in SA-1110 Developer's manual says that + // ICPR will be set to 0 when reset. + // However, I think it should set to 0 when reset to indicate that no + // pending interrupt exist. + mICPR = 0; +} + +void IntController::checkGpioInterruptStatus() { + const uint32_t gpioStatus = mGPIOController->getInterruptStatus(); + // clear all GPIO bits in pending interrupts + mICPR &= ~(GPIO_0_10_EDGE_BIT_MASK | GPIO_11_27_EDGE_BIT_MASK); + + if (gpioStatus != 0) { + if (gpioStatus & GPIO_0_EDGE_BIT_MASK) { + mICPR |= GPIO_0_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_1_EDGE_BIT_MASK) { + mICPR |= GPIO_1_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_2_EDGE_BIT_MASK) { + mICPR |= GPIO_2_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_3_EDGE_BIT_MASK) { + mICPR |= GPIO_3_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_4_EDGE_BIT_MASK) { + mICPR |= GPIO_4_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_5_EDGE_BIT_MASK) { + mICPR |= GPIO_5_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_6_EDGE_BIT_MASK) { + mICPR |= GPIO_6_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_7_EDGE_BIT_MASK) { + mICPR |= GPIO_7_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_8_EDGE_BIT_MASK) { + mICPR |= GPIO_8_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_9_EDGE_BIT_MASK) { + mICPR |= GPIO_9_EDGE_BIT_MASK; + } + if (gpioStatus & GPIO_10_EDGE_BIT_MASK) { + mICPR |= GPIO_10_EDGE_BIT_MASK; + } + constexpr uint32_t GPIO_11_27_BITS = + (1 << 11) | (1 << 12) | (1 << 13) | (1 << 14) | (1 << 15) | (1 << 16) | + (1 << 17) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21) | (1 << 22) | + (1 << 23) | (1 << 24) | (1 << 25) | (1 << 26) | (1 << 27); + if (gpioStatus & GPIO_11_27_BITS) { + mICPR |= GPIO_11_27_EDGE_BIT_MASK; + } + } +} + +void IntController::checkOsTimerInterruptStatus() { + const uint32_t osTimerStatus = mOsTimer->getInterruptStatus(); + + switch (osTimerStatus) { + case 0: // 0000 + { + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } break; + + case OsTimer::OSMR0_MASK: // 0001 + { + mICPR |= OS_TIMER_0_BIT_MASK; + + mICPR &= ~(OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK | OS_TIMER_3_BIT_MASK); + } break; + + case OsTimer::OSMR1_MASK: // 0010 + { + mICPR |= OS_TIMER_1_BIT_MASK; + + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_2_BIT_MASK | OS_TIMER_3_BIT_MASK); + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK): // 0011 + { + mICPR |= OS_TIMER_0_BIT_MASK; + mICPR |= OS_TIMER_1_BIT_MASK; + + mICPR &= ~(OS_TIMER_2_BIT_MASK | OS_TIMER_3_BIT_MASK); + } break; + + case OsTimer::OSMR2_MASK: // 0100 + { + mICPR |= OS_TIMER_2_BIT_MASK; + + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_3_BIT_MASK); + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR2_MASK): // 0101 + { + mICPR |= OS_TIMER_0_BIT_MASK; + mICPR |= OS_TIMER_2_BIT_MASK; + + mICPR &= ~(OS_TIMER_1_BIT_MASK | OS_TIMER_3_BIT_MASK); + } break; + + case (OsTimer::OSMR1_MASK | OsTimer::OSMR2_MASK): // 0110 + { + mICPR |= OS_TIMER_1_BIT_MASK; + mICPR |= OS_TIMER_2_BIT_MASK; + + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_3_BIT_MASK); + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK | + OsTimer::OSMR2_MASK): // 0111 + { + mICPR |= OS_TIMER_0_BIT_MASK; + mICPR |= OS_TIMER_1_BIT_MASK; + mICPR |= OS_TIMER_2_BIT_MASK; + + mICPR &= ~OS_TIMER_3_BIT_MASK; + } break; + + case OsTimer::OSMR3_MASK: // 1000 + { + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK); + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR3_MASK): // 1001 + { + mICPR |= OS_TIMER_0_BIT_MASK; + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~(OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK); + } break; + + case (OsTimer::OSMR1_MASK | OsTimer::OSMR3_MASK): // 1010 + { + mICPR |= OS_TIMER_1_BIT_MASK; + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_2_BIT_MASK); + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK | + OsTimer::OSMR3_MASK): // 1011 + { + mICPR |= OS_TIMER_0_BIT_MASK; + mICPR |= OS_TIMER_1_BIT_MASK; + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~OS_TIMER_2_BIT_MASK; + } break; + + case (OsTimer::OSMR2_MASK | OsTimer::OSMR3_MASK): // 1100 + { + mICPR |= OS_TIMER_2_BIT_MASK; + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK); + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1101 + { + mICPR |= OS_TIMER_0_BIT_MASK; + mICPR |= OS_TIMER_2_BIT_MASK; + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~OS_TIMER_1_BIT_MASK; + } break; + + case (OsTimer::OSMR1_MASK | OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1110 + { + mICPR |= OS_TIMER_1_BIT_MASK; + mICPR |= OS_TIMER_2_BIT_MASK; + mICPR |= OS_TIMER_3_BIT_MASK; + + mICPR &= ~OS_TIMER_0_BIT_MASK; + } break; + + case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK | OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1111 + { + mICPR |= (OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } break; + + default: + break; + } +} + +void IntController::checkRtcInterruptStatus() { + const uint32_t rtcStatus = mRTC->getInterruptStatus(); + + switch (rtcStatus) { + case 0: + mICPR &= ~(RTC_ALARM_BIT_MASK | RTC_HZ_BIT_MASK); + break; + + case RTC::STATUS_AL_BIT: + mICPR |= RTC_ALARM_BIT_MASK; + mICPR &= ~RTC_HZ_BIT_MASK; + break; + + case RTC::STATUS_HZ_BIT: + mICPR |= RTC_HZ_BIT_MASK; + mICPR &= ~RTC_ALARM_BIT_MASK; + break; + + case (RTC::STATUS_AL_BIT | RTC::STATUS_HZ_BIT): + mICPR |= (RTC_ALARM_BIT_MASK | RTC_HZ_BIT_MASK); + break; + + default: + break; + } +} + +void IntController::checkLcdInterruptStatus() { + const uint32_t lcdStatus = mLCDController->getInterruptStatus(); + + if (0 == lcdStatus) { + mICPR &= ~LCD_SERVICE_BIT_MASK; + } else { + if ((lcdStatus & ~(LCDController::LCSR_BAU | LCDController::LCSR_LDD)) != + 0) { + mICPR |= LCD_SERVICE_BIT_MASK; + } else { + switch (lcdStatus & (LCDController::LCSR_BAU | LCDController::LCSR_LDD)) { + case 0: + // If we go here, means (lcdStatus == 0), however, (lcdStatus == 0) + // should go to the upper 'if' block, thus if we go here, it is a bug. + break; + + case LCDController::LCSR_BAU: { + const uint32_t lcdCtrl = mLCDController->getLcdCtrlRegister(); + + if (0 == (lcdCtrl & LCDController::LCCR0_BAM)) { + // Enable BAU + mICPR |= LCD_SERVICE_BIT_MASK; + } else { + mICPR &= ~LCD_SERVICE_BIT_MASK; + } + } break; + + case LCDController::LCSR_LDD: { + const uint32_t lcdCtrl = mLCDController->getLcdCtrlRegister(); + + if (0 == (lcdCtrl & LCDController::LCCR0_LDM)) { + // Enable LDD + mICPR |= LCD_SERVICE_BIT_MASK; + } else { + mICPR &= ~LCD_SERVICE_BIT_MASK; + } + } break; + + case (LCDController::LCSR_BAU | LCDController::LCSR_LDD): { + const uint32_t lcdCtrl = mLCDController->getLcdCtrlRegister(); + + switch (lcdCtrl & + (LCDController::LCCR0_BAM | LCDController::LCCR0_LDM)) { + case 0: + case LCDController::LCCR0_BAM: + case LCDController::LCCR0_LDM: + // Enable BAU + mICPR |= LCD_SERVICE_BIT_MASK; + break; + + case (LCDController::LCCR0_BAM | LCDController::LCCR0_LDM): + mICPR &= ~LCD_SERVICE_BIT_MASK; + break; + + default: + break; + } + } break; + } + } + } +} + +void IntController::checkUartInterruptStatus() { + const uint32_t uartStatus = mUart->getInterruptStatus(); + + if (0 == uartStatus) { + mICPR &= ~SERIAL_3_BIT_MASK; + } else { + const uint32_t uartCtrl = mUart->getCtrlRegister(); + + switch (uartCtrl & (Uart::UTCR3_TIE | Uart::UTCR3_RIE)) { + case 0: + break; + + case Uart::UTCR3_TIE: + if ((uartStatus & Uart::UTSR0_TFS) != 0) { + mICPR |= SERIAL_3_BIT_MASK; + } else { + mICPR &= ~SERIAL_3_BIT_MASK; + } + break; + + case Uart::UTCR3_RIE: + if ((uartStatus & (Uart::UTSR0_RFS | Uart::UTSR0_RID)) != 0) { + mICPR |= SERIAL_3_BIT_MASK; + } else { + mICPR &= ~SERIAL_3_BIT_MASK; + } + break; + + case (Uart::UTCR3_TIE | Uart::UTCR3_RIE): + if ((uartStatus & + (Uart::UTSR0_TFS | Uart::UTSR0_RFS | Uart::UTSR0_RID)) != 0) { + mICPR |= SERIAL_3_BIT_MASK; + } else { + mICPR &= ~SERIAL_3_BIT_MASK; + } + break; + } + } +} + +void IntController::run() { + checkGpioInterruptStatus(); + checkOsTimerInterruptStatus(); + checkRtcInterruptStatus(); + checkLcdInterruptStatus(); + checkUartInterruptStatus(); + + // Determine IRQ & FIQ + if (mICPR != 0) { + const uint32_t temp = mICPR & mICMR; + + mICIP = temp & (~mICLR); + mICFP = temp & mICLR; + } else { + mICIP = 0; + mICFP = 0; + } +} + +uint32_t IntController::read32(const uint32_t address) const { + switch (address) { + case ICIP: + LOG_REG_R("IntController ICIP", address, mICIP); + return mICIP; + case ICMR: + LOG_REG_R("IntController ICMR", address, mICMR); + return mICMR; + case ICLR: + LOG_REG_R("IntController ICLR", address, mICLR); + return mICLR; + case ICCR: + LOG_REG_R("IntController ICCR", address, mICCR); + return mICCR; + case ICFP: + LOG_REG_R("IntController ICFP", address, mICFP); + return mICFP; + case ICPR: + LOG_REG_R("IntController ICPR", address, mICPR); + return mICPR; + + default: + LOG_REG_R("IntController Unknown", address, 0); + return 0; + } +} + +void IntController::write32(const uint32_t address, const uint32_t value) { + switch (address) { + case ICIP: + // :SA-1110 Developer's Manual: Wei 2003-Dec-09: + // + // ICIP is a read-only register + LOG_REG_W("IntController ICIP", address, value); + break; + + case ICMR: + LOG_REG_W("IntController ICMR", address, value); + mICMR = value; + break; + + case ICLR: + LOG_REG_W("IntController ICLR", address, value); + mICLR = value; + break; + + case ICCR: + // :SA-1110 Developer's Manual: p.89: Wei 2004-May-09: + // + // bits[31:1] are reserved. + // LOG_REG_W("IntController ICCR", address, value); + mICCR = (value & 0x1); + break; + + case ICFP: + // :SA-1110 Developer's Manual: Wei 2003-Dec-09: + // + // ICFP is a read-only register + LOG_REG_W("IntController ICFP", address, value); + break; + + case ICPR: + // :SA-1110 Developer's Manual: Wei 2003-Dec-09: + // + // ICPR is a read-only register + LOG_REG_W("IntController ICPR", address, value); + break; + + default: + LOG_REG_W("IntController Unknown", address, value); + break; + } +} + +} // namespace SA1100 diff --git a/WindCore/series7/interrupt_controller.h b/WindCore/series7/interrupt_controller.h new file mode 100644 index 0000000..42b36ef --- /dev/null +++ b/WindCore/series7/interrupt_controller.h @@ -0,0 +1,118 @@ +// ARMware - an ARM emulator +// Copyright (C) <2007> Wei Hu +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INTERRUPT_CONTROLLER_H +#define INTERRUPT_CONTROLLER_H + +#include + +#include "gpio_controller.h" +#include "lcd_controller.h" +#include "os_timer.h" +#include "rtc.h" +#include "uart.h" + +namespace SA1100 { + +class IntController { +public: + IntController(const LCDController* lcdController, const OsTimer* osTimer, + const GPIOController* gpioController, const Uart* uart, + const RTC* rtc); + + void run(); + void reset(); + + uint32_t read32(const uint32_t address) const; + void write32(const uint32_t address, const uint32_t value); + + inline bool havePendingIrq() const { return (0 == mICIP) ? false : true; } + inline bool havePendingFiq() const { return (0 == mICFP) ? false : true; } + +private: + void initRegisters(); + + void checkGpioInterruptStatus(); + void checkOsTimerInterruptStatus(); + void checkRtcInterruptStatus(); + void checkLcdInterruptStatus(); + void checkUartInterruptStatus(); + + enum IntCtrlBitMaskEnum { + GPIO_0_EDGE_BIT_MASK = (1 << 0), + GPIO_1_EDGE_BIT_MASK = (1 << 1), + GPIO_2_EDGE_BIT_MASK = (1 << 2), + GPIO_3_EDGE_BIT_MASK = (1 << 3), + GPIO_4_EDGE_BIT_MASK = (1 << 4), + GPIO_5_EDGE_BIT_MASK = (1 << 5), + GPIO_6_EDGE_BIT_MASK = (1 << 6), + GPIO_7_EDGE_BIT_MASK = (1 << 7), + GPIO_8_EDGE_BIT_MASK = (1 << 8), + GPIO_9_EDGE_BIT_MASK = (1 << 9), + GPIO_10_EDGE_BIT_MASK = (1 << 10), + + GPIO_0_10_EDGE_BIT_MASK = + (GPIO_0_EDGE_BIT_MASK | GPIO_1_EDGE_BIT_MASK | GPIO_2_EDGE_BIT_MASK | + GPIO_3_EDGE_BIT_MASK | GPIO_4_EDGE_BIT_MASK | GPIO_5_EDGE_BIT_MASK | + GPIO_6_EDGE_BIT_MASK | GPIO_7_EDGE_BIT_MASK | GPIO_8_EDGE_BIT_MASK | + GPIO_9_EDGE_BIT_MASK | GPIO_10_EDGE_BIT_MASK), + + GPIO_11_27_EDGE_BIT_MASK = (1 << 11), + + LCD_SERVICE_BIT_MASK = (1 << 12), + + SERIAL_1_BIT_MASK = (1 << 15), + SERIAL_2_BIT_MASK = (1 << 16), + SERIAL_3_BIT_MASK = (1 << 17), + + OS_TIMER_0_BIT_MASK = (1 << 26), + OS_TIMER_1_BIT_MASK = (1 << 27), + OS_TIMER_2_BIT_MASK = (1 << 28), + OS_TIMER_3_BIT_MASK = (1 << 29), + + RTC_HZ_BIT_MASK = (1 << 30), + RTC_ALARM_BIT_MASK = (1 << 31) + }; + + typedef enum IntCtrlBitMaskEnum IntCtrlBitMaskEnum; + + enum { + ICIP = 0x90050000, + ICMR = 0x90050004, + ICLR = 0x90050008, + ICCR = 0x9005000C, + ICFP = 0x90050010, + ICPR = 0x90050020 + }; + + const OsTimer* mOsTimer; + const LCDController* mLCDController; + const GPIOController* mGPIOController; + const RTC* mRTC; + const Uart* mUart; + + uint32_t mICIP; + uint32_t mICMR; + uint32_t mICLR; + uint32_t mICCR; + uint32_t mICFP; + uint32_t mICPR; +}; + +} // namespace SA1100 + +#endif // INTERRUPT_CONTROLLER_H -- 2.45.2