Compare commits

...

5 Commits

Author SHA1 Message Date
Wolfvak
7d2e729ac6 Merge remote-tracking branch 'origin/master' into mcuev 2020-11-18 22:12:56 -03:00
Wolfvak
670ff3345c event model seems to work
refactors all the ugly "pendingX" atomic operations into a single "Event" subsystem/interface thing with two operations

moves all existing code to use this instead

also changes the "bkpt" macro to also indicate unreachable code
2020-11-18 17:06:45 -03:00
Wolfvak
6b5ddf94d8 simplify the sharedmem buffers
also made the wait on boot unconditional
2020-11-18 17:06:30 -03:00
Wolfvak
e4dd8511cd 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
2020-08-25 10:49:52 -03:00
Wolfvak
b4fccd4a3c initial mcu events implementation
- backlight power control is reinstated but currently buggy, for some reason the __builtin_trap is tripped on GFX_powerOnBacklights and GFX_powerOffBacklights

- also refactored a bunch of other code pertaining to mcu and other hw init, moved the gpu init to a later point since lcd init now depends on mcu events
2020-08-21 23:36:00 -03:00
18 changed files with 387 additions and 315 deletions

View File

@ -19,8 +19,12 @@
#include <common.h> #include <common.h>
#include <arm.h> #include <arm.h>
#include <stdatomic.h>
#include "arm/gic.h" #include "arm/gic.h"
#include "system/event.h"
/* Generic Interrupt Controller Registers */ /* Generic Interrupt Controller Registers */
#define REG_GIC(cpu, o, t) REG_ARM_PMR(0x200 + ((cpu) * 0x100) + (o), t) #define REG_GIC(cpu, o, t) REG_ARM_PMR(0x200 + ((cpu) * 0x100) + (o), t)
@ -72,6 +76,7 @@
#define IRQN_IS_VALID(n) ((n) < DIC_MAX_IRQ) #define IRQN_IS_VALID(n) ((n) < DIC_MAX_IRQ)
static gicIrqHandler gicIrqHandlers[DIC_MAX_IRQ]; static gicIrqHandler gicIrqHandlers[DIC_MAX_IRQ];
static _Atomic(u32) gicIrqPending[DIC_MAX_IRQ / 32];
static struct { static struct {
u8 tgt; u8 tgt;
@ -106,14 +111,13 @@ static u8 gicGetDefaultIrqCfg(u32 irqn) {
return gicDefaultIrqCfg[i].mode; return gicDefaultIrqCfg[i].mode;
} }
// TODO: would it be considerably faster to use bsearch? // TODO: would it be considerably faster to use bsearch?
return GIC_RISINGEDGE_1N; return GIC_RISINGEDGE_1N;
} }
void gicTopHandler(void) void gicTopHandler(void)
{ {
while(1) { while(1) {
u32 irqn; u32 irqn, irqsource, index, mask;
/** /**
If more than one of these CPUs reads the Interrupt Acknowledge Register at the If more than one of these CPUs reads the Interrupt Acknowledge Register at the
@ -121,16 +125,22 @@ void gicTopHandler(void)
routine must ensure that only one of them tries to process the interrupt, with the routine must ensure that only one of them tries to process the interrupt, with the
others returning after writing the ID to the End of Interrupt Register. others returning after writing the ID to the End of Interrupt Register.
*/ */
irqn = REG_GIC_IRQACK(GIC_THIS_CPU_ALIAS); irqsource = REG_GIC_IRQACK(GIC_THIS_CPU_ALIAS);
if (irqn == GIC_IRQ_SPURIOUS) // no further processing is needed if (irqsource == GIC_IRQ_SPURIOUS) // no further processing is needed
break; break;
(gicIrqHandlers[irqn & ~IRQN_SRC_MASK])(irqn); irqn = irqsource & ~IRQN_SRC_MASK;
index = irqn / 32;
mask = BIT(irqn % 32);
atomic_fetch_or(&gicIrqPending[index], mask);
(gicIrqHandlers[irqn])(irqsource);
// if the id is < 16, the source CPU can be obtained from irqn // if the id is < 16, the source CPU can be obtained from irqn
// if the handler isn't set, it'll try to branch to 0 and trigger a prefetch abort // if the handler isn't set, it'll try to branch to 0 and trigger a prefetch abort
REG_GIC_IRQEND(GIC_THIS_CPU_ALIAS) = irqn; REG_GIC_IRQEND(GIC_THIS_CPU_ALIAS) = irqsource;
} }
} }
@ -152,6 +162,7 @@ void gicGlobalReset(void)
gicn = MAX_CPU; gicn = MAX_CPU;
// clear the interrupt handler and config table // clear the interrupt handler and config table
getEventIRQ()->reset();
memset(gicIrqHandlers, 0, sizeof(gicIrqHandlers)); memset(gicIrqHandlers, 0, sizeof(gicIrqHandlers));
memset(gicIrqConfig, 0, sizeof(gicIrqConfig)); memset(gicIrqConfig, 0, sizeof(gicIrqConfig));
@ -263,3 +274,27 @@ void gicTriggerSoftInterrupt(u32 softirq)
{ {
REG_DIC_SOFTINT = softirq; REG_DIC_SOFTINT = softirq;
} }
static void irqEvReset(void) {
memset(&gicIrqPending, 0, sizeof(gicIrqPending));
}
static u32 irqEvTest(u32 param, u32 clear) {
u32 index, tstmask, clrmask;
if (param >= DIC_MAX_IRQ)
bkpt;
index = param / 32;
tstmask = BIT(param % 32);
clrmask = clear ? tstmask : 0;
return !!(atomic_fetch_and(&gicIrqPending[index], ~clrmask) & tstmask);
}
static const EventInterface evIRQ = {
.reset = irqEvReset,
.test = irqEvTest
};
const EventInterface *getEventIRQ(void) {
return &evIRQ;
}

