From c9e9b87bd0d783d24d268804c28bc4f35a67f264 Mon Sep 17 00:00:00 2001 From: souldbminersmwc Date: Sat, 3 Oct 2026 19:47:29 -0400 Subject: [PATCH] hocclk: add watchdog --- Source/hoc-clk/sysmodule/src/board/board.cpp | 80 +++++++++++-- .../sysmodule/src/i2c/max77620_wdt.cpp | 106 ++++++++++++++++++ .../sysmodule/src/i2c/max77620_wdt.hpp | 37 ++++++ .../sysmodule/src/mgr/clock_manager.cpp | 9 +- 4 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.cpp create mode 100644 Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.hpp diff --git a/Source/hoc-clk/sysmodule/src/board/board.cpp b/Source/hoc-clk/sysmodule/src/board/board.cpp index 1ca1f922..9146adf7 100644 --- a/Source/hoc-clk/sysmodule/src/board/board.cpp +++ b/Source/hoc-clk/sysmodule/src/board/board.cpp @@ -50,6 +50,8 @@ #include #include "../mapping/mem_map.hpp" #include "../soc/dram_mrr.hpp" +#include "../i2c/max77620_wdt.hpp" + namespace board { u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr; @@ -63,13 +65,52 @@ namespace board { u32 fd = 0, fd2 = 0; -#define PMC_BASE 0x7000E400 -#define APB_MISC_GP_HIDREV 0x804 -#define GP_HIDREV_MAJOR_T210 0x1 -#define GP_HIDREV_MAJOR_T210B01 0x2 -#define APB_BASE 0x70000000 -#define FUSE_RESERVED_ODMX(x) (0x1C8 + 4 * (x)) -#define FUSE_OFFSET 0x800 + #define PMC_BASE 0x7000E400 + #define APB_MISC_GP_HIDREV 0x804 + #define GP_HIDREV_MAJOR_T210 0x1 + #define GP_HIDREV_MAJOR_T210B01 0x2 + #define APB_BASE 0x70000000 + #define FUSE_RESERVED_ODMX(x) (0x1C8 + 4 * (x)) + #define FUSE_OFFSET 0x800 + + constexpr u32 pscDependencies[] = { PscPmModuleId_Olsc }; + constexpr PscPmModuleId PscModuleId = (PscPmModuleId)705; + + PscPmModule s_pscModule; + bool s_pscPrepared = false; + Thread s_pscThread; + bool s_pscExit = false; + + std::atomic_bool s_isAwake = true; + + void PscThreadFunc(void *) { + while (!s_pscExit) { + Result rc = eventWait(&s_pscModule.event, 1'000'000'000ULL); + if (R_FAILED(rc)) { + continue; // Timeout + } + + PscPmState state; + u32 flags; + rc = pscPmModuleGetRequest(&s_pscModule, &state, &flags); + if (R_SUCCEEDED(rc)) { + if (state == PscPmState_Awake) { + i2c::wdt::Arm(i2c::wdt::MAX77620_WDT_2S); + s_isAwake = true; + } else if (state == PscPmState_ReadySleep) { + i2c::wdt::Disarm(); + s_isAwake = false; + } else if (state == PscPmState_ReadyShutdown) { + i2c::wdt::Disarm(); + s_isAwake = false; + + } + + pscPmModuleAcknowledge(&s_pscModule, state); + } + } + } + void FetchHardwareInfos() { ReadFuses(fuseData, fuseVirtAddr); SetGpuBracket(fuseData.gpuSpeedo, speedoBracket); @@ -198,6 +239,21 @@ namespace board { CacheDfllData(); CacheGpuVoltTable(); + + rc = pscmInitialize(); + ASSERT_RESULT_OK(rc, "pscmInitialize"); + + rc = pscmGetPmModule(&s_pscModule, PscModuleId, pscDependencies, sizeof(pscDependencies) / sizeof(u32), true); + ASSERT_RESULT_OK(rc, "pscmGetPmModule"); + + rc = threadCreate(&s_pscThread, PscThreadFunc, nullptr, NULL, 0x1000, 0x10, 3); + ASSERT_RESULT_OK(rc, "threadCreate"); + + rc = threadStart(&s_pscThread); + ASSERT_RESULT_OK(rc, "threadStart"); + + /* Arm the watchdog */ + i2c::wdt::Arm(i2c::wdt::MAX77620_WDT_2S); } void Exit() { @@ -228,6 +284,16 @@ namespace board { batteryInfoExit(); pmdmntExit(); nvExit(); + + eventFire(&s_pscModule.event); + threadWaitForExit(&s_pscThread); + threadClose(&s_pscThread); + + pscPmModuleFinalize(&s_pscModule); + pscPmModuleClose(&s_pscModule); + pscmExit(); + + i2c::wdt::Disarm(); } HocClkSocType GetSocType() { diff --git a/Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.cpp b/Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.cpp new file mode 100644 index 00000000..803741b5 --- /dev/null +++ b/Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.cpp @@ -0,0 +1,106 @@ +/* + * + * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors + * + * 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 "max77620_wdt.hpp" + +/* PMIC Registers */ +#define MAX77620_REG_CNFGGLBL2 ((u8)0x01) +#define MAX77620_REG_CNFGGLBL3 ((u8)0x02) +#define MAX77620_REG_ONOFFCNFG2 ((u8)0x42) + +/* CNFGGLBL2 bits */ +#define MAX77620_WDTOFFC ((u8)(1u << 4)) /* WDT clear in OFF mode */ +#define MAX77620_WDTSLPC ((u8)(1u << 3)) /* WDT clear in sleep mode */ +#define MAX77620_WDTEN ((u8)(1u << 2)) /* WDT enabled */ + +/* Watchdog timeout period (CNFGGLBL2[1:0]) */ +#define MAX77620_TWD_MASK ((u8)0x03) +#define MAX77620_TWD_2s ((u8)0x00) +#define MAX77620_TWD_16s ((u8)0x01) +#define MAX77620_TWD_64s ((u8)0x02) +#define MAX77620_TWD_128s ((u8)0x03) + +/* CNFGGLBL3 watchdog-clear field (WDTC[1:0]). Writing 0x1 disables WDT. */ +#define MAX77620_WDTC_MASK ((u8)0x03) +#define MAX77620_WDTC_KICK ((u8)0x01) + +/* ONOFFCNFG2 bits */ +#define MAX77620_ONOFFCNFG2_WD_RST_WK ((u8)(1u << 6)) + +/* Is this a watchdog or a watchcat, that is the question */ + +namespace i2c::wdt { + + static Result update_bits(u8 reg, u8 mask, u8 val) { + u8 cur; + Result rc; + + rc = I2cRead_OutU8(I2cDevice_Max77620Pmic, reg, &cur); + if (R_FAILED(rc)) + return rc; + + cur = (u8)((cur & (u8)~mask) | (val & mask)); + return I2cSet_U8(I2cDevice_Max77620Pmic, reg, cur); + } + + void Arm(max77620_wdt_time_t timeout) { + Result rc; + u8 twd; + + if ((int)timeout < (int)MAX77620_WDT_2S || (int)timeout > (int)MAX77620_WDT_128S) + return; + + twd = (u8)timeout; + + /* WDT expiry results in a restart */ + rc = update_bits(MAX77620_REG_ONOFFCNFG2, + MAX77620_ONOFFCNFG2_WD_RST_WK, + MAX77620_ONOFFCNFG2_WD_RST_WK); + ASSERT_RESULT_OK(rc, "update_bits"); + + /* Clear WDT in OFF and sleep modes. */ + rc = update_bits(MAX77620_REG_CNFGGLBL2, + (u8)(MAX77620_WDTOFFC | MAX77620_WDTSLPC), + (u8)(MAX77620_WDTOFFC | MAX77620_WDTSLPC)); + ASSERT_RESULT_OK(rc, "update_bits"); + + /* Do what the kernel dows */ + rc = update_bits(MAX77620_REG_CNFGGLBL3, + MAX77620_WDTC_MASK, MAX77620_WDTC_KICK); + ASSERT_RESULT_OK(rc, "update_bits"); + + /* Program the period. */ + rc = update_bits(MAX77620_REG_CNFGGLBL2, MAX77620_TWD_MASK, twd); + ASSERT_RESULT_OK(rc, "update_bits"); + + /* Enable. */ + rc = update_bits(MAX77620_REG_CNFGGLBL2, MAX77620_WDTEN, MAX77620_WDTEN); + ASSERT_RESULT_OK(rc, "update_bits"); + } + + void Disarm() { + Result rc = update_bits(MAX77620_REG_CNFGGLBL2, MAX77620_WDTEN, (u8)0x00); + ASSERT_RESULT_OK(rc, "update_bits"); + } + + void Pet() { + Result rc = update_bits(MAX77620_REG_CNFGGLBL3, MAX77620_WDTC_MASK, MAX77620_WDTC_KICK); + ASSERT_RESULT_OK(rc, "update_bits"); + } + +} // namespace pwr::wdt diff --git a/Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.hpp b/Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.hpp new file mode 100644 index 00000000..25baa408 --- /dev/null +++ b/Source/hoc-clk/sysmodule/src/i2c/max77620_wdt.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors + * + * 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 "i2cDrv.h" +#include "../file/errors.hpp" + +namespace i2c::wdt { + + typedef enum { + MAX77620_WDT_2S = 0, /* ~2 seconds */ + MAX77620_WDT_16S = 1, /* ~16 seconds */ + MAX77620_WDT_64S = 2, /* ~64 seconds */ + MAX77620_WDT_128S = 3, /* ~128 seconds */ + } max77620_wdt_time_t; + + void Arm(max77620_wdt_time_t timeout); + void Disarm(); + + void Pet(); + +} // namespace pwr::wdt diff --git a/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp b/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp index 03a9ed26..a2d2b1da 100644 --- a/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp +++ b/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp @@ -44,6 +44,8 @@ #include "../util/lockable_mutex.h" #include "clock_manager.hpp" #include "governor.hpp" +#include "../i2c/max77620_wdt.hpp" + #define HOSPPC_HAS_BOOST (hosversionAtLeast(7, 0, 0)) @@ -879,10 +881,7 @@ namespace mgr { } void WaitForNextTick() { - - if (board::GetHz(HocClkModule_MEM) > 665000000) - svcSleepThread(file::config::GetConfigValue(HocClkConfigValue_PollingIntervalMs) * 1000000ULL); - else - svcSleepThread(5000 * 1000000ULL); // 5 seconds in sleep mode + i2c::wdt::Pet(); + svcSleepThread(file::config::GetConfigValue(HocClkConfigValue_PollingIntervalMs) * 1000000ULL); } } // namespace mgr