Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
wb-ec-firmware (2.4.0) stable; urgency=medium

* WB85: warm SoC reset via short PWROK/RESET pulse — PMIC rails and DRAM
stay powered, so pstore/ramoops panic logs survive the reboot
* WB85: two-stage watchdog — first timeout does a warm reset
(new poweron reason 8 "Watchdog (warm reset)"); if Linux does not feed
the watchdog before the next timeout, a hard power cycle follows
(reason 5 "Watchdog"). Consumers detecting watchdog resets must treat
both 5 and 8 as watchdog
* WB85: reset_pmic request (POWER_CTRL bit 2) now performs the short warm
pulse instead of the 2 s PMIC RESET hold
* WB74: behavior unchanged (no warm reset support)
* Fix state-machine wedge: a watchdog timeout racing a 3.3V loss or a
reset_pmic request in the same main loop pass started a second power
sequence on top of the warm reset (or PMIC reset) already in flight,
aborting the pulse and leaving the PMIC RESET (PWROK) line latched -
the SoC stayed held in reset forever (5V on, no SPL/U-Boot output,
watchdog resets no longer able to recover the board). Now only one
power action is taken per pass, hard_reset/hard_off/power-on always
release the reset line, and a completed warm pulse guarantees 5V is on
* Watchdog escalation restarts after each hard power cycle: with no
feeds the resets alternate warm/hard instead of latching to hard-only,
so the first hang of every fresh boot still gets a DRAM-preserving
warm reset (ramoops stays readable)
* Poweroff request racing a watchdog timeout no longer resurrects the
board: the power-off wins, the deferred timeout is absorbed
* Add wbec + linux-power-control + wdt integration test suite with a
board model (PMIC/SoC/5V rail simulation): reproduces the wedge,
sweeps PMIC failure timing across all reset sequence phases and
asserts the watchdog always recovers the SoC in bounded time

-- Wiren Board Robot <info@wirenboard.com> Fri, 04 Jul 2026 14:00:00 +0300

wb-ec-firmware (2.3.0) stable; urgency=medium

* Port for Debian 13
Expand Down
5 changes: 5 additions & 0 deletions include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@

// Время, на которое выключается питание при перезагрузке
#define WBEC_POWER_RESET_TIME_MS 1000
// Длительность импульса на линии PMIC RESET (PWROK) при тёплом сбросе.
// Тёплый сброс перезапускает SoC через его линию RESET, не отключая
// питание PMIC — содержимое DRAM (в т.ч. ramoops) сохраняется.
// Работает только если в PMIC не включен рестарт по PWROK (AXP REG32[4]=0)
#define WBEC_WARM_RESET_PULSE_MS 100
// Время загрузки Linux и драйверов WBEC
// До этого времени питание выключается сразу при коротком нажатии
// После - отправляется запрос в Linux
Expand Down
7 changes: 7 additions & 0 deletions include/config_wb85.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@
// Поддержка spi-uart
#define EC_UART_REGMAP_SUPPORT

// Тёплый сброс SoC импульсом на линии PWROK/RESET (см. linux_cpu_pwr_seq_warm_reset).
// Проверено на WB 8.5: линия заведена на RESET процессора T507, PMIC (AXP15060)
// игнорирует импульс при REG32[4]=0 и не отключает питание DRAM.
// На WB74 не включаем: поведение импульса на этой платформе не проверялось,
// там watchdog по-прежнему сразу делает жёсткий сброс по питанию
#define WBEC_HAS_WARM_RESET

// name freq sda scl
#define SOFTWARE_I2C_DESC(macro) macro(WBMZ6, 100000, GPIOF, 1, GPIOF, 0)

Expand Down
2 changes: 2 additions & 0 deletions include/linux-power-control.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
void linux_cpu_pwr_seq_init(bool on);
void linux_cpu_pwr_seq_off_and_goto_standby(uint16_t wakeup_after_s);
void linux_cpu_pwr_seq_on(void);
void linux_cpu_pwr_seq_wakeup(void);
void linux_cpu_pwr_seq_hard_off(void);
void linux_cpu_pwr_seq_hard_reset(void);
void linux_cpu_pwr_seq_reset_pmic(void);
void linux_cpu_pwr_seq_warm_reset(void);
bool linux_cpu_pwr_seq_is_busy(void);
void linux_cpu_pwr_seq_do_periodic_work(void);
5 changes: 5 additions & 0 deletions include/regmap-structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
/* -//- */ uint16_t reset_pmic : 1; \
) \
/* Addr Name RO/RW */ \
m( 0xA4, SUSPEND_CTRL, RW, \
/* 0xA4 */ uint16_t timeout_s; \
/* 0xA5 */ uint16_t off_mode : 1; \
) \
/* Addr Name RO/RW */ \
m( 0xB0, IRQ_FLAGS, RO, \
/* 0xB0 */ uint16_t irqs; \
) \
Expand Down
1 change: 1 addition & 0 deletions include/rtc-alarm-subsystem.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once