View File

@ -27,6 +27,8 @@
#include "hw/mcu.h" #include "hw/mcu.h"
#include "hw/gpulcd.h" #include "hw/gpulcd.h"
#include "system/event.h"
static struct static struct
{ {
u16 lcdIds; // Bits 0-7 top screen, 8-15 bottom screen. u16 lcdIds; // Bits 0-7 top screen, 8-15 bottom screen.
@ -112,13 +114,13 @@ unsigned GFX_init(GfxFbFmt mode)
TIMER_WaitMS(10); TIMER_WaitMS(10);
resetLcdsMaybe(); resetLcdsMaybe();
MCU_controlLCDPower(2u); // Power on LCDs. MCU_controlLCDPower(2u); // Power on LCDs.
if(MCU_waitEvents(0x3Fu<<24) != 2u<<24) __builtin_trap(); if(eventWait(getEventMCU(), 0x3Fu<<24, 0x3Fu<<24) != 2u<<24) __builtin_trap();
waitLcdsReady(); waitLcdsReady();
REG_LCD_ABL0_LIGHT_PWM = 0x1023E; REG_LCD_ABL0_LIGHT_PWM = 0x1023E;
REG_LCD_ABL1_LIGHT_PWM = 0x1023E; REG_LCD_ABL1_LIGHT_PWM = 0x1023E;
MCU_controlLCDPower(0x28u); // Power on backlights. MCU_controlLCDPower(0x28u); // Power on backlights.
if(MCU_waitEvents(0x3Fu<<24) != 0x28u<<24) __builtin_trap(); if(eventWait(getEventMCU(), 0x3Fu<<24, 0x3Fu<<24) != 0x28u<<24) __builtin_trap();
g_gfxState.lcdPower = 0x15; // All on. g_gfxState.lcdPower = 0x15; // All on.
// Make sure the fills finished. // Make sure the fills finished.
@ -212,8 +214,9 @@ void GFX_powerOnBacklights(GfxBlight mask)
mask <<= 1; mask <<= 1;
MCU_controlLCDPower(mask); // Power on backlights. MCU_controlLCDPower(mask); // Power on backlights.
if(MCU_waitEvents(0x3Fu<<24) != (u32)mask<<24) eventWait(getEventMCU(), 0x3F<<24, 0x3F<<24);
__builtin_trap(); /*if(mcuEventWait(0x3Fu<<24) != (u32)mask<<24)
__builtin_trap();*/
} }
void GFX_powerOffBacklights(GfxBlight mask) void GFX_powerOffBacklights(GfxBlight mask)
@ -221,8 +224,9 @@ void GFX_powerOffBacklights(GfxBlight mask)
g_gfxState.lcdPower &= ~mask; g_gfxState.lcdPower &= ~mask;
MCU_controlLCDPower(mask); // Power off backlights. MCU_controlLCDPower(mask); // Power off backlights.
if(MCU_waitEvents(0x3Fu<<24) != (u32)mask<<24) eventWait(getEventMCU(), 0x3F<<24, 0x3F<<24);
__builtin_trap(); /*if(mcuEventWait(0x3Fu<<24) != (u32)mask<<24)
__builtin_trap();*/
} }
u8 GFX_getBrightness(void) u8 GFX_getBrightness(void)

View File

@ -52,7 +52,7 @@ u64 HID_GetState(void)
CODEC_Get(&codec); CODEC_Get(&codec);
ret = REG_HID | MCU_GetSpecialHID(); ret = REG_HID | mcuGetSpecialHID();
if (!(ret & BUTTON_ARROW)) if (!(ret & BUTTON_ARROW))
ret |= HID_ConvertCPAD(codec.cpad_x, codec.cpad_y); ret |= HID_ConvertCPAD(codec.cpad_x, codec.cpad_y);

