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, ...);