[PATCH v1] mach-snapdragon:: Add support for fastboot reboot reason detection
Varadarajan Narayanan
varadarajan.narayanan at oss.qualcomm.com
Thu Jan 8 08:59:47 CET 2026
On Thu, Jan 08, 2026 at 12:25:33PM +0530, Aswin Murugan wrote:
> Add functionality to detect and handle reboot-to-bootloader requests
> by reading PMIC PON (Power On) registers. When the device is rebooted
> with the bootloader flag set, U-Boot will automatically enter fastboot
> mode.
>
> This implementation supports multiple PMIC generations:
> - Gen 4 PMICs: Uses PON_SOFT_RB_SPARE register (0x88F)
> - Newer PMICs: Uses SDAM-based PON_REBOOT_REASON register (0x7148)
>
> The PMIC model is automatically detected via revision ID registers to
> determine the correct register addresses. After detecting a fastboot
> reboot reason, the register is cleared and fastboot mode is entered
> via the "run fastboot" command.
>
> Signed-off-by: Aswin Murugan <aswin.murugan at oss.qualcomm.com>
> ---
> arch/arm/mach-snapdragon/board.c | 146 +++++++++++++++++++++++++++++++
> 1 file changed, 146 insertions(+)
>
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index 5fb3240acc5..02b84297e05 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -19,6 +19,7 @@
> #include <dm/uclass-internal.h>
> #include <dm/read.h>
> #include <power/regulator.h>
> +#include <power/pmic.h>
> #include <env.h>
> #include <fdt_support.h>
> #include <init.h>
> @@ -32,9 +33,32 @@
> #include <usb.h>
> #include <sort.h>
> #include <time.h>
> +#include <command.h>
>
> #include "qcom-priv.h"
>
> +/* Reboot mode definitions */
> +#define QCOM_REBOOT_MODE_FASTBOOT 0x2
> +#define QCOM_REBOOT_MODE_MASK 0xFE
> +
> +/* PON (Power On) register addresses - Gen 4 PMICs */
> +#define QCOM_PON_BASE_ADDR 0x800
> +#define QCOM_PON_PERPH_SUBTYPE (QCOM_PON_BASE_ADDR + 0x5)
> +#define QCOM_PON_SOFT_RB_SPARE (QCOM_PON_BASE_ADDR + 0x8F)
> +
> +/* PON register addresses - Newer PMICs (SDAM-based) */
> +#define QCOM_PON_REBOOT_REASON_SDAM 0x7148
> +
> +/* PMIC Revision ID registers */
> +#define QCOM_PMIC_REVID_BASE 0x0100
> +#define QCOM_PMIC_METAL_REV (QCOM_PMIC_REVID_BASE + 0x02)
> +
> +/* PMIC identification values */
> +#define QCOM_PMIC_TYPE_QC 0x51
> +#define QCOM_PMIC_SUBTYPE_GEN4 0x4
> +#define QCOM_PMIC_MODEL_PM855 30
> +#define QCOM_PMIC_MODEL_INVALID 0x00
> +
> DECLARE_GLOBAL_DATA_PTR;
>
> enum qcom_boot_source qcom_boot_source __section(".data") = 0;
> @@ -489,6 +513,125 @@ static void configure_env(void)
> qcom_set_serialno();
> }
>
> +/**
> + * detect_pmic_model() - Detect PMIC model from revision ID registers
> + * @dev: PMIC device
> + *
> + * Reads the PMIC revision ID registers to identify the PMIC model.
> + * This is used to determine which register addresses to use for
> + * reading the reboot reason.
> + *
> + * Return: PMIC model number or QCOM_PMIC_MODEL_INVALID on error
> + */
> +static int detect_pmic_model(struct udevice *dev)
return type u8?
> +{
> + u8 rev_id[4] = {0};
> + int ret, i;
> + u8 pmic_model;
> +
> + /* Read 4 bytes from PMIC REVID registers */
> + for (i = 0; i < 4; i++) {
> + ret = pmic_reg_read(dev, QCOM_PMIC_METAL_REV + i);
> + if (ret < 0) {
> + log_debug("Failed to read PMIC REVID reg 0x%X: %d\n",
> + QCOM_PMIC_METAL_REV + i, ret);
> + return QCOM_PMIC_MODEL_INVALID;
> + }
> + rev_id[i] = (u8)ret;
> + }
> +
> + /* Verify this is a Qualcomm PMIC device */
> + if (rev_id[2] != QCOM_PMIC_TYPE_QC) {
> + log_debug("Not a Qualcomm PMIC (type=0x%02X)\n",
> + rev_id[2]);
> + return QCOM_PMIC_MODEL_INVALID;
> + }
> +
> + pmic_model = rev_id[3];
> + log_debug("PMIC Model: 0x%02X (Metal=0x%02X, Layer=0x%02X)\n",
> + pmic_model, rev_id[0], rev_id[1]);
> +
> + return pmic_model;
> +}
> +
> +/**
> + * check_fastboot_mode() - Check if device should enter fastboot mode
> + *
> + * Reads the PMIC PON (Power On) register to check if the reboot reason
> + * indicates that fastboot mode should be entered. The register address
> + * varies depending on the PMIC generation.
> + *
> + * For Gen 4 PMICs: Uses PON_SOFT_RB_SPARE register
> + * For newer PMICs: Uses SDAM-based PON_REBOOT_REASON register
> + *
> + * If fastboot mode is detected, clears the reboot reason and enters
> + * fastboot mode via the "run fastboot" command.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int check_fastboot_mode(void)
> +{
> + struct udevice *dev;
> + int ret, reboot_reason, reg_val;
> + u32 pon_reset_addr;
> + u8 peripheral_subtype;
> + int pmic_model;
u8?
> +
> + if (!IS_ENABLED(CONFIG_DM_PMIC))
> + return 0;
> +
> + ret = pmic_get("pmic at 0", &dev);
> + if (ret)
> + return ret;
> +
> + /* Detect PMIC model for address resolution */
> + pmic_model = detect_pmic_model(dev);
> + if (pmic_model == QCOM_PMIC_MODEL_INVALID)
> + return -1;
> +
> + peripheral_subtype = pmic_reg_read(dev, QCOM_PON_PERPH_SUBTYPE);
> +
> + /* Select register address based on PMIC generation */
> + if (peripheral_subtype == QCOM_PMIC_SUBTYPE_GEN4)
> + pon_reset_addr = QCOM_PON_SOFT_RB_SPARE;
> + else
> + pon_reset_addr = QCOM_PON_REBOOT_REASON_SDAM;
> +
> + log_debug("PON reg: 0x%X (subtype=0x%X, model=0x%X)\n",
> + pon_reset_addr, peripheral_subtype, pmic_model);
> +
> + /* Read reboot reason */
> + reg_val = pmic_reg_read(dev, pon_reset_addr);
> + if (reg_val < 0) {
> + log_debug("Failed to read reboot reason: %d\n", reg_val);
> + return reg_val;
> + }
> +
> + reboot_reason = (reg_val & QCOM_REBOOT_MODE_MASK) >> 1;
> + log_debug("Reboot reason: 0x%02X (raw=0x%02X)\n",
> + reboot_reason, reg_val);
> +
> + if (reboot_reason == QCOM_REBOOT_MODE_FASTBOOT) {
> + log_info("Fastboot mode detected, entering fastboot...\n");
> +
> + /* Clear reboot reason */
> + reg_val &= (~QCOM_REBOOT_MODE_MASK);
> +
> + if (pmic_model == QCOM_PMIC_MODEL_PM855)
> + pon_reset_addr = QCOM_PON_SOFT_RB_SPARE;
Why the pon_reset_addr identified in the 'if (peripheral_subtype ==
QCOM_PMIC_SUBTYPE_GEN4)' check can't be used here? Is there a
possibility of reading from one register and clearing another register?
-Varada
> + ret = pmic_reg_write(dev, pon_reset_addr, reg_val);
> + if (ret != 0)
> + log_warning("Failed to clear reboot reason (%d)\n",
> + ret);
> +
> + /* Enter fastboot mode */
> + ret = run_command("run fastboot", 0);
> + }
> +
> + return ret;
> +}
> +
> void qcom_show_boot_source(void)
> {
> const char *name = "UNKNOWN";
> @@ -573,6 +716,9 @@ int board_late_init(void)
> /* Configure the dfu_string for capsule updates */
> qcom_configure_capsule_updates();
>
> + /* Check reboot reason for fastboot */
> + check_fastboot_mode();
> +
> return 0;
> }
>
> --
> 2.34.1
>
More information about the U-Boot
mailing list