View File

@ -20,40 +20,37 @@
#include <types.h> #include <types.h>
#include <arm.h> #include <arm.h>
#include <stdatomic.h>
#include "arm/timer.h" #include "arm/timer.h"
#include "hw/gpio.h" #include "hw/gpio.h"
#include "hw/gpulcd.h" #include "hw/gpulcd.h"
#include "hw/mcu.h" #include "hw/mcu.h"
enum { #include "system/event.h"
MCU_PWR_BTN = 0,
MCU_PWR_HOLD = 1, #define MCUEV_HID_MASK ( \
MCU_HOME_BTN = 2, MCUEV_HID_PWR_DOWN | MCUEV_HID_PWR_HOLD | \
MCU_HOME_LIFT = 3, MCUEV_HID_HOME_DOWN | MCUEV_HID_HOME_UP | MCUEV_HID_WIFI_SWITCH)
MCU_WIFI_SWITCH = 4,
MCU_SHELL_CLOSE = 5,
MCU_SHELL_OPEN = 6,
MCU_VOL_SLIDER = 22,
};
enum { enum {
REG_VOL_SLIDER = 0x09, MCUREG_VOLUME_SLIDER = 0x09,
REG_BATTERY_LEVEL = 0x0B, MCUREG_BATTERY_LEVEL = 0x0B,
REG_CONSOLE_STATE = 0x0F, MCUREG_CONSOLE_STATE = 0x0F,
REG_INT_MASK = 0x10, MCUREG_INT_MASK = 0x10,
REG_INT_EN = 0x18, MCUREG_INT_EN = 0x18,
REG_LCD_STATE = 0x22, MCUREG_LCD_STATE = 0x22,
REG_LED_WIFI = 0x2A, MCUREG_LED_WIFI = 0x2A,
REG_LED_CAMERA = 0x2B, MCUREG_LED_CAMERA = 0x2B,
REG_LED_SLIDER = 0x2C, MCUREG_LED_SLIDER = 0x2C,
REG_LED_NOTIF = 0x2D, MCUREG_LED_STATUS = 0x2D,
REG_RTC = 0x30, MCUREG_RTC = 0x30,
}; };
typedef struct { typedef struct {
@ -64,47 +61,73 @@ typedef struct {
u32 red[8]; u32 red[8];
u32 green[8]; u32 green[8];
u32 blue[8]; u32 blue[8];
} PACKED_STRUCT MCU_NotificationLED; } PACKED_STRUCT mcuStatusLED;
static u8 cached_volume_slider = 0; static u8 volumeSliderValue;
static u32 spec_hid = 0, shell_state = 0; static u32 shellState;
static _Atomic(u32) pendingEvents;
static void MCU_UpdateVolumeSlider(void) static void mcuEventUpdate(void)
{ {
cached_volume_slider = MCU_ReadReg(REG_VOL_SLIDER); u32 mask;
// lazily update the mask on each test attempt
if (!getEventIRQ()->test(MCU_INTERRUPT, true))
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;
} }
static void MCU_UpdateShellState(bool open) if (mask & MCUEV_HID_SHELL_CLOSE) {
{ shellState = SHELL_CLOSED;
shell_state = open ? SHELL_OPEN : SHELL_CLOSED;
} }
u8 MCU_GetVolumeSlider(void) atomic_fetch_or(&pendingEvents, mask);
{
return cached_volume_slider;
} }
u32 MCU_GetSpecialHID(void) u8 mcuGetVolumeSlider(void)
{ {
u32 ret = spec_hid | shell_state; mcuEventUpdate();
spec_hid = 0; return volumeSliderValue;
return ret;
} }
void MCU_SetNotificationLED(u32 period_ms, u32 color) u32 mcuGetSpecialHID(void)
{
u32 ret = 0, pend = getEventMCU()->test(MCUEV_HID_MASK, 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 | shellState;
}
void mcuSetStatusLED(u32 period_ms, u32 color)
{ {
u32 r, g, b; u32 r, g, b;
MCU_NotificationLED led_state; mcuStatusLED ledState;
// handle proper non-zero periods // handle proper non-zero periods
// so small the hardware can't handle it // so small the hardware can't handle it
if (period_ms != 0 && period_ms < 63) if (period_ms != 0 && period_ms < 63)
period_ms = 63; period_ms = 63;
led_state.delay = (period_ms * 0x10) / 1000; ledState.delay = (period_ms * 0x10) / 1000;
led_state.smoothing = 0x40; ledState.smoothing = 0x40;
led_state.loop_delay = 0x10; ledState.loop_delay = 0x10;
led_state.unk = 0; ledState.unk = 0;
// all colors look like 0x00ZZ00ZZ // all colors look like 0x00ZZ00ZZ
// in order to alternate between // in order to alternate between
@ -112,93 +135,65 @@ void MCU_SetNotificationLED(u32 period_ms, u32 color)
r = (color >> 16) & 0xFF; r = (color >> 16) & 0xFF;
r |= r << 16; r |= r << 16;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
led_state.red[i] = r; ledState.red[i] = r;
g = (color >> 8) & 0xFF; g = (color >> 8) & 0xFF;
g |= g << 16; g |= g << 16;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
led_state.green[i] = g; ledState.green[i] = g;
b = color & 0xFF; b = color & 0xFF;
b |= b << 16; b |= b << 16;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
led_state.blue[i] = b; ledState.blue[i] = b;
MCU_WriteRegBuf(REG_LED_NOTIF, (const u8*)&led_state, sizeof(led_state)); mcuWriteRegBuf(MCUREG_LED_STATUS, (const u8*)&ledState, sizeof(ledState));
} }
void MCU_ResetLED(void) void mcuResetLEDs(void)
{ {
MCU_WriteReg(REG_LED_WIFI, 0); mcuWriteReg(MCUREG_LED_WIFI, 0);
MCU_WriteReg(REG_LED_CAMERA, 0); mcuWriteReg(MCUREG_LED_CAMERA, 0);
MCU_WriteReg(REG_LED_SLIDER, 0); mcuWriteReg(MCUREG_LED_SLIDER, 0);
MCU_SetNotificationLED(0, 0); mcuSetStatusLED(0, 0);
} }
void MCU_HandleInterrupts(u32 __attribute__((unused)) irqn) void mcuReset(void)
{ {
u32 ints; u32 intmask = 0;
// Reading the pending mask automagically acknowledges atomic_init(&pendingEvents, 0);
// the interrupts so all of them must be processed in one go
MCU_ReadRegBuf(REG_INT_MASK, (u8*)&ints, sizeof(ints));
while(ints != 0) { // set register mask and clear any pending registers
u32 mcu_int_id = 31 - __builtin_clz(ints); mcuWriteRegBuf(MCUREG_INT_EN, (const u8*)&intmask, sizeof(intmask));
mcuReadRegBuf(MCUREG_INT_MASK, (u8*)&intmask, sizeof(intmask));
switch(mcu_int_id) { mcuResetLEDs();
case MCU_PWR_BTN:
case MCU_PWR_HOLD:
spec_hid |= BUTTON_POWER;
break;
case MCU_HOME_BTN: volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER);
spec_hid |= BUTTON_HOME; shellState = SHELL_OPEN;
break; // assume the shell is always open on boot
// knowing the average 3DS user, there will be plenty
case MCU_HOME_LIFT: // of laughs when this comes back to bite us in the rear
spec_hid &= ~BUTTON_HOME;
break;
case MCU_WIFI_SWITCH:
spec_hid |= BUTTON_WIFI;
break;
case MCU_SHELL_OPEN:
MCU_UpdateShellState(true);
MCU_ResetLED();
break;
case MCU_SHELL_CLOSE:
MCU_UpdateShellState(false);
break;
case MCU_VOL_SLIDER:
MCU_UpdateVolumeSlider();
break;
default:
break;
}
ints &= ~BIT(mcu_int_id);
}
}
void MCU_Init(void)
{
u32 clrpend, mask = 0;
shell_state = SHELL_OPEN;
/* set register mask and clear any pending registers */
MCU_WriteRegBuf(REG_INT_EN, (const u8*)&mask, sizeof(mask));
MCU_ReadRegBuf(REG_INT_MASK, (u8*)&clrpend, sizeof(clrpend));
MCU_ResetLED();
MCU_UpdateVolumeSlider();
MCU_UpdateShellState(MCU_ReadReg(REG_CONSOLE_STATE) & BIT(1));
GPIO_setBit(19, 9); GPIO_setBit(19, 9);
} }
static void evReset(void) {
atomic_store(&pendingEvents, 0);
}
static u32 evTest(u32 mask, u32 clear) {
mcuEventUpdate();
return atomic_fetch_and(&pendingEvents, ~clear) & mask;
}
static const EventInterface evMCU = {
.reset = evReset,
.test = evTest
};
const EventInterface *getEventMCU(void) {
return &evMCU;
}

