--- /dev/null
+// ARMware - an ARM emulator
+// Copyright (C) <2007> Wei Hu <wei.hu.tw@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#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
--- /dev/null
+// ARMware - an ARM emulator
+// Copyright (C) <2007> Wei Hu <wei.hu.tw@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INTERRUPT_CONTROLLER_H
+#define INTERRUPT_CONTROLLER_H
+
+#include <cstdint>
+
+#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