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 <[email protected]>
> ---
>  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@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
>

Reply via email to