From e4dd8511cdcb6624997940b46e0c66884c79668b Mon Sep 17 00:00:00 2001 From: Wolfvak Date: Tue, 25 Aug 2020 10:49:52 -0300 Subject: [PATCH] make interrupt handlers more lazy, most processing is done in interruptible context now - completely moved MCU interrupt handling outside of the critical section - refactored a bit of the PXI code and command names - merge the I2C read and write cmds to be one - remove SET_VMODE cmd, now it's always initialized to BGR565 on boot and to RGB565 on firmlaunch - atomic-ize more stuff --- arm11/source/hw/mcu.c | 67 +++++---- arm11/source/main.c | 257 +++++++++++++++++----------------- arm9/source/common/hid.c | 2 +- arm9/source/common/power.c | 2 +- arm9/source/godmode.c | 3 +- arm9/source/main.c | 2 +- arm9/source/system/i2c.c | 6 +- arm9/source/system/spiflash.c | 4 +- arm9/source/utils/scripting.c | 3 +- common/pxi.h | 17 ++- common/shmem.h | 6 +- 11 files changed, 185 insertions(+), 184 deletions(-) diff --git a/arm11/source/hw/mcu.c b/arm11/source/hw/mcu.c index 0d81768..aa45c6d 100755 --- a/arm11/source/hw/mcu.c +++ b/arm11/source/hw/mcu.c @@ -63,57 +63,73 @@ typedef struct { static u8 volumeSliderValue; static u32 shellState; -static _Atomic(u32) pendingEvents; +static _Atomic(u32) pendingEvents, pendingUpdate; -static void mcuUpdateVolumeSlider(void) +static void mcuEventUpdate(void) { - volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER); -} + u32 mask; -static void mcuUpdateShellState(bool open) -{ - shellState = open ? SHELL_OPEN : SHELL_CLOSED; + if (!atomic_exchange(&pendingUpdate, 0)) + return; + + // reading the pending mask automagically acknowledges + // the interrupts so all of them must be processed in one go + mcuReadRegBuf(MCUREG_INT_MASK, (u8*)&mask, sizeof(mask)); + + if (mask & MCUEV_HID_VOLUME_SLIDER) + volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER); + + if (mask & MCUEV_HID_SHELL_OPEN) { + mcuResetLEDs(); + shellState = SHELL_OPEN; + } + + if (mask & MCUEV_HID_SHELL_CLOSE) { + shellState = SHELL_CLOSED; + } + + atomic_fetch_or(&pendingEvents, mask); } u32 mcuEventTest(u32 mask) { + mcuEventUpdate(); return atomic_load(&pendingEvents) & mask; } u32 mcuEventClear(u32 mask) { + mcuEventUpdate(); return atomic_fetch_and(&pendingEvents, ~mask) & mask; } u32 mcuEventWait(u32 mask) { do { + mcuEventUpdate(); u32 x = mcuEventClear(mask); if (x) return x; - ARM_WFE(); } while(1); } u8 mcuGetVolumeSlider(void) { + mcuEventUpdate(); return volumeSliderValue; } u32 mcuGetSpecialHID(void) { - u32 ret = shellState, pend = mcuEventClear(MCUEV_HID_MASK); + u32 ret, pend = mcuEventClear(MCUEV_HID_MASK); // hopefully gets unrolled if (pend & (MCUEV_HID_PWR_DOWN | MCUEV_HID_PWR_HOLD)) ret |= BUTTON_POWER; - if (pend & MCUEV_HID_HOME_DOWN) ret |= BUTTON_HOME; - if (pend & MCUEV_HID_HOME_UP) ret &= ~BUTTON_HOME; - - return ret; + return ret | shellState; } void mcuSetStatusLED(u32 period_ms, u32 color) @@ -162,25 +178,7 @@ void mcuResetLEDs(void) void mcuInterruptHandler(u32 __attribute__((unused)) irqn) { - u32 mask; - - // reading the pending mask automagically acknowledges - // the interrupts so all of them must be processed in one go - mcuReadRegBuf(MCUREG_INT_MASK, (u8*)&mask, sizeof(mask)); - - if (mask & MCUEV_HID_VOLUME_SLIDER) - mcuUpdateVolumeSlider(); - - if (mask & MCUEV_HID_SHELL_OPEN) { - mcuResetLEDs(); - mcuUpdateShellState(true); - } - - if (mask & MCUEV_HID_SHELL_CLOSE) { - mcuUpdateShellState(false); - } - - atomic_fetch_or(&pendingEvents, mask); + atomic_store(&pendingUpdate, 1); } void mcuReset(void) @@ -188,6 +186,7 @@ void mcuReset(void) u32 intmask = 0; atomic_init(&pendingEvents, 0); + atomic_init(&pendingUpdate, 0); // set register mask and clear any pending registers mcuWriteRegBuf(MCUREG_INT_EN, (const u8*)&intmask, sizeof(intmask)); @@ -195,8 +194,8 @@ void mcuReset(void) mcuResetLEDs(); - mcuUpdateVolumeSlider(); - mcuUpdateShellState(true); + volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER); + shellState = SHELL_OPEN; // assume the shell is always open on boot // knowing the average 3DS user, there will be plenty // of laughs when this comes back to bite us in the rear diff --git a/arm11/source/main.c b/arm11/source/main.c index 0cb6ac2..9e550a6 100644 --- a/arm11/source/main.c +++ b/arm11/source/main.c @@ -22,6 +22,8 @@ #include #include +#include + #include "arm/gic.h" #include "hw/hid.h" @@ -43,10 +45,20 @@ static int prev_bright_lvl; static bool auto_brightness; #endif -static SystemSHMEM __attribute__((section(".shared"))) SharedMemoryState; +static SystemSHMEM __attribute__((section(".shared"))) sharedMem; -void vblankHandler(u32 __attribute__((unused)) irqn) +static _Atomic(u32) pendingVblank, pendingPxiRx; + +static void vblankHandler(u32 __attribute__((unused)) irqn) { + atomic_store(&pendingVblank, 1); +} + +static void vblankUpdate(void) +{ + if (!atomic_exchange(&pendingVblank, 0)) + return; + #ifndef FIXED_BRIGHTNESS int cur_bright_lvl = (mcuGetVolumeSlider() >> 2) % countof(brightness_lvls); if ((cur_bright_lvl != prev_bright_lvl) && auto_brightness) { @@ -56,128 +68,43 @@ void vblankHandler(u32 __attribute__((unused)) irqn) } #endif - SharedMemoryState.hidState.full = HID_GetState(); + // handle shell events + u32 shell = mcuEventClear(MCUEV_HID_SHELL_OPEN | MCUEV_HID_SHELL_CLOSE); + if (shell & MCUEV_HID_SHELL_CLOSE) { + GFX_powerOffBacklights(GFX_BLIGHT_BOTH); + } else if (shell & MCUEV_HID_SHELL_OPEN) { + GFX_powerOnBacklights(GFX_BLIGHT_BOTH); + } + + sharedMem.hidState.full = HID_GetState(); } -static bool legacy_boot = false; - -void pxiRxHandler(u32 __attribute__((unused)) irqn) +static void pxiRxHandler(u32 __attribute__((unused)) irqn) { - u32 ret, msg, cmd, argc, args[PXI_MAX_ARGS]; + atomic_store(&pendingPxiRx, 1); +} + +static void pxiRxUpdate(u32 *cmd, u32 *args) +{ + u32 msg, lo, hi; + + *cmd = PXICMD_NONE; + + if (!atomic_exchange(&pendingPxiRx, 0)) + return; msg = PXI_Recv(); - cmd = msg & 0xFFFF; - argc = msg >> 16; + lo = msg & 0xFFFF; + hi = msg >> 16; - if (argc >= PXI_MAX_ARGS) { - PXI_Send(0xFFFFFFFF); - return; - } - - PXI_RecvArray(args, argc); - - switch (cmd) { - case PXI_LEGACY_MODE: - { - // TODO: If SMP is enabled, an IPI should be sent here (with a DSB) - legacy_boot = true; - ret = 0; - break; - } - - case PXI_GET_SHMEM: - { - ret = (u32)&SharedMemoryState; - break; - } - - case PXI_SET_VMODE: - { - GFX_init(args[0] ? GFX_BGR8 : GFX_RGB565); - ret = 0; - break; - } - - case PXI_I2C_READ: - { - u32 devId, regAddr, size; - - devId = (args[0] & 0xff); - regAddr = (args[0] >> 8) & 0xff; - size = (args[0] >> 16) % I2C_SHARED_BUFSZ; - - ret = I2C_readRegBuf(devId, regAddr, SharedMemoryState.i2cBuffer, size); - break; - } - - case PXI_I2C_WRITE: - { - u32 devId, regAddr, size; - - devId = (args[0] & 0xff); - regAddr = (args[0] >> 8) & 0xff; - size = (args[0] >> 16) % I2C_SHARED_BUFSZ; - - ret = I2C_writeRegBuf(devId, regAddr, SharedMemoryState.i2cBuffer, size); - break; - } - - case PXI_NVRAM_ONLINE: - { - ret = (NVRAM_Status() & NVRAM_SR_WIP) == 0; - break; - } - - case PXI_NVRAM_READ: - { - NVRAM_Read(args[0], (u32*)SharedMemoryState.spiBuffer, args[1]); - ret = 0; - break; - } - - case PXI_NOTIFY_LED: - { - mcuSetStatusLED(args[0], args[1]); - ret = 0; - break; - } - - case PXI_BRIGHTNESS: - { - s32 newbrightness = (s32)args[0]; - ret = GFX_getBrightness(); - #ifndef FIXED_BRIGHTNESS - if ((newbrightness > 0) && (newbrightness < 0x100)) { - GFX_setBrightness(newbrightness, newbrightness); - auto_brightness = false; - } else { - prev_bright_lvl = -1; - auto_brightness = true; - } - #endif - break; - } - - /* New CMD template: - case CMD_ID: - { - - - - break; - } - */ - - default: - ret = 0xFFFFFFFF; - break; - } - - PXI_Send(ret); + *cmd = lo; + PXI_RecvArray(args, hi); } void __attribute__((noreturn)) MainLoop(void) { + bool runCmdProcessor = true; + #ifdef FIXED_BRIGHTNESS LCD_SetBrightness(FIXED_BRIGHTNESS); #else @@ -185,11 +112,14 @@ void __attribute__((noreturn)) MainLoop(void) auto_brightness = true; #endif - memset(&SharedMemoryState, 0, sizeof(SharedMemoryState)); + // initialize state stuff + atomic_init(&pendingVblank, 0); + atomic_init(&pendingPxiRx, 0); + memset(&sharedMem, 0, sizeof(sharedMem)); // configure interrupts - gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO2, pxiRxHandler); - gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO1, mcuInterruptHandler); + gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO0, pxiRxHandler); + gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO0, mcuInterruptHandler); gicSetInterruptConfig(VBLANK_INTERRUPT, BIT(0), GIC_PRIO0, vblankHandler); // enable interrupts @@ -205,18 +135,93 @@ void __attribute__((noreturn)) MainLoop(void) // ARM9 won't try anything funny until this point PXI_Barrier(ARM11_READY_BARRIER); - // Process IRQs until the ARM9 tells us it's time to boot something else + // Process commands until the ARM9 tells + // us it's time to boot something else + // also handles Vblank events as needed do { - ARM_WFI(); + u32 cmd, resp, args[PXI_MAX_ARGS]; - // handle shell events - u32 shell = mcuEventClear(MCUEV_HID_SHELL_OPEN | MCUEV_HID_SHELL_CLOSE); - if (shell & MCUEV_HID_SHELL_CLOSE) { - GFX_powerOffBacklights(GFX_BLIGHT_BOTH); - } else if (shell & MCUEV_HID_SHELL_OPEN) { - GFX_powerOnBacklights(GFX_BLIGHT_BOTH); + vblankUpdate(); + pxiRxUpdate(&cmd, args); + + switch(cmd) { + case PXICMD_NONE: + ARM_WFI(); + break; + + case PXICMD_LEGACY_BOOT: + runCmdProcessor = false; + resp = 0; + break; + + case PXICMD_GET_SHMEM_ADDRESS: + resp = (u32)&sharedMem; + break; + + case PXICMD_I2C_OP: + { + u32 devId, regAddr, size; + + devId = (args[0] & 0xff); + regAddr = (args[0] >> 8) & 0xff; + size = (args[0] >> 16) % I2C_SHARED_BUFSZ; + + if (args[0] & BIT(31)) { + resp = I2C_writeRegBuf(devId, regAddr, sharedMem.i2cBuffer, size); + } else { + resp = I2C_readRegBuf(devId, regAddr, sharedMem.i2cBuffer, size); + } + break; + } + + case PXICMD_NVRAM_ONLINE: + resp = (NVRAM_Status() & NVRAM_SR_WIP) == 0; + break; + + case PXICMD_NVRAM_READ: + NVRAM_Read(args[0], (u32*)sharedMem.spiBuffer, args[1]); + resp = 0; + break; + + case PXICMD_SET_NOTIFY_LED: + mcuSetStatusLED(args[0], args[1]); + resp = 0; + break; + + case PXICMD_SET_BRIGHTNESS: + { + s32 newbrightness = (s32)args[0]; + resp = GFX_getBrightness(); + #ifndef FIXED_BRIGHTNESS + if ((newbrightness > 0) && (newbrightness < 0x100)) { + GFX_setBrightness(newbrightness, newbrightness); + auto_brightness = false; + } else { + prev_bright_lvl = -1; + auto_brightness = true; + } + #endif + break; + } + + default: + resp = 0xFFFFFFFF; + break; } - } while(!legacy_boot); + + if (cmd != PXICMD_NONE) + PXI_Send(resp); // was a command sent from the ARM9, send a response + } while(runCmdProcessor); + + // perform deinit in reverse order + gicDisableInterrupt(VBLANK_INTERRUPT); + gicDisableInterrupt(PXI_RX_INTERRUPT); + + // unconditionally reinitialize the screens + // in RGB24 framebuffer mode + GFX_init(GFX_BGR8); + + gicDisableInterrupt(MCU_INTERRUPT); SYS_CoreZeroShutdown(); SYS_CoreShutdown(); diff --git a/arm9/source/common/hid.c b/arm9/source/common/hid.c index bc5f12e..a0aa264 100644 --- a/arm9/source/common/hid.c +++ b/arm9/source/common/hid.c @@ -19,7 +19,7 @@ static void SetNotificationLED(u32 period_ms, u32 rgb565_color) (rgb565_color >> 5) << (8+2) | (rgb565_color << 3)); u32 args[] = {period_ms, rgb888_color}; - PXI_DoCMD(PXI_NOTIFY_LED, args, 2); + PXI_DoCMD(PXICMD_SET_NOTIFY_LED, args, 2); } // there's some weird thing going on when reading this diff --git a/arm9/source/common/power.c b/arm9/source/common/power.c index 62ffbfa..57eeac4 100644 --- a/arm9/source/common/power.c +++ b/arm9/source/common/power.c @@ -12,7 +12,7 @@ u32 SetScreenBrightness(int level) { arg = 0; } - return PXI_DoCMD(PXI_BRIGHTNESS, &arg, 1); + return PXI_DoCMD(PXICMD_SET_BRIGHTNESS, &arg, 1); } u32 GetBatteryPercent() { diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index c56a0b5..a9354f1 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -108,8 +108,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) { if (delete) PathDelete(bootpath); DeinitExtFS(); DeinitSDCardFS(); - PXI_DoCMD(PXI_SET_VMODE, (u32[]){1}, 1); - PXI_DoCMD(PXI_LEGACY_MODE, NULL, 0); + PXI_DoCMD(PXICMD_LEGACY_BOOT, NULL, 0); BootFirm((FirmHeader*) firm, fixpath); while(1); } diff --git a/arm9/source/main.c b/arm9/source/main.c index 36be0c0..afceb75 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -7,7 +7,7 @@ #include "hid.h" -SystemSHMEM *shmemGlobalBase; +SystemSHMEM *shmemBasePtr; void main(int argc, char** argv, int entrypoint) { diff --git a/arm9/source/system/i2c.c b/arm9/source/system/i2c.c index 5a90bc7..e6b7aec 100755 --- a/arm9/source/system/i2c.c +++ b/arm9/source/system/i2c.c @@ -13,7 +13,7 @@ bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size) if (size >= I2C_SHARED_BUFSZ) return false; - ret = PXI_DoCMD(PXI_I2C_READ, &arg, 1); + ret = PXI_DoCMD(PXICMD_I2C_OP, &arg, 1); ARM_InvDC_Range(ARM_GetSHMEM()->i2cBuffer, size); memcpy(out, ARM_GetSHMEM()->i2cBuffer, size); @@ -23,7 +23,7 @@ bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size) bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size) { int ret; - const u32 arg = devId | (regAddr << 8) | (size << 16); + const u32 arg = devId | (regAddr << 8) | (size << 16) | BIT(31); if (size >= I2C_SHARED_BUFSZ) return false; @@ -33,7 +33,7 @@ bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size) ARM_WbDC_Range(ARM_GetSHMEM()->i2cBuffer, size); ARM_DSB(); - ret = PXI_DoCMD(PXI_I2C_WRITE, &arg, 1); + ret = PXI_DoCMD(PXICMD_I2C_OP, &arg, 1); return ret; } diff --git a/arm9/source/system/spiflash.c b/arm9/source/system/spiflash.c index 6964770..cb1d3f3 100755 --- a/arm9/source/system/spiflash.c +++ b/arm9/source/system/spiflash.c @@ -5,7 +5,7 @@ bool spiflash_get_status(void) { - return PXI_DoCMD(PXI_NVRAM_ONLINE, NULL, 0); + return PXI_DoCMD(PXICMD_NVRAM_ONLINE, NULL, 0); } bool spiflash_read(u32 offset, u32 size, u8 *buf) @@ -19,7 +19,7 @@ bool spiflash_read(u32 offset, u32 size, u8 *buf) args[1] = blksz; ARM_WbDC_Range(ARM_GetSHMEM()->spiBuffer, blksz); - PXI_DoCMD(PXI_NVRAM_READ, args, 2); + PXI_DoCMD(PXICMD_NVRAM_READ, args, 2); ARM_InvDC_Range(ARM_GetSHMEM()->spiBuffer, blksz); ARM_DSB(); memcpy(buf, ARM_GetSHMEM()->spiBuffer, blksz); diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index 2d24d3f..84e79e8 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -1426,8 +1426,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { fixpath[255] = '\0'; DeinitExtFS(); DeinitSDCardFS(); - PXI_DoCMD(PXI_SET_VMODE, (u32[]){1}, 1); - PXI_DoCMD(PXI_LEGACY_MODE, NULL, 0); + PXI_DoCMD(PXICMD_LEGACY_BOOT, NULL, 0); BootFirm((FirmHeader*)(void*)firm, fixpath); while(1); } else if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a bootable firm"); diff --git a/common/pxi.h b/common/pxi.h index 37922d8..27d3f7c 100644 --- a/common/pxi.h +++ b/common/pxi.h @@ -28,18 +28,17 @@ #endif enum { - PXI_LEGACY_MODE = 0, - PXI_GET_SHMEM, - PXI_SET_VMODE, + PXICMD_LEGACY_BOOT = 0, + PXICMD_GET_SHMEM_ADDRESS, - PXI_I2C_READ, - PXI_I2C_WRITE, + PXICMD_I2C_OP, + PXICMD_NVRAM_ONLINE, + PXICMD_NVRAM_READ, - PXI_NVRAM_ONLINE, - PXI_NVRAM_READ, + PXICMD_SET_NOTIFY_LED, + PXICMD_SET_BRIGHTNESS, - PXI_NOTIFY_LED, - PXI_BRIGHTNESS + PXICMD_NONE, }; /* diff --git a/common/shmem.h b/common/shmem.h index 0370a5c..660983f 100755 --- a/common/shmem.h +++ b/common/shmem.h @@ -36,7 +36,7 @@ typedef struct { #ifdef ARM9 #include -extern SystemSHMEM *shmemGlobalBase; +extern SystemSHMEM *shmemBasePtr; static inline SystemSHMEM *ARM_GetSHMEM(void) { @@ -44,11 +44,11 @@ static inline SystemSHMEM *ARM_GetSHMEM(void) // insert a compiler barrier to force the compiler not to assume // memory values will remain constant in between calls to getSHMEM asm_v("":::"memory", "cc"); - return shmemGlobalBase; + return shmemBasePtr; } static inline void ARM_InitSHMEM(void) { - shmemGlobalBase = (SystemSHMEM*)PXI_DoCMD(PXI_GET_SHMEM, NULL, 0); + shmemBasePtr = (SystemSHMEM*)PXI_DoCMD(PXICMD_GET_SHMEM_ADDRESS, NULL, 0); } #endif