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
This commit is contained in:
Wolfvak 2020-08-25 10:49:52 -03:00
parent b4fccd4a3c
commit e4dd8511cd
11 changed files with 185 additions and 184 deletions

View File

@ -63,57 +63,73 @@ typedef struct {
static u8 volumeSliderValue; static u8 volumeSliderValue;
static u32 shellState; 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) if (!atomic_exchange(&pendingUpdate, 0))
{ return;
shellState = open ? SHELL_OPEN : SHELL_CLOSED;
// 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) u32 mcuEventTest(u32 mask)
{ {
mcuEventUpdate();
return atomic_load(&pendingEvents) & mask; return atomic_load(&pendingEvents) & mask;
} }
u32 mcuEventClear(u32 mask) u32 mcuEventClear(u32 mask)
{ {
mcuEventUpdate();
return atomic_fetch_and(&pendingEvents, ~mask) & mask; return atomic_fetch_and(&pendingEvents, ~mask) & mask;
} }
u32 mcuEventWait(u32 mask) u32 mcuEventWait(u32 mask)
{ {
do { do {
mcuEventUpdate();
u32 x = mcuEventClear(mask); u32 x = mcuEventClear(mask);
if (x) return x; if (x) return x;
ARM_WFE();
} while(1); } while(1);
} }
u8 mcuGetVolumeSlider(void) u8 mcuGetVolumeSlider(void)
{ {
mcuEventUpdate();
return volumeSliderValue; return volumeSliderValue;
} }
u32 mcuGetSpecialHID(void) u32 mcuGetSpecialHID(void)
{ {
u32 ret = shellState, pend = mcuEventClear(MCUEV_HID_MASK); u32 ret, pend = mcuEventClear(MCUEV_HID_MASK);
// hopefully gets unrolled // hopefully gets unrolled
if (pend & (MCUEV_HID_PWR_DOWN | MCUEV_HID_PWR_HOLD)) if (pend & (MCUEV_HID_PWR_DOWN | MCUEV_HID_PWR_HOLD))
ret |= BUTTON_POWER; ret |= BUTTON_POWER;
if (pend & MCUEV_HID_HOME_DOWN) if (pend & MCUEV_HID_HOME_DOWN)
ret |= BUTTON_HOME; ret |= BUTTON_HOME;
if (pend & MCUEV_HID_HOME_UP) if (pend & MCUEV_HID_HOME_UP)
ret &= ~BUTTON_HOME; ret &= ~BUTTON_HOME;
return ret | shellState;
return ret;
} }
void mcuSetStatusLED(u32 period_ms, u32 color) void mcuSetStatusLED(u32 period_ms, u32 color)
@ -162,25 +178,7 @@ void mcuResetLEDs(void)
void mcuInterruptHandler(u32 __attribute__((unused)) irqn) void mcuInterruptHandler(u32 __attribute__((unused)) irqn)
{ {
u32 mask; atomic_store(&pendingUpdate, 1);
// 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);
} }
void mcuReset(void) void mcuReset(void)
@ -188,6 +186,7 @@ void mcuReset(void)
u32 intmask = 0; u32 intmask = 0;
atomic_init(&pendingEvents, 0); atomic_init(&pendingEvents, 0);
atomic_init(&pendingUpdate, 0);
// set register mask and clear any pending registers // set register mask and clear any pending registers
mcuWriteRegBuf(MCUREG_INT_EN, (const u8*)&intmask, sizeof(intmask)); mcuWriteRegBuf(MCUREG_INT_EN, (const u8*)&intmask, sizeof(intmask));
@ -195,8 +194,8 @@ void mcuReset(void)
mcuResetLEDs(); mcuResetLEDs();
mcuUpdateVolumeSlider(); volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER);
mcuUpdateShellState(true); shellState = SHELL_OPEN;
// assume the shell is always open on boot // assume the shell is always open on boot
// knowing the average 3DS user, there will be plenty // knowing the average 3DS user, there will be plenty
// of laughs when this comes back to bite us in the rear // of laughs when this comes back to bite us in the rear

View File