View File

@ -26,51 +26,48 @@
#define MCU_INTERRUPT (0x71) #define MCU_INTERRUPT (0x71)
#define I2C_MCU_DEVICE (3) #define I2C_MCU_DEVICE (3)
u8 MCU_GetVolumeSlider(void); enum {
u32 MCU_GetSpecialHID(void); MCUEV_HID_PWR_DOWN = BIT(0),
MCUEV_HID_PWR_HOLD = BIT(1),
MCUEV_HID_HOME_DOWN = BIT(2),
MCUEV_HID_HOME_UP = BIT(3),
MCUEV_HID_WIFI_SWITCH = BIT(4),
MCUEV_HID_SHELL_CLOSE = BIT(5),
MCUEV_HID_SHELL_OPEN = BIT(6),
MCUEV_HID_VOLUME_SLIDER = BIT(22),
};
void MCU_SetNotificationLED(u32 period_ms, u32 color); u8 mcuGetVolumeSlider(void);
void MCU_ResetLED(void); u32 mcuGetSpecialHID(void);
void MCU_HandleInterrupts(u32 irqn); void mcuSetStatusLED(u32 period_ms, u32 color);
void mcuResetLEDs(void);
void MCU_Init(void); void mcuReset(void);
static inline u8 MCU_ReadReg(u8 addr) static inline u8 mcuReadReg(u8 addr)
{ {
u8 val; u8 val;
I2C_readRegBuf(I2C_MCU_DEVICE, addr, &val, 1); I2C_readRegBuf(I2C_MCU_DEVICE, addr, &val, 1);
return val; return val;
} }
static inline bool MCU_ReadRegBuf(u8 addr, u8 *buf, u32 size) static inline bool mcuReadRegBuf(u8 addr, u8 *buf, u32 size)
{ {
return I2C_readRegBuf(I2C_MCU_DEVICE, addr, buf, size); return I2C_readRegBuf(I2C_MCU_DEVICE, addr, buf, size);
} }
static inline bool MCU_WriteReg(u8 addr, u8 val) static inline bool mcuWriteReg(u8 addr, u8 val)
{ {
return I2C_writeRegBuf(I2C_MCU_DEVICE, addr, &val, 1); return I2C_writeRegBuf(I2C_MCU_DEVICE, addr, &val, 1);
} }
static inline bool MCU_WriteRegBuf(u8 addr, const u8 *buf, u32 size) static inline bool mcuWriteRegBuf(u8 addr, const u8 *buf, u32 size)
{ {
return I2C_writeRegBuf(I2C_MCU_DEVICE, addr, buf, size); return I2C_writeRegBuf(I2C_MCU_DEVICE, addr, buf, size);
} }
static inline u32 MCU_waitEvents(u32 mask) {
u32 v;
while(1) {
TIMER_WaitMS(10);
MCU_ReadRegBuf(0x10, (u8*)&v, 4);
v &= mask;
if (v)
break;
}
return v;
}
static inline void MCU_controlLCDPower(u8 bits) static inline void MCU_controlLCDPower(u8 bits)
{ {
MCU_WriteReg(0x22u, bits); mcuWriteReg(0x22u, bits);
} }

