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 u32 shellState;
static _Atomic(u32) pendingEvents;
static _Atomic(u32) pendingEvents, pendingUpdate;
static void mcuUpdateVolumeSlider(void)
static void mcuEventUpdate(void)
{
u32 mask;
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);
}
static void mcuUpdateShellState(bool open)
{
shellState = open ? SHELL_OPEN : SHELL_CLOSED;
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

View File

@ -22,6 +22,8 @@
#include <arm.h>
#include <pxi.h>
#include <stdatomic.h>
#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:
{
<var declarations/assignments>
<execute the command>
<set the return value>
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);
}
} while(!legacy_boot);
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;
}
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();

View File

@ -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

View File

@ -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() {

View File

@ -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);
}

View File

@ -7,7 +7,7 @@
#include "hid.h"
SystemSHMEM *shmemGlobalBase;
SystemSHMEM *shmemBasePtr;
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)
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;
}

View File

@ -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);

View File

@ -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");

View File

@ -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,
};
/*

View File

@ -36,7 +36,7 @@ typedef struct {
#ifdef ARM9
#include <pxi.h>
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