@ -22,6 +22,8 @@
#include <arm.h> #include <arm.h>
#include <pxi.h> #include <pxi.h>
#include <stdatomic.h>
#include "arm/gic.h" #include "arm/gic.h"
#include "hw/hid.h" #include "hw/hid.h"
@ -43,10 +45,20 @@ static int prev_bright_lvl;
static bool auto_brightness; static bool auto_brightness;
#endif #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 #ifndef FIXED_BRIGHTNESS
int cur_bright_lvl = (mcuGetVolumeSlider() >> 2) % countof(brightness_lvls); int cur_bright_lvl = (mcuGetVolumeSlider() >> 2) % countof(brightness_lvls);
if ((cur_bright_lvl != prev_bright_lvl) && auto_brightness) { if ((cur_bright_lvl != prev_bright_lvl) && auto_brightness) {
@ -56,128 +68,43 @@ void vblankHandler(u32 __attribute__((unused)) irqn)
} }
#endif #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; static void pxiRxHandler(u32 __attribute__((unused)) irqn)
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(); msg = PXI_Recv();
cmd = msg & 0xFFFF; lo = msg & 0xFFFF;
argc = msg >> 16; hi = msg >> 16;
if (argc >= PXI_MAX_ARGS) { *cmd = lo;
PXI_Send(0xFFFFFFFF); PXI_RecvArray(args, hi);
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:
{
<var declarations/assignments>
<execute the command>
<set the return value>
break;
}
*/
default:
ret = 0xFFFFFFFF;
break;
}
PXI_Send(ret);
} }
void __attribute__((noreturn)) MainLoop(void) void __attribute__((noreturn)) MainLoop(void)
{ {
bool runCmdProcessor = true;
#ifdef FIXED_BRIGHTNESS #ifdef FIXED_BRIGHTNESS
LCD_SetBrightness(FIXED_BRIGHTNESS); LCD_SetBrightness(FIXED_BRIGHTNESS);
#else #else
@ -185,11 +112,14 @@ void __attribute__((noreturn)) MainLoop(void)
auto_brightness = true; auto_brightness = true;
#endif #endif
memset(&SharedMemoryState, 0, sizeof(SharedMemoryState)); // initialize state stuff
atomic_init(&pendingVblank, 0);
atomic_init(&pendingPxiRx, 0);
memset(&sharedMem, 0, sizeof(sharedMem));
// configure interrupts // configure interrupts
gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO2, pxiRxHandler); gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO0, pxiRxHandler);
gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO1, mcuInterruptHandler); gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO0, mcuInterruptHandler);
gicSetInterruptConfig(VBLANK_INTERRUPT, BIT(0), GIC_PRIO0, vblankHandler); gicSetInterruptConfig(VBLANK_INTERRUPT, BIT(0), GIC_PRIO0, vblankHandler);
// enable interrupts // enable interrupts
@ -205,18 +135,93 @@ void __attribute__((noreturn)) MainLoop(void)
// ARM9 won't try anything funny until this point // ARM9 won't try anything funny until this point
PXI_Barrier(ARM11_READY_BARRIER); 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 { do {
ARM_WFI(); u32 cmd, resp, args[PXI_MAX_ARGS];
// handle shell events vblankUpdate();
u32 shell = mcuEventClear(MCUEV_HID_SHELL_OPEN | MCUEV_HID_SHELL_CLOSE); pxiRxUpdate(&cmd, args);
if (shell & MCUEV_HID_SHELL_CLOSE) {
GFX_powerOffBacklights(GFX_BLIGHT_BOTH); switch(cmd) {
} else if (shell & MCUEV_HID_SHELL_OPEN) { case PXICMD_NONE:
GFX_powerOnBacklights(GFX_BLIGHT_BOTH); 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_CoreZeroShutdown();
SYS_CoreShutdown(); SYS_CoreShutdown();

View File

@ -19,7 +19,7 @@ static void SetNotificationLED(u32 period_ms, u32 rgb565_color)
(rgb565_color >> 5) << (8+2) | (rgb565_color >> 5) << (8+2) |
(rgb565_color << 3)); (rgb565_color << 3));
u32 args[] = {period_ms, rgb888_color}; 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 // there's some weird thing going on when reading this

View File

@ -12,7 +12,7 @@ u32 SetScreenBrightness(int level) {
arg = 0; arg = 0;
} }
return PXI_DoCMD(PXI_BRIGHTNESS, &arg, 1); return PXI_DoCMD(PXICMD_SET_BRIGHTNESS, &arg, 1);
} }
u32 GetBatteryPercent() { u32 GetBatteryPercent() {

View File

@ -108,8 +108,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
if (delete) PathDelete(bootpath); if (delete) PathDelete(bootpath);
DeinitExtFS(); DeinitExtFS();
DeinitSDCardFS(); DeinitSDCardFS();
PXI_DoCMD(PXI_SET_VMODE, (u32[]){1}, 1); PXI_DoCMD(PXICMD_LEGACY_BOOT, NULL, 0);
PXI_DoCMD(PXI_LEGACY_MODE, NULL, 0);
BootFirm((FirmHeader*) firm, fixpath); BootFirm((FirmHeader*) firm, fixpath);
while(1); while(1);
} }

