diff --git a/arm11/source/main.c b/arm11/source/main.c index 48fecbc56..7c186cc8f 100644 --- a/arm11/source/main.c +++ b/arm11/source/main.c @@ -123,8 +123,8 @@ static void initScreens(u32 brightnessLevel, struct fb *fbs) for(u32 i = 0; i < 256; i++) *(vu32 *)0x10400584 = 0x10101 * i; - *(vu32 *)0x10202204 = 0x00000000; //unset LCD fill - *(vu32 *)0x10202A04 = 0x00000000; + //*(vu32 *)0x10202204 = 0x00000000; //unset LCD fill + //*(vu32 *)0x10202A04 = 0x00000000; } static void setupFramebuffers(struct fb *fbs) diff --git a/arm9/linker.ld b/arm9/linker.ld index 4365b8c38..4cde67bb3 100644 --- a/arm9/linker.ld +++ b/arm9/linker.ld @@ -45,16 +45,19 @@ SECTIONS chainloader.o(.text*) i2c.o(.text*) + mcu.o(.text*) arm9_exception_handlers.o(.text*) *(.arm9_exception_handlers.rodata*) chainloader.o(.rodata*) i2c.o(.rodata*) + mcu.o(.rodata*) arm9_exception_handlers.o(.rodata*) *(.arm9_exception_handlers.data*) chainloader.o(.data*) i2c.o(.data*) + mcu.o(.data*) arm9_exception_handlers.o(.data*) . = ALIGN(32); } >itcm AT>main :itcm @@ -67,6 +70,7 @@ SECTIONS *(.arm9_exception_handlers.bss*) chainloader.o(.bss* COMMON) i2c.o(.bss* COMMON) + mcu.o(.bss* COMMON) arm9_exception_handlers.o(.bss* COMMON) . = ALIGN(32); PROVIDE (__itcm_end__ = ABSOLUTE(.)); diff --git a/arm9/source/arm9_exception_handlers.c b/arm9/source/arm9_exception_handlers.c index d7fbed38a..2cca61a3a 100644 --- a/arm9/source/arm9_exception_handlers.c +++ b/arm9/source/arm9_exception_handlers.c @@ -25,8 +25,7 @@ */ #include "arm9_exception_handlers.h" -#include "i2c.h" -#include "screen.h" +#include "mcu.h" #define FINAL_BUFFER 0x25000000 @@ -103,10 +102,9 @@ void __attribute__((noreturn)) arm9ExceptionHandlerMain(u32 *registerDump, u32 t //Copy header (actually optimized by the compiler) *(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader; - if(ARESCREENSINITIALIZED) I2C_writeReg(I2C_DEV_MCU, 0x22, 1 << 0); //Shutdown LCD + mcuPowerBacklightsOff(); ((void (*)())0xFFFF0830)(); //Ensure that all memory transfers have completed and that the data cache has been flushed - I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot - while(true); + mcuReboot(); } diff --git a/arm9/source/exceptions.c b/arm9/source/exceptions.c index 07106840e..ae8f9f967 100644 --- a/arm9/source/exceptions.c +++ b/arm9/source/exceptions.c @@ -200,5 +200,5 @@ void detectAndProcessExceptionDumps(void) exit: memset((void *)dumpHeader, 0, dumpHeader->totalSize); - mcuPowerOff(); + powerOff(); } diff --git a/arm9/source/firm.c b/arm9/source/firm.c index 4a5dacd99..b1e0c6f56 100755 --- a/arm9/source/firm.c +++ b/arm9/source/firm.c @@ -37,6 +37,7 @@ #include "screen.h" #include "fmt.h" #include "chainloader.h" +#include "mcu.h" static Firm *firm = (Firm *)0x20001000; @@ -561,6 +562,7 @@ u32 patch1x2xNativeAndSafeFirm(void) void launchFirm(int argc, char **argv) { + mcuFinalize(); prepareArm11ForFirmlaunch(); chainload(argc, argv, firm); } diff --git a/arm9/source/gpio.h b/arm9/source/gpio.h new file mode 100644 index 000000000..4e5dd5fd9 --- /dev/null +++ b/arm9/source/gpio.h @@ -0,0 +1,225 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2021 Aurora Wright, TuxSH +* +* 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 . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include "types.h" + +typedef struct GpioRegisters { + u8 gpio1_data; + u8 _0x001[0x010 - 0x001]; + + u8 gpio2_data; + u8 gpio2_dir; + u8 gpio2_intcfg; + u8 gpio2_inten; + u16 gpio2_data2; + u8 _0x016[0x020 - 0x016]; + + u16 gpio3_data; + u16 gpio3_dir; + u16 gpio3_intcfg; + u16 gpio3_inten; + u16 gpio3_data2; + u8 _0x02a[0x100 - 0x02A]; +} GpioRegisters; + +typedef enum GpioDirection { + GPIO_DIR_INPUT = 0, + GPIO_DIR_OUTPUT = 1, +} GpioDirection; + +typedef enum GpioInterruptConfig { + GPIO_INTCFG_FALLING_EDGE = 0, + GPIO_INTCFG_RISING_EDGE = 1, +} GpioInterruptConfig; + +#define GPIO_PIN(bank, idx) (((bank) << 4) | ((idx) & 0xF)) +#define BANK_OF(pin) ((pin) >> 4) +#define BIT_OF(pin) ((pin) & 0xF) + +typedef enum GpioPin { + // GPIO1 + GPIO_DEBUG_BUTTON = GPIO_PIN(1, 0), // active-low + GPIO_TOUCH_SCREEN = GPIO_PIN(1, 1), // active-low, 0 when the touch screen is pressed + GPIO_SHELL_CLOSED = GPIO_PIN(1, 2), + + // GPIO2 + GPIO_HEADPHONES_INSERTED = GPIO_PIN(2, 0), + GPIO_TWL_DEPOP = GPIO_PIN(2, 1), // active-low + + // GPIO2 (DATA2) + GPIO_WIFI_MODE = GPIO_PIN(4, 0), // 0 is CTR, 1 is MP (DS WiFi) + + // GPIO3 + GPIO_CSTICK_INT = GPIO_PIN(3, 0), + GPIO_IRDA_INT = GPIO_PIN(3, 1), // active-low + GPIO_GYRO_INT = GPIO_PIN(3, 2), + GPIO_CSTICK_STOP = GPIO_PIN(3, 3), // output + GPIO_IRDA_TXRC = GPIO_PIN(3, 4), // output + GPIO_IRDA_RXD = GPIO_PIN(3, 5), // active-low + GPIO_NFC_OUT1 = GPIO_PIN(3, 6), // output + GPIO_NFC_OUT2 = GPIO_PIN(3, 7), // output + GPIO_HEADPHONES_BUTTON = GPIO_PIN(3, 8), // active-low ("half-inserted") + GPIO_MCU_INT = GPIO_PIN(3, 9), + GPIO_NFC_INT = GPIO_PIN(3, 10), + GPIO_QTM_OUT = GPIO_PIN(3, 11), // output + + // GPIO3 (DATA2) + GPIO_WIFI_ENABLED = GPIO_PIN(5, 0), +} GpioPin; + +static volatile GpioRegisters *const GPIO = (volatile GpioRegisters *)0x10147000; + +static inline bool gpioRead(GpioPin pin) +{ + u32 bank = BANK_OF(pin); + u32 bit = BIT_OF(pin); + + switch (bank) { + case 1: + return (GPIO->gpio1_data & BIT(bit)) != 0; + case 2: + return (GPIO->gpio2_data & BIT(bit)) != 0; + case 3: + return (GPIO->gpio3_data & BIT(bit)) != 0; + case 4: + return (GPIO->gpio2_data2 & BIT(bit)) != 0; + case 5: + return (GPIO->gpio3_data2 & BIT(bit)) != 0; + + default: + return false; + } +} + +static inline void gpioWrite(GpioPin pin, bool val) +{ + u32 bank = BANK_OF(pin); + u32 bit = BIT_OF(pin); + + u32 valMask = (val ? 1 : 0) << bit; + u32 tmp; + + switch (bank) { + case 1: + tmp = GPIO->gpio1_data & ~BIT(bit); + GPIO->gpio1_data = (u8)(tmp | valMask); + break; + case 2: + tmp = GPIO->gpio2_data & ~BIT(bit); + GPIO->gpio2_data = (u8)(tmp | valMask); + break; + case 3: + tmp = GPIO->gpio3_data & ~BIT(bit); + GPIO->gpio3_data = (u16)(tmp | valMask); + break; + case 4: + tmp = GPIO->gpio2_data2 & ~BIT(bit); + GPIO->gpio2_data2 = (u16)(tmp | valMask); + break; + case 5: + tmp = GPIO->gpio3_data2 & ~BIT(bit); + GPIO->gpio3_data2 = (u16)(tmp | valMask); + break; + + default: + break; + } +} + +static inline void gpioSetDirection(GpioPin pin, GpioDirection direction) +{ + u32 bank = BANK_OF(pin); + u32 bit = BIT_OF(pin); + + u32 valMask = (direction == GPIO_DIR_OUTPUT ? 1 : 0) << bit; + u32 tmp; + + switch (bank) { + case 2: + tmp = GPIO->gpio2_dir & ~BIT(bit); + GPIO->gpio2_dir = (u8)(tmp | valMask); + break; + case 3: + tmp = GPIO->gpio3_dir & ~BIT(bit); + GPIO->gpio3_dir = (u16)(tmp | valMask); + break; + + default: + break; + } +} + +static inline void gpioConfigureInterrupt(GpioPin pin, GpioInterruptConfig cfg) +{ + u32 bank = BANK_OF(pin); + u32 bit = BIT_OF(pin); + + u32 valMask = (cfg == GPIO_INTCFG_RISING_EDGE ? 1 : 0) << bit; + u32 tmp; + + switch (bank) { + case 2: + tmp = GPIO->gpio2_intcfg & ~BIT(bit); + GPIO->gpio2_intcfg = (u8)(tmp | valMask); + break; + case 3: + tmp = GPIO->gpio3_data & ~BIT(bit); + GPIO->gpio3_intcfg = (u16)(tmp | valMask); + break; + + default: + break; + } +} + +static inline void gpioSetInterruptEnabled(GpioPin pin, bool enabled) +{ + u32 bank = BANK_OF(pin); + u32 bit = BIT_OF(pin); + + u32 valMask = (enabled ? 1 : 0) << bit; + u32 tmp; + + switch (bank) { + case 2: + tmp = GPIO->gpio2_inten & ~BIT(bit); + GPIO->gpio2_inten = (u8)(tmp | valMask); + break; + case 3: + tmp = GPIO->gpio3_inten & ~BIT(bit); + GPIO->gpio3_inten = (u16)(tmp | valMask); + break; + + default: + break; + } +} + +#undef GPIO_PIN +#undef BIT_OF +#undef BANK_OF diff --git a/arm9/source/i2c.c b/arm9/source/i2c.c index d2d37e8bb..20b60b302 100644 --- a/arm9/source/i2c.c +++ b/arm9/source/i2c.c @@ -16,7 +16,10 @@ * along with this program. If not, see . */ +// Modified 2021 TuxSH + #include +#include #include "types.h" #include "i2c.h" #include "utils.h" @@ -158,11 +161,11 @@ static bool i2cStartTransfer(I2cDevice devId, u8 regAddr, bool read, I2cRegs *co else return false; } -bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size) +bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, void *out, u32 size) { const u8 busId = i2cDevTable[devId].busId; I2cRegs *const regs = i2cGetBusRegsBase(busId); - + u8 *out8 = (u8 *)out; if(!i2cStartTransfer(devId, regAddr, true, regs)) return false; @@ -170,27 +173,28 @@ bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size) { regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK; i2cWaitBusy(regs); - *out++ = regs->REG_I2C_DATA; + *out8++ = regs->REG_I2C_DATA; } regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP; i2cWaitBusy(regs); - *out = regs->REG_I2C_DATA; // Last byte + *out8 = regs->REG_I2C_DATA; // Last byte return true; } -bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size) +bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const void *in, u32 size) { const u8 busId = i2cDevTable[devId].busId; I2cRegs *const regs = i2cGetBusRegsBase(busId); + const u8 *in8 = (const u8 *)in; if(!i2cStartTransfer(devId, regAddr, false, regs)) return false; while(--size) { - regs->REG_I2C_DATA = *in++; + regs->REG_I2C_DATA = *in8++; regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE; i2cWaitBusy(regs); if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. @@ -200,7 +204,7 @@ bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size) } } - regs->REG_I2C_DATA = *in; + regs->REG_I2C_DATA = *in8; regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE | I2C_STOP; i2cWaitBusy(regs); if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. diff --git a/arm9/source/i2c.h b/arm9/source/i2c.h index ea9442250..ce2e9cb83 100644 --- a/arm9/source/i2c.h +++ b/arm9/source/i2c.h @@ -66,7 +66,7 @@ void I2C_init(void); * * @return Returns true on success and false on failure. */ -bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size); +bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, void *out, u32 size); /** * @brief Writes a buffer to a I2C register. @@ -78,7 +78,7 @@ bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size); * * @return Returns true on success and false on failure. */ -bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size); +bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const void *in, u32 size); /** * @brief Reads a byte from a I2C register. diff --git a/arm9/source/main.c b/arm9/source/main.c index 50b66668c..7360cecb7 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -36,7 +36,7 @@ #include "crypto.h" #include "memory.h" #include "screen.h" -#include "i2c.h" +#include "mcu.h" #include "fatfs/sdmmc/sdmmc.h" extern u8 __itcm_start__[], __itcm_lma__[], __itcm_bss_start__[], __itcm_end__[]; @@ -116,7 +116,8 @@ void main(int argc, char **argv, u32 magicWord) // Set up the additional sections, overwrites argc memcpy(__itcm_start__, __itcm_lma__, __itcm_bss_start__ - __itcm_start__); memset(__itcm_bss_start__, 0, __itcm_end__ - __itcm_bss_start__); - I2C_init(); + mcuInit(); + if(isInvalidLoader) error("Launched using an unsupported loader."); installArm9Handlers(); @@ -141,7 +142,7 @@ void main(int argc, char **argv, u32 magicWord) { while(HID_PAD & NTRBOOT_BUTTONS); loadHomebrewFirm(0); - mcuPowerOff(); + powerOff(); } } else @@ -164,7 +165,7 @@ void main(int argc, char **argv, u32 magicWord) //Determine if this is a firmlaunch boot if(bootType == FIRMLAUNCH) { - if(needConfig == CREATE_CONFIGURATION) mcuPowerOff(); + if(needConfig == CREATE_CONFIGURATION) powerOff(); switch(firmlaunchTidLow & 0xF) { diff --git a/arm9/source/mcu.c b/arm9/source/mcu.c new file mode 100644 index 000000000..f88a4a71d --- /dev/null +++ b/arm9/source/mcu.c @@ -0,0 +1,127 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2021 Aurora Wright, TuxSH +* +* 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 . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "mcu.h" +#include "i2c.h" +#include "gpio.h" + +static u32 g_pendingMcuInterrupts = 0; + +u32 mcuGetInterruptMask(void) +{ + u32 mask; + I2C_readRegBuf(I2C_DEV_MCU, 0x18, &mask, 4); + return mask; +} + +void mcuSetInterruptMask(u32 mask) +{ + I2C_writeRegBuf(I2C_DEV_MCU, 0x18, &mask, 4); +} + +u32 mcuGetPendingInterrupts(u32 mask) +{ + u32 curMcuInts = 0; + if (gpioRead(GPIO_MCU_INT)) + { + // MCU IRQ pin raised + I2C_readRegBuf(I2C_DEV_MCU, 0x10, &curMcuInts, 4); // this clears the interrupts on the MCU side + + // Add all new MCU interrupts to the pending list + g_pendingMcuInterrupts |= curMcuInts; + } + + // Remove the interrupts we'll return from the pending list + u32 ret = g_pendingMcuInterrupts & mask; + g_pendingMcuInterrupts &= ~mask; + return ret; +} + +void mcuPowerBacklightsOn(void) +{ + // Doesn't matter if they're already on, it should be idempotent on the MCU side + + u32 prevMask = mcuGetInterruptMask(); + u32 pend = 0; + + mcuSetInterruptMask(~MCU_INT_LCD_BL_ON); + mcuGetPendingInterrupts(MCU_INT_LCD_BL_ON) ; // Clear any pending interrupts + I2C_writeReg(I2C_DEV_MCU, 0x22, (u8)(MCU_INT_LCD_BL_ON >> 24)); + + // Wait for LCD and backlights to be on + do + { + pend |= mcuGetPendingInterrupts(MCU_INT_LCD_BL_ON); + } while (pend != MCU_INT_LCD_BL_ON); + + mcuSetInterruptMask(prevMask); +} + +void mcuPowerBacklightsOff(void) +{ + // Doesn't matter if they're already off, it should be idempotent on the MCU side + + u32 prevMask = mcuGetInterruptMask(); + u32 pend = 0; + + mcuSetInterruptMask(~MCU_INT_LCD_BL_OFF); + mcuGetPendingInterrupts(MCU_INT_LCD_BL_OFF) ; // Clear any pending interrupts + I2C_writeReg(I2C_DEV_MCU, 0x22, (u8)(MCU_INT_LCD_BL_OFF >> 24)); + + // Wait for LCD and backlights to be off + do + { + pend |= mcuGetPendingInterrupts(MCU_INT_LCD_BL_OFF); + } while (pend != MCU_INT_LCD_BL_OFF); + + mcuSetInterruptMask(prevMask); +} + +void mcuPowerOff(void) +{ + mcuFinalize(); + I2C_writeReg(I2C_DEV_MCU, 0x20, BIT(0)); + while(true); +} + +void mcuReboot(void) +{ + mcuFinalize(); + I2C_writeReg(I2C_DEV_MCU, 0x20, BIT(2)); + while(true); +} + +void mcuInit(void) +{ + I2C_init(); + mcuSetInterruptMask(~MCU_INT_MASK_FOR_INPUT); +} + +void mcuFinalize(void) +{ + mcuGetPendingInterrupts(0xFFFFFFFF); // purge pending MCU interrupts & internal pending list + mcuSetInterruptMask(~MCU_INT_DEFAULT_MASK); // Reset MCU mask +} diff --git a/arm9/source/mcu.h b/arm9/source/mcu.h new file mode 100644 index 000000000..e26a3a175 --- /dev/null +++ b/arm9/source/mcu.h @@ -0,0 +1,55 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2021 Aurora Wright, TuxSH +* +* 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 . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include "types.h" + +/* + * Bit13: Battery low (at 10%, 5% and sub-1%) + * Bit7: MCU WDT reset + * Bit6: Shell opened (GPIO1_0 1->0) + * Bit5: Shell closed (GPIO1_0 0->1) + * Bit1: Power button held + * Bit0: Power button pressed + */ +#define MCU_INT_MASK_FOR_INPUT (BIT(13)|BIT(7)|BIT(6)|BIT(5)|BIT(1)|BIT(0)) +#define MCU_INT_LCD_BL_ON (BIT(29)|BIT(27)|BIT(25)) +#define MCU_INT_LCD_BL_OFF (BIT(28)|BIT(26)|BIT(24)) +#define MCU_INT_DEFAULT_MASK 0x0000E7FFu + +u32 mcuGetInterruptMask(void); +void mcuSetInterruptMask(u32 mask); // 1 for each interrupt you want to *disable* (mask) +u32 mcuGetPendingInterrupts(u32 mask); + +void mcuPowerBacklightsOn(void); +void mcuPowerBacklightsOff(void); + +void NORETURN mcuPowerOff(void); +void NORETURN mcuReboot(void); + +void mcuInit(void); +void mcuFinalize(void); diff --git a/arm9/source/pin.c b/arm9/source/pin.c index a2cf2e2be..50c79665b 100644 --- a/arm9/source/pin.c +++ b/arm9/source/pin.c @@ -198,7 +198,7 @@ bool verifyPin(u32 pinMode) } while(!(pressed & PIN_BUTTONS)); - if(pressed & BUTTON_START) mcuPowerOff(); + if(pressed & BUTTON_START) powerOff(); pressed &= PIN_BUTTONS; diff --git a/arm9/source/screen.c b/arm9/source/screen.c index bb01525be..d6860190d 100644 --- a/arm9/source/screen.c +++ b/arm9/source/screen.c @@ -32,8 +32,7 @@ #include "screen.h" #include "config.h" #include "memory.h" -#include "i2c.h" -#include "utils.h" +#include "mcu.h" bool needToSetupScreens = true; @@ -69,6 +68,7 @@ void prepareArm11ForFirmlaunch(void) void deinitScreens(void) { + mcuPowerBacklightsOff(); if(ARESCREENSINITIALIZED) invokeArm11Function(DEINIT_SCREENS); } @@ -102,19 +102,20 @@ void initScreens(void) memcpy((void *)(ARM11_PARAMETERS_ADDRESS + 4), fbs, sizeof(fbs)); invokeArm11Function(INIT_SCREENS); - //Turn on backlight - I2C_writeReg(I2C_DEV_MCU, 0x22, 0x2A); - wait(5); + mcuPowerBacklightsOn(); } else updateBrightness(MULTICONFIG(BRIGHTNESS)); + clearScreens(false); + clearScreens(true); + memcpy((void *)ARM11_PARAMETERS_ADDRESS, fbs, sizeof(fbs)); invokeArm11Function(SETUP_FRAMEBUFFERS); - - clearScreens(true); needToSetupScreens = false; } - - clearScreens(false); - swapFramebuffers(false); + else + { + clearScreens(false); + swapFramebuffers(false); + } } diff --git a/arm9/source/types.h b/arm9/source/types.h index e4646bbc4..01517b8c2 100644 --- a/arm9/source/types.h +++ b/arm9/source/types.h @@ -49,6 +49,14 @@ typedef volatile s16 vs16; typedef volatile s32 vs32; typedef volatile s64 vs64; +/// Creates a bitmask from a bit number. +#define BIT(n) (1U<<(n)) + +/// Aligns a struct (and other types?) to m, making sure that the size of the struct is a multiple of m. +#define ALIGN(m) __attribute__((aligned(m))) + +#define NORETURN __attribute__((noreturn)) + #include "3dsheaders.h" #define CFG_BOOTENV (*(vu32 *)0x10010000) diff --git a/arm9/source/utils.c b/arm9/source/utils.c index d789761fe..7089071d5 100644 --- a/arm9/source/utils.c +++ b/arm9/source/utils.c @@ -37,6 +37,7 @@ #include "fmt.h" #include "memory.h" #include "fs.h" +#include "mcu.h" static void startChrono(void) { @@ -65,13 +66,24 @@ static u64 chrono(void) return res; } +void powerOff(void) +{ + if(!needToSetupScreens) clearScreens(false); + + mcuPowerBacklightsOff(); + + // Ensure that all memory transfers have completed and that the data cache has been flushed + flushEntireDCache(); + + mcuPowerOff(); +} + u32 waitInput(bool isMenu) { static u64 dPadDelay = 0ULL; u64 initialValue = 0ULL; u32 key, oldKey = HID_PAD; - bool shouldShellShutdown = bootType != B9SNTR && bootType != NTR; if(isMenu) { @@ -82,21 +94,34 @@ u32 waitInput(bool isMenu) while(true) { - key = HID_PAD; + // There are two sources of truth for the shell state: the gpio reg and the MCU - if(!key) + /* + * Bit13: Battery low (at 10%, 5% and sub-1%) + * Bit7: MCU WDT reset + * Bit6: Shell opened (GPIO1_0 1->0) + * Bit5: Shell closed (GPIO1_0 0->1) + * Bit1: Power button held + * Bit0: Power button pressed + */ + + // Ignore "shell opened", just ack + u32 mcuInts = mcuGetPendingInterrupts(MCU_INT_MASK_FOR_INPUT); + + if (mcuInts & (BIT(7) | BIT(5) | BIT(1) | BIT(0))) + powerOff(); + + if (mcuInts & BIT(13)) { - if(shouldShellShutdown) - { - u8 shellState = I2C_readReg(I2C_DEV_MCU, 0xF); - wait(5); - if(!(shellState & 2)) mcuPowerOff(); - } + u8 battLevelIntPart = I2C_readReg(I2C_DEV_MCU, 0xB); + if (battLevelIntPart == 0) + powerOff(); + } - u8 intStatus = I2C_readReg(I2C_DEV_MCU, 0x10); - wait(5); - if(intStatus & 1) mcuPowerOff(); //Power button pressed + key = HID_PAD; + if(!key) + { oldKey = 0; dPadDelay = 0; continue; @@ -113,20 +138,6 @@ u32 waitInput(bool isMenu) return key; } -void mcuPowerOff(void) -{ - if(!needToSetupScreens) clearScreens(false); - - //Shutdown LCD - if(ARESCREENSINITIALIZED) I2C_writeReg(I2C_DEV_MCU, 0x22, 1 << 0); - - //Ensure that all memory transfers have completed and that the data cache has been flushed - flushEntireDCache(); - - I2C_writeReg(I2C_DEV_MCU, 0x20, 1 << 0); - while(true); -} - void wait(u64 amount) { startChrono(); @@ -151,6 +162,5 @@ void error(const char *fmt, ...) drawString(true, 10, posY + 2 * SPACING_Y, COLOR_WHITE, "Press any button to shutdown"); waitInput(false); - - mcuPowerOff(); + powerOff(); } diff --git a/arm9/source/utils.h b/arm9/source/utils.h index 83bf7ea50..862310271 100644 --- a/arm9/source/utils.h +++ b/arm9/source/utils.h @@ -39,7 +39,7 @@ #define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF)) #define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF)) +void NORETURN powerOff(void); u32 waitInput(bool isMenu); -void mcuPowerOff(void); void wait(u64 amount); -void error(const char *fmt, ...); +void NORETURN error(const char *fmt, ...);