diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/dts/ipq5424-rdp466-u-boot.dtsi | 37 | ||||
-rw-r--r-- | arch/arm/dts/qcs615-ride-u-boot.dtsi | 14 | ||||
-rw-r--r-- | arch/arm/dts/qcs8300-ride-u-boot.dtsi | 19 | ||||
-rw-r--r-- | arch/arm/mach-snapdragon/board.c | 34 | ||||
-rw-r--r-- | arch/arm/mach-snapdragon/capsule_update.c | 274 | ||||
-rw-r--r-- | arch/arm/mach-snapdragon/of_fixup.c | 13 | ||||
-rw-r--r-- | arch/arm/mach-snapdragon/qcom-priv.h | 14 |
8 files changed, 340 insertions, 66 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9a22c7f6cea..8795eac0dc7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1125,6 +1125,7 @@ config ARCH_SNAPDRAGON select LINUX_KERNEL_IMAGE_HEADER if !ENABLE_ARM_SOC_BOOT0_HOOK select SYSRESET select SYSRESET_PSCI + select ANDROID_BOOT_IMAGE_IGNORE_BLOB_ADDR imply OF_UPSTREAM imply CMD_DM imply DM_USB_GADGET diff --git a/arch/arm/dts/ipq5424-rdp466-u-boot.dtsi b/arch/arm/dts/ipq5424-rdp466-u-boot.dtsi new file mode 100644 index 00000000000..9e4af4d9f72 --- /dev/null +++ b/arch/arm/dts/ipq5424-rdp466-u-boot.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IPQ5424 RDP466 board device tree source + * + * Copyright (c) 2025 The Linux Foundation. All rights reserved. + */ + +/ { + /* Will be removed when SMEM parsing is updated */ + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x20000000>; + }; + +}; + + &sdhc { + sdhci-caps-mask = <0x0 0x04000000>; + sdhci-caps = <0x0 0x04000000>; /* SDHCI_CAN_VDD_180 */ + mmc-ddr-1_8v; + mmc-hs200-1_8v; + max-frequency = <192000000>; + bus-width = <4>; + pinctrl-0 = <&sdc_default_state>; + pinctrl-names = "default"; + non-removable; + + /* + * This reset is needed to clear out the settings done by + * previous boot loader. Without this the SDHCI_RESET_ALL + * reset done sdhci_init() times out. + */ + resets = <&gcc GCC_SDCC_BCR>; + + status = "okay"; + }; + diff --git a/arch/arm/dts/qcs615-ride-u-boot.dtsi b/arch/arm/dts/qcs615-ride-u-boot.dtsi new file mode 100644 index 00000000000..68fffc70fcb --- /dev/null +++ b/arch/arm/dts/qcs615-ride-u-boot.dtsi @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/ { + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x7a00000>, + <0x0 0x89600000 0x0 0x30100000>, + <0x0 0xc0000000 0x0 0xc0000000>, + <0x1 0x80000000 0x1 0x00000000>; + }; +}; diff --git a/arch/arm/dts/qcs8300-ride-u-boot.dtsi b/arch/arm/dts/qcs8300-ride-u-boot.dtsi new file mode 100644 index 00000000000..8c353ace71e --- /dev/null +++ b/arch/arm/dts/qcs8300-ride-u-boot.dtsi @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/ { + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x11a80000>, + <0x0 0xc0000000 0x0 0x10000000>, + <0x0 0xd3100000 0x0 0x26b00000>, + <0xe 0x80000000 0x1 0x00000000>, + <0xa 0x80000000 0x1 0x80000000>, + <0x0 0xb0800000 0x0 0x0f200000>, + <0x0 0xd0100000 0x0 0x01800000>, + <0x0 0x91b00000 0x0 0x1e500000>; + }; +}; + diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 5547d6d054f..8f7fc19cadc 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -37,6 +37,8 @@ DECLARE_GLOBAL_DATA_PTR; +enum qcom_boot_source qcom_boot_source __section(".data") = 0; + static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } }; struct mm_region *mem_map = rbx_mem_map; @@ -238,6 +240,12 @@ int board_fdt_blob_setup(void **fdtp) if (ret < 0) panic("No valid memory ranges found!\n"); + /* If we have an external FDT, it can only have come from the Android bootloader. */ + if (external_valid) + qcom_boot_source = QCOM_BOOT_SOURCE_ANDROID; + else + qcom_boot_source = QCOM_BOOT_SOURCE_XBL; + debug("ram_base = %#011lx, ram_size = %#011llx\n", gd->ram_base, gd->ram_size); @@ -481,6 +489,23 @@ static void configure_env(void) qcom_set_serialno(); } +void qcom_show_boot_source(void) +{ + const char *name = "UNKNOWN"; + + switch (qcom_boot_source) { + case QCOM_BOOT_SOURCE_ANDROID: + name = "ABL"; + break; + case QCOM_BOOT_SOURCE_XBL: + name = "XBL"; + break; + } + + log_info("U-Boot loaded from %s\n", name); + env_set("boot_source", name); +} + void __weak qcom_late_init(void) { } @@ -508,8 +533,12 @@ int board_late_init(void) status |= env_set_hex("ramdisk_addr_r", addr_alloc(SZ_128M)); status |= env_set_hex("kernel_comp_addr_r", addr_alloc(KERNEL_COMP_SIZE)); status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE); - if (IS_ENABLED(CONFIG_FASTBOOT)) - status |= env_set_hex("fastboot_addr_r", addr_alloc(FASTBOOT_BUF_SIZE)); + if (IS_ENABLED(CONFIG_FASTBOOT)) { + addr = addr_alloc(FASTBOOT_BUF_SIZE); + status |= env_set_hex("fastboot_addr_r", addr); + /* override loadaddr for memory rich soc */ + status |= env_set_hex("loadaddr", addr); + } status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M)); status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M)); addr = addr_alloc(SZ_2M); @@ -524,6 +553,7 @@ int board_late_init(void) configure_env(); qcom_late_init(); + qcom_show_boot_source(); /* Configure the dfu_string for capsule updates */ qcom_configure_capsule_updates(); diff --git a/arch/arm/mach-snapdragon/capsule_update.c b/arch/arm/mach-snapdragon/capsule_update.c index bf75a9a1b24..4dced4961b6 100644 --- a/arch/arm/mach-snapdragon/capsule_update.c +++ b/arch/arm/mach-snapdragon/capsule_update.c @@ -20,22 +20,19 @@ #include "qcom-priv.h" /* - * NOTE: for now this implementation only supports the rb3gen2. Supporting other - * boards that boot in different ways (e.g. chainloaded from ABL) will require - * additional complexity to properly create the dfu string and fw_images array. - */ - -/* - * To handle different variants like chainloaded U-Boot here we'll need to - * build the fw_images array dynamically at runtime. It looks like - * mach-rockchip is a good example for how to do this. - * Detecting which image types a board uses is TBD, hence for now we only - * support the one new board that runs U-Boot as its primary bootloader. + * To handle different variants like chainloaded U-Boot here we need to + * build the fw_images array dynamically at runtime. These are the possible + * implementations: + * + * - Devices with U-Boot on the uefi_a/b partition + * - Devices with U-Boot on the boot (a/b) partition + * - Devices with U-Boot on the xbl (a/b) partition + * + * Which partition actually has U-Boot on it is determined based on the + * qcom_boot_source variable and additional logic in find_target_partition(). */ struct efi_fw_image fw_images[] = { { - /* U-Boot flashed to the uefi_X partition (e.g. rb3gen2) */ - .fw_name = u"UBOOT_UEFI_PARTITION", .image_index = 1, }, }; @@ -47,6 +44,12 @@ struct efi_capsule_update_info update_info = { .images = fw_images, }; +enum target_part_type { + TARGET_PART_UEFI = 1, + TARGET_PART_XBL, + TARGET_PART_BOOT, +}; + /* LSB first */ struct part_slot_status { u16: 2; @@ -57,35 +60,202 @@ struct part_slot_status { u16 tries_remaining : 4; }; -static int find_boot_partition(const char *partname, struct blk_desc *blk_dev, char *name) +enum ab_slot { + SLOT_NONE, + SLOT_A, + SLOT_B, +}; + +static enum ab_slot get_part_slot(const char *partname) +{ + int len = strlen(partname); + + if (partname[len - 2] != '_') + return SLOT_NONE; + if (partname[len - 1] == 'a') + return SLOT_A; + if (partname[len - 1] == 'b') + return SLOT_B; + + return SLOT_NONE; +} + +/* + * Determine which partition U-Boot is flashed to based on the boot source (ABL/XBL), + * the slot status, and prioritizing the uefi partition over xbl if found. + */ +static int find_target_partition(int *devnum, enum uclass_id *uclass, + enum target_part_type *target_part_type) { int ret; - int partnum; + int partnum, uefi_partnum = -1, xbl_partnum = -1; struct disk_partition info; struct part_slot_status *slot_status; + struct udevice *dev = NULL; + struct blk_desc *desc = NULL, *xbl_desc = NULL; + uchar ptn_name[32] = { 0 }; + bool have_ufs = false; + + /* + * Check to see if we have UFS storage, if so U-Boot MUST be on it and we can skip + * all non-UFS block devices + */ + uclass_foreach_dev_probe(UCLASS_UFS, dev) { + have_ufs = true; + break; + } - for (partnum = 1;; partnum++) { - ret = part_get_info(blk_dev, partnum, &info); - if (ret) - return ret; + uclass_foreach_dev_probe(UCLASS_BLK, dev) { + if (device_get_uclass_id(dev) != UCLASS_BLK) + continue; - slot_status = (struct part_slot_status *)&info.type_flags; - log_io("%16s: Active: %1d, Successful: %1d, Unbootable: %1d, Tries left: %1d\n", - info.name, slot_status->active, - slot_status->successful, slot_status->unbootable, - slot_status->tries_remaining); + /* If we have a UFS then don't look at any other block devices */ + if (have_ufs) { + if (device_get_uclass_id(dev->parent->parent) != UCLASS_UFS) + continue; /* - * FIXME: eventually we'll want to find the active/inactive variant of the partition - * but on the rb3gen2 these values might all be 0 + * If we don't have UFS, then U-Boot must be on the eMMC which is always the first + * MMC device. */ - if (!strncmp(info.name, partname, strlen(partname))) { - log_debug("Found active %s partition: '%s'!\n", partname, info.name); - strlcpy(name, info.name, sizeof(info.name)); - return partnum; + } else if (dev->parent->seq_ > 0) { + continue; } + + desc = dev_get_uclass_plat(dev); + if (!desc || desc->part_type == PART_TYPE_UNKNOWN) + continue; + for (partnum = 1;; partnum++) { + ret = part_get_info(desc, partnum, &info); + if (ret) + break; + + slot_status = (struct part_slot_status *)&info.type_flags; + + /* + * Qualcomm Linux devices have a "uefi" partition, it's A/B but the + * flags might not be set so we assume the A partition unless the B + * partition is active. + */ + if (!strncmp(info.name, "uefi", strlen("uefi"))) { + /* + * If U-Boot was chainloaded somehow we can't be flashed to + * the uefi partition + */ + if (qcom_boot_source != QCOM_BOOT_SOURCE_XBL) + continue; + + *target_part_type = TARGET_PART_UEFI; + /* + * Found an active UEFI partition, this is where U-Boot is + * flashed. + */ + if (slot_status->active) + goto found; + + /* Prefer A slot if it's not marked active */ + if (get_part_slot(info.name) == SLOT_A) { + /* + * If we found the A slot after the B slot (both + * inactive) then we assume U-Boot is on the A slot. + */ + if (uefi_partnum >= 0) + goto found; + + /* Didn't find the B slot yet */ + uefi_partnum = partnum; + strlcpy(ptn_name, info.name, 32); + } else { + /* + * Found inactive B slot after inactive A slot, return + * the A slot + */ + if (uefi_partnum >= 0) { + partnum = uefi_partnum; + goto found; + } + + /* + * Didn't find the A slot yet. Record that we found the + * B slot + */ + uefi_partnum = partnum; + strlcpy(ptn_name, info.name, 32); + } + /* xbl and aboot are effectively the same */ + } else if ((!strncmp(info.name, "xbl", strlen("xbl")) && + strlen(info.name) == 5) || + !strncmp(info.name, "aboot", strlen("aboot"))) { + /* + * If U-Boot was booted via ABL, we can't be flashed to the + * XBL partition + */ + if (qcom_boot_source != QCOM_BOOT_SOURCE_XBL) + continue; + + /* + * ignore xbl partition if we have uefi partitions, U-Boot will + * always be on the UEFI partition in this case. + */ + if (*target_part_type == TARGET_PART_UEFI) + continue; + + /* Either non-A/B or find the active XBL partition */ + if (slot_status->active || !get_part_slot(info.name)) { + /* + * No quick return since we might find a uefi partition + * later + */ + xbl_partnum = partnum; + *target_part_type = TARGET_PART_XBL; + xbl_desc = desc; + strlcpy(ptn_name, info.name, 32); + } + + /* + * No fast return since we might also have a uefi partition which + * will take priority. + */ + } else if (!strncmp(info.name, "boot", strlen("boot"))) { + /* We can only be flashed to boot if we were chainloaded */ + if (qcom_boot_source != QCOM_BOOT_SOURCE_ANDROID) + continue; + + /* + * Either non-A/B or find the active partition. We can return + * immediately here since we've narrowed it down to a single option + */ + if (slot_status->active || !get_part_slot(info.name)) { + *target_part_type = TARGET_PART_BOOT; + goto found; + } + } + } + } + + /* + * Now we've exhausted all options, if we didn't find a uefi partition + * then we are indeed flashed to the xbl partition. + */ + if (*target_part_type == TARGET_PART_XBL) { + partnum = xbl_partnum; + desc = xbl_desc; + goto found; } + /* Found no candidate partitions */ return -1; + +found: + if (desc) { + *devnum = desc->devnum; + *uclass = desc->uclass_id; + } + + /* info won't match for XBL hence the copy. */ + log_info("Capsule update target: %s (disk %d:%d)\n", + *target_part_type == TARGET_PART_BOOT ? info.name : ptn_name, + *devnum, partnum); + return partnum; } /** @@ -101,12 +271,10 @@ static int find_boot_partition(const char *partname, struct blk_desc *blk_dev, c */ void qcom_configure_capsule_updates(void) { - struct blk_desc *desc; int ret = 0, partnum = -1, devnum; static char dfu_string[32] = { 0 }; - char name[32]; /* GPT partition name */ - char *partname = "uefi_a"; - struct udevice *dev = NULL; + enum target_part_type target_part_type = 0; + enum uclass_id dev_uclass; if (IS_ENABLED(CONFIG_SCSI)) { /* Scan for SCSI devices */ @@ -117,26 +285,30 @@ void qcom_configure_capsule_updates(void) } } - uclass_foreach_dev_probe(UCLASS_BLK, dev) { - if (device_get_uclass_id(dev) != UCLASS_BLK) - continue; - - desc = dev_get_uclass_plat(dev); - if (!desc || desc->part_type == PART_TYPE_UNKNOWN) - continue; - devnum = desc->devnum; - partnum = find_boot_partition(partname, desc, - name); - if (partnum >= 0) - break; - } - + partnum = find_target_partition(&devnum, &dev_uclass, &target_part_type); if (partnum < 0) { log_err("Failed to find boot partition\n"); return; } - switch (desc->uclass_id) { + /* + * Set the fw_name based on the partition type. This causes the GUID to be different + * so we will never accidentally flash a U-Boot image intended for XBL to the boot + * partition. + */ + switch (target_part_type) { + case TARGET_PART_UEFI: + fw_images[0].fw_name = u"UBOOT_UEFI_PARTITION"; + break; + case TARGET_PART_XBL: + fw_images[0].fw_name = u"UBOOT_XBL_PARTITION"; + break; + case TARGET_PART_BOOT: + fw_images[0].fw_name = u"UBOOT_BOOT_PARTITION"; + break; + } + + switch (dev_uclass) { case UCLASS_SCSI: snprintf(dfu_string, 32, "scsi %d=u-boot.bin part %d", devnum, partnum); break; @@ -144,10 +316,10 @@ void qcom_configure_capsule_updates(void) snprintf(dfu_string, 32, "mmc 0=u-boot.bin part %d %d", devnum, partnum); break; default: - debug("Unsupported storage uclass: %d\n", desc->uclass_id); + debug("Unsupported storage uclass: %d\n", dev_uclass); return; } - log_debug("boot partition is %s, DFU string: '%s'\n", name, dfu_string); + log_debug("DFU string: '%s'\n", dfu_string); update_info.dfu_string = dfu_string; } diff --git a/arch/arm/mach-snapdragon/of_fixup.c b/arch/arm/mach-snapdragon/of_fixup.c index b398c6b7b9f..328c7812f30 100644 --- a/arch/arm/mach-snapdragon/of_fixup.c +++ b/arch/arm/mach-snapdragon/of_fixup.c @@ -99,19 +99,6 @@ static int fixup_qcom_dwc3(struct device_node *root, struct device_node *glue_np return ret; } - /* - * The RB1/2 boards only have a single USB controller and it's muxed between the type-C port - * and a USB hub. Since we can't do OTG in U-Boot properly we prefer to put it into host mode. - */ - if (of_device_is_compatible(root, "qcom,qrb4210-rb2", NULL, NULL) || - of_device_is_compatible(root, "qcom,qrb2210-rb1", NULL, NULL)) { - ret = of_write_prop(dwc3, "dr_mode", sizeof("host"), "host"); - if (ret) { - log_err("Failed to set 'dr_mode' property: %d\n", ret); - return ret; - } - } - return 0; } diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h index 4f398e2ba37..b8bf574e8bb 100644 --- a/arch/arm/mach-snapdragon/qcom-priv.h +++ b/arch/arm/mach-snapdragon/qcom-priv.h @@ -3,6 +3,20 @@ #ifndef __QCOM_PRIV_H__ #define __QCOM_PRIV_H__ +/** + * enum qcom_boot_source - Track where we got loaded from. + * Used for capsule update logic. + * + * @QCOM_BOOT_SOURCE_ANDROID: chainloaded (typically from ABL) + * @QCOM_BOOT_SOURCE_XBL: flashed to the XBL or UEFI partition + */ +enum qcom_boot_source { + QCOM_BOOT_SOURCE_ANDROID = 1, + QCOM_BOOT_SOURCE_XBL, +}; + +extern enum qcom_boot_source qcom_boot_source; + #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) void qcom_configure_capsule_updates(void); #else |