View File

@ -7,7 +7,7 @@
#include "hid.h" #include "hid.h"
SystemSHMEM *shmemGlobalBase; SystemSHMEM *shmemBasePtr;
void main(int argc, char** argv, int entrypoint) void main(int argc, char** argv, int entrypoint)
{ {

View File

@ -13,7 +13,7 @@ bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
if (size >= I2C_SHARED_BUFSZ) if (size >= I2C_SHARED_BUFSZ)
return false; 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); ARM_InvDC_Range(ARM_GetSHMEM()->i2cBuffer, size);
memcpy(out, 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) bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size)
{ {
int ret; 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) if (size >= I2C_SHARED_BUFSZ)
return false; 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_WbDC_Range(ARM_GetSHMEM()->i2cBuffer, size);
ARM_DSB(); ARM_DSB();
ret = PXI_DoCMD(PXI_I2C_WRITE, &arg, 1); ret = PXI_DoCMD(PXICMD_I2C_OP, &arg, 1);
return ret; return ret;
} }

View File

@ -5,7 +5,7 @@
bool spiflash_get_status(void) 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) 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; args[1] = blksz;
ARM_WbDC_Range(ARM_GetSHMEM()->spiBuffer, 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_InvDC_Range(ARM_GetSHMEM()->spiBuffer, blksz);
ARM_DSB(); ARM_DSB();
memcpy(buf, ARM_GetSHMEM()->spiBuffer, blksz); memcpy(buf, ARM_GetSHMEM()->spiBuffer, blksz);

View File

@ -1426,8 +1426,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
fixpath[255] = '\0'; fixpath[255] = '\0';
DeinitExtFS(); DeinitExtFS();
DeinitSDCardFS(); DeinitSDCardFS();
PXI_DoCMD(PXI_SET_VMODE, (u32[]){1}, 1); PXI_DoCMD(PXICMD_LEGACY_BOOT, NULL, 0);
PXI_DoCMD(PXI_LEGACY_MODE, NULL, 0);
BootFirm((FirmHeader*)(void*)firm, fixpath); BootFirm((FirmHeader*)(void*)firm, fixpath);
while(1); while(1);
} else if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a bootable firm"); } else if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a bootable firm");

View File

@ -28,18 +28,17 @@
#endif #endif
enum { enum {
PXI_LEGACY_MODE = 0, PXICMD_LEGACY_BOOT = 0,
PXI_GET_SHMEM, PXICMD_GET_SHMEM_ADDRESS,
PXI_SET_VMODE,
PXI_I2C_READ, PXICMD_I2C_OP,
PXI_I2C_WRITE, PXICMD_NVRAM_ONLINE,
PXICMD_NVRAM_READ,
PXI_NVRAM_ONLINE, PXICMD_SET_NOTIFY_LED,
PXI_NVRAM_READ, PXICMD_SET_BRIGHTNESS,
PXI_NOTIFY_LED, PXICMD_NONE,
PXI_BRIGHTNESS
}; };
/* /*

View File

@ -36,7 +36,7 @@ typedef struct {
#ifdef ARM9 #ifdef ARM9
#include <pxi.h> #include <pxi.h>
extern SystemSHMEM *shmemGlobalBase; extern SystemSHMEM *shmemBasePtr;
static inline SystemSHMEM *ARM_GetSHMEM(void) 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 // insert a compiler barrier to force the compiler not to assume
// memory values will remain constant in between calls to getSHMEM // memory values will remain constant in between calls to getSHMEM
asm_v("":::"memory", "cc"); asm_v("":::"memory", "cc");
return shmemGlobalBase; return shmemBasePtr;
} }
static inline void ARM_InitSHMEM(void) 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 #endif