From 82efffc38f8f834393ab06375d4e8bd38dec024a Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:24 +0200 Subject: [PATCH 01/21] mach-snapdragon: refactor board_fdt_blob_setup() If U-Boot has a DTB built in (appended to the image directly) then this was likely intentional, we should prioritise it over one provided by ABL (if there was one). Make this behaviour explicit, and panic if no valid DTB could be found anywhere. Returning an error is not useful in this case as U-Boot would just crash later in a more confusing way. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 22a7d2a637d..92492c3a033 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -85,26 +86,35 @@ static void show_psci_version(void) PSCI_VERSION_MINOR(res.a0)); } +/* We support booting U-Boot with an internal DT when running as a first-stage bootloader + * or for supporting quirky devices where it's easier to leave the downstream DT in place + * to improve ABL compatibility. Otherwise, we use the DT provided by ABL. + */ void *board_fdt_blob_setup(int *err) { - phys_addr_t fdt; - /* Return DTB pointer passed by ABL */ + struct fdt_header *fdt; + bool internal_valid, external_valid; + *err = 0; - fdt = get_prev_bl_fdt_addr(); + fdt = (struct fdt_header *)get_prev_bl_fdt_addr(); + external_valid = fdt && !fdt_check_header(fdt); + internal_valid = !fdt_check_header(gd->fdt_blob); /* - * If we bail then the board will simply not boot, instead let's - * try and use the FDT built into U-Boot if there is one... - * This avoids having a hard dependency on the previous stage bootloader + * There is no point returning an error here, U-Boot can't do anything useful in this situation. + * Bail out while we can still print a useful error message. */ + if (!internal_valid && !external_valid) + panic("Internal FDT is invalid and no external FDT was provided! (fdt=%#llx)\n", + (phys_addr_t)fdt); - if (IS_ENABLED(CONFIG_OF_SEPARATE) && (!fdt || fdt != ALIGN(fdt, SZ_4K) || - fdt_check_header((void *)fdt))) { - debug("%s: Using built in FDT, bootloader gave us %#llx\n", __func__, fdt); + if (internal_valid) { + debug("Using built in FDT\n"); return (void *)gd->fdt_blob; + } else { + debug("Using external FDT\n"); + return (void *)fdt; } - - return (void *)fdt; } void reset_cpu(void) From 2f99cea71dd2bd0d1fb961e83803ded4165c5460 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:25 +0200 Subject: [PATCH 02/21] mach-snapdragon: parse memory ourselves The generic memory parsing code in U-Boot lacks a few things that we need on Qualcomm: 1. It sets gd->ram_size and gd->ram_base to represent a single memory block. 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end of that first memory block. This results in all memory beyond U-Boot being unusable in Linux when booting with EFI. Since the ranges in the memory node may be out of order, the only way for us to correctly determine the relocation address for U-Boot is to parse all memory regions and find the highest valid address. We can't use fdtdec_setup_memory_banksize() since it stores the result in gd->bd which is not yet allocated. Hence, this commit, which implements an optimised parser to read the memory blocks and store them in the .data section where they will survive relocation. We set ram_base and ram_size to describe the entire address space of memory, with the assumption that the last memory region is big enough for U-Boot, its DTB, and heap. On all boards tested so far this seems to be a reasonable assumption. As a nice side effect, our fdt parsing also winds up being faster since we avoid the overhead of checking address/size-cells or populating struct resource. We can safely make these optimisations since we only support ARM64, and trust the reg property to be populated correctly. After relocation, we then populate gd->bd->bi_dram with the data we parsed earlier. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 93 +++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 92492c3a033..93de516d9ea 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -38,9 +38,18 @@ static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } }; struct mm_region *mem_map = rbx_mem_map; +static struct { + phys_addr_t start; + phys_size_t size; +} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 }; + int dram_init(void) { - return fdtdec_setup_mem_size_base(); + /* + * gd->ram_base / ram_size have been setup already + * in qcom_parse_memory(). + */ + return 0; } static int ddr_bank_cmp(const void *v1, const void *v2) @@ -58,23 +67,71 @@ static int ddr_bank_cmp(const void *v1, const void *v2) return (res1->start >> 24) - (res2->start >> 24); } +/* This has to be done post-relocation since gd->bd isn't preserved */ +static void qcom_configure_bi_dram(void) +{ + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start; + gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size; + } +} + int dram_init_banksize(void) { - int ret; - - ret = fdtdec_setup_memory_banksize(); - if (ret < 0) - return ret; - - if (CONFIG_NR_DRAM_BANKS < 2) - return 0; - - /* Sort our RAM banks -_- */ - qsort(gd->bd->bi_dram, CONFIG_NR_DRAM_BANKS, sizeof(gd->bd->bi_dram[0]), ddr_bank_cmp); + qcom_configure_bi_dram(); return 0; } +static void qcom_parse_memory(void) +{ + ofnode node; + const fdt64_t *memory; + int memsize; + phys_addr_t ram_end = 0; + int i, j, banks; + + node = ofnode_path("/memory"); + if (!ofnode_valid(node)) { + log_err("No memory node found in device tree!\n"); + return; + } + memory = ofnode_read_prop(node, "reg", &memsize); + if (!memory) { + log_err("No memory configuration was provided by the previous bootloader!\n"); + return; + } + + banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS); + + if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2) + log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS); + + if (banks > CONFIG_NR_DRAM_BANKS) + log_err("Provided more memory banks than we can handle\n"); + + for (i = 0, j = 0; i < banks * 2; i += 2, j++) { + prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]); + prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]); + /* SM8650 boards sometimes have empty regions! */ + if (!prevbl_ddr_banks[j].size) { + j--; + continue; + } + ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); + } + + /* Sort our RAM banks -_- */ + qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp); + + gd->ram_base = prevbl_ddr_banks[0].start; + gd->ram_size = ram_end - gd->ram_base; + debug("ram_base = %#011lx, ram_size = %#011llx, ram_end = %#011llx\n", + gd->ram_base, gd->ram_size, ram_end); +} + static void show_psci_version(void) { struct arm_smccc_res res; @@ -110,11 +167,19 @@ void *board_fdt_blob_setup(int *err) if (internal_valid) { debug("Using built in FDT\n"); - return (void *)gd->fdt_blob; } else { debug("Using external FDT\n"); - return (void *)fdt; + /* So we can use it before returning */ + gd->fdt_blob = fdt; } + + /* + * Parse the /memory node while we're here, + * this makes it easy to do other things early. + */ + qcom_parse_memory(); + + return (void *)gd->fdt_blob; } void reset_cpu(void) From d57a6da542b9d6810992a980f09abfe7da47e1b7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 9 Aug 2024 01:59:26 +0200 Subject: [PATCH 03/21] mach-snapdragon: use 1MiB for get_page_table_size() With 14+ entries in the memory map, we need quite a bit more space for the page tables. Signed-off-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 93de516d9ea..3b9178925c8 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -414,7 +414,7 @@ static void build_mem_map(void) u64 get_page_table_size(void) { - return SZ_64K; + return SZ_1M; } static int fdt_cmp_res(const void *v1, const void *v2) From 3819a6d16737089ea5e232006501a694a9a7a9c4 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:27 +0200 Subject: [PATCH 04/21] mach-snapdragon: set serial number In the typical case where we chainload from ABL, the serial number is available in the DT bootargs. Read it out and set the serial# environment variable so that it can be used by fastboot. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 3b9178925c8..09de967b260 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -244,6 +244,66 @@ int board_init(void) return 0; } +/** + * out_len includes the trailing null space + */ +static int get_cmdline_option(const char *cmdline, const char *key, char *out, int out_len) +{ + const char *p, *p_end; + int len; + + p = strstr(cmdline, key); + if (!p) + return -ENOENT; + + p += strlen(key); + p_end = strstr(p, " "); + if (!p_end) + return -ENOENT; + + len = p_end - p; + if (len > out_len) + len = out_len; + + strncpy(out, p, len); + out[len] = '\0'; + + return 0; +} + +/* The bootargs are populated by the previous stage bootloader */ +static const char *get_cmdline(void) +{ + ofnode node; + static const char *cmdline = NULL; + + if (cmdline) + return cmdline; + + node = ofnode_path("/chosen"); + if (!ofnode_valid(node)) + return NULL; + + cmdline = ofnode_read_string(node, "bootargs"); + + return cmdline; +} + +void qcom_set_serialno(void) +{ + const char *cmdline = get_cmdline(); + char serial[32]; + + if (!cmdline) { + log_debug("Failed to get bootargs\n"); + return; + } + + get_cmdline_option(cmdline, "androidboot.serialno=", serial, sizeof(serial)); + if (serial[0] != '\0') + env_set("serial#", serial); +} + /* Sets up the "board", and "soc" environment variables as well as constructing the devicetree * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a * comprehensive solution to automatically picking the DTB, but aims to be correct for the @@ -342,6 +402,8 @@ static void configure_env(void) snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb", env_get("soc"), env_get("board")); env_set("fdtfile", dt_path); + + qcom_set_serialno(); } void __weak qcom_late_init(void) From 8bf5cadcb434b2170a5390b41927632b45902325 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:28 +0200 Subject: [PATCH 05/21] mach-snapdragon: allocate fastboot buffer dynamically We don't know at build time where a sensible place for this is, allocate it at runtime like the other variables. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 09de967b260..6dd39a6fbf3 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -411,6 +411,11 @@ void __weak qcom_late_init(void) } #define KERNEL_COMP_SIZE SZ_64M +#ifdef CONFIG_FASTBOOT_BUF_SIZE +#define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE +#else +#define FASTBOOT_BUF_SIZE 0 +#endif #define addr_alloc(size) lmb_alloc(size, SZ_2M) @@ -424,6 +429,8 @@ 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)); status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M)); status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M)); status |= env_set_hex("fdt_addr_r", addr_alloc(SZ_2M)); From f05b69e1c4a843d36706b79aad2e508ca9dc0938 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:29 +0200 Subject: [PATCH 06/21] mach-snapdragon: populate fallback FDT Set the fdt_addr_r environment variable to a region of LMB allocated memory, and populate it by default with a copy of U-Boots FDT. This will be used for Linux if no other DT is provided. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 6dd39a6fbf3..05664acb76d 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -423,6 +423,8 @@ void __weak qcom_late_init(void) int board_late_init(void) { u32 status = 0; + phys_addr_t fdt_addr; + struct fdt_header *fdt_blob = (struct fdt_header *)gd->fdt_blob; /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */ status |= env_set_hex("kernel_addr_r", addr_alloc(SZ_128M)); @@ -433,11 +435,15 @@ int board_late_init(void) status |= env_set_hex("fastboot_addr_r", addr_alloc(FASTBOOT_BUF_SIZE)); status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M)); status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M)); - status |= env_set_hex("fdt_addr_r", addr_alloc(SZ_2M)); + fdt_addr = addr_alloc(SZ_2M); + status |= env_set_hex("fdt_addr_r", fdt_addr); if (status) log_warning("%s: Failed to set run time variables\n", __func__); + /* By default copy U-Boots FDT, it will be used as a fallback */ + memcpy((void *)fdt_addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize)); + configure_env(); qcom_late_init(); From a9337c6651781c2ac0f3e381648cf1ab81eea79a Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:30 +0200 Subject: [PATCH 07/21] mach-snapdragon: set loadaddr This variable is used by default in some commands, set it to the same as kernel_addr_r. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/mach-snapdragon/board.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 05664acb76d..0af297470a6 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -423,11 +423,13 @@ void __weak qcom_late_init(void) int board_late_init(void) { u32 status = 0; - phys_addr_t fdt_addr; + phys_addr_t addr; struct fdt_header *fdt_blob = (struct fdt_header *)gd->fdt_blob; /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */ - status |= env_set_hex("kernel_addr_r", addr_alloc(SZ_128M)); + addr = addr_alloc(SZ_128M); + status |= env_set_hex("kernel_addr_r", addr); + status |= env_set_hex("loadaddr", addr); 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); @@ -435,14 +437,14 @@ int board_late_init(void) status |= env_set_hex("fastboot_addr_r", addr_alloc(FASTBOOT_BUF_SIZE)); status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M)); status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M)); - fdt_addr = addr_alloc(SZ_2M); - status |= env_set_hex("fdt_addr_r", fdt_addr); + addr = addr_alloc(SZ_2M); + status |= env_set_hex("fdt_addr_r", addr); if (status) log_warning("%s: Failed to set run time variables\n", __func__); /* By default copy U-Boots FDT, it will be used as a fallback */ - memcpy((void *)fdt_addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize)); + memcpy((void *)addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize)); configure_env(); qcom_late_init(); From 9f2d4561465b42f8cdc478ea5df44b93e12a90d9 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:31 +0200 Subject: [PATCH 08/21] armv8: mmu: add a way to map additional regions In some cases we might want to map some memory region after enabling caches. Introduce a new helper for this. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- arch/arm/cpu/armv8/cache_v8.c | 25 +++++++++++++++++++++++++ arch/arm/include/asm/system.h | 10 ++++++++++ 2 files changed, 35 insertions(+) diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index c3f8dac648b..631d9efa5e1 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -339,6 +339,31 @@ static void map_range(u64 virt, u64 phys, u64 size, int level, } } +void mmu_map_region(phys_addr_t addr, u64 size, bool emergency) +{ + u64 va_bits; + int level = 0; + u64 attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE; + + attrs |= PTE_TYPE_BLOCK | PTE_BLOCK_AF; + + get_tcr(NULL, &va_bits); + if (va_bits < 39) + level = 1; + + if (emergency) + map_range(addr, addr, size, level, + (u64 *)gd->arch.tlb_emerg, attrs); + + /* Switch pagetables while we update the primary one */ + __asm_switch_ttbr(gd->arch.tlb_emerg); + + map_range(addr, addr, size, level, + (u64 *)gd->arch.tlb_addr, attrs); + + __asm_switch_ttbr(gd->arch.tlb_addr); +} + static void add_map(struct mm_region *map) { u64 attrs = map->attrs | PTE_TYPE_BLOCK | PTE_BLOCK_AF; diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 7e30cac32a0..2237d7d0066 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -277,6 +277,16 @@ void protect_secure_region(void); void smp_kick_all_cpus(void); void flush_l3_cache(void); + +/** + * mmu_map_region() - map a region of previously unmapped memory. + * Will be mapped MT_NORMAL & PTE_BLOCK_INNER_SHARE. + * + * @start: Start address of the region + * @size: Size of the region + * @emerg: Also map the region in the emergency table + */ +void mmu_map_region(phys_addr_t start, u64 size, bool emerg); void mmu_change_region_attr(phys_addr_t start, size_t size, u64 attrs); /* From 75acc51189117b9c42232d0ea5c75cf72e39a4d8 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:32 +0200 Subject: [PATCH 09/21] soc: qcom: cmd-db: use strncmp() instead of memcmp() memcmp() can cause aborts on some platforms and generally seems to be the wrong approach here. Use strncmp() instead which is more correct. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- drivers/soc/qcom/cmd-db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 08736ea936a..c7c5230983d 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -141,7 +141,7 @@ static int cmd_db_get_header(const char *id, const struct entry_header **eh, ent = rsc_to_entry_header(rsc_hdr); for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) { - if (memcmp(ent->id, query, sizeof(ent->id)) == 0) { + if (strncmp(ent->id, query, sizeof(ent->id)) == 0) { if (eh) *eh = ent; if (rh) From a9cbf76e4dd3c3295ed975a14ae4dfb258aefd4b Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:33 +0200 Subject: [PATCH 10/21] soc: qcom: cmd-db: map cmd-db region On at least SM8650 this region might not be included in the memory map. Use the new mmu_map_region() helper to map it during bind(). Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- drivers/soc/qcom/cmd-db.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index c7c5230983d..67be18e89f4 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "cmd-db: " fmt +#include #include #include #include @@ -182,9 +183,10 @@ u32 cmd_db_read_addr(const char *id) } EXPORT_SYMBOL_GPL(cmd_db_read_addr); -int cmd_db_bind(struct udevice *dev) +static int cmd_db_bind(struct udevice *dev) { void __iomem *base; + fdt_size_t size; ofnode node; if (cmd_db_header) @@ -194,12 +196,15 @@ int cmd_db_bind(struct udevice *dev) debug("%s(%s)\n", __func__, ofnode_get_name(node)); - base = (void __iomem *)ofnode_get_addr(node); + base = (void __iomem *)ofnode_get_addr_size(node, "reg", &size); if ((fdt_addr_t)base == FDT_ADDR_T_NONE) { log_err("%s: Failed to read base address\n", __func__); return -ENOENT; } + /* On SM8550/SM8650 and newer SoCs cmd-db might not be mapped */ + mmu_map_region((phys_addr_t)base, (phys_size_t)size, false); + cmd_db_header = base; if (!cmd_db_magic_matches(cmd_db_header)) { log_err("%s: Invalid Command DB Magic\n", __func__); From a01ed791c9d16ec07c02deb94dd9732df6eab96a Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 9 Aug 2024 01:59:34 +0200 Subject: [PATCH 11/21] qcom_defconfig: bump CONFIG_NR_DRAM_BANKS Some newer boards end up with a bunch of holes in the memory map due to how Qualcomm's hypervisor and ABL work. The end result is 14+ memory regions. Bump CONFIG_NR_DRAM_BANKS to 24 so we can handle these and any future expansion easily. Yes, this is ridiculous, but there is no other way. Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- configs/qcom_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/qcom_defconfig b/configs/qcom_defconfig index edbb624baf0..3488edd07d4 100644 --- a/configs/qcom_defconfig +++ b/configs/qcom_defconfig @@ -3,6 +3,7 @@ CONFIG_SKIP_LOWLEVEL_INIT=y CONFIG_POSITION_INDEPENDENT=y CONFIG_SYS_INIT_SP_BSS_OFFSET=1572864 CONFIG_ARCH_SNAPDRAGON=y +CONFIG_NR_DRAM_BANKS=24 CONFIG_DEFAULT_DEVICE_TREE="qcom/sdm845-db845c" CONFIG_SYS_LOAD_ADDR=0xA0000000 CONFIG_BUTTON_CMD=y From fdbd2fa400f5cb994d7c686cdb665b4c601796d8 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 3 Sep 2024 18:13:30 +0200 Subject: [PATCH 12/21] soc: qcom: rpmh-rsc: add back __tcs_set_trigger() for SM8550/SM8650 The TCS writes has no effect after the removal of the __tcs_set_trigger() call, obviously it seems the RSC version 3 requires it to complete the transactions. Fixes: 80c5be164ad ("soc: qcom: rpmh-rsc: drop unused multi-threading and non-active TCS support") Signed-off-by: Neil Armstrong Tested-by: Caleb Connolly # sm8250 rb5 Reviewed-by: Caleb Connolly --- drivers/soc/qcom/rpmh-rsc.c | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 61fb2e69558..aee9e55194e 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -293,6 +293,48 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, cmd_enable); } +/** + * __tcs_set_trigger() - Start xfer on a TCS or unset trigger on a borrowed TCS + * @drv: The controller. + * @tcs_id: The global ID of this TCS. + * @trigger: If true then untrigger/retrigger. If false then just untrigger. + * + * In the normal case we only ever call with "trigger=true" to start a + * transfer. That will un-trigger/disable the TCS from the last transfer + * then trigger/enable for this transfer. + * + * If we borrowed a wake TCS for an active-only transfer we'll also call + * this function with "trigger=false" to just do the un-trigger/disable + * before using the TCS for wake purposes again. + * + * Note that the AP is only in charge of triggering active-only transfers. + * The AP never triggers sleep/wake values using this function. + */ +static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) +{ + u32 enable; + u32 reg = drv->regs[RSC_DRV_CONTROL]; + + /* + * HW req: Clear the DRV_CONTROL and enable TCS again + * While clearing ensure that the AMC mode trigger is cleared + * and then the mode enable is cleared. + */ + enable = read_tcs_reg(drv, reg, tcs_id); + enable &= ~TCS_AMC_MODE_TRIGGER; + write_tcs_reg_sync(drv, reg, tcs_id, enable); + enable &= ~TCS_AMC_MODE_ENABLE; + write_tcs_reg_sync(drv, reg, tcs_id, enable); + + if (trigger) { + /* Enable the AMC mode on the TCS and then trigger the TCS */ + enable = TCS_AMC_MODE_ENABLE; + write_tcs_reg_sync(drv, reg, tcs_id, enable); + enable |= TCS_AMC_MODE_TRIGGER; + write_tcs_reg(drv, reg, tcs_id, enable); + } +} + /** * rpmh_rsc_send_data() - Write / trigger active-only message. * @drv: The controller. @@ -348,6 +390,7 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) * of __tcs_set_trigger() below. */ __tcs_buffer_write(drv, tcs_id, 0, msg); + __tcs_set_trigger(drv, tcs_id, true); /* U-Boot: Now wait for the TCS to be cleared, indicating that we're done */ for (i = 0; i < USEC_PER_SEC; i++) { From 3e36ada42e16bcc4253ca87a66716af4a1742520 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 3 Sep 2024 18:13:31 +0200 Subject: [PATCH 13/21] regulator: qcom-rpmh-regulator: add support for PM8550 & related regulators Add the PM8550 & related regulators found on the SM8550 and SM8650 platforms. The tables are imported from the Linux driver. Signed-off-by: Neil Armstrong Reviewed-by: Caleb Connolly --- drivers/power/regulator/qcom-rpmh-regulator.c | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index 06fd3f31956..2dc261d83e3 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -357,6 +357,69 @@ static const struct dm_regulator_ops rpmh_regulator_vrm_drms_ops = { .get_mode = rpmh_regulator_vrm_get_mode, }; +static struct dm_regulator_mode pmic_mode_map_pmic5_bob[] = { + { + .id = REGULATOR_MODE_LPM, + .register_value = PMIC5_BOB_MODE_PFM, + .name = "PMIC5_BOB_MODE_PFM" + }, { + .id = REGULATOR_MODE_AUTO, + .register_value = PMIC5_BOB_MODE_AUTO, + .name = "PMIC5_BOB_MODE_AUTO" + }, { + .id = REGULATOR_MODE_HPM, + .register_value = PMIC5_BOB_MODE_PWM, + .name = "PMIC5_BOB_MODE_PWM" + }, +}; + +static struct dm_regulator_mode pmic_mode_map_pmic5_smps[] = { + { + .id = REGULATOR_MODE_RETENTION, + .register_value = PMIC5_SMPS_MODE_RETENTION, + .name = "PMIC5_SMPS_MODE_RETENTION" + }, { + .id = REGULATOR_MODE_LPM, + .register_value = PMIC5_SMPS_MODE_PFM, + .name = "PMIC5_SMPS_MODE_PFM" + }, { + .id = REGULATOR_MODE_AUTO, + .register_value = PMIC5_SMPS_MODE_AUTO, + .name = "PMIC5_SMPS_MODE_AUTO" + }, { + .id = REGULATOR_MODE_HPM, + .register_value = PMIC5_SMPS_MODE_PWM, + .name = "PMIC5_SMPS_MODE_PWM" + }, +}; + +static const struct rpmh_vreg_hw_data pmic5_bob = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(3000000, 0, 31, 32000), + .n_voltages = 32, + .pmic_mode_map = pmic_mode_map_pmic5_bob, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_bob), +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps525_lv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(300000, 0, 267, 4000), + .n_voltages = 268, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_smps), +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps525_mv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(600000, 0, 267, 8000), + .n_voltages = 268, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_smps), +}; + static struct dm_regulator_mode pmic_mode_map_pmic5_ldo[] = { { .id = REGULATOR_MODE_RETENTION, @@ -393,6 +456,16 @@ static const struct rpmh_vreg_hw_data pmic5_pldo_lv = { .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), }; +static const struct rpmh_vreg_hw_data pmic5_nldo515 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 210, 8000), + .n_voltages = 211, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), +}; + #define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ { \ .name = _name, \ @@ -412,6 +485,57 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm8550_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1-l4-l10"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l13-l14"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l1-l4-l10"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l16"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l6-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l1-l4-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo515, "vdd-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l2-l13-l14"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l2-l13-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo515, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l17"), + RPMH_VREG("bob1", "bob%s1", &pmic5_bob, "vdd-bob1"), + RPMH_VREG("bob2", "bob%s2", &pmic5_bob, "vdd-bob2"), + {} +}; + +static const struct rpmh_vreg_init_data pm8550vs_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_lv, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_mv, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + {} +}; + +static const struct rpmh_vreg_init_data pm8550ve_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + {} +}; + /* probe an individual regulator */ static int rpmh_regulator_probe(struct udevice *dev) { @@ -526,6 +650,18 @@ static const struct udevice_id rpmh_regulator_ids[] = { .compatible = "qcom,pm8150l-rpmh-regulators", .data = (ulong)pm8150l_vreg_data, }, + { + .compatible = "qcom,pm8550-rpmh-regulators", + .data = (ulong)pm8550_vreg_data, + }, + { + .compatible = "qcom,pm8550ve-rpmh-regulators", + .data = (ulong)pm8550ve_vreg_data, + }, + { + .compatible = "qcom,pm8550vs-rpmh-regulators", + .data = (ulong)pm8550vs_vreg_data, + }, { /* sentinal */ }, }; From f50e7be6bb18f4a1e8c5acdb39d0164165bc0089 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:46 +0200 Subject: [PATCH 14/21] clk/qcom: add initial clock driver for sc7280 We don't actually need any clocks to get UFS up and running, resets are useful though. Reviewed-by: Neil Armstrong Reviewed-by: Simon Glass Signed-off-by: Caleb Connolly --- drivers/clk/qcom/Kconfig | 8 ++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clock-qcom.h | 1 + drivers/clk/qcom/clock-sc7280.c | 132 ++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 drivers/clk/qcom/clock-sc7280.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 45d63c6d6db..0d2c0ac225c 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -86,6 +86,14 @@ config CLK_QCOM_SM8650 on the Snapdragon SM8650 SoC. This driver supports the clocks and resets exposed by the GCC hardware block. +config CLK_QCOM_SC7280 + bool "Qualcomm SC7280 GCC" + select CLK_QCOM + help + Say Y here to enable support for the Global Clock Controller + on the Snapdragon SC7280 SoC. This driver supports the clocks + and resets exposed by the GCC hardware block. + endmenu endif diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index dec20e4b594..e223c131ee4 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o +obj-$(CONFIG_CLK_QCOM_SC7280) += clock-sc7280.o obj-$(CONFIG_CLK_QCOM_SM6115) += clock-sm6115.o obj-$(CONFIG_CLK_QCOM_SM8250) += clock-sm8250.o obj-$(CONFIG_CLK_QCOM_SM8550) += clock-sm8550.o diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h index f6445c8f566..7aa6ca59aad 100644 --- a/drivers/clk/qcom/clock-qcom.h +++ b/drivers/clk/qcom/clock-qcom.h @@ -11,6 +11,7 @@ #define CFG_CLK_SRC_GPLL0 (1 << 8) #define CFG_CLK_SRC_GPLL0_AUX2 (2 << 8) #define CFG_CLK_SRC_GPLL9 (2 << 8) +#define CFG_CLK_SRC_GPLL0_ODD (3 << 8) #define CFG_CLK_SRC_GPLL6 (4 << 8) #define CFG_CLK_SRC_GPLL7 (3 << 8) #define CFG_CLK_SRC_GPLL4 (5 << 8) diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c new file mode 100644 index 00000000000..5d343f12051 --- /dev/null +++ b/drivers/clk/qcom/clock-sc7280.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock drivers for Qualcomm sc7280 + * + * (C) Copyright 2024 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clock-qcom.h" + +#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038 +#define USB30_PRIM_MASTER_CLK_CMD_RCGR 0xf020 + +static ulong sc7280_set_rate(struct clk *clk, ulong rate) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id < priv->data->num_clks) + debug("%s: %s, requested rate=%ld\n", __func__, priv->data->clks[clk->id].name, rate); + + switch (clk->id) { + case GCC_USB30_PRIM_MOCK_UTMI_CLK: + WARN(rate != 19200000, "Unexpected rate for USB30_PRIM_MOCK_UTMI_CLK: %lu\n", rate); + clk_rcg_set_rate(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO); + return rate; + case GCC_USB30_PRIM_MASTER_CLK: + WARN(rate != 200000000, "Unexpected rate for USB30_PRIM_MASTER_CLK: %lu\n", rate); + clk_rcg_set_rate_mnd(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, + 1, 0, 0, CFG_CLK_SRC_GPLL0_ODD, 8); + clk_rcg_set_rate(priv->base, 0xf064, 0, 0); + return rate; + default: + return 0; + } +} + +static const struct gate_clk sc7280_clks[] = { + GATE_CLK(GCC_CFG_NOC_USB3_PRIM_AXI_CLK, 0xf07c, 1), + GATE_CLK(GCC_USB30_PRIM_MASTER_CLK, 0xf010, 1), + GATE_CLK(GCC_AGGRE_USB3_PRIM_AXI_CLK, 0xf080, 1), + GATE_CLK(GCC_USB30_PRIM_SLEEP_CLK, 0xf018, 1), + GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1), + GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1), + GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1), +}; + +static int sc7280_enable(struct clk *clk) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + if (priv->data->num_clks < clk->id) { + debug("%s: unknown clk id %lu\n", __func__, clk->id); + return 0; + } + + debug("%s: clk %ld: %s\n", __func__, clk->id, sc7280_clks[clk->id].name); + + switch (clk->id) { + case GCC_AGGRE_USB3_PRIM_AXI_CLK: + qcom_gate_clk_en(priv, GCC_USB30_PRIM_MASTER_CLK); + fallthrough; + case GCC_USB30_PRIM_MASTER_CLK: + qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_AUX_CLK); + qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_COM_AUX_CLK); + break; + } + + qcom_gate_clk_en(priv, clk->id); + + return 0; +} + +static const struct qcom_reset_map sc7280_gcc_resets[] = { + [GCC_PCIE_0_BCR] = { 0x6b000 }, + [GCC_PCIE_0_PHY_BCR] = { 0x6c01c }, + [GCC_PCIE_1_BCR] = { 0x8d000 }, + [GCC_PCIE_1_PHY_BCR] = { 0x8e01c }, + [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x12004 }, + [GCC_SDCC1_BCR] = { 0x75000 }, + [GCC_SDCC2_BCR] = { 0x14000 }, + [GCC_SDCC4_BCR] = { 0x16000 }, + [GCC_UFS_PHY_BCR] = { 0x77000 }, + [GCC_USB30_PRIM_BCR] = { 0xf000 }, + [GCC_USB30_SEC_BCR] = { 0x9e000 }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x50008 }, + [GCC_USB3_PHY_PRIM_BCR] = { 0x50000 }, + [GCC_USB3PHY_PHY_PRIM_BCR] = { 0x50004 }, + [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 }, +}; + +static const struct qcom_power_map sc7280_gdscs[] = { + [GCC_UFS_PHY_GDSC] = { 0x77004 }, + [GCC_USB30_PRIM_GDSC] = { 0xf004 }, +}; + +static struct msm_clk_data qcs404_gcc_data = { + .resets = sc7280_gcc_resets, + .num_resets = ARRAY_SIZE(sc7280_gcc_resets), + .clks = sc7280_clks, + .num_clks = ARRAY_SIZE(sc7280_clks), + + .power_domains = sc7280_gdscs, + .num_power_domains = ARRAY_SIZE(sc7280_gdscs), + + .enable = sc7280_enable, + .set_rate = sc7280_set_rate, +}; + +static const struct udevice_id gcc_sc7280_of_match[] = { + { + .compatible = "qcom,gcc-sc7280", + .data = (ulong)&qcs404_gcc_data, + }, + { } +}; + +U_BOOT_DRIVER(gcc_sc7280) = { + .name = "gcc_sc7280", + .id = UCLASS_NOP, + .of_match = gcc_sc7280_of_match, + .bind = qcom_cc_bind, + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; From 259dba80dc8f53ea344ed2b3d4e9f5f06edf6422 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:47 +0200 Subject: [PATCH 15/21] dts: qcs6490-rb3gen2-u-boot: add override dtsi For running U-Boot as primary bootloader we must define the memory layout statically. Signed-off-by: Caleb Connolly --- arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi diff --git a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi new file mode 100644 index 00000000000..c3ec4a317f7 --- /dev/null +++ b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2024 Linaro Ltd. + */ +/ { + /* When running as the primary bootloader there is no prior + * stage to populate the memory layout for us. We *should* + * have two nodes here, but ABL does NOT like that. + * sooo we're stuck with this. + */ + memory@80000000 { + device_type = "memory"; + reg = <0 0x80000000 0 0x3A800000>, + <0 0xC0000000 0 0x01800000>, + <0 0xC3400000 0 0x3CC00000>, + <1 0x00000000 1 0x00000000>; + }; +}; + +// RAM Entry 0 : Base 0x0080000000 Size 0x003A800000 +// RAM Entry 1 : Base 0x00C0000000 Size 0x0001800000 +// RAM Entry 2 : Base 0x00C3400000 Size 0x003CC00000 +// RAM Entry 3 : Base 0x0100000000 Size 0x0100000000 From 39e0e9fd191d0f964f91c0e66f236a6825c3642c Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:48 +0200 Subject: [PATCH 16/21] dts: qcs6490-rb3gen2-u-boot: USB host mode Adjust DTS so USB runs in host mode. The type-c port is the only supported port (since the others need PCIe). Booting from USB is possible with a powered type-c dock. Signed-off-by: Caleb Connolly --- arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi index c3ec4a317f7..fbe72595f5a 100644 --- a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi +++ b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi @@ -17,6 +17,11 @@ }; }; +&usb_1_dwc3 { + dr_mode = "host"; + /delete-property/ usb-role-switch; +}; + // RAM Entry 0 : Base 0x0080000000 Size 0x003A800000 // RAM Entry 1 : Base 0x00C0000000 Size 0x0001800000 // RAM Entry 2 : Base 0x00C3400000 Size 0x003CC00000 From 2bb90ac29fcf04ecc3ff8eb5d419549ba59077b0 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:49 +0200 Subject: [PATCH 17/21] iommu: qcom-smmu: add sc7280-smmu-500 compatible This soc doesn't have the generic compatible. Reviewed-by: Neil Armstrong Reviewed-by: Simon Glass Signed-off-by: Caleb Connolly --- drivers/iommu/qcom-hyp-smmu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/qcom-hyp-smmu.c b/drivers/iommu/qcom-hyp-smmu.c index 7b646d840dd..1b5a09bb7b3 100644 --- a/drivers/iommu/qcom-hyp-smmu.c +++ b/drivers/iommu/qcom-hyp-smmu.c @@ -381,6 +381,7 @@ static struct iommu_ops qcom_smmu_ops = { static const struct udevice_id qcom_smmu500_ids[] = { { .compatible = "qcom,sdm845-smmu-500" }, + { .compatible = "qcom,sc7280-smmu-500" }, { .compatible = "qcom,smmu-500", }, { /* sentinel */ } }; From 3176b5a2b21db3fe2ba7071bb35b309d29ff3d22 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:50 +0200 Subject: [PATCH 18/21] qcom_defconfig: enable SC7280 clocks Enable clocks on SC7280 Reviewed-by: Neil Armstrong Reviewed-by: Simon Glass Signed-off-by: Caleb Connolly --- configs/qcom_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/qcom_defconfig b/configs/qcom_defconfig index 3488edd07d4..2a2253f766d 100644 --- a/configs/qcom_defconfig +++ b/configs/qcom_defconfig @@ -45,6 +45,7 @@ CONFIG_CLK_QCOM_APQ8016=y CONFIG_CLK_QCOM_APQ8096=y CONFIG_CLK_QCOM_QCM2290=y CONFIG_CLK_QCOM_QCS404=y +CONFIG_CLK_QCOM_SC7280=y CONFIG_CLK_QCOM_SDM845=y CONFIG_CLK_QCOM_SM6115=y CONFIG_CLK_QCOM_SM8250=y From adc310c7afc2d4c9cf1a1498624fce6a09c924f3 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:51 +0200 Subject: [PATCH 19/21] configs: add qcm6490_defconfig Introduce a defconfig for the RB3 Gen 2 and other QCM6490 boards with a dedicated uefi partition. These can replace EDK2 entirely with U-Boot. Reviewed-by: Simon Glass Signed-off-by: Caleb Connolly --- MAINTAINERS | 1 + configs/qcm6490_defconfig | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 configs/qcm6490_defconfig diff --git a/MAINTAINERS b/MAINTAINERS index daaf0345d0e..7ab39d91a55 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -617,6 +617,7 @@ R: Sumit Garg L: u-boot-qcom@groups.io S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-snapdragon.git +F: configs/qcm6490_defconfig F: drivers/*/*/pm8???-* F: drivers/gpio/msm_gpio.c F: drivers/mmc/msm_sdhci.c diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig new file mode 100644 index 00000000000..5ddc5ab3ef8 --- /dev/null +++ b/configs/qcm6490_defconfig @@ -0,0 +1,21 @@ +# Configuration for building U-Boot to be flashed +# to the uefi partition of QCM6490 dev boards with +# the "Linux Embedded" partition layout (which have +# a dedicated "uefi" partition for edk2/U-Boot) + +#include "qcom_defconfig" + +# Otherwise buildman thinks this isn't an ARM platform +CONFIG_ARM=y + +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0x994000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=14745600 + +# Address where U-Boot will be loaded +CONFIG_TEXT_BASE=0x9fc00000 +CONFIG_REMAKE_ELF=y + +CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs6490-rb3gen2" From 224c90ede42f8ab7dc54c321fca3945287ff85a5 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 15:41:52 +0200 Subject: [PATCH 20/21] doc: board/qualcomm: document rb3gen2 building/flashing The process here is almost identical to the Dragonboard 410c, we've come full circle! Reviewed-by: Simon Glass Signed-off-by: Caleb Connolly --- doc/board/qualcomm/index.rst | 1 + doc/board/qualcomm/rb3gen2.rst | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 doc/board/qualcomm/rb3gen2.rst diff --git a/doc/board/qualcomm/index.rst b/doc/board/qualcomm/index.rst index 4955274a39b..8c7969987a9 100644 --- a/doc/board/qualcomm/index.rst +++ b/doc/board/qualcomm/index.rst @@ -7,5 +7,6 @@ Qualcomm :maxdepth: 2 dragonboard410c + rb3gen2 board debugging diff --git a/doc/board/qualcomm/rb3gen2.rst b/doc/board/qualcomm/rb3gen2.rst new file mode 100644 index 00000000000..4240606224f --- /dev/null +++ b/doc/board/qualcomm/rb3gen2.rst @@ -0,0 +1,53 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Caleb Connolly + +Qualcomm Robotics RB3 Gen 2 +=========================== + +The RB3 Gen 2 is a development board based on the Qualcomm QCM6490 SoC (a derivative +of SC7280). More information can be found on `Qualcomm's product page`_. + +U-Boot can be used as a replacement for Qualcomm's original EDK2 bootloader by +flashing it directly to the uefi_a (or _b) partition. + +.. _Qualcomm's product page: https://www.qualcomm.com/developer/hardware/rb3-gen-2-development-kit + +Installation +------------ +First, setup ``CROSS_COMPILE`` for aarch64. Then, build U-Boot for ``qcm6490``:: + + $ export CROSS_COMPILE= + $ make qcm6490_defconfig + $ make -j8 + +This will build ``u-boot.elf`` in the configured output directory. + +Although the RB3 Gen 2 does not have secure boot set up by default, +the firmware still expects firmware ELF images to be "signed". The signature +does not provide any security in this case, but it provides the firmware with +some required metadata. + +To "sign" ``u-boot.elf`` you can use e.g. `qtestsign`_:: + + $ qtestsign -v6 aboot -o u-boot.mbn u-boot.elf + +Then install the resulting ``u-boot.mbn`` to the ``uefi_a`` partition +on your device with ``fastboot flash uefi_a u-boot.mbn``. + +U-Boot should be running after a reboot (``fastboot reboot``). + +Note that fastboot is not yet supported in U-Boot on this board, as a result, +to flash back the original firmware, or new versoins of the U-Boot, EDL mode +must be used. This can be accessed by pressing the EDL mode button as described +in the Qualcomm Linux documentation. A tool like bkerler's `edl`_ can be used +for flashing with the firehose loader binary appropriate for the board. + +.. _qtestsign: https://github.com/msm8916-mainline/qtestsign +.. _edl: https://github.com/bkerler/edl + +Usage +----- + +The USB Type-A ports are connected via a PCIe USB hub, which is not supported yet. +However, the Type-C port can be used with a powered USB dock to connect peripherals +like a USB stick. From 41864bb2b66d0c68e6b2f6ae0c70d3c239ad258d Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 21 Aug 2024 01:00:27 +0200 Subject: [PATCH 21/21] board/qualcomm: add debug config fragments for some SoCs We already have some documentation describing how to enable debug UART for Qualcomm SoCs. However the UART address varies per-soc... Add some config fragments to enable debug UART for few well supported SoCs. These can be used like: $ make qcom_defconfig debug-sdm845.config Reviewed-by: Neil Armstrong Signed-off-by: Caleb Connolly --- board/qualcomm/debug-sdm845.config | 5 +++++ board/qualcomm/debug-sm6115.config | 5 +++++ board/qualcomm/debug-sm8250.config | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 board/qualcomm/debug-sdm845.config create mode 100644 board/qualcomm/debug-sm6115.config create mode 100644 board/qualcomm/debug-sm8250.config diff --git a/board/qualcomm/debug-sdm845.config b/board/qualcomm/debug-sdm845.config new file mode 100644 index 00000000000..31ad6d02a3a --- /dev/null +++ b/board/qualcomm/debug-sdm845.config @@ -0,0 +1,5 @@ +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0xa84000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=7372800 diff --git a/board/qualcomm/debug-sm6115.config b/board/qualcomm/debug-sm6115.config new file mode 100644 index 00000000000..131c6e230de --- /dev/null +++ b/board/qualcomm/debug-sm6115.config @@ -0,0 +1,5 @@ +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0x4a90000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=14745600 diff --git a/board/qualcomm/debug-sm8250.config b/board/qualcomm/debug-sm8250.config new file mode 100644 index 00000000000..4d3cc4cc97f --- /dev/null +++ b/board/qualcomm/debug-sm8250.config @@ -0,0 +1,5 @@ +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0xa90000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=14745600