[PATCH v1] mach-snapdragon:: Add support for fastboot reboot reason detection
Casey Connolly
casey.connolly at linaro.org
Thu Jan 8 18:50:25 CET 2026
Hi Aswin,
On 08/01/2026 07:55, 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)
Could you give a few examples of gen4/newer PMICs and/or release years
so we can get a better idea of which codepaths different SoCs will use?
Having a code comment for this would also be great for anyone having to
debug this in the future.>
> 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.
This is a great feature and useful to have, I'm glad to see it being
implemented.
As I understand it this is the approach a bunch of other architectures
take, but I'm not really happy with having all this driver code in
mach-snapdragon or with hardcoding so much.
Can you move the driver code to the existing pmic_qcom.c and then have
it set an environment variable based on the boot mode?
At this point I think it's ok to then just run commands based on the
value of that variable just before qcom_configure_capsule_updates().
There's an argument to be made for an extra level of indirection and
having this be fully generic (so other platforms can make use of it) but
our usecase here doesn't really justify it.
Kind regards,
>
> 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)
> +{
> + 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;
> +
> + 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;
> +
> + 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;
> }
>
--
// Casey (she/her)
More information about the U-Boot
mailing list