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 <subratabanik@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/91485
Reviewed-by: Kapil Porwal <kapilporwal@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Subrata Banik 2026-03-01 11:04:34 +05:30
commit 12710eafff
3 changed files with 100 additions and 12 deletions

View file

@ -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 */

View file

@ -1,6 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include "board.h"
#include <delay.h>
#include <ec/google/chromeec/ec.h>
#include <reset.h>
#include <soc/pmic.h>
#include <soc/qcom_spmi.h>
#include <types.h>
@ -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.

View file

@ -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;
}