diff --git a/stratosphere/boot/source/boot_boot_reason.cpp b/stratosphere/boot/source/boot_boot_reason.cpp
new file mode 100644
index 000000000..bbaf6c568
--- /dev/null
+++ b/stratosphere/boot/source/boot_boot_reason.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 .
+ */
+
+#include "boot_functions.hpp"
+#include "boot_pmic_driver.hpp"
+#include "boot_rtc_driver.hpp"
+
+static u32 g_boot_reason = 0;
+static bool g_detected_boot_reason = false;
+
+struct BootReasonValue {
+ union {
+ struct {
+ u8 power_intr;
+ u8 rtc_intr;
+ u8 nv_erc;
+ u8 boot_reason;
+ };
+ u32 value;
+ };
+};
+
+static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
+ if (power_intr & 0x08) {
+ return 2;
+ }
+ if (rtc_intr & 0x02) {
+ return 3;
+ }
+ if (power_intr & 0x80) {
+ return 1;
+ }
+ if (rtc_intr & 0x04) {
+ if (nv_erc != 0x80 && !Boot::IsRecoveryBoot()) {
+ return 4;
+ }
+ }
+ if ((nv_erc & 0x40) && ac_ok) {
+ return 1;
+ }
+ return 0;
+}
+
+void Boot::DetectBootReason() {
+ u8 power_intr;
+ u8 rtc_intr;
+ u8 rtc_intr_m;
+ u8 nv_erc;
+ bool ac_ok;
+
+ /* Get values from PMIC. */
+ {
+ PmicDriver pmic_driver;
+ if (R_FAILED(pmic_driver.GetPowerIntr(&power_intr))) {
+ std::abort();
+ }
+ if (R_FAILED(pmic_driver.GetNvErc(&nv_erc))) {
+ std::abort();
+ }
+ if (R_FAILED(pmic_driver.GetAcOk(&ac_ok))) {
+ std::abort();
+ }
+ }
+
+ /* Get values from RTC. */
+ {
+ RtcDriver rtc_driver;
+ if (R_FAILED(rtc_driver.GetRtcIntr(&rtc_intr))) {
+ std::abort();
+ }
+ if (R_FAILED(rtc_driver.GetRtcIntrM(&rtc_intr_m))) {
+ std::abort();
+ }
+ }
+
+ /* Set global derived boot reason. */
+ g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok);
+
+ /* Set boot reason for SPL. */
+ {
+ BootReasonValue boot_reason_value;
+ boot_reason_value.power_intr = power_intr;
+ boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m;
+ boot_reason_value.nv_erc = nv_erc;
+ boot_reason_value.boot_reason = g_boot_reason;
+ if (R_FAILED(splSetBootReason(boot_reason_value.value))) {
+ std::abort();
+ }
+ }
+
+ g_detected_boot_reason = true;
+}
+
+u32 Boot::GetBootReason() {
+ if (!g_detected_boot_reason) {
+ std::abort();
+ }
+
+ return g_boot_reason;
+}
diff --git a/stratosphere/boot/source/boot_functions.hpp b/stratosphere/boot/source/boot_functions.hpp
index 4430cc08a..1ae03612e 100644
--- a/stratosphere/boot/source/boot_functions.hpp
+++ b/stratosphere/boot/source/boot_functions.hpp
@@ -29,6 +29,7 @@ class Boot {
static void ChangeGpioVoltageTo1_8v();
static void SetInitialGpioConfiguration();
static void CheckClock();
+ static void DetectBootReason();
/* Power utilities. */
static void RebootSystem();
@@ -45,6 +46,8 @@ class Boot {
/* SPL Utilities. */
static HardwareType GetHardwareType();
+ static u32 GetBootReason();
+ static bool IsRecoveryBoot();
/* I2C Utilities. */
static Result ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size);
diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp
index 114c5e939..274e25c8d 100644
--- a/stratosphere/boot/source/boot_main.cpp
+++ b/stratosphere/boot/source/boot_main.cpp
@@ -119,7 +119,8 @@ int main(int argc, char **argv)
/* Check USB PLL/UTMIP clock. */
Boot::CheckClock();
- /* TODO: DetectBootReason(); */
+ /* Talk to PMIC/RTC, set boot reason with SPL. */
+ Boot::DetectBootReason();
/* TODO: ShowSplashScreen(); */
diff --git a/stratosphere/boot/source/boot_pmic_driver.cpp b/stratosphere/boot/source/boot_pmic_driver.cpp
new file mode 100644
index 000000000..74482e1b0
--- /dev/null
+++ b/stratosphere/boot/source/boot_pmic_driver.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 .
+ */
+
+#include
+#include
+#include "boot_functions.hpp"
+#include "boot_pmic_driver.hpp"
+
+void PmicDriver::ShutdownSystem() {
+ if (R_FAILED(this->ShutdownSystem(false))) {
+ std::abort();
+ }
+}
+
+void PmicDriver::RebootSystem() {
+ if (R_FAILED(this->ShutdownSystem(true))) {
+ std::abort();
+ }
+}
+
+Result PmicDriver::GetAcOk(bool *out) {
+ u8 power_status;
+ Result rc = this->GetPowerStatus(&power_status);
+ if (R_FAILED(rc)) {
+ return rc;
+ }
+
+ *out = (power_status & 0x02) != 0;
+ return ResultSuccess;
+}
+
+Result PmicDriver::GetPowerIntr(u8 *out) {
+ const u8 addr = 0x0B;
+ return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
+}
+
+Result PmicDriver::GetPowerStatus(u8 *out) {
+ const u8 addr = 0x15;
+ return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
+}
+
+Result PmicDriver::GetNvErc(u8 *out) {
+ const u8 addr = 0x0C;
+ return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
+}
+
+Result PmicDriver::ShutdownSystem(bool reboot) {
+ /* TODO: Implement this. */
+ std::abort();
+}
\ No newline at end of file
diff --git a/stratosphere/boot/source/boot_pmic_driver.hpp b/stratosphere/boot/source/boot_pmic_driver.hpp
new file mode 100644
index 000000000..ec185fac6
--- /dev/null
+++ b/stratosphere/boot/source/boot_pmic_driver.hpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 .
+ */
+
+#pragma once
+#include
+#include
+
+#include "i2c_driver/i2c_api.hpp"
+
+class PmicDriver {
+ private:
+ I2cSessionImpl i2c_session;
+ public:
+ PmicDriver() {
+ I2cDriver::Initialize();
+ I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
+ }
+
+ ~PmicDriver() {
+ I2cDriver::CloseSession(this->i2c_session);
+ I2cDriver::Finalize();
+ }
+ private:
+ Result GetPowerStatus(u8 *out);
+ Result ShutdownSystem(bool reboot);
+ public:
+ void ShutdownSystem();
+ void RebootSystem();
+ Result GetAcOk(bool *out);
+ Result GetPowerIntr(u8 *out);
+ Result GetNvErc(u8 *out);
+};
\ No newline at end of file
diff --git a/stratosphere/boot/source/boot_rtc_driver.cpp b/stratosphere/boot/source/boot_rtc_driver.cpp
new file mode 100644
index 000000000..f142c655e
--- /dev/null
+++ b/stratosphere/boot/source/boot_rtc_driver.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 .
+ */
+
+#include
+#include
+#include "boot_functions.hpp"
+#include "boot_rtc_driver.hpp"
+
+Result RtcDriver::ReadRtcRegister(u8 *out, u8 address) {
+ const u8 update_addr = 0x04;
+ const u8 update_val = 0x10;
+ Result rc = Boot::WriteI2cRegister(this->i2c_session, &update_val, sizeof(update_val), &update_addr, sizeof(update_addr));
+ if (R_FAILED(rc)) {
+ return rc;
+ }
+ svcSleepThread(16'000'000ul);
+ return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &address, sizeof(address));
+}
+
+Result RtcDriver::GetRtcIntr(u8 *out) {
+ const u8 addr = 0x00;
+ return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
+}
+
+Result RtcDriver::GetRtcIntrM(u8 *out) {
+ const u8 addr = 0x01;
+ return this->ReadRtcRegister(out, addr);
+}
diff --git a/stratosphere/boot/source/boot_rtc_driver.hpp b/stratosphere/boot/source/boot_rtc_driver.hpp
new file mode 100644
index 000000000..0fdafece6
--- /dev/null
+++ b/stratosphere/boot/source/boot_rtc_driver.hpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 .
+ */
+
+#pragma once
+#include
+#include
+
+#include "i2c_driver/i2c_api.hpp"
+
+class RtcDriver {
+ private:
+ I2cSessionImpl i2c_session;
+ public:
+ RtcDriver() {
+ I2cDriver::Initialize();
+ I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
+ }
+
+ ~RtcDriver() {
+ I2cDriver::CloseSession(this->i2c_session);
+ I2cDriver::Finalize();
+ }
+ private:
+ Result ReadRtcRegister(u8 *out, u8 address);
+ public:
+ Result GetRtcIntr(u8 *out);
+ Result GetRtcIntrM(u8 *out);
+};
\ No newline at end of file
diff --git a/stratosphere/boot/source/boot_spl_utils.cpp b/stratosphere/boot/source/boot_spl_utils.cpp
index 2602deb3c..79bb9db4f 100644
--- a/stratosphere/boot/source/boot_spl_utils.cpp
+++ b/stratosphere/boot/source/boot_spl_utils.cpp
@@ -23,3 +23,11 @@ HardwareType Boot::GetHardwareType() {
}
return static_cast(out_val);
}
+
+bool Boot::IsRecoveryBoot() {
+ u64 val = 0;
+ if (R_FAILED(splGetConfig(SplConfigItem_IsRecoveryBoot, &val))) {
+ std::abort();
+ }
+ return val != 0;
+}
\ No newline at end of file
diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp
index c4cebbe2f..dc96bc9d6 100644
--- a/stratosphere/spl/source/spl_types.hpp
+++ b/stratosphere/spl/source/spl_types.hpp
@@ -65,7 +65,7 @@ struct AsyncOperationKey {
struct BootReasonValue {
u8 power_intr;
u8 rtc_intr;
- u8 _0x3;
+ u8 nv_erc;
u8 boot_reason;
};
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");