From 12710eafff6612a71c5efa9bed0a67e04d7137df Mon Sep 17 00:00:00 2001 From: Subrata Banik Date: Sun, 1 Mar 2026 11:04:34 +0530 Subject: [PATCH] mb/google/bluey: Implement off-mode charging applet Add launch_charger_applet() to handle the system state when booting in off-mode charging or low-power modes with a charger present. Key features: 1. Monitoring: Periodically checks battery current (I-current) via SPMI/PMIC registers. 2. Event Handling: Detects and clears EC power button and lid events. If a manual power-on event is detected, the system triggers a full board reset to ensure a clean boot to the OS (preventing firmware state conflicts like ADSP-lite vs ADSP). 3. Shutdown: If the charger is removed, it signals the EC via off-mode heartbeat and initiates an AP power-off. BUG=b:439819922 BRANCH=None TEST=Verified that the device enters the charging loop when plugged in while off, and transitions to a full boot when the power button is pressed. Change-Id: I152f71eac89f5b522ea7b286517724e213c31e9a Signed-off-by: Subrata Banik Reviewed-on: https://review.coreboot.org/c/coreboot/+/91485 Reviewed-by: Kapil Porwal Tested-by: build bot (Jenkins) --- src/mainboard/google/bluey/board.h | 4 ++ src/mainboard/google/bluey/charging.c | 94 ++++++++++++++++++++++++-- src/mainboard/google/bluey/mainboard.c | 14 ++-- 3 files changed, 100 insertions(+), 12 deletions(-) diff --git a/src/mainboard/google/bluey/board.h b/src/mainboard/google/bluey/board.h index d5218b469b..98e34fc64b 100644 --- a/src/mainboard/google/bluey/board.h +++ b/src/mainboard/google/bluey/board.h @@ -35,6 +35,9 @@ #define GPIO_PANEL_POWER_ON GPIO(70) #define GPIO_PANEL_HPD GPIO(119) +/* Charging GPIOs */ +#define GPIO_PARALLEL_CHARGING_CFG GPIO(71) + /* SD card specific GPIOs. Only for SD-enabled devices. */ #if CONFIG(MAINBOARD_HAS_SD_CONTROLLER) #define GPIO_SD_CD_L GPIO(71) @@ -56,5 +59,6 @@ void configure_parallel_charging(void); void configure_parallel_charging_late(void); void enable_slow_battery_charging(void); void disable_slow_battery_charging(void); +void launch_charger_applet(void); #endif /* MAINBOARD_GOOGLE_BLUEY_BOARD_H */ diff --git a/src/mainboard/google/bluey/charging.c b/src/mainboard/google/bluey/charging.c index 5252a1e531..f1af963ec2 100644 --- a/src/mainboard/google/bluey/charging.c +++ b/src/mainboard/google/bluey/charging.c @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include "board.h" + +#include +#include +#include #include #include #include @@ -16,16 +20,14 @@ #define SMB2_CHGR_CHRG_EN_CMD ((SMB2_SLAVE_ID << 16) | SCHG_CHGR_CHARGING_ENABLE_CMD) #define SMB1_SCHG_TYPE_C_TYPE_C_DEBUG_ACCESS_SNK_CFG \ ((SMB1_SLAVE_ID << 16) | SCHG_TYPE_C_TYPE_C_DEBUG_ACCESS_SNK_CFG) +#define SCHG_CHGR_CHARGING_FCC 0x260A +#define SMB1_CHGR_CHARGING_FCC ((SMB1_SLAVE_ID << 16) | SCHG_CHGR_CHARGING_FCC) +#define SMB2_CHGR_CHARGING_FCC ((SMB2_SLAVE_ID << 16) | SCHG_CHGR_CHARGING_FCC) #define FCC_1A_STEP_50MA 0x14 #define FCC_DISABLE 0x8c #define EN_DEBUG_ACCESS_SNK 0x1B -enum charging_status { - CHRG_DISABLE, - CHRG_ENABLE, -}; - #define PMC8380F_SLAVE_ID 0x05 #define GPIO07_MODE_CTL 0x8E40 #define GPIO07_DIG_OUT_SOURCE_CTL 0x8E44 @@ -38,7 +40,87 @@ enum charging_status { #define OUTPUT_INVERT 0x80 #define PERPH_EN 0x80 -#define GPIO_PARALLEL_CHARGING_CFG GPIO(71) +#define DELAY_CHARGING_APPLET_MS 2000 /* 2sec */ + +enum charging_status { + CHRG_DISABLE, + CHRG_ENABLE, +}; + +static int get_battery_icurr_ma(void) +{ + /* Read battery i-current value */ + int icurr = spmi_read8(SMB1_CHGR_CHARGING_FCC); + if (icurr <= 0) + icurr = spmi_read8(SMB2_CHGR_CHARGING_FCC); + if (icurr < 0) + icurr = 0; + + icurr *= 50; + return icurr; +} + +static void clear_ec_manual_poweron_event(void) +{ + const uint64_t manual_pwron_event_mask = + (EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN)); + google_chromeec_clear_events_b(manual_pwron_event_mask); +} + +static int detect_ec_manual_poweron_event(void) +{ + const uint64_t manual_pwron_event_mask = + (EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON) | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN)); + uint64_t events = google_chromeec_get_events_b(); + + if (!!(events & manual_pwron_event_mask)) + return 1; + + return 0; +} + +void launch_charger_applet(void) +{ + if (!CONFIG(EC_GOOGLE_CHROMEEC)) + return; + + printk(BIOS_INFO, "Inside %s. Initiating charging\n", __func__); + + /* clear any pending power button press and lid open event */ + clear_ec_manual_poweron_event(); + + do { + /* Add static delay before reading the charging applet pre-requisites */ + mdelay(DELAY_CHARGING_APPLET_MS); + + /* + * Issue a shutdown if not charging. + */ + if (!get_battery_icurr_ma()) { + printk(BIOS_INFO, "Issuing power-off due to change in charging state.\n"); + google_chromeec_offmode_heartbeat(); + google_chromeec_ap_poweroff(); + } + + /* + * Exit the charging loop in the event of lid open or power + * button press. + * + * Reset the device to ensure a fresh boot to OS. + * This is required to avoid any kind of tear-down due to ADSP-lite + * being loaded and need some clean up before loading ADSP firmware by + * linux kernel. + */ + if (detect_ec_manual_poweron_event()) { + printk(BIOS_INFO, "Exiting charging applet to boot to OS\n"); + do_board_reset(); + } + + /* TODO: add Tsense support and issue a shutdown in the event of temperature trip */ + } while (true); +} /* * Enable PMC8380F GPIO07 & GPIO09 for parallel charging. diff --git a/src/mainboard/google/bluey/mainboard.c b/src/mainboard/google/bluey/mainboard.c index 2fc8cfdde9..30cf5b13d8 100644 --- a/src/mainboard/google/bluey/mainboard.c +++ b/src/mainboard/google/bluey/mainboard.c @@ -121,12 +121,6 @@ void lb_add_boot_mode(struct lb_header *header) mode->tag = LB_TAG_BOOT_MODE; mode->size = sizeof(*mode); mode->boot_mode = get_boot_mode(); - - configure_parallel_charging_late(); - - /* Enable charging only during off-mode or low-battery mode with charger present */ - if (is_low_power_boot_with_charger()) - enable_slow_battery_charging(); } bool mainboard_needs_pcie_init(void) @@ -177,14 +171,22 @@ static void mainboard_init(struct device *dev) if (get_boot_mode() == LB_BOOT_MODE_LOW_BATTERY) trigger_critical_battery_shutdown(); + configure_parallel_charging_late(); + /* Skip mainboard initialization if boot mode is "low-battery" or "off-mode charging" */ if (is_low_power_boot_with_charger()) { + /* TODO: enable fast charging */ + enable_slow_battery_charging(); + /* Disable the lightbar for Low-Battery or Off-Mode charging sequences. * This maintains visual consistency between the built-in display * indicators and the external lightbar. */ if (CONFIG(EC_GOOGLE_CHROMEEC)) google_chromeec_lightbar_off(); + + /* Boot to charging applet */ + launch_charger_applet(); return; }