bool rtc_alarm_is_alarm_enabled(void);
bool rtc_alarm_take_fired(void);
void rtc_alarm_do_periodic_work(void);
1 change: 1 addition & 0 deletions include/wdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ void wdt_set_timeout(uint16_t secs);
void wdt_start_reset(void);
void wdt_stop(void);
bool wdt_handle_timed_out(void);
bool wdt_handle_fed(void);
void wdt_do_periodic_work(void);
80 changes: 79 additions & 1 deletion src/linux-power-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ enum pwr_state {

PS_RESET_5V_WAIT, // Нужно при перезаргрузке - выключаем 5В и ждём разрядку линий
PS_RESET_PMIC_WAIT, // Сброс PMIC через PMIC_RESET_PWROK. Ждём, пока пропадёт 3.3В
PS_WARM_RESET_PULSE, // Тёплый сброс SoC: короткий импульс на PMIC_RESET_PWROK
};

struct pwr_ctx {
// Пробуждение из suspend-to-off: после появления 3.3В нужен
// импульс на PWROK - PMIC при выходе из сна восстанавливает
// питание, но не выдаёт сброс, и SoC сам не стартует
bool wake_pending;
enum pwr_state state;
systime_t timestamp;
unsigned attempt;
Expand Down Expand Up @@ -136,6 +141,28 @@ void linux_cpu_pwr_seq_on(void)
return;
}

// Линия PMIC RESET (PWROK) не должна достаться последовательности
// включения взведённой (например, от прерванного тёплого сброса) -
// иначе SoC останется заклиненным в сбросе
pmic_reset_gpio_off();
linux_cpu_pwr_5v_gpio_on();
new_state(PS_ON_STEP1_WAIT_3V3);
}

/**
* @brief Пробуждение PMIC из сна (AXP sleep, режим suspend-to-off).
* 5В уже включено, 3.3В выключил сам PMIC по команде из BL31.
* Перезапускаем последовательность включения с шага ожидания 3.3В:
* если оно не появляется само, штатная эскалация "нажимает" PWRON -
* для AXP853T в состоянии sleep это источник пробуждения (POK
* negedge), по которому PMIC восстанавливает записанную конфигурацию.
* Без сброса состояния: PS_ON_COMPLETE блокирует обычный
* linux_cpu_pwr_seq_on().
*/
void linux_cpu_pwr_seq_wakeup(void)
{
pwr_ctx.wake_pending = true;
pmic_reset_gpio_off();
linux_cpu_pwr_5v_gpio_on();
new_state(PS_ON_STEP1_WAIT_3V3);
}
Expand All @@ -148,6 +175,9 @@ void linux_cpu_pwr_seq_hard_off(void)
{
linux_cpu_pwr_5v_gpio_off();
pmic_pwron_gpio_off();
// Отпускаем линию сброса: никакая последовательность не должна
// оставлять её взведённой после своего завершения или прерывания
pmic_reset_gpio_off();
new_state(PS_OFF_COMPLETE);
}

Expand All @@ -159,6 +189,10 @@ void linux_cpu_pwr_seq_hard_reset()
{
linux_cpu_pwr_5v_gpio_off();
pmic_pwron_gpio_off();
// Отпускаем линию сброса: если жёсткий сброс прервал тёплый сброс
// или сброс PMIC, линия не должна остаться взведённой - иначе после
// подачи 5В SoC навсегда останется в сбросе
pmic_reset_gpio_off();
new_state(PS_RESET_5V_WAIT);
}

Expand All @@ -173,6 +207,22 @@ void linux_cpu_pwr_seq_reset_pmic(void)
new_state(PS_RESET_PMIC_WAIT);
}