View File

@ -31,6 +31,7 @@
#include "hw/nvram.h" #include "hw/nvram.h"
#include "system/sys.h" #include "system/sys.h"
#include "system/event.h"
static const u8 brLvlTbl[] = { static const u8 brLvlTbl[] = {
0x10, 0x17, 0x1E, 0x25, 0x10, 0x17, 0x1E, 0x25,
@ -44,12 +45,15 @@ static int oldBrLvl;
static bool autoBr; static bool autoBr;
#endif #endif
static SystemSHMEM __attribute__((section(".shared"))) SharedMemoryState; static SystemSHMEM __attribute__((section(".shared"))) sharedMem;
void VBlank_Handler(u32 __attribute__((unused)) irqn) static void vblankUpdate(void)
{ {
if (!getEventIRQ()->test(VBLANK_INTERRUPT, true))
return;
#ifndef FIXED_BRIGHTNESS #ifndef FIXED_BRIGHTNESS
int newBrLvl = (MCU_GetVolumeSlider() >> 2) % countof(brLvlTbl); int newBrLvl = (mcuGetVolumeSlider() >> 2) % countof(brLvlTbl);
if ((newBrLvl != oldBrLvl) && autoBr) { if ((newBrLvl != oldBrLvl) && autoBr) {
oldBrLvl = newBrLvl; oldBrLvl = newBrLvl;
u8 br = brLvlTbl[newBrLvl]; u8 br = brLvlTbl[newBrLvl];
@ -57,97 +61,135 @@ void VBlank_Handler(u32 __attribute__((unused)) irqn)
} }
#endif #endif
SharedMemoryState.hidState.full = HID_GetState(); // handle shell events
static const u32 mcuEvShell = MCUEV_HID_SHELL_OPEN | MCUEV_HID_SHELL_CLOSE;
u32 shell = getEventMCU()->test(mcuEvShell, mcuEvShell);
if (shell & MCUEV_HID_SHELL_CLOSE) {
GFX_powerOffBacklights(GFX_BLIGHT_BOTH);
} else if (shell & MCUEV_HID_SHELL_OPEN) {
GFX_powerOnBacklights(GFX_BLIGHT_BOTH);
} }
static bool legacy_boot = false; sharedMem.hidState.full = HID_GetState();
}
void PXI_RX_Handler(u32 __attribute__((unused)) irqn) static u32 pxiRxUpdate(u32 *args)
{ {
u32 ret, msg, cmd, argc, args[PXI_MAX_ARGS]; u32 msg, lo, hi;
if (!getEventIRQ()->test(PXI_RX_INTERRUPT, true))
return PXICMD_NONE;
msg = PXI_Recv(); msg = PXI_Recv();
cmd = msg & 0xFFFF; lo = msg & 0xFFFF;
argc = msg >> 16; hi = msg >> 16;
if (argc >= PXI_MAX_ARGS) { PXI_RecvArray(args, hi);
PXI_Send(0xFFFFFFFF); return lo;
return;
} }
PXI_RecvArray(args, argc); void __attribute__((noreturn)) MainLoop(void)
switch (cmd) {
case PXI_LEGACY_MODE:
{ {
// TODO: If SMP is enabled, an IPI should be sent here (with a DSB) bool runPxiCmdProcessor = true;
legacy_boot = true;
ret = 0;
break;
}
case PXI_GET_SHMEM: #ifdef FIXED_BRIGHTNESS
{ u8 fixBrLvl = brLvlTbl[clamp(FIXED_BRIGHTNESS, 0, countof(brLvlTbl)-1)];
ret = (u32)&SharedMemoryState; GFX_setBrightness(fixBrLvl, fixBrLvl);
break; #else
} oldBrLvl = -1;
autoBr = true;
#endif
case PXI_SET_VMODE: // initialize state stuff
{ getEventIRQ()->reset();
GFX_init(args[0] ? GFX_BGR8 : GFX_RGB565); getEventMCU()->reset();
ret = 0; memset(&sharedMem, 0, sizeof(sharedMem));
break;
}
case PXI_I2C_READ: // configure interrupts
gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO0, NULL);
gicSetInterruptConfig(VBLANK_INTERRUPT, BIT(0), GIC_PRIO0, NULL);
gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO0, NULL);
// enable interrupts
gicEnableInterrupt(MCU_INTERRUPT);
// perform gpu init after initializing mcu but before
// enabling the pxi system and the vblank handler
GFX_init(GFX_RGB565);
gicEnableInterrupt(PXI_RX_INTERRUPT);
gicEnableInterrupt(VBLANK_INTERRUPT);
// ARM9 won't try anything funny until this point
PXI_Barrier(PXI_BOOT_BARRIER);
// Process commands until the ARM9 tells
// us it's time to boot something else
// also handles VBlank events as needed
do {
u32 pxiCmd, pxiReply, args[PXI_MAX_ARGS];
vblankUpdate();
pxiCmd = pxiRxUpdate(args);
switch(pxiCmd) {
// ignore args and just wait until the next event
case PXICMD_NONE:
ARM_WFI();
break;
// revert to legacy boot mode
case PXICMD_LEGACY_BOOT:
runPxiCmdProcessor = false;
pxiReply = 0;
break;
// returns the shared memory address
case PXICMD_GET_SHMEM_ADDRESS:
pxiReply = (u32)&sharedMem;
break;
// takes in a single argument word and performs either an
// I2C read or write depending on the value of the top bit
case PXICMD_I2C_OP:
{ {
u32 devId, regAddr, size; u32 devId, regAddr, size;
devId = (args[0] & 0xff); devId = (args[0] & 0xff);
regAddr = (args[0] >> 8) & 0xff; regAddr = (args[0] >> 8) & 0xFF;
size = (args[0] >> 16) % I2C_SHARED_BUFSZ; size = (args[0] >> 16) % SHMEM_BUFFER_SIZE;
ret = I2C_readRegBuf(devId, regAddr, SharedMemoryState.i2cBuffer, size); if (args[0] & BIT(31)) {
pxiReply = I2C_writeRegBuf(devId, regAddr, sharedMem.dataBuffer.b, size);
} else {
pxiReply = I2C_readRegBuf(devId, regAddr, sharedMem.dataBuffer.b, size);
}
break; break;
} }
case PXI_I2C_WRITE: // checks whether the NVRAM chip is online (not doing any work)
{ case PXICMD_NVRAM_ONLINE:
u32 devId, regAddr, size; pxiReply = (NVRAM_Status() & NVRAM_SR_WIP) == 0;
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; break;
}
case PXI_NVRAM_ONLINE: // reads data from the NVRAM chip
{ case PXICMD_NVRAM_READ:
ret = (NVRAM_Status() & NVRAM_SR_WIP) == 0; NVRAM_Read(args[0], sharedMem.dataBuffer.w, args[1]);
pxiReply = 0;
break; break;
}
case PXI_NVRAM_READ: // sets the notification LED with the given color and period
{ case PXICMD_SET_NOTIFY_LED:
NVRAM_Read(args[0], (u32*)SharedMemoryState.spiBuffer, args[1]); mcuSetStatusLED(args[0], args[1]);
ret = 0; pxiReply = 0;
break; break;
}
case PXI_NOTIFY_LED: // sets the LCDs brightness (if FIXED_BRIGHTNESS is disabled)
case PXICMD_SET_BRIGHTNESS:
{ {
MCU_SetNotificationLED(args[0], args[1]);
ret = 0;
break;
}
case PXI_BRIGHTNESS:
{
ret = GFX_getBrightness();
#ifndef FIXED_BRIGHTNESS
s32 newbrightness = (s32)args[0]; s32 newbrightness = (s32)args[0];
pxiReply = GFX_getBrightness();
#ifndef FIXED_BRIGHTNESS
if ((newbrightness > 0) && (newbrightness < 0x100)) { if ((newbrightness > 0) && (newbrightness < 0x100)) {
GFX_setBrightness(newbrightness, newbrightness); GFX_setBrightness(newbrightness, newbrightness);
autoBr = false; autoBr = false;
@ -159,51 +201,25 @@ void PXI_RX_Handler(u32 __attribute__((unused)) irqn)
break; break;
} }
/* New CMD template: // replies -1 on default
case CMD_ID:
{
<var declarations/assignments>
<execute the command>
<set the return value>
break;
}
*/
default: default:
ret = 0xFFFFFFFF; pxiReply = 0xFFFFFFFF;
break; break;
} }
PXI_Send(ret); if (pxiCmd != PXICMD_NONE)
} PXI_Send(pxiReply); // was a command sent from the ARM9, send a response
} while(runPxiCmdProcessor);
void __attribute__((noreturn)) MainLoop(void) // perform deinit in reverse order
{ gicDisableInterrupt(VBLANK_INTERRUPT);
#ifdef FIXED_BRIGHTNESS gicDisableInterrupt(PXI_RX_INTERRUPT);
u8 fixBrLvl = brLvlTbl[clamp(FIXED_BRIGHTNESS, 0, countof(brLvlTbl)-1)];
GFX_setBrightness(fixBrLvl, fixBrLvl);
#else
oldBrLvl = -1;
autoBr = true;
#endif
// configure interrupts // unconditionally reinitialize the screens
gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO2, PXI_RX_Handler); // in RGB24 framebuffer mode
gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO1, MCU_HandleInterrupts); GFX_init(GFX_BGR8);
gicSetInterruptConfig(VBLANK_INTERRUPT, BIT(0), GIC_PRIO0, VBlank_Handler);
// enable interrupts gicDisableInterrupt(MCU_INTERRUPT);
gicEnableInterrupt(PXI_RX_INTERRUPT);
gicEnableInterrupt(MCU_INTERRUPT);
gicEnableInterrupt(VBLANK_INTERRUPT);
// ARM9 won't try anything funny until this point
PXI_Barrier(PXI_BOOT_BARRIER);
// Process IRQs until the ARM9 tells us it's time to boot something else
do {
ARM_WFI();
} while(!legacy_boot);
// Wait for the ARM9 to do its firmlaunch setup // Wait for the ARM9 to do its firmlaunch setup
PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); PXI_Barrier(PXI_FIRMLAUNCH_BARRIER);

View File

@ -0,0 +1,26 @@
#pragma once
#include <types.h>
typedef struct {
void (*reset)(void);
u32 (*test)(u32 param, u32 clear);
} EventInterface;
const EventInterface *getEventIRQ(void);
const EventInterface *getEventMCU(void);
static inline void eventReset(const EventInterface *ei) {
ei->reset();
}
static inline u32 eventTest(const EventInterface *ei, u32 param, u32 clear) {
return ei->test(param, clear);
}
static inline u32 eventWait(const EventInterface *ei, u32 param, u32 clear) {
while(1) {
u32 ret = ei->test(param, clear);
if (ret) return ret;
}
}

View File

@ -107,20 +107,17 @@ void SYS_CoreZeroInit(void)
mmuMapArea(0x20000000, 0x20000000, 128UL << 20, MMU_FLAGS(MMU_CACHE_WB, MMU_READ_WRITE, 1, 1)); mmuMapArea(0x20000000, 0x20000000, 128UL << 20, MMU_FLAGS(MMU_CACHE_WB, MMU_READ_WRITE, 1, 1));
} }
if (SYS_IsNewConsole()) { // screen init magicks
TIMER_WaitMS(150); TIMER_WaitMS(64);
}
// Initialize peripherals // Initialize peripherals
PXI_Reset(); PXI_Reset();
I2C_init(); I2C_init();
MCU_Init(); mcuReset();
SPI_Init(); SPI_Init();
CODEC_Init(); CODEC_Init();
GFX_init(GFX_RGB565);
} }
void SYS_CoreInit(void) void SYS_CoreInit(void)

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);
PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); PXI_Barrier(PXI_FIRMLAUNCH_BARRIER);
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