/**
* @brief Тёплый сброс SoC коротким импульсом на линии PMIC_RESET_PWROK.
* Линия одновременно заведена на PWROK PMIC и RESET процессора T507.
* Если в PMIC отключен рестарт по PWROK (AXP REG32[4]=0, значение по
* умолчанию), PMIC игнорирует импульс и все его выходы, включая питание
* DRAM, остаются включёнными — сбрасывается только SoC, содержимое DRAM
* сохраняется (это позволяет ramoops пережить сброс).
* Если же PMIC настроен на рестарт по PWROK, пропадёт 3.3В и штатная
* логика включения (PS_ON_STEP1_WAIT_3V3) выполнит полный цикл включения.
*/
void linux_cpu_pwr_seq_warm_reset(void)
{
pmic_reset_gpio_on();
new_state(PS_WARM_RESET_PULSE);
}

/**
* @brief Статус работы алгоритма управления питанием
*
Expand Down Expand Up @@ -239,6 +289,14 @@ void linux_cpu_pwr_seq_do_periodic_work(void)
// Первый шаг включения питания: проверка, что 3.3В появилось, после того как подали 5В
case PS_ON_STEP1_WAIT_3V3:
if (vmon_get_ch_status(VMON_CHANNEL_V33)) {
if (pwr_ctx.wake_pending) {
// Питание восстановлено после сна PMIC: SoC ещё в
// сбросе, толкаем его импульсом на PWROK
pwr_ctx.wake_pending = false;
pmic_reset_gpio_on();
new_state(PS_WARM_RESET_PULSE);
break;
}
// Если 3.3В появилось, то считаем что питание включено
new_state(PS_ON_COMPLETE);
}
Expand All @@ -257,8 +315,13 @@ void linux_cpu_pwr_seq_do_periodic_work(void)
// PMIC должен включаться сам после подачи 5В
case PS_ON_STEP2_PMIC_PWRON:
if (vmon_get_ch_status(VMON_CHANNEL_V33)) {
// Если 3.3В
pmic_pwron_gpio_off();
if (pwr_ctx.wake_pending) {
pwr_ctx.wake_pending = false;
pmic_reset_gpio_on();
new_state(PS_WARM_RESET_PULSE);
break;
}
new_state(PS_ON_COMPLETE);
}
if (in_state_time_ms() > 1500) {
Expand Down Expand Up @@ -294,6 +357,21 @@ void linux_cpu_pwr_seq_do_periodic_work(void)
}
break;

// Тёплый сброс SoC: отпускаем линию RESET после короткого импульса.
// Если PMIC проигнорировал импульс, 3.3В на месте и включение
// завершится сразу; если PMIC перезапустился - штатное включение
case PS_WARM_RESET_PULSE:
if (in_state_time_ms() > WBEC_WARM_RESET_PULSE_MS) {
pmic_reset_gpio_off();
pmic_pwron_gpio_off();
// Тёплый сброс всегда заканчивается последовательностью включения.
// Гарантируем ей 5В: если тёплый сброс был запрошен, когда 5В
// оказалось снятым, ожидание 3.3В без 5В бессмысленно
linux_cpu_pwr_5v_gpio_on();
new_state(PS_ON_STEP1_WAIT_3V3);
}
break;

// Сброс PMIC через RESET самого PMIC
case PS_RESET_PMIC_WAIT:
if ((!vmon_get_ch_status(VMON_CHANNEL_V33)) || (in_state_time_ms() > 2000)) {
Expand Down
11 changes: 11 additions & 0 deletions src/rtc-alarm-subsystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@
*/

static bool alarm_enabled = 0;
static bool alarm_fired_latch = 0;

bool rtc_alarm_is_alarm_enabled(void)
{
return alarm_enabled;
}

// Возвращает true один раз после срабатывания будильника.
// Используется режимом suspend-to-off для пробуждения PMIC.
bool rtc_alarm_take_fired(void)
{
bool ret = alarm_fired_latch;
alarm_fired_latch = false;
return ret;
}

void rtc_alarm_do_periodic_work(void)
{
if (rtc_get_ready_read()) {
Expand Down Expand Up @@ -46,6 +56,7 @@ void rtc_alarm_do_periodic_work(void)
regmap_set_region_data(REGMAP_REGION_RTC_CFG, &cfg, sizeof(cfg));

if (rtc_alarm.flag) {
alarm_fired_latch = true;
irq_set_flag(IRQ_ALARM);
rtc_clear_alarm_flag();
// После сработки будильника нужно его выключить
Expand Down
Loading