@ -8,32 +8,33 @@
bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size) bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
{ {
int ret; int ret;
u8 *const dataBuffer = ARM_GetSHMEM()->dataBuffer.b;
const u32 arg = devId | (regAddr << 8) | (size << 16); const u32 arg = devId | (regAddr << 8) | (size << 16);
if (size >= I2C_SHARED_BUFSZ) if (size >= SHMEM_BUFFER_SIZE)
return false; return false;
ret = PXI_DoCMD(PXI_I2C_READ, &arg, 1); ret = PXI_DoCMD(PXICMD_I2C_OP, &arg, 1);
ARM_InvDC_Range(dataBuffer, size);
ARM_InvDC_Range(ARM_GetSHMEM()->i2cBuffer, size); memcpy(out, dataBuffer, size);
memcpy(out, ARM_GetSHMEM()->i2cBuffer, size);
return ret; return ret;
} }
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); u8 *const dataBuffer = ARM_GetSHMEM()->dataBuffer.b;
const u32 arg = devId | (regAddr << 8) | (size << 16) | BIT(31);
if (size >= I2C_SHARED_BUFSZ) if (size >= SHMEM_BUFFER_SIZE)
return false; return false;
ARM_InvDC_Range(ARM_GetSHMEM()->i2cBuffer, size); memcpy(dataBuffer, in, size);
memcpy(ARM_GetSHMEM()->i2cBuffer, in, size); ARM_WbDC_Range(dataBuffer, 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,24 +5,25 @@
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)
{ {
u32 *const dataBuffer = ARM_GetSHMEM()->dataBuffer.w;
u32 args[2]; u32 args[2];
while(size > 0) { while(size > 0) {
u32 blksz = min(size, SPI_SHARED_BUFSZ); u32 blksz = min(size, SHMEM_BUFFER_SIZE);
args[0] = offset; args[0] = offset;
args[1] = blksz; args[1] = blksz;
ARM_WbDC_Range(ARM_GetSHMEM()->spiBuffer, blksz); PXI_DoCMD(PXICMD_NVRAM_READ, args, 2);
PXI_DoCMD(PXI_NVRAM_READ, args, 2); ARM_InvDC_Range(dataBuffer, blksz);
ARM_InvDC_Range(ARM_GetSHMEM()->spiBuffer, blksz);
ARM_DSB(); ARM_DSB();
memcpy(buf, ARM_GetSHMEM()->spiBuffer, blksz);
memcpy(buf, dataBuffer, blksz);
buf += blksz; buf += blksz;
size -= blksz; size -= 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);
PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); PXI_Barrier(PXI_FIRMLAUNCH_BARRIER);
BootFirm((FirmHeader*)(void*)firm, fixpath); BootFirm((FirmHeader*)(void*)firm, fixpath);
while(1); while(1);

View File

@ -53,7 +53,7 @@
(sizeof(x) / sizeof(*(x))) (sizeof(x) / sizeof(*(x)))
#define bkpt \ #define bkpt \
__builtin_trap() do{__builtin_trap(); __builtin_unreachable();}while(0)
#define assert(x) \ #define assert(x) \
(!!(x) ? (void)0 : __builtin_trap()) (!!(x) ? (void)0 : __builtin_trap())

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

@ -20,8 +20,7 @@
#include <arm.h> #include <arm.h>
#define I2C_SHARED_BUFSZ 1024 #define SHMEM_BUFFER_SIZE 2048
#define SPI_SHARED_BUFSZ 1024
typedef struct { typedef struct {
union { union {
@ -29,14 +28,18 @@ typedef struct {
u64 full; u64 full;
} hidState; } hidState;
u8 i2cBuffer[I2C_SHARED_BUFSZ]; union {
u32 spiBuffer[SPI_SHARED_BUFSZ/4]; uint8_t b[SHMEM_BUFFER_SIZE];
uint16_t s[SHMEM_BUFFER_SIZE / 2];
uint32_t w[SHMEM_BUFFER_SIZE / 4];
uint64_t q[SHMEM_BUFFER_SIZE / 8];
} dataBuffer;
} __attribute__((packed, aligned(8))) SystemSHMEM; } __attribute__((packed, aligned(8))) SystemSHMEM;
#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 +47,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