diff --git a/MAINTAINERS b/MAINTAINERS index 0dcc7243124e..4d54a61327c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -688,7 +688,6 @@ F: drivers/phy/msm8916-usbh-phy.c F: drivers/phy/qcom/ F: drivers/serial/serial_msm.c F: drivers/serial/serial_msm_geni.c -F: drivers/smem/msm_smem.c F: drivers/spmi/spmi-msm.c F: drivers/usb/host/ehci-msm.c N: qcom diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index f624675eadf8..1da0677d85b3 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1140,9 +1140,8 @@ config ARCH_SNAPDRAGON select DM_RESET select POWER_DOMAIN select GPIO_EXTRA_HEADER - select MSM_SMEM select OF_CONTROL - select SMEM + select QCOM_SMEM select SPMI select BOARD_LATE_INIT select OF_BOARD diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 39479df7b21f..72c6eb200f2e 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -163,7 +163,7 @@ u64 get_tcr(u64 *pips, u64 *pva_bits) static int pte_type(u64 *pte) { - return *pte & PTE_TYPE_MASK; + return *pte & PTE_TYPE_VALID ? *pte & PTE_TYPE_MASK : PTE_TYPE_FAULT; } /* Returns the LSB number for a PTE on level */ @@ -388,13 +388,18 @@ static void map_range(u64 virt, u64 phys, u64 size, int level, } } -void mmu_map_region(phys_addr_t addr, u64 size, bool emergency) +void mmu_map_region(phys_addr_t addr, u64 size, bool cached, bool emergency) { u64 va_bits; int level = 0; - u64 attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE; + u64 attrs; - attrs |= PTE_TYPE_BLOCK | PTE_BLOCK_AF; + if (cached) + attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL); + else + attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC); + + attrs |= PTE_BLOCK_INNER_SHARE | PTE_TYPE_BLOCK | PTE_BLOCK_AF; get_tcr(NULL, &va_bits); if (va_bits < 39) @@ -940,6 +945,18 @@ static u64 set_one_region(u64 start, u64 size, u64 attrs, bool flag, int level) u64 levelsize = 1ULL << levelshift; u64 *pte = find_pte(start, level); + /* + * If we're trying to unmap a region then check if it's already unmapped or if it's bigger + * then the PTE we're looking at right now, in the first case we can do nothing and in the + * second case we just need to unmap this page/block. + * Otherwise we will needlessly create new tables until we have traversed every single page + * in the region. + */ + if (attrs == PTE_TYPE_FAULT && (pte_type(pte) == PTE_TYPE_FAULT || size >= levelsize)) { + *pte &= ~(PMD_ATTRMASK | PTE_TYPE_MASK); + return levelsize; + } + /* Can we can just modify the current level block PTE? */ if (is_aligned(start, size, levelsize)) { if (flag) { @@ -1082,6 +1099,10 @@ void mmu_change_region_attr(phys_addr_t addr, size_t siz, u64 attrs) gd->arch.tlb_addr + gd->arch.tlb_size); __asm_invalidate_tlb_all(); + /* If we were unmapping a region then we're done! */ + if (attrs == PTE_TYPE_FAULT) + return; + mmu_change_region_attr_nobreak(addr, siz, attrs); } diff --git a/arch/arm/dts/qcs615-ride-u-boot.dtsi b/arch/arm/dts/qcs615-ride-u-boot.dtsi index 68fffc70fcb4..687e9a67a9f0 100644 --- a/arch/arm/dts/qcs615-ride-u-boot.dtsi +++ b/arch/arm/dts/qcs615-ride-u-boot.dtsi @@ -11,4 +11,30 @@ <0x0 0xc0000000 0x0 0xc0000000>, <0x1 0x80000000 0x1 0x00000000>; }; + + reboot-mode { + compatible = "nvmem-reboot-mode"; + nvmem-cells = <&reboot_reason>; + nvmem-cell-names = "reboot-mode"; + + mode-bootloader = <0x02>; + mode-recovery = <0x01>; + }; +}; + +&pm8150_0 { + /* Virtual NVMEM node for PON-based reboot reason storage */ + nvram@800 { + compatible = "qcom,spmi-sdam"; + reg = <0x800>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x00 0x800 0x100>; + + /* Reboot reason cell at PON_SOFT_RB_SPARE (0x88F) */ + reboot_reason: reboot-reason@8f { + reg = <0x8f 0x1>; + bits = <1 7>; + }; + }; }; diff --git a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi index c3ec4a317f7c..3c0d61861c75 100644 --- a/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi +++ b/arch/arm/dts/qcs6490-rb3gen2-u-boot.dtsi @@ -3,21 +3,29 @@ * 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>; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + axon_dma_mem: axon-dma@80600000 { + reg = <0x0 0x80600000 0x0 0x100000>; + no-map; + }; + + xbl_dt_mem: xbl-dt@80894000 { + reg = <0x0 0x80894000 0x0 0x40000>; + no-map; + }; + + pil_reserved_mem: pil-reserved@84300000 { + reg = <0x0 0x84300000 0x0 0x16b00000>; + no-map; + }; + + display_mem: display@e1000000 { + reg = <0x0 0xe1000000 0x0 0x2400000>; + no-map; + }; }; }; - -// 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 diff --git a/arch/arm/dts/talos-evk-u-boot.dtsi b/arch/arm/dts/talos-evk-u-boot.dtsi new file mode 100644 index 000000000000..068ececd7100 --- /dev/null +++ b/arch/arm/dts/talos-evk-u-boot.dtsi @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hyp_mem: hyp@80000000 { + reg = <0x0 0x80000000 0x0 0x600000>; + no-map; + }; + + xbl_boot_mem: xbl-boot@85d00000 { + reg = <0x0 0x85d00000 0x0 0x200000>; + no-map; + }; + + aop_mem: aop@85f00000 { + reg = <0x0 0x85f00000 0x0 0x20000>; + no-map; + }; + + xbl_dt_mem: xbl-dt@85f40000 { + reg = <0x0 0x85f40000 0x0 0x30000>; + no-map; + }; + + tz_stat_mem: tz-stat@86200000 { + reg = <0x0 0x86200000 0x0 0x100000>; + no-map; + }; + + tags_mem: tags@86300000 { + reg = <0x0 0x86300000 0x0 0x1200000>; + no-map; + }; + + tz_mem: tz@87500000 { + reg = <0x0 0x87500000 0x0 0x500000>; + no-map; + }; + + tzapps_mem: tzapps@87a00000 { + reg = <0x0 0x87a00000 0x0 0x1c00000>; + no-map; + }; + + pil_mem: pil@8ab00000 { + reg = <0x0 0x8ab00000 0x0 0xcc17000>; + no-map; + }; + + secure_dsp_mem: secure-dsp@a0000000 { + reg = <0x0 0xa0000000 0x0 0x1600000>; + no-map; + }; + }; +}; + +/* Disable SD card controller */ +&sdhc_2 { + status = "disabled"; +}; diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 5ed6833c155c..52ab86112fb1 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -306,9 +306,10 @@ void flush_l3_cache(void); * * @start: Start address of the region * @size: Size of the region + * @cached: Map memory as cacheable or not * @emerg: Also map the region in the emergency table */ -void mmu_map_region(phys_addr_t start, u64 size, bool emerg); +void mmu_map_region(phys_addr_t start, u64 size, bool cached, bool emerg); /** * mmu_change_region_attr() - change a mapped region attributes diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig index d3de8693b5a8..6e90376007e5 100644 --- a/arch/arm/mach-snapdragon/Kconfig +++ b/arch/arm/mach-snapdragon/Kconfig @@ -42,4 +42,35 @@ config SYS_CONFIG_NAME Based on this option include/configs/.h header will be used for board configuration. +config QCOM_FIT_MULTIDTB + bool "Enable FIT multi-DTB selection for Qualcomm platforms" + depends on FIT + help + Enable FIT multi-DTB selection for Qualcomm platforms. + This allows U-Boot to select the appropriate device tree + from a FIT image. + +choice + prompt "Qualcomm boot0.h workaround" + optional + help + While U-Boot on Qualcomm platforms doesn't generally need compile-time + adjustments based on the target SoC, workarounds in boot0.h can't + practically detect the SoC at runtime. Enable one of these workarounds + if you know you need it. + +config BOOT0_MSM8916_PSCI_WORKAROUND + bool "boot0.h workaround for buggy PSCI on the msm8916 SoC" + help + Select this if you are building U-Boot proper for an msm8916 board that + uses the buggy PSCI implementation. + +config QCOM_EL2_GUNYAH_EXIT_SUPPORT + bool "Enable early EL2 switch by exiting Gunyah" + help + Exit Gunyah hypervisor and switch to EL2 during early boot. This must + happen before EL register configuration in start.S so that U-Boot can + run properly at EL2. +endchoice + endif diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile index 343e825c6fdd..63b6db05552f 100644 --- a/arch/arm/mach-snapdragon/Makefile +++ b/arch/arm/mach-snapdragon/Makefile @@ -2,6 +2,7 @@ # # (C) Copyright 2015 Mateusz Kulikowski -obj-y += board.o +obj-y += board.o dram.o obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o obj-$(CONFIG_OF_LIVE) += of_fixup.o +obj-$(CONFIG_QCOM_FIT_MULTIDTB) += qcom_fit_multidtb.o diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index 829a0109ac78..6acbdbb1f6a1 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -30,134 +32,23 @@ #include #include #include +#include #include +#include #include +#include "qcom_fit_multidtb.h" #include "qcom-priv.h" DECLARE_GLOBAL_DATA_PTR; enum qcom_boot_source qcom_boot_source __section(".data") = 0; +enum qcom_memmap_source qcom_memmap_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; -static struct { - phys_addr_t start; - phys_size_t size; -} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 }; - -int dram_init(void) -{ - /* - * 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) -{ - const struct { - phys_addr_t start; - phys_size_t size; - } *res1 = v1, *res2 = v2; - - if (!res1->size) - return 1; - if (!res2->size) - return -1; - - 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) -{ - qcom_configure_bi_dram(); - - return 0; -} - -/** - * 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. - * - * @fdt: FDT blob to parse /memory node from - * - * Return: 0 on success or -ENODATA if /memory node is missing or incomplete - */ -static int qcom_parse_memory(const void *fdt) -{ - int offset; - const fdt64_t *memory; - int memsize; - phys_addr_t ram_end = 0; - int i, j, banks; - - offset = fdt_path_offset(fdt, "/memory"); - if (offset < 0) - return -ENODATA; - - memory = fdt_getprop(fdt, offset, "reg", &memsize); - if (!memory) - return -ENODATA; - - 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]); - if (!prevbl_ddr_banks[j].size) { - j--; - continue; - } - ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); - } - - if (!banks || !prevbl_ddr_banks[0].size) - return -ENODATA; - - /* 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; - - return 0; -} - static void show_psci_version(void) { struct arm_smccc_res res; @@ -225,7 +116,7 @@ int board_fdt_blob_setup(void **fdtp) /* Prefer memory information from internal DT if it's present */ if (internal_valid) - ret = qcom_parse_memory(internal_fdt); + ret = qcom_parse_memory(internal_fdt, true); if (ret < 0 && external_valid) { /* No internal FDT or it lacks a proper /memory node. @@ -234,7 +125,7 @@ int board_fdt_blob_setup(void **fdtp) if (internal_valid) debug("No memory info in internal FDT, falling back to external\n"); - ret = qcom_parse_memory(external_fdt); + ret = qcom_parse_memory(external_fdt, false); } if (ret < 0) @@ -365,9 +256,14 @@ static const char *get_cmdline(void) void qcom_set_serialno(void) { - const char *cmdline = get_cmdline(); + const char *cmdline; char serial[32]; + if (!qcom_socinfo_init()) + return; + + cmdline = get_cmdline(); + if (!cmdline) { log_debug("Failed to get bootargs\n"); return; @@ -489,21 +385,37 @@ static void configure_env(void) qcom_set_serialno(); } -void qcom_show_boot_source(void) +static void qcom_show_boot_context(void) { - const char *name = "UNKNOWN"; + const char *boot_source = "UNKNOWN"; + const char *memmap_source = "UNKNOWN"; switch (qcom_boot_source) { case QCOM_BOOT_SOURCE_ANDROID: - name = "ABL"; + boot_source = "ABL"; break; case QCOM_BOOT_SOURCE_XBL: - name = "XBL"; + boot_source = "XBL"; break; } - log_info("U-Boot loaded from %s\n", name); - env_set("boot_source", name); + log_info("U-Boot loaded from %s\n", boot_source); + env_set("boot_source", boot_source); + + switch (qcom_memmap_source) { + case QCOM_MEMMAP_SOURCE_INTERNAL_FDT: + memmap_source = "INTERNAL_FDT"; + break; + case QCOM_MEMMAP_SOURCE_EXTERNAL_FDT: + memmap_source = "EXTERNAL_FDT"; + break; + case QCOM_MEMMAP_SOURCE_SMEM: + memmap_source = "SMEM"; + break; + } + + log_info("Memory map loaded from %s\n", memmap_source); + env_set("memmap_source", memmap_source); } void __weak qcom_late_init(void) @@ -571,13 +483,22 @@ int board_late_init(void) memcpy((void *)addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize)); + /* Initialise SMEM if it wasn't done already and ensure it's memory is mapped */ + qcom_smem_init(); + configure_env(); qcom_late_init(); - qcom_show_boot_source(); + qcom_show_boot_context(); /* Configure the dfu_string for capsule updates */ qcom_configure_capsule_updates(); + /* Try FIT multi-DTB selection if enabled */ + if (IS_ENABLED(CONFIG_QCOM_FIT_MULTIDTB)) { + if (qcom_fit_multidtb_setup()) + log_debug("FIT multi-DTB selection not available or failed\n"); + } + return 0; } @@ -623,25 +544,34 @@ u64 get_page_table_size(void) return SZ_1M; } +struct mem_resource_attrs { + fdt_addr_t start; + fdt_addr_t size; + u64 attrs; +}; + static int fdt_cmp_res(const void *v1, const void *v2) { - const struct fdt_resource *res1 = v1, *res2 = v2; + const struct mem_resource_attrs *res1 = v1, *res2 = v2; return res1->start - res2->start; } -#define N_RESERVED_REGIONS 32 +#define N_RESERVED_REGIONS 64 -/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access. +/* Map and unmap reserved memory regions as appropriate. + * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access. * On some platforms this is enough to trigger a security violation and trap * to EL3. + * Regions that may be accessed by drivers get mapped explicitly. */ -static void carve_out_reserved_memory(void) +static void configure_reserved_memory(void) { - static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 }; + static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 }; int parent, rmem, count, i = 0; phys_addr_t start; size_t size; + u64 attrs; /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise * attempt to access them, causing a security exception. @@ -652,12 +582,17 @@ static void carve_out_reserved_memory(void) return; } - /* Collect the reserved memory regions */ + /* Collect the reserved memory regions and appropriate attrs */ fdt_for_each_subnode(rmem, gd->fdt_blob, parent) { const fdt32_t *ptr; - int len; + attrs = PTE_TYPE_FAULT; + /* If the no-map property isn't set then the region is valid */ if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL)) - continue; + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL); + /* If the compatible property is set then this region may be accessed by drivers and should + * be marked valid too. */ + if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL)) + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL); if (i == N_RESERVED_REGIONS) { log_err("Too many reserved regions!\n"); @@ -668,48 +603,109 @@ static void carve_out_reserved_memory(void) * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here * takes <1ms... Oh the woes of no dcache. */ - ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len); + ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL); if (ptr) { /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within * the 32-bit address space. So we can cheat here for speed. */ res[i].start = fdt32_to_cpu(ptr[1]); - res[i].end = res[i].start + fdt32_to_cpu(ptr[3]); + res[i].size = fdt32_to_cpu(ptr[3]); + res[i].attrs = attrs; i++; } } /* Sort the reserved memory regions by address */ count = i; - qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res); + qsort(res, count, sizeof(res[0]), fdt_cmp_res); + debug("Mapping %d regions!\n", count); /* Now set the right attributes for them. Often a lot of the regions are tightly packed together - * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent + * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent * regions. */ - start = ALIGN_DOWN(res[0].start, SZ_2M); - size = ALIGN(res[0].end - start, SZ_2M); + start = res[0].start; + size = res[0].size; + attrs = res[0].attrs; + /* For each region after the first one, either increase the `size` to eventually be mapped or + * map the region we have and start a new one, this allows us to reduce the number of calls to + * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */ for (i = 1; i <= count; i++) { - /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid - * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with - * compatible properties). - * If within 2M of the previous region, bump the size to include this region. Otherwise - * start a new region. - */ - if (i == count || start + size < res[i].start - SZ_2M) { - debug(" 0x%016llx - 0x%016llx: reserved\n", - start, start + size); - mmu_change_region_attr(start, size, PTE_TYPE_FAULT); - /* If this is the final region then quit here before we index - * out of bounds... - */ + /* If i == count we are done, just map the last region. If the last region is + * too far away or the attrs don't match then map the meta-region we have and + * start a new one. */ + if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) { + debug(" 0x%016llx - 0x%016llx: %s\n", + start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID"); + /* No need to break-before-make since dcache is disabled */ + mmu_change_region_attr_nobreak(start, size, attrs); + /* We have now mapped all the regions */ if (i == count) break; - start = ALIGN_DOWN(res[i].start, SZ_2M); - size = ALIGN(res[i].end - start, SZ_2M); + /* Start a new meta-region */ + start = res[i].start; + size = res[i].size; + attrs = res[i].attrs; } else { - /* Bump size if this region is immediately after the previous one */ - size = ALIGN(res[i].end - start, SZ_2M); + /* This region is next to (<8K) the previous one so combine them. + * Accounting for any small (<8K) gap. */ + size = (res[i].start - start) + res[i].size; + } + } +} + +/** + * efi_add_known_memory() - Add platform-specific reserved memory to EFI map + * + * This function is called by the EFI memory initialization code to allow + * platforms to add their reserved memory regions to the EFI memory map. + * For Qualcomm platforms, this parses the reserved-memory nodes from the + * device tree and adds them to the EFI memory map. + */ +void efi_add_known_memory(void) +{ + ofnode parent, node; + fdt_addr_t addr; + fdt_size_t size; + const char *name; + + if (!IS_ENABLED(CONFIG_EFI_LOADER)) + return; + + /* Parse reserved-memory nodes from device tree */ + parent = ofnode_path("/reserved-memory"); + if (!ofnode_valid(parent)) { + log_debug("No reserved-memory node found in device tree\n"); + return; + } + + log_debug("Adding reserved-memory regions to EFI memory map\n"); + + ofnode_for_each_subnode(node, parent) { + if (!ofnode_is_enabled(node)) + continue; + + addr = ofnode_get_addr_size_index(node, 0, &size); + if (addr != FDT_ADDR_T_NONE) { + efi_status_t ret; + + name = ofnode_get_name(node); + ret = efi_add_memory_map(addr, size, + EFI_RESERVED_MEMORY_TYPE); + + if (ret != EFI_SUCCESS) { + log_err("Failed to reserve %s (0x%llx-0x%llx): %lu\n", + name ? name : "unknown", + (unsigned long long)addr, + (unsigned long long)(addr + size), + ret & ~EFI_ERROR_MASK); + } else { + log_debug("Reserved %s: 0x%llx-0x%llx (%llu KB)\n", + name ? name : "unknown", + (unsigned long long)addr, + (unsigned long long)(addr + size), + (unsigned long long)(size / 1024)); + } } } } @@ -745,11 +741,15 @@ void enable_caches(void) gd->arch.tlb_addr = tlb_addr; gd->arch.tlb_size = tlb_size; - /* We do the carveouts only for QCS404, for now. */ - if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) { + /* + * On some boards speculative access may trigger a NOC or XPU violation so explicitly mark + * reserved regions as inacessible (PTE_TYPE_FAULT) + */ + if (qcom_memmap_source == QCOM_MEMMAP_SOURCE_SMEM || + fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) { carveout_start = get_timer(0); /* Takes ~20-50ms on SDM845 */ - carve_out_reserved_memory(); + configure_reserved_memory(); debug("carveout time: %lums\n", get_timer(carveout_start)); } dcache_enable(); diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c new file mode 100644 index 000000000000..af5f3c84882c --- /dev/null +++ b/arch/arm/mach-snapdragon/dram.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Memory layout parsing for Qualcomm. + */ + +#define LOG_CATEGORY LOGC_BOARD +#define pr_fmt(fmt) "QCOM-DRAM: " fmt + +#include +#include +#include +#include +#include + +#include "qcom-priv.h" +#include "rampart.h" + +static struct { + phys_addr_t start; + phys_size_t size; +} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 }; + +int dram_init(void) +{ + /* + * 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) +{ + const struct { + phys_addr_t start; + phys_size_t size; + } *res1 = v1, *res2 = v2; + + if (!res1->size) + return 1; + if (!res2->size) + return -1; + + 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; + debug("Bank[%d]: start = %#011llx, size = %#011llx\n", + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); + if (!prevbl_ddr_banks[i].size) + break; + } +} + +int dram_init_banksize(void) +{ + qcom_configure_bi_dram(); + + return 0; +} + +#define entry_field(v, e, field) (v == 0 ? ((struct ram_partition_entry_v0 *)(e))->field : \ + (v == 1 ? ((struct ram_partition_entry_v1 *)(e))->field : \ + ((struct ram_partition_entry_v3 *)(e))->field)) +#define entry_start(v, e) entry_field(v, e, start_address) +#define entry_length(v, e) entry_field(v, e, length) +#define entry_category(v, e) entry_field(v, e, partition_category) +#define entry_domain(v, e) entry_field(v, e, partition_domain) +#define entry_type(v, e) entry_field(v, e, partition_type) + +/* Parse memory map from SMEM, return the number of entries */ +static int qcom_parse_memory_smem(phys_addr_t *ram_end) +{ + size_t size; + int i, j = 0, ret; + struct usable_ram_partition_table_header *header; + u32 ver; + void *entry; + u32 entry_size; // Size of each RAM partition entry (version dependent) + + ret = qcom_smem_init(); + if (ret) { + debug("Failed to initialize SMEM: %d.\n", ret); + return ret; + } + + header = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_USABLE_RAM_PARTITION_TABLE, &size); + if (!header) { + debug("Failed to find SMEM partition.\n"); + return -ENODEV; + } + + ver = header->version; + debug("SMEM RAM partition table version %u. %u entries\n", ver, header->num_partitions); + + switch (ver) { + case 0: + entry_size = sizeof(struct ram_partition_entry_v0); + entry = ((struct usable_ram_partition_table_v0 *)header)->entries; + break; + case 1: + entry_size = sizeof(struct ram_partition_entry_v1); + entry = ((struct usable_ram_partition_table_v1 *)header)->entries; + break; + default: + pr_warn("Unknown SMEM ram partition table version!\n"); + case 2: + case 3: + entry_size = sizeof(struct ram_partition_entry_v3); + entry = ((struct usable_ram_partition_table_v3 *)header)->entries; + break; + } + + debug("SMEM RAM partition entry size: %u bytes\n", entry_size); + + /* Check validy of RAM */ + for (i = 0; i < header->num_partitions && j < CONFIG_NR_DRAM_BANKS; i++, entry += entry_size) { + debug("Entry %d: [%#010llx - %#010llx]\n", i, entry_start(ver, entry), + (u64)entry_start(ver, entry) + entry_length(ver, entry)); + debug(" cat: %#04x type: %#04x domain: %#04x\n", entry_category(ver, entry), + entry_type(ver, entry), entry_domain(ver, entry)); + + + if (entry_category(ver, entry) != RAM_PARTITION_SDRAM || + entry_type(ver, entry) != RAM_PARTITION_SYS_MEMORY) + continue; + if (!entry_length(ver, entry) && !entry_start(ver, entry)) + break; + + prevbl_ddr_banks[j].start = entry_start(ver, entry); + prevbl_ddr_banks[j].size = entry_length(ver, entry); + *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); + j++; + } + + if (j == CONFIG_NR_DRAM_BANKS) + pr_err("SMEM: More than CONFIG_NR_DRAM_BANKS (%u) entries!", CONFIG_NR_DRAM_BANKS); + + return j; +} + +static void qcom_parse_memory_dt(const fdt64_t *fdt, int *banks, phys_addr_t *ram_end) +{ + int offset; + const fdt64_t *memory; + int memsize; + int i, j; + + *ram_end = 0; + + offset = fdt_path_offset(fdt, "/memory"); + if (offset < 0) + return; + + memory = fdt_getprop(fdt, offset, "reg", &memsize); + if (!memory) + 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]); + if (!prevbl_ddr_banks[j].size) { + j--; + continue; + } + *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size); + } +} + +/** + * 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. + * + * @fdt: FDT blob to parse /memory node from + * @fdt_is_internal: is the FDT one embedded into U-Boot or was it provided by a prior + * bootloader stage? This determined if we should try to rely on SMEM + * (internal FDT) or error. We currently assume that if we are passed + * an external FDT then it already has the memory map populated. + * + * Return: 0 on success or -ENODATA if /memory node is missing or incomplete + */ +int qcom_parse_memory(const void *fdt, bool fdt_is_internal) +{ + phys_addr_t ram_end = 0; + int banks; + + qcom_memmap_source = fdt_is_internal ? QCOM_MEMMAP_SOURCE_INTERNAL_FDT + : QCOM_MEMMAP_SOURCE_EXTERNAL_FDT; + + qcom_parse_memory_dt(fdt, &banks, &ram_end); + + /* + * If using an internal FDT but the memory node is empty + * then fall back to SMEM. + */ + if (!prevbl_ddr_banks[0].size && fdt_is_internal) { + banks = qcom_parse_memory_smem(&ram_end); + if (banks < 0) + panic("Couldn't find a valid memory map!\n"); + qcom_memmap_source = QCOM_MEMMAP_SOURCE_SMEM; + } + + /* 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; + + return 0; +} diff --git a/arch/arm/mach-snapdragon/include/mach/boot0.h b/arch/arm/mach-snapdragon/include/mach/boot0.h index 953cccad7908..032a9fbdb12b 100644 --- a/arch/arm/mach-snapdragon/include/mach/boot0.h +++ b/arch/arm/mach-snapdragon/include/mach/boot0.h @@ -1,54 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Workaround for "PSCI bug" on DragonBoard 410c - * Copyright (C) 2021 Stephan Gerhold - * - * Syscall parameters taken from Qualcomm's LK fork (scm.h): - * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * - * The PSCI implementation in the TrustZone/tz firmware on DragonBoard 410c has - * a bug that starts all other CPU cores in 32-bit mode unless the TZ syscall - * that switches from 32-bit to 64-bit mode is executed at least once. - * - * Normally this happens inside Qualcomm's LK bootloader which runs in 32-bit - * mode and uses the TZ syscall to boot a kernel in 64-bit mode. However, if - * U-Boot is installed to the "aboot" partition (replacing LK) the switch to - * 64-bit mode never happens since U-Boot is already running in 64-bit mode. - * - * A workaround for this "PSCI bug" is to execute the TZ syscall when entering - * U-Boot. That way PSCI is made aware of the 64-bit switch and starts all other - * CPU cores in 64-bit mode as well. - */ -#include -#define ARM_SMCCC_SIP32_FAST_CALL \ - ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, ARM_SMCCC_OWNER_SIP, 0) - - /* - * U-Boot might be started in EL2 or EL3 with custom firmware. - * In that case, we assume that the workaround is not necessary or is - * handled already by the alternative firmware. Using the syscall in EL2 - * would demote U-Boot to EL1; in EL3 it would probably just crash. - */ - mrs x0, CurrentEL - cmp x0, #(1 << 2) /* EL1 */ - bne reset - - /* Prepare TZ syscall parameters */ - mov x0, #ARM_SMCCC_SIP32_FAST_CALL - movk x0, #0x10f /* SCM_SVC_MILESTONE_CMD_ID */ - mov x1, #0x12 /* MAKE_SCM_ARGS(0x2, SMC_PARAM_TYPE_BUFFER_READ) */ - adr x2, el1_system_param - mov x3, el1_system_param_end - el1_system_param - - /* Switch PSCI to 64-bit mode. Resets CPU and returns at el1_elr */ - smc #0 - - /* Something went wrong, perhaps PSCI is already in 64-bit mode? */ +#if defined(CONFIG_SPL_BUILD) b reset - - .align 3 -el1_system_param: - .quad 0, 0, 0, 0, 0, 0, 0, 0, 0 /* el1_x0-x8 */ - .quad reset /* el1_elr */ -el1_system_param_end: +#else +#if defined(CONFIG_BOOT0_MSM8916_PSCI_WORKAROUND) +#include "msm8916_boot0.h" +#elif defined(CONFIG_QCOM_EL2_GUNYAH_EXIT_SUPPORT) +#include "gunyah_exit_boot0.h" +#else + b reset +#endif +#endif diff --git a/arch/arm/mach-snapdragon/include/mach/gunyah_exit_boot0.h b/arch/arm/mach-snapdragon/include/mach/gunyah_exit_boot0.h new file mode 100644 index 000000000000..5c5bd3b66919 --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/gunyah_exit_boot0.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Early Gunyah Hypervisor Exit + * + * Exit Gunyah hypervisor and switch to EL2 during early boot. This must + * happen before EL register configuration in start.S so that U-Boot can + * run properly at EL2. + */ + +#include +#include + +/* TrustZone SMC IDs for hypervisor configuration */ +#define TZ_EL2_SWITCH_SMC_ID 0x02000121 +#define TZ_EL2_SWITCH_PARAM_ID 0x00000023 +#define TZ_EL2_SWITCH_PARAM2_EXIT_GUNYAH 0x1 + + /* Only perform hypervisor switch if we're at EL1 */ + switch_el x9, 3f, 2f, 1f + + /* Save FDT address before we modify x0 */ +1: mov x9, x0 + + /* Switch to EL2 (exit Gunyah) */ + ldr w0, =TZ_EL2_SWITCH_SMC_ID + ldr w1, =TZ_EL2_SWITCH_PARAM_ID + mov w2, wzr + mov w3, wzr + ldr w4, =TZ_EL2_SWITCH_PARAM2_EXIT_GUNYAH + smc #0 + + /* Restore FDT address */ + mov x0, x9 +2: +3: b reset diff --git a/arch/arm/mach-snapdragon/include/mach/msm8916_boot0.h b/arch/arm/mach-snapdragon/include/mach/msm8916_boot0.h new file mode 100644 index 000000000000..953cccad7908 --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/msm8916_boot0.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Workaround for "PSCI bug" on DragonBoard 410c + * Copyright (C) 2021 Stephan Gerhold + * + * Syscall parameters taken from Qualcomm's LK fork (scm.h): + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * + * The PSCI implementation in the TrustZone/tz firmware on DragonBoard 410c has + * a bug that starts all other CPU cores in 32-bit mode unless the TZ syscall + * that switches from 32-bit to 64-bit mode is executed at least once. + * + * Normally this happens inside Qualcomm's LK bootloader which runs in 32-bit + * mode and uses the TZ syscall to boot a kernel in 64-bit mode. However, if + * U-Boot is installed to the "aboot" partition (replacing LK) the switch to + * 64-bit mode never happens since U-Boot is already running in 64-bit mode. + * + * A workaround for this "PSCI bug" is to execute the TZ syscall when entering + * U-Boot. That way PSCI is made aware of the 64-bit switch and starts all other + * CPU cores in 64-bit mode as well. + */ +#include + +#define ARM_SMCCC_SIP32_FAST_CALL \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, ARM_SMCCC_OWNER_SIP, 0) + + /* + * U-Boot might be started in EL2 or EL3 with custom firmware. + * In that case, we assume that the workaround is not necessary or is + * handled already by the alternative firmware. Using the syscall in EL2 + * would demote U-Boot to EL1; in EL3 it would probably just crash. + */ + mrs x0, CurrentEL + cmp x0, #(1 << 2) /* EL1 */ + bne reset + + /* Prepare TZ syscall parameters */ + mov x0, #ARM_SMCCC_SIP32_FAST_CALL + movk x0, #0x10f /* SCM_SVC_MILESTONE_CMD_ID */ + mov x1, #0x12 /* MAKE_SCM_ARGS(0x2, SMC_PARAM_TYPE_BUFFER_READ) */ + adr x2, el1_system_param + mov x3, el1_system_param_end - el1_system_param + + /* Switch PSCI to 64-bit mode. Resets CPU and returns at el1_elr */ + smc #0 + + /* Something went wrong, perhaps PSCI is already in 64-bit mode? */ + b reset + + .align 3 +el1_system_param: + .quad 0, 0, 0, 0, 0, 0, 0, 0, 0 /* el1_x0-x8 */ + .quad reset /* el1_elr */ +el1_system_param_end: diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h index b8bf574e8bbb..39dc8fcc76ad 100644 --- a/arch/arm/mach-snapdragon/qcom-priv.h +++ b/arch/arm/mach-snapdragon/qcom-priv.h @@ -3,6 +3,8 @@ #ifndef __QCOM_PRIV_H__ #define __QCOM_PRIV_H__ +#include + /** * enum qcom_boot_source - Track where we got loaded from. * Used for capsule update logic. @@ -17,10 +19,25 @@ enum qcom_boot_source { extern enum qcom_boot_source qcom_boot_source; +/* + * enum qcom_memmap_source - Track where we got the memory map from. + * used for debugging and validation. + */ +enum qcom_memmap_source { + QCOM_MEMMAP_SOURCE_INTERNAL_FDT = 1, + QCOM_MEMMAP_SOURCE_EXTERNAL_FDT, + QCOM_MEMMAP_SOURCE_SMEM, +}; + +/* Set by qcom_parse_memory() */ +extern enum qcom_memmap_source qcom_memmap_source; + #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) void qcom_configure_capsule_updates(void); #else void qcom_configure_capsule_updates(void) {} #endif /* EFI_HAVE_CAPSULE_SUPPORT */ +int qcom_parse_memory(const void *fdt, bool fdt_is_internal); + #endif /* __QCOM_PRIV_H__ */ diff --git a/arch/arm/mach-snapdragon/qcom_fit_multidtb.c b/arch/arm/mach-snapdragon/qcom_fit_multidtb.c new file mode 100644 index 000000000000..cc0321201a11 --- /dev/null +++ b/arch/arm/mach-snapdragon/qcom_fit_multidtb.c @@ -0,0 +1,1132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm FIT Multi-DTB Selection + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Automatic DTB selection from FIT images based on hardware detection via SMEM. + * Loads qclinux_fit.img from dtb partition, detects hardware parameters, + * and selects the best matching DTB configuration. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_fit_multidtb.h" + +/* FIT image filename on FAT partition and raw partition name */ +#define QCOM_FIT_FILENAME "qclinux_fit.img" +#define QCOM_FIT_PARTNAME "dtb_a" + +#define TCSR_SOC_HW_VERSION 0x01fc8000 +#define TCSR_MAJOR_VERSION_MASK 0x0000ff00 +#define TCSR_MAJOR_VERSION_SHIFT 8 +#define TCSR_MINOR_VERSION_MASK 0x000000ff +#define TCSR_MINOR_VERSION_SHIFT 0 + +#define lmb_alloc(size, addr) lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, addr, size, LMB_NONE) + +/* Maximum values to match (SOC needs 2) */ +#define MAX_MATCH_VALUES 2 + +/* Metadata DTB node names */ +#define META_NODE_OEM "oem" +#define META_NODE_SOC "soc" +#define META_NODE_SOCVER "socver" +#define META_NODE_BOARD "board" +#define META_NODE_BOARDREV "boardrev" +#define META_NODE_SOC_SKU "soc-sku" +#define META_NODE_BOARD_SUBTYPE_PERIPHERAL "board-subtype-peripheral-subtype" +#define META_NODE_BOARD_SUBTYPE_STORAGE "board-subtype-storage-type" +#define META_NODE_BOARD_SUBTYPE_DDR_SIZE "board-subtype-memory-size" +#define META_NODE_SOFTSKU "softsku" + +/* Property names */ +#define PROP_OEM_ID "oem-id" +#define PROP_MSM_ID "msm-id" +#define PROP_SOCVER_ID "socver-id" +#define PROP_BOARD_ID "board-id" +#define PROP_BOARDREV_ID "boardrev-id" +#define PROP_BOARD_SUBTYPE "board-subtype" +#define PROP_SOFTSKU_ID "softsku-id" +#define PROP_COMPATIBLE "compatible" +#define PROP_FDT "fdt" +#define PROP_DATA "data" + +/** + * add_to_bucket() - Add a node name to the bucket list + * @name: Node name to add + * @name_len: Length of the name + * @bucket_head: Head of the bucket list + * + * Return: 0 on success, negative error code on failure + */ +static int add_to_bucket(const char *name, int name_len, struct list_head *bucket_head) +{ + struct bucket_node *node; + + node = malloc(sizeof(*node)); + if (!node) + return -ENOMEM; + + node->name = malloc(name_len + 1); + if (!node->name) { + free(node); + return -ENOMEM; + } + + strlcpy(node->name, name, name_len + 1); + + list_add_tail(&node->list, bucket_head); + + return 0; +} + +/** + * search_in_bucket() - Check if a name exists in the bucket list + * @name: Name to search for + * @bucket_head: Head of the bucket list + * + * Return: true if found, false otherwise + */ +static bool search_in_bucket(const char *name, struct list_head *bucket_head) +{ + struct bucket_node *node; + + list_for_each_entry(node, bucket_head, list) { + if (!strcmp(node->name, name)) + return true; + } + + return false; +} + +/** + * free_bucket_list() - Free all nodes in the bucket list + * @bucket_head: Head of the bucket list + */ +static void free_bucket_list(struct list_head *bucket_head) +{ + struct bucket_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, bucket_head, list) { + list_del(&node->list); + free(node->name); + free(node); + } +} + +/** + * qcom_get_ddr_size_type() - Get DDR size type from gd->ram_size + * @ddr_type: Pointer to store DDR type + * + * Uses gd->ram_size which is already calculated by dram.c from SMEM/FDT + * during early boot (same value U-Boot prints as "DRAM: X GiB"). + * Avoids redundant SMEM re-parsing. + * + * Return: 0 on success + */ +static int qcom_get_ddr_size_type(u32 *ddr_type) +{ + u64 total_ddr_size = gd->ram_size; + + log_info("Total DDR Size: 0x%llx (%llu MB)\n", + total_ddr_size, total_ddr_size / SZ_1M); + + *ddr_type = 0; + if (total_ddr_size <= DDR_128MB) + *ddr_type = DDRTYPE_128MB; + else if (total_ddr_size <= DDR_256MB) + *ddr_type = DDRTYPE_256MB; + else if (total_ddr_size <= DDR_512MB) + *ddr_type = DDRTYPE_512MB; + else if (total_ddr_size <= DDR_1024MB) + *ddr_type = DDRTYPE_1024MB; + else if (total_ddr_size <= DDR_2048MB) + *ddr_type = DDRTYPE_2048MB; + else if (total_ddr_size <= DDR_3072MB) + *ddr_type = DDRTYPE_3072MB; + else if (total_ddr_size <= DDR_4096MB) + *ddr_type = DDRTYPE_4096MB; + + log_debug("DDR Type: %u\n", *ddr_type); + + return 0; +} + +/** + * qcom_get_imem_address() - Look up shared IMEM cookie address for this SoC + * + * Reads the root node compatible string from the device tree and searches + * the qcom_imem_table for a matching entry. + * + * Return: Physical address of shared IMEM cookie, or 0 if not found + */ +static uintptr_t qcom_get_imem_address(void) +{ + const struct qcom_imem_info *entry; + const char *soc_compat; + int len; + + soc_compat = fdt_getprop(gd->fdt_blob, 0, "compatible", &len); + if (!soc_compat) { + log_warning("Cannot read SoC compatible from DT\n"); + return 0; + } + + for (entry = qcom_imem_table; entry->compatible; entry++) { + if (fdt_stringlist_contains(soc_compat, len, entry->compatible)) + return entry->shared_imem_addr; + } + + log_warning("SoC not found in IMEM table\n"); + return 0; +} + +/** + * qcom_get_storage_type() - Detect storage type (UFS/EMMC/NAND) + * + * Reads the boot device type from the shared IMEM cookie structure populated + * by the bootloader. The shared IMEM address is looked up from a per-SoC + * table (qcom_imem_table) using the device tree compatible string. + * + * Falls back to UFS if the SoC is not in the table or if the IMEM cookie + * magic/version validation fails. + * + * Return: mem_card_type enum value (UFS/EMMC/NAND), or UFS as fallback + */ +static enum mem_card_type qcom_get_storage_type(void) +{ + struct boot_imem_cookie *imem; + uintptr_t shared_imem_addr; + + shared_imem_addr = qcom_get_imem_address(); + if (!shared_imem_addr) { + log_warning("SoC not in IMEM table, defaulting to UFS\n"); + return UFS; + } + + imem = (struct boot_imem_cookie *)shared_imem_addr; + + if (imem->shared_imem_magic != BOOT_SHARED_IMEM_MAGIC_NUM) { + log_warning("Invalid shared IMEM magic: 0x%x, defaulting to UFS\n", + imem->shared_imem_magic); + return UFS; + } + + if (imem->shared_imem_version < BOOT_SHARED_IMEM_VERSION_NUM) { + log_warning("Invalid shared IMEM version: %u, defaulting to UFS\n", + imem->shared_imem_version); + return UFS; + } + + log_info("Shared IMEM: magic=0x%x, version=%u, boot_device_type=%u\n", + imem->shared_imem_magic, imem->shared_imem_version, + imem->boot_device_type); + + switch (imem->boot_device_type) { + case UFS_FLASH: + return UFS; + case MMC_FLASH: + case SDC_FLASH: + return EMMC; + case NAND_FLASH: + return NAND; + default: + log_warning("Unknown shared IMEM boot device: %u, defaulting to UFS\n", + imem->boot_device_type); + return UFS; + } +} + +static u32 qcom_read_tcsr_soc_version(void) +{ + u32 reg_val = readl(TCSR_SOC_HW_VERSION); + u32 major = (reg_val & TCSR_MAJOR_VERSION_MASK) >> TCSR_MAJOR_VERSION_SHIFT; + u32 minor = (reg_val & TCSR_MINOR_VERSION_MASK) >> TCSR_MINOR_VERSION_SHIFT; + + return (major << 4) | minor; +} + +/** + * qcom_detect_hardware_params() - Detect all hardware parameters from SMEM + * @params: Pointer to hardware parameters structure + * + * Reads hardware information directly from SMEM using qcom_smem_get() and + * populates the qcom_hw_params structure with all data needed for DTB selection. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_detect_hardware_params(struct qcom_hw_params *params) +{ + struct socinfo *soc_info; + size_t size; + int ret; + u32 raw_version; + + memset(params, 0, sizeof(*params)); + + soc_info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, &size); + if (IS_ERR_OR_NULL(soc_info)) { + log_err("Failed to get SOC info from SMEM\n"); + return -ENODEV; + } + + params->chip_id = le32_to_cpu(soc_info->id) & 0xffff; + + raw_version = le32_to_cpu(soc_info->plat_ver); + params->board_version = (SOCINFO_MAJOR(raw_version) << 4) | SOCINFO_MINOR(raw_version); + + params->chip_version = qcom_read_tcsr_soc_version(); + + params->platform = le32_to_cpu(soc_info->hw_plat); + params->subtype = le32_to_cpu(soc_info->hw_plat_subtype); + + if (le32_to_cpu(soc_info->fmt) >= 17) + params->oem_variant_id = le32_to_cpu(soc_info->oem_variant); + + if (le32_to_cpu(soc_info->fmt) >= 9) + params->foundry_id = le32_to_cpu(soc_info->foundry_id); + + ret = qcom_get_ddr_size_type(¶ms->ddr_size_type); + if (ret) + log_warning("Failed to get DDR size, defaulting to 0\n"); + + params->storage_type = qcom_get_storage_type(); + + log_info("Hardware Parameters:\n"); + log_info(" Chip ID: 0x%x\n", params->chip_id); + log_info(" Chip Version: 0x%x\n", params->chip_version); + log_info(" Board Version: 0x%x\n", params->board_version); + log_info(" Platform: 0x%x\n", params->platform); + log_info(" Subtype: 0x%x\n", params->subtype); + log_info(" OEM Variant ID: 0x%x\n", params->oem_variant_id); + log_info(" DDR Size Type: %u\n", params->ddr_size_type); + log_info(" Storage Type: %u\n", params->storage_type); + log_info(" Foundry ID: 0x%x\n", params->foundry_id); + + return 0; +} + +/** + * log_match_values() - Log matched hardware parameter values + * @log_type: Type label for logging (e.g., "SOC", "Board") + * @subnode_name: Name of the matched node + * @num_match_values: Number of values to log + * @match_values: Array of matched values + */ +static void log_match_values(const char *log_type, const char *subnode_name, + int num_match_values, const u32 *match_values) +{ + int i; + + log_info("Matched %s: %s (", log_type, subnode_name); + + for (i = 0; i < num_match_values; i++) { + if (i > 0) + log_info(", "); + log_info("val%d=0x%x", i + 1, match_values[i]); + } + + log_info(")\n"); +} + +/** + * process_node() - Generic metadata node processor + * @type: Type of node to process + * @metadata: Metadata DTB pointer + * @root_offset: Root node offset + * @params: Hardware parameters + * @bucket_head: Bucket list head + * + * Processes different types of nodes in the metadata DTB. Handles matching + * hardware parameters against DTB properties, with support for bit masking/shifting + * and fallback values. + * + * Return: 0 on success, -ENOENT if no match, other negative on error + */ +static int process_node(enum node_process_type type, + void *metadata, + int root_offset, + struct qcom_hw_params *params, + struct list_head *bucket_head) +{ + const char *node_name, *prop_name, *log_type; + const char *fallback; + const char *subnode_name; + int node_offset, subnode, len, name_len; + int num_match_values, i; + u32 match_values[MAX_MATCH_VALUES]; + u32 masks[MAX_MATCH_VALUES]; + int shifts[MAX_MATCH_VALUES]; + bool all_match; + + fallback = NULL; + num_match_values = 1; + memset(shifts, 0, sizeof(shifts)); + memset(masks, 0xff, sizeof(masks)); + + switch (type) { + case NODE_TYPE_OEM: + node_name = META_NODE_OEM; + prop_name = PROP_OEM_ID; + match_values[0] = params->oem_variant_id; + log_type = "OEM"; + fallback = "qcom"; + break; + case NODE_TYPE_SOC: + node_name = META_NODE_SOC; + prop_name = PROP_MSM_ID; + match_values[0] = params->chip_id; + masks[0] = 0xffff; + num_match_values = 1; + log_type = "SOC"; + break; + case NODE_TYPE_SOCVER: + node_name = META_NODE_SOCVER; + prop_name = PROP_SOCVER_ID; + match_values[0] = params->chip_version; + num_match_values = 1; + log_type = "SOCVER"; + break; + case NODE_TYPE_BOARD: + node_name = META_NODE_BOARD; + prop_name = PROP_BOARD_ID; + match_values[0] = params->platform; + log_type = "Board"; + break; + case NODE_TYPE_BOARDREV: + node_name = META_NODE_BOARDREV; + prop_name = PROP_BOARDREV_ID; + match_values[0] = params->board_version; + num_match_values = 1; + log_type = "BoardRev"; + break; + case NODE_TYPE_PERIPHERAL: + node_name = META_NODE_BOARD_SUBTYPE_PERIPHERAL; + prop_name = PROP_BOARD_SUBTYPE; + match_values[0] = params->subtype; + log_type = "Peripheral Subtype"; + break; + case NODE_TYPE_STORAGE: + node_name = META_NODE_BOARD_SUBTYPE_STORAGE; + prop_name = PROP_BOARD_SUBTYPE; + match_values[0] = params->storage_type; + masks[0] = 0x7000; + shifts[0] = 12; + log_type = "Storage"; + break; + case NODE_TYPE_DDR_SIZE: + node_name = META_NODE_BOARD_SUBTYPE_DDR_SIZE; + prop_name = PROP_BOARD_SUBTYPE; + match_values[0] = params->ddr_size_type; + masks[0] = 0xf00; + shifts[0] = 8; + log_type = "DDR Size"; + break; + case NODE_TYPE_SOFTSKU: + node_name = META_NODE_SOFTSKU; + prop_name = PROP_SOFTSKU_ID; + match_values[0] = params->softsku_id; + log_type = "SoftSKU"; + break; + default: + return -EINVAL; + } + + node_offset = fdt_subnode_offset(metadata, root_offset, node_name); + if (node_offset < 0) { + log_debug("%s node not found\n", log_type); + return node_offset; + } + + fdt_for_each_subnode(subnode, metadata, node_offset) { + const u32 *prop = fdt_getprop(metadata, subnode, prop_name, &len); + + if (!prop || len < (int)(num_match_values * sizeof(u32))) + continue; + + all_match = true; + for (i = 0; i < num_match_values; i++) { + u32 dtb_value = fdt32_to_cpu(prop[i]); + + dtb_value = (dtb_value & masks[i]) >> shifts[i]; + + if (dtb_value != match_values[i]) { + all_match = false; + break; + } + } + + if (!all_match) + continue; + + subnode_name = fdt_get_name(metadata, subnode, &name_len); + if (subnode_name) { + log_match_values(log_type, subnode_name, num_match_values, + match_values); + return add_to_bucket(subnode_name, name_len, bucket_head); + } + } + + if (fallback) { + log_info("No %s match, using fallback '%s'\n", log_type, fallback); + return add_to_bucket(fallback, strlen(fallback), bucket_head); + } + + log_debug("No %s match\n", log_type); + + return -ENOENT; +} + +/** + * qcom_build_bucket_list() - Build bucket list from metadata DTB + * @metadata: Metadata DTB pointer + * @params: Hardware parameters + * @bucket_head: Bucket list head + * + * This function parses the metadata DTB and builds a list of matching + * node names based on the detected hardware parameters. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_build_bucket_list(void *metadata, + struct qcom_hw_params *params, + struct list_head *bucket_head) +{ + int root_offset; + int ret; + struct bucket_node *node; + + log_debug("Building bucket list from hardware parameters\n"); + + root_offset = fdt_path_offset(metadata, "/"); + if (root_offset < 0) { + log_err("Failed to find root node in metadata DTB\n"); + return root_offset; + } + + ret = process_node(NODE_TYPE_OEM, metadata, root_offset, + params, bucket_head); + if (ret < 0 && ret != -ENOENT) + return ret; + + ret = process_node(NODE_TYPE_SOC, metadata, root_offset, + params, bucket_head); + if (ret < 0 && ret != -ENOENT) + return ret; + + ret = process_node(NODE_TYPE_SOCVER, metadata, root_offset, + params, bucket_head); + if (ret < 0 && ret != -ENOENT) + return ret; + + ret = process_node(NODE_TYPE_BOARD, metadata, root_offset, + params, bucket_head); + if (ret < 0 && ret != -ENOENT) + return ret; + + ret = process_node(NODE_TYPE_BOARDREV, metadata, root_offset, + params, bucket_head); + if (ret < 0 && ret != -ENOENT) + return ret; + + process_node(NODE_TYPE_PERIPHERAL, metadata, root_offset, + params, bucket_head); + + process_node(NODE_TYPE_STORAGE, metadata, root_offset, + params, bucket_head); + + process_node(NODE_TYPE_DDR_SIZE, metadata, root_offset, + params, bucket_head); + + process_node(NODE_TYPE_SOFTSKU, metadata, root_offset, + params, bucket_head); + + log_debug("Bucket list: "); + list_for_each_entry(node, bucket_head, list) + log_debug("%s ", node->name); + log_debug("\n"); + + return 0; +} + +/** + * qcom_find_fat_file() - Find a file on any FAT partition across all block devices + * @filename: Filename to search for + * @descp: Returns block descriptor of the partition containing the file + * @part_info: Returns partition info of the found partition + * + * Iterates through all block devices and their partitions, mounting each as + * FAT and checking if the file exists. + * + * Return: 0 on success, -ENOENT if not found + */ +static int qcom_find_fat_file(const char *filename, struct blk_desc **descp, + struct disk_partition *part_info) +{ + struct udevice *blk_dev; + struct blk_desc *desc; + loff_t file_size; + int partnum; + + blk_foreach_probe(BLKF_BOTH, blk_dev) { + desc = dev_get_uclass_plat(blk_dev); + if (!desc) + continue; + + for (partnum = 1; partnum <= MAX_SEARCH_PARTITIONS; partnum++) { + if (part_get_info(desc, partnum, part_info)) + break; + if (fat_set_blk_dev(desc, part_info)) + continue; + if (!fat_size(filename, &file_size)) { + *descp = desc; + return 0; + } + } + } + + return -ENOENT; +} + +/** + * qcom_load_raw_partition() - Load FIT image from a raw partition by name + * @partname: GPT partition name to search for + * @fitp: Pointer to store FIT image address + * @fit_sizep: Pointer to store FIT image size + * + * Searches all block devices for a GPT partition with the given name and + * reads the entire partition into a buffer. The caller is responsible for + * validating the content (fdt_check_header, fit_check_format). + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_load_raw_partition(const char *partname, void **fitp, + size_t *fit_sizep) +{ + struct udevice *blk_dev; + struct blk_desc *desc = NULL; + struct disk_partition part_info; + void *part_buf; + size_t part_size; + + blk_foreach_probe(BLKF_BOTH, blk_dev) { + desc = dev_get_uclass_plat(blk_dev); + if (!desc) + continue; + if (part_get_info_by_name(desc, partname, &part_info) >= 0) + break; + desc = NULL; + } + + if (!desc) { + log_err("Partition '%s' not found on any block device\n", partname); + return -ENOENT; + } + + part_size = (size_t)(part_info.size * part_info.blksz); + part_buf = malloc(part_size); + if (!part_buf) + return -ENOMEM; + + if (blk_dread(desc, part_info.start, part_info.size, part_buf) != part_info.size) { + log_err("Failed to read partition '%s'\n", partname); + free(part_buf); + return -EIO; + } + + *fitp = part_buf; + *fit_sizep = part_size; + + log_info("Loaded raw partition '%s': %zu bytes\n", partname, part_size); + return 0; +} + +/** + * qcom_load_fit_image() - Load FIT image from FAT partition or raw partition + * @filename: Filename to search for on FAT partitions + * @partname: GPT partition name to use as fallback (raw image) + * @fitp: Pointer to store FIT image address + * @fit_sizep: Pointer to store FIT image size + * + * First tries to find @filename on any FAT partition across all block devices. + * If not found, falls back to reading the raw partition named @partname. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_load_fit_image(const char *filename, const char *partname, + void **fitp, size_t *fit_sizep) +{ + struct blk_desc *desc; + struct disk_partition part_info; + loff_t file_size; + void *fit_buf; + int ret; + + log_info("%s: Loading FIT image\n", __func__); + + /* Try FAT first: search for filename on any FAT partition */ + ret = qcom_find_fat_file(filename, &desc, &part_info); + if (!ret) { + fat_size(filename, &file_size); + + fit_buf = malloc(file_size); + if (!fit_buf) + return -ENOMEM; + + ret = file_fat_read(filename, fit_buf, file_size); + if (ret >= 0) { + log_info("Loaded '%s' from FAT partition: %lld bytes\n", + filename, file_size); + *fitp = fit_buf; + *fit_sizep = file_size; + return 0; + } + + log_debug("FAT read of '%s' failed (%d), trying raw partition\n", + filename, ret); + free(fit_buf); + } + + /* Fallback: try raw partition by name */ + return qcom_load_raw_partition(partname, fitp, fit_sizep); +} + +/** + * qcom_extract_metadata_dtb() - Extract metadata DTB from FIT image + * @fit: FIT image pointer + * @metadata: Pointer to store metadata DTB address + * @metadata_size: Pointer to store metadata DTB size + * + * The metadata DTB is the first image in the FIT (fdt-0). + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_extract_metadata_dtb(void *fit, void **metadata, + size_t *metadata_size) +{ + int images_node, first_image; + const void *data; + size_t size; + int ret; + + images_node = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_node < 0) { + log_err("Cannot find /images node in FIT\n"); + return images_node; + } + + first_image = fdt_first_subnode(fit, images_node); + if (first_image < 0) { + log_err("Cannot find first image in FIT\n"); + return first_image; + } + + ret = fit_image_get_data(fit, first_image, &data, &size); + if (ret) { + log_err("Failed to get metadata DTB data\n"); + return ret; + } + + *metadata = malloc(size); + if (!*metadata) { + log_err("Failed to allocate memory for metadata DTB\n"); + return -ENOMEM; + } + + memcpy(*metadata, data, size); + *metadata_size = size; + + log_info("Extracted metadata DTB: %zu bytes\n", size); + + return 0; +} + +/** + * qcom_count_compatible_matches() - Count matching tokens in compatible string + * @compatible: Compatible string from FIT configuration + * @compat_len: Length of compatible string + * @bucket_head: Bucket list head + * + * Parses the compatible string and counts how many tokens match entries + * in the bucket list. The compatible string format is typically: + * "vendor,device-variant-subtype" where tokens are separated by commas and dashes. + * + * Return: Number of matching tokens + */ +static int qcom_count_compatible_matches(const char *compatible, int compat_len, + struct list_head *bucket_head) +{ + char *compat_copy; + char *str_ptr; + char *token; + int match_count = 0; + + compat_copy = malloc(compat_len + 1); + if (!compat_copy) + return 0; + + memcpy(compat_copy, compatible, compat_len); + compat_copy[compat_len] = '\0'; + + str_ptr = compat_copy; + + /* First split by comma to get vendor prefix (e.g., "qcom") */ + token = strsep(&str_ptr, ","); + if (token && search_in_bucket(token, bucket_head)) + match_count++; + + /* Then split remaining parts by dash */ + token = strsep(&str_ptr, "-"); + while (token) { + if (search_in_bucket(token, bucket_head)) + match_count++; + token = strsep(&str_ptr, "-"); + } + + free(compat_copy); + return match_count; +} + +/** + * qcom_find_matching_config() - Find matching FIT configuration + * @fit: FIT image pointer + * @bucket_head: Bucket list head + * @config_node: Pointer to store matching configuration node offset + * + * This function iterates through all FIT configurations and finds the one + * with the most matching tokens in its compatible string against the bucket list. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_find_matching_config(void *fit, struct list_head *bucket_head, + int *config_node) +{ + int configs_node, cfg; + const char *compatible; + int compat_len; + const char *cfg_name; + int name_len; + int best_match_count = 0; + int best_config = -1; + int match_count; + + configs_node = fdt_path_offset(fit, FIT_CONFS_PATH); + if (configs_node < 0) { + log_err("Cannot find /configurations node in FIT\n"); + return configs_node; + } + + fdt_for_each_subnode(cfg, fit, configs_node) { + cfg_name = fdt_get_name(fit, cfg, &name_len); + compatible = fdt_getprop(fit, cfg, PROP_COMPATIBLE, &compat_len); + + if (!compatible || compat_len <= 0) { + log_debug("Config %s has no compatible property\n", cfg_name); + continue; + } + + log_debug("Checking config: %s, compatible: %s\n", + cfg_name, compatible); + + match_count = qcom_count_compatible_matches(compatible, compat_len, + bucket_head); + + log_debug("Config %s: %d matches\n", cfg_name, match_count); + + if (match_count > best_match_count) { + best_match_count = match_count; + best_config = cfg; + } + } + + if (best_config < 0) { + log_err("No matching configuration found\n"); + return -ENOENT; + } + + cfg_name = fdt_get_name(fit, best_config, &name_len); + compatible = fdt_getprop(fit, best_config, PROP_COMPATIBLE, &compat_len); + log_info("Selected configuration: %s (compatible: %s, matches: %d)\n", + cfg_name, compatible, best_match_count); + + *config_node = best_config; + return 0; +} + +/** + * qcom_get_fdt_image_data() - Get FDT image data from FIT + * @fit: FIT image pointer + * @images_node: Images node offset + * @fdt_name: FDT image name to load + * @fdt_datap: Pointer to store FDT data address + * @fdt_sizep: Pointer to store FDT data size + * + * Helper function to load an FDT image from the FIT by name. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_get_fdt_image_data(void *fit, int images_node, + const char *fdt_name, + const void **fdt_datap, size_t *fdt_sizep) +{ + int fdt_node; + int ret; + + fdt_node = fdt_subnode_offset(fit, images_node, fdt_name); + if (fdt_node < 0) { + log_err("Cannot find FDT node: %s\n", fdt_name); + return fdt_node; + } + + ret = fit_image_get_data(fit, fdt_node, fdt_datap, fdt_sizep); + if (ret) { + log_err("Failed to get FDT data for %s\n", fdt_name); + return ret; + } + + return 0; +} + +/** + * qcom_load_dtb_with_overlays() - Load DTB and apply overlays + * @fit: FIT image pointer + * @config_node: Configuration node offset + * @final_dtb: Pointer to store final DTB address + * @final_dtb_size: Pointer to store final DTB size + * + * This function loads the base DTB and applies all DTBOs specified in the + * configuration's "fdt" property. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_load_dtb_with_overlays(void *fit, int config_node, + void **final_dtb, + size_t *final_dtb_size) +{ + int images_node; + const char *fdt_name; + int fdt_name_len; + const void *fdt_data; + size_t fdt_size; + void *base_dtb = NULL; + size_t base_dtb_size = 0; + phys_addr_t dtb_addr; + int i, ret; + int fixups_offset; + + images_node = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_node < 0) + return images_node; + + fdt_name = fdt_stringlist_get(fit, config_node, PROP_FDT, 0, &fdt_name_len); + if (!fdt_name) { + log_err("No fdt property in configuration\n"); + return -EINVAL; + } + + log_info("DTB: %s\n", fdt_name); + + ret = qcom_get_fdt_image_data(fit, images_node, fdt_name, + &fdt_data, &fdt_size); + if (ret) + return ret; + + /* Allocate base DTB with extra space for overlays using LMB */ + base_dtb_size = fdt_size + (8 * 1024); /* Add 8KB for overlays */ + ret = lmb_alloc(base_dtb_size, &dtb_addr); + if (ret) { + log_err("Failed to allocate LMB memory for base DTB: %zu bytes\n", base_dtb_size); + return -ENOMEM; + } + base_dtb = (void *)dtb_addr; + + memcpy(base_dtb, fdt_data, fdt_size); + ret = fdt_open_into(base_dtb, base_dtb, base_dtb_size); + if (ret) { + log_err("Failed to open DTB: %d\n", ret); + return ret; + } + + /* Apply overlays (remaining fdt entries) */ + for (i = 1; ; i++) { + fdt_name = fdt_stringlist_get(fit, config_node, PROP_FDT, i, + &fdt_name_len); + if (!fdt_name) + break; + + log_info("Applying overlay: %s\n", fdt_name); + + ret = qcom_get_fdt_image_data(fit, images_node, fdt_name, + &fdt_data, &fdt_size); + if (ret) + continue; + + fixups_offset = fdt_path_offset(fdt_data, "/__fixups__"); + if (fixups_offset == -FDT_ERR_NOTFOUND) { + log_warning("%s is not a valid overlay (no __fixups__)\n", fdt_name); + continue; + } + + ret = fdt_overlay_apply_verbose(base_dtb, (void *)fdt_data); + if (ret) + log_err("Failed to apply overlay %s: %d\n", fdt_name, ret); + } + + ret = fdt_pack(base_dtb); + if (ret) { + log_err("Failed to pack DTB: %d\n", ret); + return ret; + } + + *final_dtb = base_dtb; + *final_dtb_size = fdt_totalsize(base_dtb); + + log_info("Final DTB size: %zu bytes\n", *final_dtb_size); + + return 0; +} + +/** + * qcom_fit_multidtb_setup() - Main entry point for FIT multi-DTB selection + * + * This is the main function that orchestrates the entire DTB selection process: + * 1. Load qclinux_fit.img from EFI partition + * 2. Extract metadata DTB + * 3. Detect hardware parameters from SMEM + * 4. Build bucket list from metadata + * 5. Find matching FIT configuration + * 6. Load DTB and apply overlays + * 7. Install FDT for EFI + * + * Return: 0 on success, negative error code on failure + */ +int qcom_fit_multidtb_setup(void) +{ + void *fit = NULL; + size_t fit_size = 0; + void *metadata = NULL; + size_t metadata_size = 0; + struct qcom_hw_params hw_params; + LIST_HEAD(bucket_list); + int config_node; + void *final_dtb = NULL; + size_t final_dtb_size = 0; + int ret; + + log_debug("=== FIT Multi-DTB Selection ===\n"); + + log_debug("Loading FIT image\n"); + ret = qcom_load_fit_image(QCOM_FIT_FILENAME, QCOM_FIT_PARTNAME, + &fit, &fit_size); + if (ret) { + log_err("Failed to load FIT image\n"); + goto cleanup_fit; + } + + ret = fdt_check_header(fit); + if (ret) { + log_err("Invalid FIT header\n"); + ret = -EINVAL; + goto cleanup_fit; + } + + ret = fit_check_format(fit, IMAGE_SIZE_INVAL); + if (ret) { + log_err("Invalid FIT format\n"); + ret = -EINVAL; + goto cleanup_fit; + } + + log_debug("Extracting metadata DTB\n"); + ret = qcom_extract_metadata_dtb(fit, &metadata, &metadata_size); + if (ret) { + log_err("Failed to extract metadata DTB\n"); + goto cleanup_metadata; + } + + log_debug("Detecting hardware parameters\n"); + ret = qcom_detect_hardware_params(&hw_params); + if (ret) { + log_err("Failed to detect hardware parameters\n"); + goto cleanup_metadata; + } + + log_debug("Building bucket list\n"); + ret = qcom_build_bucket_list(metadata, &hw_params, &bucket_list); + if (ret) { + log_err("Failed to build bucket list\n"); + goto cleanup_bucket; + } + + log_debug("Finding matching configuration\n"); + ret = qcom_find_matching_config(fit, &bucket_list, &config_node); + if (ret) { + log_err("Failed to find matching configuration\n"); + goto cleanup_bucket; + } + + log_debug("Loading DTB and applying overlays\n"); + ret = qcom_load_dtb_with_overlays(fit, config_node, &final_dtb, + &final_dtb_size); + if (ret) { + log_err("Failed to load DTB with overlays\n"); + goto cleanup_dtb; + } + + log_debug("Setting fdt_addr to selected DTB address\n"); + + ret = fdt_check_header(final_dtb); + if (ret) { + log_err("Invalid final DTB header: %d\n", ret); + ret = -EINVAL; + goto cleanup_dtb; + } + + /* Update fdt_addr environment variable to point to our DTB */ + env_set_hex("fdt_addr", (ulong)final_dtb); + log_info("Updated fdt_addr=0x%lx, DTB size=%zu bytes\n", (ulong)final_dtb, final_dtb_size); + log_info("EFI boot flow will use DTB directly from this address\n"); + + /* Don't free final_dtb - LMB manages memory and EFI boot flow will use it */ + final_dtb = NULL; + + log_debug("=== FIT Multi-DTB Selection Complete ===\n"); + + ret = 0; + goto cleanup_success; + +cleanup_dtb: + if (ret && final_dtb) + final_dtb = NULL; + +cleanup_success: +cleanup_bucket: + free_bucket_list(&bucket_list); + +cleanup_metadata: + if (metadata) + free(metadata); + +cleanup_fit: + if (fit) + free(fit); + + return ret; +} diff --git a/arch/arm/mach-snapdragon/qcom_fit_multidtb.h b/arch/arm/mach-snapdragon/qcom_fit_multidtb.h new file mode 100644 index 000000000000..802eb749fe80 --- /dev/null +++ b/arch/arm/mach-snapdragon/qcom_fit_multidtb.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm FIT Multi-DTB Selection + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This implements automatic DTB selection from FIT images based on hardware + * detection via SMEM. + */ + +#ifndef __QCOM_FIT_MULTIDTB_H__ +#define __QCOM_FIT_MULTIDTB_H__ + +#include +#include +#include + +/* DDR size thresholds */ +#define DDR_128MB SZ_128M +#define DDR_256MB SZ_256M +#define DDR_512MB SZ_512M +#define DDR_1024MB SZ_1G +#define DDR_2048MB SZ_2G +#define DDR_3072MB (SZ_2G + SZ_1G) +#define DDR_4096MB SZ_4G + +/* DDR type enum */ +enum ddr_type { + DDRTYPE_256MB = 1, + DDRTYPE_512MB, /* 2 */ + DDRTYPE_1024MB, /* 3 */ + DDRTYPE_2048MB, /* 4 */ + DDRTYPE_3072MB, /* 5 */ + DDRTYPE_4096MB, /* 6 */ + DDRTYPE_128MB, /* 7 */ +}; + +/* Storage type enum */ +enum mem_card_type { + UFS = 0, + EMMC = 1, + NAND = 2, + STORAGE_UNKNOWN, +}; + +/* Boot device types from shared IMEM */ +enum boot_media_type { + NO_FLASH = 0, + NOR_FLASH = 1, + NAND_FLASH = 2, + ONENAND_FLASH = 3, + SDC_FLASH = 4, + MMC_FLASH = 5, + SPI_FLASH = 6, + PCIE_FLASHLESS = 7, + UFS_FLASH = 8, + RESERVED_0_FLASH = 9, + RESERVED_1_FLASH = 10, + USB_FLASHLESS = 11 +}; + +/* Shared IMEM constants */ +#define BOOT_SHARED_IMEM_MAGIC_NUM 0xc1f8db40 +#define BOOT_SHARED_IMEM_VERSION_NUM 0x3 + +/** + * struct qcom_imem_info - SoC-specific shared IMEM cookie address mapping + * @compatible: SoC compatible string (e.g., "qcom,qcs6490") + * @shared_imem_addr: Physical address of the boot shared IMEM cookie + * + * The shared IMEM cookie is populated by the bootloader and contains + * boot device type and other boot parameters. Its location varies per SoC + * and is calculated as: SCL_IMEM_BASE + IMEM_SIZE - 0x1000 (4KB cookie). + * Only SoCs with CONFIG_QCOM_FIT_MULTIDTB support are listed here. + */ +struct qcom_imem_info { + const char *compatible; + uintptr_t shared_imem_addr; +}; + +/* + * Per-SoC shared IMEM cookie address table. + * Address = SCL_IMEM_BASE(0x14680000) + IMEM_SIZE - 0x1000 + * QCM6490/QCS6490/QCS615: IMEM_SIZE=0x2B000 -> 0x146aa000 + * QCS9100: IMEM_SIZE=0x59000 -> 0x146d8000 + */ +static const struct qcom_imem_info qcom_imem_table[] = { + { "qcom,qcm6490", 0x146aa000 }, + { "qcom,qcs6490", 0x146aa000 }, + { "qcom,qcs615", 0x146aa000 }, + { "qcom,qcs9100", 0x146d8000 }, + { } +}; + +/* Boot shared IMEM cookie structure */ +struct boot_imem_cookie { + u32 shared_imem_magic; + u32 shared_imem_version; + u64 etb_buf_addr; + u64 l2_cache_dump_buff_addr; + u32 a64_pointer_padding; + u32 uefi_ram_dump_magic; + u32 ddr_training_cookie; + u32 abnormal_reset_occurred; + u32 reset_status_register; + u32 rpm_sync_cookie; + u32 debug_config; + u64 boot_log_addr; + u32 boot_log_size; + u32 boot_fail_count; + u32 sbl1_error_type; + u32 uefi_image_magic; + u32 boot_device_type; + u64 boot_devtree_addr; + u64 boot_devtree_size; +}; + +/** + * struct qcom_hw_params - Hardware parameters detected from SMEM + * @chip_id: SoC chip ID (from socinfo->id) + * @chip_version: SoC version (from TCSR register) + * @platform: Hardware platform ID (from socinfo->hw_plat) + * @board_version: Board version (from socinfo->plat_ver) + * @subtype: Hardware platform subtype (from socinfo->hw_plat_subtype) + * @oem_variant_id: OEM variant ID (from socinfo->oem_variant) + * @ddr_size_type: DDR size type (0-10, calculated from RAM partitions) + * @storage_type: Storage type (UFS=0, EMMC=1, NAND=2) + * @foundry_id: Foundry ID (from socinfo->foundry_id) + * @softsku_id: Software SKU ID (if available) + * + * This structure holds all hardware parameters needed for DTB selection. + */ +struct qcom_hw_params { + u32 chip_id; + u32 chip_version; + u32 platform; + u32 board_version; + u32 subtype; + u32 oem_variant_id; + u32 ddr_size_type; + u32 storage_type; + u32 foundry_id; + u32 softsku_id; +}; + +/** + * struct bucket_node - Node in the bucket list + * @list: List head for linking nodes + * @name: Node name string (e.g., "qcom", "sa8775p-v2", "ride", "ufs", "8gb") + * + * The bucket list contains all matching node names from the metadata DTB. + * These are used to match against FIT configuration compatible strings. + */ +struct bucket_node { + struct list_head list; + char *name; +}; + +/* Node processing types for metadata DTB parsing */ +enum node_process_type { + NODE_TYPE_OEM, + NODE_TYPE_SOC, + NODE_TYPE_SOCVER, + NODE_TYPE_BOARD, + NODE_TYPE_BOARDREV, + NODE_TYPE_PERIPHERAL, + NODE_TYPE_STORAGE, + NODE_TYPE_DDR_SIZE, + NODE_TYPE_SOFTSKU, +}; + +/* Function prototypes */ + +/** + * qcom_fit_multidtb_setup() - Main entry point for FIT multi-DTB selection + * + * This function: + * 1. Loads qclinux_fit.img from EFI partition + * 2. Extracts metadata DTB + * 3. Detects hardware parameters from SMEM + * 4. Builds bucket list from metadata + * 5. Finds matching FIT configuration + * 6. Loads DTB and applies overlays + * 7. Sets FDT for EFI + * + * Return: 0 on success, negative error code on failure + */ +int qcom_fit_multidtb_setup(void); + +#endif /* __QCOM_FIT_MULTIDTB_H__ */ diff --git a/arch/arm/mach-snapdragon/rampart.h b/arch/arm/mach-snapdragon/rampart.h new file mode 100644 index 000000000000..57f3dbb7eec0 --- /dev/null +++ b/arch/arm/mach-snapdragon/rampart.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RAM partition table definitions + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#ifndef __QCOM_SMEM_RAMPART_H__ +#define __QCOM_SMEM_RAMPART_H__ + +#include + +#define SMEM_USABLE_RAM_PARTITION_TABLE 402 + +#define RAM_PARTITION_H_MAJOR 03 +#define RAM_PARTITION_H_MINOR 00 + +/** + * Total length of zero filled name string. This is not a C + * string, as it can occupy the total number of bytes, and if + * it does, it does not require a zero terminator. It cannot + * be manipulated with standard string handling library functions. + */ +#define RAM_PART_NAME_LENGTH 16 + +/** + * Number of RAM partition entries which are usable by APPS. + */ +#define RAM_NUM_PART_ENTRIES 32 + +/** + * @name: Magic numbers + * Used in identifying valid RAM partition table. + */ +#define RAM_PART_MAGIC1 0x9da5e0a8 +#define RAM_PART_MAGIC2 0xaf9ec4e2 + +/** + * RAM partition attributes. + */ +enum ram_partition_attribute_t { + RAM_PARTITION_READ_ONLY = 0, /* Read-only RAM partition */ + RAM_PARTITION_READWRITE, /* Read/write RAM partition */ +}; + +/** + * RAM partition categories. + */ +enum ram_partition_category_t { + RAM_PARTITION_IRAM = 4, /* IRAM RAM partition */ + RAM_PARTITION_IMEM = 5, /* IMEM RAM partition */ + RAM_PARTITION_SDRAM = 14, /* SDRAM type without specific bus information**/ +}; + +/** + * RAM Partition domains. + * @note: For shared RAM partition, domain value would be 0b11:\n + * RAM_PARTITION_APPS_DOMAIN | RAM_PARTITION_MODEM_DOMAIN. + */ +enum ram_partition_domain_t { + RAM_PARTITION_DEFAULT_DOMAIN = 0, /* 0b00: No specific domain definition */ + RAM_PARTITION_APPS_DOMAIN = 1, /* 0b01: APPS RAM partition */ + RAM_PARTITION_MODEM_DOMAIN = 2, /* 0b10: MODEM RAM partition */ +}; + +/** + * RAM Partition types. + * @note: The RAM_PARTITION_SYS_MEMORY type represents DDR rams that are attached + * to the current system. + */ +enum ram_partition_type_t { + RAM_PARTITION_SYS_MEMORY = 1, /* system memory */ + RAM_PARTITION_BOOT_REGION_MEMORY1, /* boot loader memory 1 */ + RAM_PARTITION_BOOT_REGION_MEMORY2, /* boot loader memory 2, reserved */ + RAM_PARTITION_APPSBL_MEMORY, /* apps boot loader memory */ + RAM_PARTITION_APPS_MEMORY, /* apps usage memory */ + RAM_PARTITION_TOOLS_FV_MEMORY, /* tools usage memory */ + RAM_PARTITION_QUANTUM_FV_MEMORY, /* quantum usage memory */ + RAM_PARTITION_QUEST_FV_MEMORY, /* quest usage memory */ +}; + +/* Common table header between versions */ +struct usable_ram_partition_table_header { + u32 magic1; /* Magic number to identify valid RAM partition table */ + u32 magic2; /* Magic number to identify valid RAM partition table */ + u32 version; /* Version number to track structure definition changes */ + u32 reserved1; /* Reserved for future use */ + + u32 num_partitions; /* Number of RAM partition table entries */ +}; + +/* Holds information for an entry in the RAM partition table */ +struct ram_partition_entry_v3 { + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */ + u64 start_address; /* Partition start address in RAM */ + u64 length; /* Partition length in RAM in Bytes */ + u32 partition_attribute; /* Partition attribute */ + u32 partition_category; /* Partition category */ + u32 partition_domain; /* Partition domain */ + u32 partition_type; /* Partition type */ + u32 num_partitions; /* Number of partitions on device */ + u32 hw_info; /* hw information such as type and frequency */ + u8 highest_bank_bit; /* Highest bit corresponding to a bank */ + u8 reserve0; /* Reserved for future use */ + u8 reserve1; /* Reserved for future use */ + u8 reserve2; /* Reserved for future use */ + u32 min_pasr_size; /* Minimum PASR size in MB */ + u64 available_length; /* Available Partition length in RAM in Bytes */ +}; + +/* + * Defines the RAM partition table structure + * + * Do not change the placement of the first four elements so that future + * compatibility will always be guaranteed at least for the identifiers. + * + * The other portion of the structure may be changed as necessary to accommodate + * new features. Be sure to increment version number if you change it. + */ +struct usable_ram_partition_table_v3 { + struct usable_ram_partition_table_header header; + + u32 reserved2; /* Added for 8 bytes alignment of header */ + + /* RAM partition table entries */ + struct ram_partition_entry_v3 entries[RAM_NUM_PART_ENTRIES]; +}; + +/* Version 1 structure 32 Bit - Holds information for an entry in the RAM partition table */ +struct ram_partition_entry_v1 { + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */ + u64 start_address; /* Partition start address in RAM */ + u64 length; /* Partition length in RAM in Bytes */ + u32 partition_attribute; /* Partition attribute */ + u32 partition_category; /* Partition category */ + u32 partition_domain; /* Partition domain */ + u32 partition_type; /* Partition type */ + u32 num_partitions; /* Number of partitions on device */ + u32 hw_info; /* hw information such as type and frequency */ + u32 reserved4; /* Reserved for future use */ + u32 reserved5; /* Reserved for future use */ +}; + +/* + * Defines the RAM partition table structure (Version 1) + * + * Do not change the placement of the first four elements so that future + * compatibility will always be guaranteed at least for the identifiers. + * + * The other portion of the structure may be changed as necessary to accommodate + * new features. Be sure to increment version number if you change it. + */ +struct usable_ram_partition_table_v1 { + struct usable_ram_partition_table_header header; + + u32 reserved2; /* Added for 8 bytes alignment of header */ + + /* RAM partition table entries */ + struct ram_partition_entry_v1 entries[RAM_NUM_PART_ENTRIES]; +}; + +/* Version 0 structure 32 Bit - Holds information for an entry in the RAM partition table */ +struct ram_partition_entry_v0 { + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */ + u32 start_address; /* Partition start address in RAM */ + u32 length; /* Partition length in RAM in Bytes */ + u32 partition_attribute; /* Partition attribute */ + u32 partition_category; /* Partition category */ + u32 partition_domain; /* Partition domain */ + u32 partition_type; /* Partition type */ + u32 num_partitions; /* Number of partitions on device */ + u32 reserved3; /* Reserved for future use */ + u32 reserved4; /* Reserved for future use */ + u32 reserved5; /* Reserved for future use */ +}; + +/* + * Defines the RAM partition table structure (Version 0) + * + * Do not change the placement of the first four elements so that future + * compatibility will always be guaranteed at least for the identifiers. + * + * The other portion of the structure may be changed as necessary to accommodate + * new features. Be sure to increment version number if you change it. + */ +struct usable_ram_partition_table_v0 { + struct usable_ram_partition_table_header header; + + /* RAM partition table entries */ + struct ram_partition_entry_v0 entries[RAM_NUM_PART_ENTRIES]; +}; + +#endif // __QCOM_SMEM_RAMPART_H__ diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 0887de4333b0..beba425888c5 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1021,6 +1021,11 @@ sandbox,filename = "i2c.bin"; sandbox,size = <256>; }; + emul_nvmem_test: emul-nvmem-test { + compatible = "sandbox,i2c-eeprom"; + sandbox,filename = "nvmem-test.bin"; + sandbox,size = <256>; + }; emul0: emul0 { compatible = "sandbox,i2c-rtc-emul"; }; @@ -1038,6 +1043,13 @@ reg = <0x41>; sandbox,emul = <&emul_pmic1>; }; + + /* Mock NVMEM device for bit field testing */ + nvmem-test@50 { + reg = <0x50>; + compatible = "i2c-eeprom"; + sandbox,emul = <&emul_nvmem_test>; + }; }; i3c0 { @@ -1582,10 +1594,6 @@ sandbox,filepath = "scsi.img"; }; - smem@0 { - compatible = "sandbox,smem"; - }; - sound { compatible = "sandbox,sound"; cpu { diff --git a/board/qualcomm/debug-sm8650.config b/board/qualcomm/debug-sm8650.config new file mode 100644 index 000000000000..8323bf25e9c1 --- /dev/null +++ b/board/qualcomm/debug-sm8650.config @@ -0,0 +1,5 @@ +CONFIG_DEBUG_UART=y +CONFIG_DEBUG_UART_ANNOUNCE=y +CONFIG_DEBUG_UART_BASE=0x89c000 +CONFIG_DEBUG_UART_MSM_GENI=y +CONFIG_DEBUG_UART_CLOCK=14745600 diff --git a/board/qualcomm/default.env b/board/qualcomm/default.env index dbf6f4e72600..e61c99870e6a 100644 --- a/board/qualcomm/default.env +++ b/board/qualcomm/default.env @@ -1,9 +1,9 @@ stdin=serial,button-kbd stdout=serial,vidconsole stderr=serial,vidconsole -preboot=scsi scan; usb start +preboot=scsi scan; usb start; if test "${reboot-mode}" = "bootloader"; then run fastboot; fi fastboot=fastboot -l $fastboot_addr_r usb 0 -do_boot=bootefi bootmgr +do_boot=bootflow scan -lb bootmenu_0=Boot first available device=run do_boot bootmenu_1=Enable fastboot mode=run fastboot bootmenu_2=Reset device=reset diff --git a/configs/dragonboard410c_defconfig b/configs/dragonboard410c_defconfig index 51ad265688c4..645f08167010 100644 --- a/configs/dragonboard410c_defconfig +++ b/configs/dragonboard410c_defconfig @@ -11,6 +11,7 @@ CONFIG_ENV_OFFSET=0x0 CONFIG_DEFAULT_DEVICE_TREE="qcom/apq8016-sbc" CONFIG_OF_LIBFDT_OVERLAY=y CONFIG_SYS_LOAD_ADDR=0x80080000 +CONFIG_BOOT0_MSM8916_PSCI_WORKAROUND=y CONFIG_IDENT_STRING="\nQualcomm-DragonBoard 410C" CONFIG_REMAKE_ELF=y CONFIG_BUTTON_CMD=y diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig index b088367f86c0..cafd9233c19b 100644 --- a/configs/qcm6490_defconfig +++ b/configs/qcm6490_defconfig @@ -16,3 +16,11 @@ CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs6490-rb3gen2" CONFIG_FASTBOOT_BUF_ADDR=0xd8800000 CONFIG_PHY_QCOM_QMP_COMBO=y + +CONFIG_ENV_IS_IN_SCSI=y +CONFIG_ENV_SCSI_PART_USE_TYPE_GUID=y +# SCSI partition type GUID for logfs partition +CONFIG_ENV_SCSI_PART_TYPE_GUID="bc0330eb-3410-4951-a617-03898dbe3372" + +CONFIG_QCOM_EL2_GUNYAH_EXIT_SUPPORT=y +CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK=y diff --git a/configs/qcom_defconfig b/configs/qcom_defconfig index d0d54ec5a70b..2f3c8f0cee51 100644 --- a/configs/qcom_defconfig +++ b/configs/qcom_defconfig @@ -13,6 +13,7 @@ CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y CONFIG_BUTTON_CMD=y CONFIG_FIT=y CONFIG_FIT_VERBOSE=y +CONFIG_QCOM_FIT_MULTIDTB=y CONFIG_BOOTSTD_FULL=y # CONFIG_BOOTMETH_VBE is not set CONFIG_BOOTDELAY=1 @@ -49,6 +50,7 @@ CONFIG_CMD_RNG=y CONFIG_CMD_REGULATOR=y CONFIG_CMD_LOG=y CONFIG_OF_LIVE=y +CONFIG_OF_LIBFDT_OVERLAY=y CONFIG_OF_UPSTREAM_BUILD_VENDOR=y CONFIG_ENV_USE_DEFAULT_ENV_TEXT_FILE=y CONFIG_ENV_DEFAULT_ENV_TEXT_FILE="board/qualcomm/default.env" @@ -79,8 +81,9 @@ CONFIG_SYS_DFU_DATA_BUF_SIZE=0x200000 CONFIG_USB_FUNCTION_FASTBOOT=y CONFIG_FASTBOOT_BUF_ADDR=0x0 CONFIG_FASTBOOT_FLASH=y -CONFIG_FASTBOOT_FLASH_MMC_DEV=0 -CONFIG_FASTBOOT_MMC_USER_SUPPORT=y +CONFIG_FASTBOOT_FLASH_BLOCK=y +CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME="scsi" +CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID=4 CONFIG_MSM_GPIO=y CONFIG_QCOM_PMIC_GPIO=y CONFIG_DM_I2C=y @@ -98,6 +101,7 @@ CONFIG_MISC=y CONFIG_NVMEM=y CONFIG_QCOM_GENI=y CONFIG_I2C_EEPROM=y +CONFIG_SYS_MMC_MAX_BLK_COUNT=16384 CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_ADMA=y CONFIG_MMC_SDHCI_MSM=y @@ -119,6 +123,7 @@ CONFIG_PINCTRL_QCOM_GENERIC=y CONFIG_QCOM_RPMH_POWER_DOMAIN=y CONFIG_DM_PMIC=y CONFIG_PMIC_QCOM=y +CONFIG_DM_REBOOT_MODE=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_QCOM_RPMH=y @@ -130,6 +135,9 @@ CONFIG_MSM_GENI_SERIAL=y CONFIG_SOC_QCOM=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QCOM_RPMH=y +CONFIG_QCOM_SPMI_SDAM=y +CONFIG_REBOOT_MODE_ENV_UPDATE=y +CONFIG_REBOOT_MODE_NVMEM=y CONFIG_SPMI_MSM=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SMBIOS=y diff --git a/configs/qcom_qcs9100_defconfig b/configs/qcom_lemans_defconfig similarity index 62% rename from configs/qcom_qcs9100_defconfig rename to configs/qcom_lemans_defconfig index 082106157bbc..c604c9989474 100644 --- a/configs/qcom_qcs9100_defconfig +++ b/configs/qcom_lemans_defconfig @@ -9,8 +9,13 @@ CONFIG_TEXT_BASE=0xaf000000 CONFIG_REMAKE_ELF=y CONFIG_FASTBOOT_BUF_ADDR=0xdb300000 -CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs9100-ride-r3" +CONFIG_DEFAULT_DEVICE_TREE="qcom/lemans-evk" CONFIG_ENV_IS_IN_SCSI=y -CONFIG_ENV_SCSI_PART_UUID="71cb9cd0-acf1-b6cb-ad91-be9572fe11a9" +CONFIG_ENV_SCSI_PART_USE_TYPE_GUID=y +# SCSI partition type GUID for logfs partition +CONFIG_ENV_SCSI_PART_TYPE_GUID="bc0330eb-3410-4951-a617-03898dbe3372" # CONFIG_ENV_IS_DEFAULT is not set # CONFIG_ENV_IS_NOWHERE is not set + +CONFIG_QCOM_EL2_GUNYAH_EXIT_SUPPORT=y +CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK=y diff --git a/configs/qcom_qcs615_defconfig b/configs/qcom_qcs615_defconfig index 27666a8129d7..c90fa8bafb19 100644 --- a/configs/qcom_qcs615_defconfig +++ b/configs/qcom_qcs615_defconfig @@ -14,7 +14,7 @@ CONFIG_DEBUG_UART_BASE=0x880000 CONFIG_DEBUG_UART_MSM_GENI=y CONFIG_DEBUG_UART_CLOCK=7372800 -CONFIG_DEFAULT_DEVICE_TREE="qcom/qcs615-ride" +CONFIG_DEFAULT_DEVICE_TREE="qcom/talos-evk" CONFIG_REMAKE_ELF=y @@ -22,3 +22,19 @@ CONFIG_REMAKE_ELF=y CONFIG_TEXT_BASE=0x9fc00000 CONFIG_FASTBOOT_BUF_ADDR=0xa1600000 + +# QCS615 has eMMC only, not UFS/SCSI - override qcom_defconfig settings +CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME="mmc" +CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID=0 + +# CONFIG_SCSI is not set + +# Environment stored in eMMC logfs partition +CONFIG_ENV_IS_IN_MMC=y +CONFIG_ENV_MMC_USE_SW_PARTITION=y +CONFIG_ENV_MMC_SW_PARTITION="logfs" +CONFIG_ENV_OFFSET=0x0 +CONFIG_ENV_SIZE=0x4000 + +CONFIG_QCOM_EL2_GUNYAH_EXIT_SUPPORT=y +CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK=y diff --git a/configs/qcom_sm8650_defconfig b/configs/qcom_sm8650_defconfig new file mode 100644 index 000000000000..e0a82c460337 --- /dev/null +++ b/configs/qcom_sm8650_defconfig @@ -0,0 +1,16 @@ +# 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 + +# Address where U-Boot will be loaded +CONFIG_TEXT_BASE=0xa7000000 +CONFIG_REMAKE_ELF=y + +CONFIG_DEFAULT_DEVICE_TREE="qcom/sm8650-hdk" + diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 5bf6146b1d0c..3cc1e7f77f64 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -232,8 +232,6 @@ CONFIG_REMOTEPROC_SANDBOX=y CONFIG_DM_RTC=y CONFIG_RTC_RV8803=y CONFIG_SANDBOX_SERIAL=y -CONFIG_SMEM=y -CONFIG_SANDBOX_SMEM=y CONFIG_SOUND_SANDBOX=y CONFIG_SOC_DEVICE=y CONFIG_SANDBOX_SPI=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index ba800f7d19da..42dc7b07b117 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -325,8 +325,6 @@ CONFIG_RTC_RV8803=y CONFIG_RTC_HT1380=y CONFIG_SANDBOX_SERIAL=y CONFIG_SANDBOX_SM=y -CONFIG_SMEM=y -CONFIG_SANDBOX_SMEM=y CONFIG_SOUND_DA7219=y CONFIG_SOUND_MAX98357A=y CONFIG_SOUND_SANDBOX=y diff --git a/disk/part.c b/disk/part.c index 4923dc44593c..4cb3204ac6e0 100644 --- a/disk/part.c +++ b/disk/part.c @@ -731,6 +731,43 @@ int part_get_info_by_uuid(struct blk_desc *desc, const char *uuid, return -ENOENT; } +int part_get_info_by_type_guid(struct blk_desc *desc, const char *type_guid, + struct disk_partition *info) +{ + struct part_driver *part_drv; + int ret; + int i; + + if (!CONFIG_IS_ENABLED(PARTITION_TYPE_GUID)) + return -ENOENT; + + part_drv = part_driver_lookup_type(desc); + if (!part_drv) + return -ENOENT; + + for (i = 1; i <= part_drv->max_entries; i++) { + ret = part_driver_get_info(part_drv, desc, i, info); + if (ret) { + /* -ENOSYS means no ->get_info method. */ + if (ret == -ENOSYS) + return ret; + /* + * Partition with this index can't be obtained, but + * further partitions might be, so keep checking. + */ + continue; + } + + if (!strncasecmp(type_guid, disk_partition_type_guid(info), + UUID_STR_LEN)) { + /* matched */ + return i; + } + } + + return -ENOENT; +} + /** * Get partition info from device number and partition name. * diff --git a/doc/android/fastboot.rst b/doc/android/fastboot.rst index 19e2ee9d4074..ce3a030e701b 100644 --- a/doc/android/fastboot.rst +++ b/doc/android/fastboot.rst @@ -147,6 +147,38 @@ The Fastboot implementation in U-Boot allows to write images into disk partitions. Target partitions are referred on the host computer by their names. +Device Selection for Block Devices +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using ``FASTBOOT_FLASH_BLOCK``, you can specify which block device to +target by prefixing the partition name with the device number and a colon. + +Syntax:: + + : + +Examples:: + + fastboot flash 0:boot boot.img # Flash to device 0, partition "boot" + fastboot flash 1:system system.img # Flash to device 1, partition "system" + fastboot flash 0:gpt gpt.img # Write GPT to device 0 + fastboot flash 1:mbr mbr.img # Write MBR to device 1 + +If no device number is specified, the default device from +``CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID`` is used:: + + fastboot flash boot boot.img # Uses default device + +This syntax is supported for: + +* Regular partition flashing +* GPT partition table updates (``gpt`` or ``N:gpt``) +* MBR partition table updates (``mbr`` or ``N:mbr``) +* Partition erasing operations + +Partition Name Formats +^^^^^^^^^^^^^^^^^^^^^^ + For GPT/EFI the respective partition name is used. For MBR the partitions are referred by generic names according to the @@ -186,6 +218,15 @@ configuration options: CONFIG_FASTBOOT_GPT_NAME CONFIG_FASTBOOT_MBR_NAME +When using block devices (``FASTBOOT_FLASH_BLOCK``), you can specify the target +device by prefixing with the device number:: + + fastboot flash 0:gpt gpt.img # Write GPT to device 0 + fastboot flash 1:mbr mbr.img # Write MBR to device 1 + +If no device number is specified, the default device from +``CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID`` is used. + In Action --------- diff --git a/drivers/Kconfig b/drivers/Kconfig index 47606ddc6c8b..aa293df37c57 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -124,8 +124,6 @@ source "drivers/serial/Kconfig" source "drivers/sm/Kconfig" -source "drivers/smem/Kconfig" - source "drivers/sound/Kconfig" source "drivers/soc/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 43d0ba332818..ca3e87ffb102 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -119,7 +119,6 @@ obj-y += reset/ obj-y += input/ obj-y += iommu/ # SOC specific infrastructure drivers. -obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/ diff --git a/drivers/clk/clk-stub.c b/drivers/clk/clk-stub.c index 117266ac7789..4a6c71016da2 100644 --- a/drivers/clk/clk-stub.c +++ b/drivers/clk/clk-stub.c @@ -49,11 +49,13 @@ static struct clk_ops stub_clk_ops = { }; static const struct udevice_id stub_clk_ids[] = { + { .compatible = "qcom,qcs615-rpmh-clk" }, { .compatible = "qcom,rpmcc" }, - { .compatible = "qcom,sdm670-rpmh-clk" }, - { .compatible = "qcom,sdm845-rpmh-clk" }, + { .compatible = "qcom,sa8775p-rpmh-clk" }, { .compatible = "qcom,sc7180-rpmh-clk" }, { .compatible = "qcom,sc7280-rpmh-clk" }, + { .compatible = "qcom,sdm670-rpmh-clk" }, + { .compatible = "qcom,sdm845-rpmh-clk" }, { .compatible = "qcom,sm6350-rpmh-clk" }, { .compatible = "qcom,sm8150-rpmh-clk" }, { .compatible = "qcom,sm8250-rpmh-clk" }, diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h index 3a4550d85366..9899cd28aade 100644 --- a/drivers/clk/qcom/clock-qcom.h +++ b/drivers/clk/qcom/clock-qcom.h @@ -14,6 +14,8 @@ #define CFG_CLK_SRC_GPLL0_AUX2 (2 << 8) #define CFG_CLK_SRC_GPLL2 (2 << 8) #define CFG_CLK_SRC_GPLL2_MAIN (2 << 8) +#define CFG_CLK_SRC_GPLL6_OUT_MAIN (2 << 8) +#define CFG_CLK_SRC_GPLL8 (2 << 8) #define CFG_CLK_SRC_GPLL9 (2 << 8) #define CFG_CLK_SRC_GPLL0_ODD (3 << 8) #define CFG_CLK_SRC_GPLL6 (4 << 8) diff --git a/drivers/clk/qcom/clock-qcs615.c b/drivers/clk/qcom/clock-qcs615.c index 2087fc38f63d..76f5f8191b5e 100644 --- a/drivers/clk/qcom/clock-qcs615.c +++ b/drivers/clk/qcom/clock-qcs615.c @@ -19,6 +19,39 @@ #define USB30_PRIM_MASTER_CLK_CMD_RCGR 0xf01c #define USB3_PRIM_PHY_AUX_CMD_RCGR 0xf060 +#define UFS_PHY_AXI_CLK_CMD_RCGR 0x77020 +#define UFS_PHY_ICE_CORE_CLK_CMD_RCGR 0x77048 +#define UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR 0x77060 +#define UFS_PHY_PHY_AUX_CLK_CMD_RCGR 0x7707c + +#define SDCC1_APPS_CLK_CMD_RCGR 0x12028 +#define SDCC2_APPS_CLK_CMD_RCGR 0x1400c + +/* + * Frequency tables for SDCC clocks + */ +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, CFG_CLK_SRC_CXO, 16, 3, 25), + F(400000, CFG_CLK_SRC_CXO, 12, 1, 4), + F(20000000, CFG_CLK_SRC_GPLL0_AUX2, 5, 1, 3), + F(25000000, CFG_CLK_SRC_GPLL0_AUX2, 6, 1, 2), + F(50000000, CFG_CLK_SRC_GPLL0_AUX2, 6, 0, 0), + F(100000000, CFG_CLK_SRC_GPLL0_AUX2, 3, 0, 0), + F(192000000, CFG_CLK_SRC_GPLL6_OUT_MAIN, 2, 0, 0), + F(384000000, CFG_CLK_SRC_GPLL6_OUT_MAIN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(400000, CFG_CLK_SRC_CXO, 12, 1, 4), + F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0), + F(25000000, CFG_CLK_SRC_GPLL0_AUX2, 12, 0, 0), + F(50000000, CFG_CLK_SRC_GPLL0_AUX2, 6, 0, 0), + F(100000000, CFG_CLK_SRC_GPLL0_AUX2, 3, 0, 0), + F(202000000, CFG_CLK_SRC_GPLL8, 2, 0, 0), + { } +}; + #define GCC_QUPV3_WRAP0_S0_CLK_ENA_BIT BIT(10) #define GCC_QUPV3_WRAP0_S1_CLK_ENA_BIT BIT(11) #define GCC_QUPV3_WRAP0_S2_CLK_ENA_BIT BIT(12) @@ -33,9 +66,37 @@ #define GCC_QUPV3_WRAP1_S4_CLK_ENA_BIT BIT(26) #define GCC_QUPV3_WRAP1_S5_CLK_ENA_BIT BIT(27) +/* UFS PHY AXI clock frequency table */ +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0), + F(50000000, CFG_CLK_SRC_GPLL0_EVEN, 6, 0, 0), + F(100000000, CFG_CLK_SRC_GPLL0, 6, 0, 0), + F(200000000, CFG_CLK_SRC_GPLL0, 3, 0, 0), + F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0), + { } +}; + +/* UFS PHY ICE CORE clock frequency table */ +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(37500000, CFG_CLK_SRC_GPLL0_EVEN, 8, 0, 0), + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0), + { } +}; + +/* UFS PHY UNIPRO CORE clock frequency table */ +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = { + F(37500000, CFG_CLK_SRC_GPLL0_EVEN, 8, 0, 0), + F(75000000, CFG_CLK_SRC_GPLL0, 8, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0), + { } +}; + static ulong qcs615_set_rate(struct clk *clk, ulong rate) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); + const struct freq_tbl *freq; if (clk->id < priv->data->num_clks) debug("%s: %s, requested rate=%ld\n", __func__, @@ -52,6 +113,34 @@ static ulong qcs615_set_rate(struct clk *clk, ulong rate) 5, 0, 0, CFG_CLK_SRC_GPLL0, 8); clk_rcg_set_rate(priv->base, USB3_PRIM_PHY_AUX_CMD_RCGR, 0, 0); return rate; + case GCC_SDCC1_APPS_CLK: + freq = qcom_find_freq(ftbl_gcc_sdcc1_apps_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, SDCC1_APPS_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_SDCC2_APPS_CLK: + freq = qcom_find_freq(ftbl_gcc_sdcc2_apps_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, SDCC2_APPS_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_AXI_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_axi_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_AXI_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_UNIPRO_CORE_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_unipro_core_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_ICE_CORE_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_ice_core_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_ICE_CORE_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_PHY_AUX_CLK: + clk_rcg_set_rate(priv->base, UFS_PHY_PHY_AUX_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO); + return 19200000; default: return 0; } @@ -81,7 +170,21 @@ static const struct gate_clk qcs615_clks[] = { GATE_CLK(GCC_QUPV3_WRAP1_S4_CLK, 0x5200c, GCC_QUPV3_WRAP1_S4_CLK_ENA_BIT), GATE_CLK(GCC_QUPV3_WRAP1_S5_CLK, 0x5200c, GCC_QUPV3_WRAP1_S5_CLK_ENA_BIT), GATE_CLK(GCC_DISP_HF_AXI_CLK, 0xb038, BIT(0)), - GATE_CLK(GCC_DISP_AHB_CLK, 0xb032, BIT(0)) + GATE_CLK(GCC_DISP_AHB_CLK, 0xb032, BIT(0)), + GATE_CLK(GCC_SDCC1_AHB_CLK, 0x12008, BIT(0)), + GATE_CLK(GCC_SDCC1_APPS_CLK, 0x12004, BIT(0)), + GATE_CLK(GCC_SDCC1_ICE_CORE_CLK, 0x1200c, BIT(0)), + GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14008, BIT(0)), + GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)), + GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77010, BIT(0)), + GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770c0, BIT(0)), + GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77014, BIT(0)), + GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x77040, BIT(0)), + GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x77044, BIT(0)), + GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x77018, BIT(0)), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x7701c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x77078, BIT(0)), + GATE_CLK(GCC_UFS_MEM_CLKREF_CLK, 0x8c000, BIT(0)) }; static int qcs615_enable(struct clk *clk) diff --git a/drivers/clk/qcom/clock-sa8775p.c b/drivers/clk/qcom/clock-sa8775p.c index 4957abf6f589..7eec4aeae489 100644 --- a/drivers/clk/qcom/clock-sa8775p.c +++ b/drivers/clk/qcom/clock-sa8775p.c @@ -19,6 +19,11 @@ #define USB30_PRIM_MASTER_CLK_CMD_RCGR 0x1b028 #define USB3_PRIM_PHY_AUX_CMD_RCGR 0x1b06c +#define UFS_PHY_AXI_CLK_CMD_RCGR 0x8302c +#define UFS_PHY_ICE_CORE_CLK_CMD_RCGR 0x83074 +#define UFS_PHY_PHY_AUX_CLK_CMD_RCGR 0x830a8 +#define UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR 0x8308c + #define GCC_QUPV3_WRAP0_S0_CLK_ENA_BIT BIT(10) #define GCC_QUPV3_WRAP0_S1_CLK_ENA_BIT BIT(11) #define GCC_QUPV3_WRAP0_S2_CLK_ENA_BIT BIT(12) @@ -44,9 +49,35 @@ #define GCC_QUPV3_WRAP3_S0_CLK_ENA_BIT BIT(25) +/* UFS AXI clock frequency table */ +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0), + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0), + { } +}; + +/* UFS ICE CORE clock frequency table */ +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0), + { } +}; + +/* UFS UNIPRO CORE clock frequency table */ +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = { + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0, 4, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0, 2, 0, 0), + { } +}; + static ulong sa8775p_set_rate(struct clk *clk, ulong rate) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); + const struct freq_tbl *freq; if (clk->id < priv->data->num_clks) debug("%s: %s, requested rate=%ld\n", __func__, @@ -63,6 +94,24 @@ static ulong sa8775p_set_rate(struct clk *clk, ulong rate) 5, 0, 0, CFG_CLK_SRC_GPLL0, 8); clk_rcg_set_rate(priv->base, USB3_PRIM_PHY_AUX_CMD_RCGR, 0, 0); return rate; + case GCC_UFS_PHY_AXI_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_axi_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_AXI_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_UNIPRO_CORE_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_unipro_core_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_ICE_CORE_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_ice_core_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_ICE_CORE_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_PHY_AUX_CLK: + clk_rcg_set_rate(priv->base, UFS_PHY_PHY_AUX_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO); + return 19200000; default: return 0; } @@ -106,6 +155,20 @@ static const struct gate_clk sa8775p_clks[] = { /* QUP Wrapper 3 clocks */ GATE_CLK(GCC_QUPV3_WRAP3_S0_CLK, 0x4b000, GCC_QUPV3_WRAP3_S0_CLK_ENA_BIT), + + /* UFS PHY clocks */ + GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x83018, 1), + GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x830d4, 1), + GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x83020, 1), + GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x83064, 1), + GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x83024, 1), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x83028, 1), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_1_CLK, 0x830c0, 1), + GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x830a4, 1), + GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x8306c, 1), + + /* EDP reference clock (used by UFS PHY) */ + GATE_CLK(GCC_EDP_REF_CLKREF_EN, 0x97448, 1), }; static int sa8775p_enable(struct clk *clk) diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c index 01c8587ac398..91e3fcc27cb2 100644 --- a/drivers/clk/qcom/clock-sc7280.c +++ b/drivers/clk/qcom/clock-sc7280.c @@ -23,6 +23,10 @@ #define PCIE_1_AUX_CLK_CMD_RCGR 0x8d058 #define PCIE1_PHY_RCHNG_CMD_RCGR 0x8d03c #define PCIE_1_PIPE_CLK_PHY_MUX 0x8d054 +#define UFS_PHY_AXI_CLK_CMD_RCGR 0x77024 +#define UFS_PHY_ICE_CORE_CLK_CMD_RCGR 0x7706c +#define UFS_PHY_PHY_AUX_CLK_CMD_RCGR 0x770a0 +#define UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR 0x77084 static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0), @@ -54,6 +58,33 @@ static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s2_clk_src[] = { { } }; +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, CFG_CLK_SRC_GPLL0_EVEN, 12, 0, 0), + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0_EVEN, 2, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0_EVEN, 2, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_phy_aux_clk_src[] = { + F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = { + F(75000000, CFG_CLK_SRC_GPLL0_EVEN, 4, 0, 0), + F(150000000, CFG_CLK_SRC_GPLL0_EVEN, 2, 0, 0), + F(300000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 0, 0), + { } +}; + static ulong sc7280_set_rate(struct clk *clk, ulong rate) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); @@ -103,6 +134,26 @@ static ulong sc7280_set_rate(struct clk *clk, ulong rate) case GCC_PCIE1_PHY_RCHNG_CLK: clk_rcg_set_rate(priv->base, PCIE1_PHY_RCHNG_CMD_RCGR, 5, CFG_CLK_SRC_GPLL0_EVEN); return 100000000; + case GCC_UFS_PHY_AXI_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_axi_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_AXI_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_ICE_CORE_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_ice_core_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_ICE_CORE_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_PHY_AUX_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_phy_aux_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_PHY_AUX_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_UFS_PHY_UNIPRO_CORE_CLK: + freq = qcom_find_freq(ftbl_gcc_ufs_phy_unipro_core_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, UFS_PHY_UNIPRO_CORE_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; default: return rate; } @@ -148,6 +199,7 @@ static const struct gate_clk sc7280_clks[] = { GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77010, BIT(0)), GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770cc, BIT(0)), GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77018, BIT(0)), + GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x77064, BIT(0)), GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x7705c, BIT(0)), GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x7709c, BIT(0)), GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x7701c, BIT(0)), diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index 90212fcf9efd..a4539844d898 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -227,7 +227,7 @@ config FASTBOOT_FLASH_BLOCK_DEVICE_ID config FASTBOOT_GPT_NAME string "Target name for updating GPT" - depends on FASTBOOT_FLASH_MMC && EFI_PARTITION + depends on (FASTBOOT_FLASH_MMC || FASTBOOT_FLASH_BLOCK) && EFI_PARTITION default "gpt" help The fastboot "flash" command supports writing the downloaded @@ -240,7 +240,7 @@ config FASTBOOT_GPT_NAME config FASTBOOT_MBR_NAME string "Target name for updating MBR" - depends on FASTBOOT_FLASH_MMC && DOS_PARTITION + depends on (FASTBOOT_FLASH_MMC || FASTBOOT_FLASH_BLOCK) && DOS_PARTITION default "mbr" help The fastboot "flash" command allows to write the downloaded image diff --git a/drivers/fastboot/fb_block.c b/drivers/fastboot/fb_block.c index 51d1abb18c77..a8c74e35d0d1 100644 --- a/drivers/fastboot/fb_block.c +++ b/drivers/fastboot/fb_block.c @@ -11,6 +11,7 @@ #include #include #include +#include /** * FASTBOOT_MAX_BLOCKS_ERASE - maximum blocks to erase per derase call @@ -124,6 +125,65 @@ static lbaint_t fb_block_sparse_reserve(struct sparse_storage *info, return blkcnt; } +/** + * parse_device_partition() - Parse and validate device:partition format + * @part_name: Input string in format "N:partition" or "partition" + * @device: Output device number + * @partition_name: Output partition name pointer (can be NULL) + * + * Parses the input string to extract device number and partition name. + * If no device is specified, uses the default from config. + * Returns: 0 on success, -EINVAL if format is invalid + */ +static int parse_device_partition(const char *part_name, int *device, + const char **partition_name) +{ + const char *colon_pos; + + *device = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK, + CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID, -1); + + /* Check for colon in partition name */ + colon_pos = strchr(part_name, ':'); + + /* Reject invalid format like ":partition" */ + if (colon_pos && colon_pos == part_name) + return -EINVAL; + + /* Override if device:partition format detected */ + if (colon_pos && colon_pos > part_name) { + *device = simple_strtoul(part_name, NULL, 10); + if (partition_name) + *partition_name = colon_pos + 1; + } else { + if (partition_name) + *partition_name = part_name; + } + + return 0; +} + +/** + * is_partition_table_name() - Check if name matches partition table target + * @part_name: Partition name to check + * @table_name: Config name for partition table (e.g., "gpt", "mbr") + * + * Returns: true if part_name matches table_name (with or without device prefix) + */ +static bool is_partition_table_name(const char *part_name, const char *table_name) +{ + const char *colon_pos; + + if (strcmp(part_name, table_name) == 0) + return true; + + colon_pos = strchr(part_name, ':'); + if (colon_pos && colon_pos > part_name && strcmp(colon_pos + 1, table_name) == 0) + return true; + + return false; +} + int fastboot_block_get_part_info(const char *part_name, struct blk_desc **dev_desc, struct disk_partition *part_info, @@ -133,25 +193,31 @@ int fastboot_block_get_part_info(const char *part_name, const char *interface = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK, CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME, NULL); - const int device = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK, - CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID, -1); + int device; + const char *partition_name; if (!part_name || !strcmp(part_name, "")) { fastboot_fail("partition not given", response); return -ENOENT; } + if (!interface || !strcmp(interface, "")) { fastboot_fail("block interface isn't provided", response); return -EINVAL; } + if (parse_device_partition(part_name, &device, &partition_name) < 0) { + fastboot_fail("invalid partition name format", response); + return -EINVAL; + } + *dev_desc = blk_get_dev(interface, device); - if (!dev_desc) { + if (!*dev_desc) { fastboot_fail("no such device", response); return -ENODEV; } - ret = part_get_info_by_name(*dev_desc, part_name, part_info); + ret = part_get_info_by_name(*dev_desc, partition_name, part_info); if (ret < 0) fastboot_fail("failed to get partition info", response); @@ -316,6 +382,34 @@ void fastboot_block_flash_write(const char *part_name, void *download_buffer, struct blk_desc *dev_desc; struct disk_partition part_info; +#if CONFIG_IS_ENABLED(EFI_PARTITION) + if (is_partition_table_name(part_name, CONFIG_FASTBOOT_GPT_NAME)) { + int device; + const char *interface = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK, + CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME, + NULL); + + parse_device_partition(part_name, &device, NULL); + fastboot_flash_gpt_partition_table(interface, device, + download_buffer, response); + return; + } +#endif + +#if CONFIG_IS_ENABLED(DOS_PARTITION) + if (is_partition_table_name(part_name, CONFIG_FASTBOOT_MBR_NAME)) { + int device; + const char *interface = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK, + CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME, + NULL); + + parse_device_partition(part_name, &device, NULL); + fastboot_flash_mbr_partition_table(interface, device, + download_buffer, response); + return; + } +#endif + if (fastboot_block_get_part_info(part_name, &dev_desc, &part_info, response) < 0) return; diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 9c52e0045881..2ff6e6567cf0 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -245,3 +245,97 @@ void fastboot_init(void *buf_addr, u32 buf_size) fastboot_set_progress_callback(NULL); } + +#if CONFIG_IS_ENABLED(EFI_PARTITION) +/** + * fastboot_flash_gpt_partition_table() - Flash GPT partition table + * @interface: Block interface name (e.g., "mmc", "scsi") + * @device: Device number + * @download_buffer: Buffer containing GPT data + * @response: Fastboot response buffer + */ +void fastboot_flash_gpt_partition_table(const char *interface, + int device, + void *download_buffer, + char *response) +{ + struct blk_desc *dev_desc; + + if (!interface || !strcmp(interface, "")) { + fastboot_fail("block interface isn't provided", response); + return; + } + + dev_desc = blk_get_dev(interface, device); + if (!dev_desc) { + fastboot_fail("no such device", response); + return; + } + + printf("%s: updating MBR, Primary and Backup GPT(s) on %s device %d\n", + __func__, interface, dev_desc->devnum); + + if (is_valid_gpt_buf(dev_desc, download_buffer)) { + printf("%s: invalid GPT - refusing to write to flash\n", __func__); + fastboot_fail("invalid GPT partition", response); + return; + } + + if (write_mbr_and_gpt_partitions(dev_desc, download_buffer)) { + printf("%s: writing GPT partitions failed\n", __func__); + fastboot_fail("writing GPT partitions failed", response); + return; + } + + part_init(dev_desc); + printf("........ success\n"); + fastboot_okay(NULL, response); +} +#endif + +#if CONFIG_IS_ENABLED(DOS_PARTITION) +/** + * fastboot_flash_mbr_partition_table() - Flash MBR partition table + * @interface: Block interface name (e.g., "mmc", "scsi") + * @device: Device number + * @download_buffer: Buffer containing MBR data + * @response: Fastboot response buffer + */ +void fastboot_flash_mbr_partition_table(const char *interface, + int device, + void *download_buffer, + char *response) +{ + struct blk_desc *dev_desc; + + if (!interface || !strcmp(interface, "")) { + fastboot_fail("block interface isn't provided", response); + return; + } + + dev_desc = blk_get_dev(interface, device); + if (!dev_desc) { + fastboot_fail("no such device", response); + return; + } + + printf("%s: updating MBR on %s device %d\n", __func__, interface, + dev_desc->devnum); + + if (is_valid_dos_buf(download_buffer)) { + printf("%s: invalid MBR - refusing to write to flash\n", __func__); + fastboot_fail("invalid MBR partition", response); + return; + } + + if (write_mbr_sector(dev_desc, download_buffer)) { + printf("%s: writing MBR partition failed\n", __func__); + fastboot_fail("writing MBR partition failed", response); + return; + } + + part_init(dev_desc); + printf("........ success\n"); + fastboot_okay(NULL, response); +} +#endif diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index 11d9c8e84602..c85853915c88 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -343,7 +343,7 @@ int fastboot_mmc_get_part_info(const char *part_name, return ret; } -static struct blk_desc *fastboot_mmc_get_dev(char *response) +static struct blk_desc __maybe_unused *fastboot_mmc_get_dev(char *response) { struct blk_desc *ret = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV); @@ -389,53 +389,18 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, #if CONFIG_IS_ENABLED(EFI_PARTITION) if (strcmp(cmd, CONFIG_FASTBOOT_GPT_NAME) == 0) { - dev_desc = fastboot_mmc_get_dev(response); - if (!dev_desc) - return; - - printf("%s: updating MBR, Primary and Backup GPT(s)\n", - __func__); - if (is_valid_gpt_buf(dev_desc, download_buffer)) { - printf("%s: invalid GPT - refusing to write to flash\n", - __func__); - fastboot_fail("invalid GPT partition", response); - return; - } - if (write_mbr_and_gpt_partitions(dev_desc, download_buffer)) { - printf("%s: writing GPT partitions failed\n", __func__); - fastboot_fail("writing GPT partitions failed", - response); - return; - } - part_init(dev_desc); - printf("........ success\n"); - fastboot_okay(NULL, response); + fastboot_flash_gpt_partition_table("mmc", + CONFIG_FASTBOOT_FLASH_MMC_DEV, + download_buffer, response); return; } #endif #if CONFIG_IS_ENABLED(DOS_PARTITION) if (strcmp(cmd, CONFIG_FASTBOOT_MBR_NAME) == 0) { - dev_desc = fastboot_mmc_get_dev(response); - if (!dev_desc) - return; - - printf("%s: updating MBR\n", __func__); - if (is_valid_dos_buf(download_buffer)) { - printf("%s: invalid MBR - refusing to write to flash\n", - __func__); - fastboot_fail("invalid MBR partition", response); - return; - } - if (write_mbr_sector(dev_desc, download_buffer)) { - printf("%s: writing MBR partition failed\n", __func__); - fastboot_fail("writing MBR partition failed", - response); - return; - } - part_init(dev_desc); - printf("........ success\n"); - fastboot_okay(NULL, response); + fastboot_flash_mbr_partition_table("mmc", + CONFIG_FASTBOOT_FLASH_MMC_DEV, + download_buffer, response); return; } #endif diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ea785793d18b..c00aa11d65d5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -93,6 +93,14 @@ config QCOM_GENI for providing a common interface for various peripherals like UART, I2C, SPI, etc. +config QCOM_SPMI_SDAM + bool "Qualcomm SPMI SDAM NVMEM driver" + depends on MISC && NVMEM && SPMI + help + Enable support for Qualcomm SPMI SDAM (Shared Direct Access Memory) blocks + as NVMEM providers. This driver support accessing SDAM blocks in PMICs + for reboot reason functionality and other NVMEM use cases. + config ROCKCHIP_EFUSE bool "Rockchip e-fuse support" depends on MISC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e2170212e5ad..13800cc8b7fd 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_QFW_SMBIOS) += qfw_smbios.o obj-$(CONFIG_SANDBOX) += qfw_sandbox.o endif obj-$(CONFIG_QCOM_GENI) += qcom_geni.o +obj-$(CONFIG_QCOM_SPMI_SDAM) += qcom-spmi-sdam.o obj-$(CONFIG_$(PHASE_)ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_$(PHASE_)ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_$(PHASE_)ROCKCHIP_IODOMAIN) += rockchip-io-domain.o diff --git a/drivers/misc/nvmem.c b/drivers/misc/nvmem.c index 33e808585657..03538343a29b 100644 --- a/drivers/misc/nvmem.c +++ b/drivers/misc/nvmem.c @@ -12,55 +12,163 @@ #include #include #include +#include +#include +#include -int nvmem_cell_read(struct nvmem_cell *cell, void *buf, size_t size) +/* Maximum supported NVMEM cell size */ +#define MAX_NVMEM_CELL_SIZE sizeof(u32) /* 4 bytes */ + +/** + * nvmem_cell_read_raw() - Read raw bytes from NVMEM cell without bit field extraction + * @cell: NVMEM cell to read from + * @buf: Buffer to store read data + * @size: Size of buffer + * + * This is an internal helper that reads raw bytes from hardware without applying + * bit field extraction. Used by both nvmem_cell_read() and nvmem_cell_write(). + * Caller must validate buffer size before calling this function. + * + * Return: Number of bytes read on success, negative error code on failure + */ +static int nvmem_cell_read_raw(struct nvmem_cell *cell, void *buf, size_t size) { - dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size); - if (size != cell->size) - return -EINVAL; + int ret; + + memset(buf, 0, size); switch (cell->nvmem->driver->id) { case UCLASS_I2C_EEPROM: - return i2c_eeprom_read(cell->nvmem, cell->offset, buf, size); - case UCLASS_MISC: { - int ret = misc_read(cell->nvmem, cell->offset, buf, size); - + ret = i2c_eeprom_read(cell->nvmem, cell->offset, buf, cell->size); + break; + case UCLASS_MISC: + ret = misc_read(cell->nvmem, cell->offset, buf, cell->size); if (ret < 0) return ret; - if (ret != size) + if (ret != cell->size) return -EIO; - return 0; - } + ret = 0; + break; case UCLASS_RTC: - return dm_rtc_read(cell->nvmem, cell->offset, buf, size); + ret = dm_rtc_read(cell->nvmem, cell->offset, buf, cell->size); + break; default: return -ENOSYS; } + + if (ret) + return ret; + + return cell->size; +} + +int nvmem_cell_read(struct nvmem_cell *cell, void *buf, size_t size) +{ + int ret, bytes_needed; + u32 value; + + dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size); + + if (cell->nbits) { + if (size != MAX_NVMEM_CELL_SIZE) { + dev_dbg(cell->nvmem, "bit field requires buffer size %zu, got %zu\n", + MAX_NVMEM_CELL_SIZE, size); + return -EINVAL; + } + + bytes_needed = DIV_ROUND_UP(cell->nbits + cell->bit_offset, BITS_PER_BYTE); + if (bytes_needed > cell->size || bytes_needed > MAX_NVMEM_CELL_SIZE) { + dev_dbg(cell->nvmem, "bit field requires %d bytes, cell size %zu\n", + bytes_needed, cell->size); + return -EINVAL; + } + } else { + if (size != cell->size) { + dev_dbg(cell->nvmem, "buffer size %zu must match cell size %zu\n", + size, cell->size); + return -EINVAL; + } + } + + ret = nvmem_cell_read_raw(cell, buf, size); + if (ret < 0) + return ret; + + if (cell->nbits) { + value = le32_to_cpu(*((__le32 *)buf)); + value >>= cell->bit_offset; + value &= GENMASK(cell->nbits - 1, 0); + *(u32 *)buf = value; + } + + return 0; } int nvmem_cell_write(struct nvmem_cell *cell, const void *buf, size_t size) { + int ret, bytes_needed; + u32 current, value, mask; + dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size); - if (size != cell->size) - return -EINVAL; + + if (cell->nbits) { + if (size != MAX_NVMEM_CELL_SIZE) { + dev_dbg(cell->nvmem, "bit field requires buffer size %zu, got %zu\n", + MAX_NVMEM_CELL_SIZE, size); + return -EINVAL; + } + + bytes_needed = DIV_ROUND_UP(cell->nbits + cell->bit_offset, BITS_PER_BYTE); + if (bytes_needed > cell->size || bytes_needed > MAX_NVMEM_CELL_SIZE) { + dev_dbg(cell->nvmem, "bit field requires %d bytes, cell size %zu\n", + bytes_needed, cell->size); + return -EINVAL; + } + + ret = nvmem_cell_read_raw(cell, ¤t, sizeof(current)); + if (ret < 0) + return ret; + + current = le32_to_cpu(*((__le32 *)¤t)); + value = *(const u32 *)buf; + value &= GENMASK(cell->nbits - 1, 0); + value <<= cell->bit_offset; + + mask = GENMASK(cell->nbits - 1, 0) << cell->bit_offset; + + current = (current & ~mask) | value; + buf = ¤t; + } else { + if (size != cell->size) { + dev_dbg(cell->nvmem, "buffer size %zu must match cell size %zu\n", + size, cell->size); + return -EINVAL; + } + } switch (cell->nvmem->driver->id) { case UCLASS_I2C_EEPROM: - return i2c_eeprom_write(cell->nvmem, cell->offset, buf, size); - case UCLASS_MISC: { - int ret = misc_write(cell->nvmem, cell->offset, buf, size); - + ret = i2c_eeprom_write(cell->nvmem, cell->offset, buf, cell->size); + break; + case UCLASS_MISC: + ret = misc_write(cell->nvmem, cell->offset, buf, cell->size); if (ret < 0) return ret; - if (ret != size) + if (ret != cell->size) return -EIO; - return 0; - } + ret = 0; + break; case UCLASS_RTC: - return dm_rtc_write(cell->nvmem, cell->offset, buf, size); + ret = dm_rtc_write(cell->nvmem, cell->offset, buf, cell->size); + break; default: return -ENOSYS; } + + if (ret) + return ret; + + return 0; } /** @@ -128,6 +236,20 @@ int nvmem_cell_get_by_index(struct udevice *dev, int index, cell->offset = offset; cell->size = size; + + ret = ofnode_read_u32_index(args.node, "bits", 0, &cell->bit_offset); + if (ret) { + cell->bit_offset = 0; + cell->nbits = 0; + } else { + ret = ofnode_read_u32_index(args.node, "bits", 1, &cell->nbits); + if (ret) + return -EINVAL; + + if (cell->bit_offset + cell->nbits > cell->size * 8) + return -EINVAL; + } + return 0; } diff --git a/drivers/misc/qcom-spmi-sdam.c b/drivers/misc/qcom-spmi-sdam.c new file mode 100644 index 000000000000..f987c19deb45 --- /dev/null +++ b/drivers/misc/qcom-spmi-sdam.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm SPMI SDAM NVMEM driver + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#define PID_SHIFT 8 +#define PID_MASK (0xFF << PID_SHIFT) +#define REG_MASK 0xFF +#define SDAM_SIZE 0x100 + +struct qcom_sdam_priv { + u32 base; + u32 size; + u32 pmic_usid; + struct udevice *spmi_dev; +}; + +/** + * qcom_sdam_find_spmi_pmic() - Find SPMI controller and PMIC USID + * @dev: SDAM device + * @spmi_dev: Returns SPMI controller device + * @pmic_usid: Returns PMIC USID for SPMI access + * + * Walks up the device tree to find the PMIC parent and SPMI controller. + * Supports both direct SDAM under PMIC and virtual NVMEM under PON. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_sdam_find_spmi_pmic(struct udevice *dev, + struct udevice **spmi_dev, + u32 *pmic_usid) +{ + struct udevice *pmic_dev = dev->parent; + int ret; + + if (!pmic_dev) { + dev_err(dev, "No parent device found\n"); + return -ENODEV; + } + + ret = dev_read_u32_index(pmic_dev, "reg", 0, pmic_usid); + if (ret) { + dev_err(dev, "Could not read PMIC USID: %d\n", ret); + return ret; + } + + *spmi_dev = pmic_dev->parent; + if (!*spmi_dev || (*spmi_dev)->uclass->uc_drv->id != UCLASS_SPMI) { + dev_err(dev, "Could not find SPMI controller\n"); + return -ENODEV; + } + + dev_dbg(dev, "Found PMIC USID=%d, SPMI controller=%s\n", + *pmic_usid, (*spmi_dev)->name); + + return 0; +} + +/** + * qcom_sdam_read() - Read data from SDAM/NVMEM region + * @dev: MISC device (SDAM) + * @offset: Offset within SDAM/NVMEM region + * @buf: Buffer to read data into + * @size: Number of bytes to read + * + * Uses the same SPMI register access pattern as pmic_qcom.c driver + * for consistency and reliability. This function is called by the + * NVMEM subsystem via misc_read(). + * + * Return: number of bytes read on success, negative error code on failure + */ +static int qcom_sdam_read(struct udevice *dev, int offset, + void *buf, int size) +{ + struct qcom_sdam_priv *priv = dev_get_priv(dev); + u8 *buffer = buf; + int ret; + + if (offset + size > priv->size) + return -EINVAL; + + for (size_t i = 0; i < size; i++) { + u32 reg = priv->base + offset + i; + + ret = spmi_reg_read(priv->spmi_dev, priv->pmic_usid, + (reg & PID_MASK) >> PID_SHIFT, + reg & REG_MASK); + if (ret < 0) { + dev_err(dev, "SPMI read failed at 0x%x: %d\n", reg, ret); + return ret; + } + buffer[i] = ret; + + dev_dbg(dev, "Read 0x%02x from 0x%x (PID=0x%02x REG=0x%02x)\n", + buffer[i], reg, (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK); + } + + return size; +} + +/** + * qcom_sdam_write() - Write data to SDAM/NVMEM region + * @dev: MISC device (SDAM) + * @offset: Offset within SDAM/NVMEM region + * @buf: Buffer containing data to write + * @size: Number of bytes to write + * + * Uses the same SPMI register access pattern as pmic_qcom.c driver + * for consistency and reliability. This function is called by the + * NVMEM subsystem via misc_write(). + * + * Return: number of bytes written on success, negative error code on failure + */ +static int qcom_sdam_write(struct udevice *dev, int offset, + const void *buf, int size) +{ + struct qcom_sdam_priv *priv = dev_get_priv(dev); + const u8 *buffer = buf; + int ret; + + if (offset + size > priv->size) + return -EINVAL; + + for (size_t i = 0; i < size; i++) { + u32 reg = priv->base + offset + i; + + ret = spmi_reg_write(priv->spmi_dev, priv->pmic_usid, + (reg & PID_MASK) >> PID_SHIFT, + reg & REG_MASK, + buffer[i]); + if (ret < 0) { + dev_err(dev, "SPMI write failed at 0x%x: %d\n", reg, ret); + return ret; + } + + dev_dbg(dev, "Wrote 0x%02x to 0x%x (PID=0x%02x REG=0x%02x)\n", + buffer[i], reg, (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK); + } + + return size; +} + +static const struct misc_ops qcom_sdam_ops = { + .read = qcom_sdam_read, + .write = qcom_sdam_write, +}; + +/** + * qcom_sdam_probe() - Probe SDAM device and register as NVMEM provider + * @dev: SDAM device + * + * Handles both real SDAM blocks and virtual NVMEM under PON blocks. + * For virtual NVMEM, adds the parent PON base address to the offset. + * + * Return: 0 on success, negative error code on failure + */ +static int qcom_sdam_probe(struct udevice *dev) +{ + struct qcom_sdam_priv *priv = dev_get_priv(dev); + fdt_addr_t base; + int ret; + + base = dev_read_addr(dev); + if (base == FDT_ADDR_T_NONE) { + dev_err(dev, "Could not read base address\n"); + return -EINVAL; + } + + priv->base = base; + priv->size = SDAM_SIZE; + + ret = qcom_sdam_find_spmi_pmic(dev, &priv->spmi_dev, &priv->pmic_usid); + if (ret) + return ret; + + dev_dbg(dev, "SDAM base=0x%x size=0x%x PMIC_USID=%d\n", + priv->base, priv->size, priv->pmic_usid); + + return 0; +} + +static const struct udevice_id qcom_sdam_ids[] = { + { .compatible = "qcom,spmi-sdam" }, + { } +}; + +U_BOOT_DRIVER(qcom_spmi_sdam) = { + .name = "qcom-spmi-sdam", + .id = UCLASS_MISC, + .of_match = qcom_sdam_ids, + .probe = qcom_sdam_probe, + .ops = &qcom_sdam_ops, + .priv_auto = sizeof(struct qcom_sdam_priv), +}; diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index aaa87923604b..3b36eb897f97 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -188,6 +188,7 @@ static int msm_sdc_probe(struct udevice *dev) struct sdhci_host *host = &prv->host; u32 core_version, core_minor, core_major; struct reset_ctl bcr_rst; + struct blk_desc *bdesc; u32 caps; int ret; @@ -251,6 +252,12 @@ static int msm_sdc_probe(struct udevice *dev) if (ret) return ret; + if (plat->cfg.host_caps & MMC_CAP_NONREMOVABLE) { + bdesc = mmc_get_blk_desc(&plat->mmc); + if (bdesc) + bdesc->removable = 0; + } + host->mmc = &plat->mmc; host->mmc->dev = dev; host->ops = &msm_sdhci_ops; diff --git a/drivers/reboot-mode/Kconfig b/drivers/reboot-mode/Kconfig index 72b33d712235..6e8825f1e67d 100644 --- a/drivers/reboot-mode/Kconfig +++ b/drivers/reboot-mode/Kconfig @@ -39,4 +39,12 @@ config REBOOT_MODE_NVMEM Use any kind of non-volatile memory (EEPROM, RTC, etc) to control the reboot mode. +config REBOOT_MODE_ENV_UPDATE + bool "Automatically update reboot-mode env variable on boot" + depends on DM_REBOOT_MODE + help + When enabled, the reboot-mode uclass will automatically call + dm_reboot_mode_update() on the first reboot-mode device at last + stage init. + endmenu diff --git a/drivers/reboot-mode/reboot-mode-uclass.c b/drivers/reboot-mode/reboot-mode-uclass.c index 7cbe02eb4ed4..976544d28107 100644 --- a/drivers/reboot-mode/reboot-mode-uclass.c +++ b/drivers/reboot-mode/reboot-mode-uclass.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -116,9 +117,33 @@ int dm_reboot_mode_pre_probe(struct udevice *dev) return 0; } +/* + * reboot_mode_last_stage_init() - Update reboot-mode env variable at last + * stage init. + * + * Called via EVT_LAST_STAGE_INIT, which fires after the environment is fully + * initialized. + * + */ +static int reboot_mode_last_stage_init(void) +{ + struct udevice *dev; + int ret; + + if (!CONFIG_IS_ENABLED(REBOOT_MODE_ENV_UPDATE)) + return 0; + + ret = uclass_first_device_err(UCLASS_REBOOT_MODE, &dev); + if (ret) + return 0; + + return dm_reboot_mode_update(dev); +} +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, reboot_mode_last_stage_init); + UCLASS_DRIVER(reboot_mode) = { - .name = "reboot-mode", - .id = UCLASS_REBOOT_MODE, + .name = "reboot-mode", + .id = UCLASS_REBOOT_MODE, .pre_probe = dm_reboot_mode_pre_probe, .per_device_plat_auto = sizeof(struct reboot_mode_uclass_platdata), diff --git a/drivers/scsi/scsi-uclass.c b/drivers/scsi/scsi-uclass.c index 39b4c7476d45..031dc01b9de6 100644 --- a/drivers/scsi/scsi-uclass.c +++ b/drivers/scsi/scsi-uclass.c @@ -30,9 +30,10 @@ int scsi_get_blk_by_uuid(const char *uuid, struct disk_partition *part_info_ptr) { struct blk_desc *blk; - int i, ret; + int i, ret, max; - for (i = 0; i < blk_find_max_devnum(UCLASS_SCSI) + 1; i++) { + max = blk_find_max_devnum(UCLASS_SCSI) + 1; + for (i = 0; i < max; i++) { ret = blk_get_desc(UCLASS_SCSI, i, &blk); if (ret) continue; @@ -47,6 +48,29 @@ int scsi_get_blk_by_uuid(const char *uuid, return -ENODEV; } +int scsi_get_blk_by_type_guid(const char *type_guid, + struct blk_desc **blk_desc_ptr, + struct disk_partition *part_info_ptr) +{ + struct blk_desc *blk; + int i, ret, max; + + max = blk_find_max_devnum(UCLASS_SCSI) + 1; + for (i = 0; i < max; i++) { + ret = blk_get_desc(UCLASS_SCSI, i, &blk); + if (ret) + continue; + + ret = part_get_info_by_type_guid(blk, type_guid, part_info_ptr); + if (ret > 0) { + *blk_desc_ptr = blk; + return 0; + } + } + + return -ENODEV; +} + int scsi_bus_reset(struct udevice *dev) { struct scsi_ops *ops = scsi_get_ops(dev); diff --git a/drivers/smem/Kconfig b/drivers/smem/Kconfig deleted file mode 100644 index e5d7dcc81b17..000000000000 --- a/drivers/smem/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -menuconfig SMEM - bool "SMEM (Shared Memory mamanger) support" - -if SMEM - -config SANDBOX_SMEM - bool "Sandbox Shared Memory Manager (SMEM)" - depends on SANDBOX && DM - help - enable SMEM support for sandbox. This is an emulation of a real SMEM - manager. - The sandbox driver allocates a shared memory from the heap and - initialzies it on start. - -config MSM_SMEM - bool "Qualcomm Shared Memory Manager (SMEM)" - depends on DM - depends on ARCH_SNAPDRAGON || ARCH_IPQ40XX - select DEVRES - help - Enable support for the Qualcomm Shared Memory Manager. - The driver provides an interface to items in a heap shared among all - processors in a Qualcomm platform. - -endif # menu "SMEM Support" diff --git a/drivers/smem/Makefile b/drivers/smem/Makefile deleted file mode 100644 index af3e9b50883c..000000000000 --- a/drivers/smem/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -# -# Makefile for the U-Boot SMEM interface drivers - -obj-$(CONFIG_SANDBOX_SMEM) += sandbox_smem.o -obj-$(CONFIG_SMEM) += smem-uclass.o -obj-$(CONFIG_MSM_SMEM) += msm_smem.o diff --git a/drivers/smem/sandbox_smem.c b/drivers/smem/sandbox_smem.c deleted file mode 100644 index fec98e5611d6..000000000000 --- a/drivers/smem/sandbox_smem.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2018 Ramon Fried - */ - -#include -#include -#include -#include - -static int sandbox_smem_alloc(unsigned int host, - unsigned int item, size_t size) -{ - return 0; -} - -static void *sandbox_smem_get(unsigned int host, - unsigned int item, size_t *size) -{ - return NULL; -} - -static int sandbox_smem_get_free_space(unsigned int host) -{ - return 0; -} - -static const struct smem_ops sandbox_smem_ops = { - .alloc = sandbox_smem_alloc, - .get = sandbox_smem_get, - .get_free_space = sandbox_smem_get_free_space, -}; - -static const struct udevice_id sandbox_smem_ids[] = { - { .compatible = "sandbox,smem" }, - { } -}; - -U_BOOT_DRIVER(smem_sandbox) = { - .name = "smem_sandbox", - .id = UCLASS_SMEM, - .of_match = sandbox_smem_ids, - .ops = &sandbox_smem_ops, -}; diff --git a/drivers/smem/smem-uclass.c b/drivers/smem/smem-uclass.c deleted file mode 100644 index 4dea5cc4bf1c..000000000000 --- a/drivers/smem/smem-uclass.c +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2018 Ramon Fried - */ - -#define LOG_CATEGORY UCLASS_SMEM - -#include -#include - -int smem_alloc(struct udevice *dev, unsigned int host, - unsigned int item, size_t size) -{ - struct smem_ops *ops = smem_get_ops(dev); - - if (!ops->alloc) - return -ENOSYS; - - return ops->alloc(host, item, size); -} - -void *smem_get(struct udevice *dev, unsigned int host, - unsigned int item, size_t *size) -{ - struct smem_ops *ops = smem_get_ops(dev); - - if (!ops->get) - return NULL; - - return ops->get(host, item, size); -} - -int smem_get_free_space(struct udevice *dev, unsigned int host) -{ - struct smem_ops *ops = smem_get_ops(dev); - - if (!ops->get_free_space) - return -ENOSYS; - - return ops->get_free_space(host); -} - -UCLASS_DRIVER(smem) = { - .id = UCLASS_SMEM, - .name = "smem", -}; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 8243805e46a3..cdd9e30f43e5 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -25,4 +25,12 @@ config QCOM_RPMH Say y here to support the Qualcomm RPMh (resource peripheral manager) if you need to control regulators on Qualcomm platforms, say y here. +config QCOM_SMEM + bool "Qualcomm SMEM support" + help + Say y here to support the Qualcomm SMEM (shared memory) client driver. + SMEM is a shared memory region that is used to pass information about + the hardware (e.g. DRAM layout, PMIC configuration, etc) between bootloader + staged and the OS. If in doubt, say y. + endif # SOC_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 78fae8bbfa16..f4102f9155a8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_RPMH) += rpmh-rsc.o rpmh.o +obj-$(CONFIG_QCOM_SMEM) += smem.o diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index d0a6047b8a65..61e157831231 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -248,7 +248,7 @@ static int cmd_db_bind(struct udevice *dev) } /* On SM8550/SM8650 and newer SoCs cmd-db might not be mapped */ - mmu_map_region((phys_addr_t)base, (phys_size_t)size, false); + mmu_map_region((phys_addr_t)base, (phys_size_t)size, false, false); cmd_db_header = base; if (!cmd_db_magic_matches(cmd_db_header)) { diff --git a/drivers/smem/msm_smem.c b/drivers/soc/qcom/smem.c similarity index 56% rename from drivers/smem/msm_smem.c rename to drivers/soc/qcom/smem.c index 7a50d5a5792c..7acf5b300eb1 100644 --- a/drivers/smem/msm_smem.c +++ b/drivers/soc/qcom/smem.c @@ -1,26 +1,25 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015, Sony Mobile Communications AB. * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. - * Copyright (c) 2018, Ramon Fried */ -#include -#include +#define pr_fmt(fmt) "smem: " fmt + +#include +#include #include -#include -#include -#include -#include +#include #include -#include -#include #include +#include #include -#include +#include +#include +#include /* - * The Qualcomm shared memory system is an allocate-only heap structure that + * The Qualcomm shared memory system is a allocate only heap structure that * consists of one of more memory areas that can be accessed by the processors * in the SoC. * @@ -33,7 +32,7 @@ * parts of the shared memory space. * * - * In addition to this global heap, a set of "private" heaps can be set up at + * In addition to this global heap a set of "private" heaps can be set up at * boot time with access restrictions so that only certain processor pairs can * access the data. * @@ -58,6 +57,10 @@ * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is * set by the bootloader. * + * To synchronize allocations in the shared memory heaps a remote spinlock must + * be held - currently lock number 3 of the sfpb or tcsr is used for this on all + * platforms. + * */ /* @@ -85,14 +88,14 @@ #define SMEM_GLOBAL_HOST 0xfffe /* Max number of processors/hosts in a system */ -#define SMEM_HOST_COUNT 25 +#define SMEM_HOST_COUNT 20 /** - * struct smem_proc_comm - proc_comm communication struct (legacy) - * @command: current command to be executed - * @status: status of the currently requested command - * @params: parameters to the command - */ + * struct smem_proc_comm - proc_comm communication struct (legacy) + * @command: current command to be executed + * @status: status of the currently requested command + * @params: parameters to the command + */ struct smem_proc_comm { __le32 command; __le32 status; @@ -123,7 +126,7 @@ struct smem_global_entry { * @free_offset: index of the first unallocated byte in smem * @available: number of bytes available for allocation * @reserved: reserved field, must be 0 - * toc: array of references to items + * @toc: array of references to items */ struct smem_header { struct smem_proc_comm proc_comm[4]; @@ -195,6 +198,20 @@ struct smem_partition_header { __le32 reserved[3]; }; +/** + * struct smem_partition - describes smem partition + * @virt_base: starting virtual address of partition + * @phys_base: starting physical address of partition + * @cacheline: alignment for "cached" entries + * @size: size of partition + */ +struct smem_partition { + void __iomem *virt_base; + phys_addr_t phys_base; + size_t cacheline; + size_t size; +}; + static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; /** @@ -234,6 +251,8 @@ struct smem_info { static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */ +#define SMEM_MAX_REGIONS 2 + /** * struct smem_region - representation of a chunk of memory used for smem * @aux_base: identifier of aux_mem base @@ -241,37 +260,37 @@ static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */ * @size: size of the memory region */ struct smem_region { - u32 aux_base; + phys_addr_t aux_base; void __iomem *virt_base; size_t size; }; /** * struct qcom_smem - device data for the smem device - * @dev: device pointer - * @global_partition: pointer to global partition when in use - * @global_cacheline: cacheline size for global partition - * @partitions: list of pointers to partitions affecting the current - * processor/host - * @cacheline: list of cacheline sizes for each host + * @ptable: virtual base of partition table + * @global_partition: describes for global partition when in use + * @partitions: list of partitions of current processor/host * @item_count: max accepted item number * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory */ struct qcom_smem { - struct udevice *dev; - - struct smem_partition_header *global_partition; - size_t global_cacheline; - struct smem_partition_header *partitions[SMEM_HOST_COUNT]; - size_t cacheline[SMEM_HOST_COUNT]; u32 item_count; + struct smem_ptable *ptable; + struct smem_partition global_partition; + struct smem_partition partitions[SMEM_HOST_COUNT]; - unsigned int num_regions; - struct smem_region regions[0]; + unsigned num_regions; + struct smem_region regions[SMEM_MAX_REGIONS]; }; -static struct smem_private_entry * +/* Pre-allocated storage for SMEM */ +static struct qcom_smem __smem __section(".data") = { 0 }; + +/* Pointer to the one and only smem handle */ +static struct qcom_smem *smem __section(".data") = NULL; + +static void * phdr_to_last_uncached_entry(struct smem_partition_header *phdr) { void *p = phdr; @@ -279,15 +298,18 @@ phdr_to_last_uncached_entry(struct smem_partition_header *phdr) return p + le32_to_cpu(phdr->offset_free_uncached); } -static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr, +static struct smem_private_entry * +phdr_to_first_cached_entry(struct smem_partition_header *phdr, size_t cacheline) { void *p = phdr; + struct smem_private_entry *e; - return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline); + return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*e), cacheline); } -static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr) +static void * +phdr_to_last_cached_entry(struct smem_partition_header *phdr) { void *p = phdr; @@ -333,40 +355,54 @@ static void *cached_entry_to_item(struct smem_private_entry *e) return p - le32_to_cpu(e->size); } -/* Pointer to the one and only smem handle */ -static struct qcom_smem *__smem; +/** + * qcom_smem_is_available() - Check if SMEM is available + * + * Return: true if SMEM is available, false otherwise. + */ +bool qcom_smem_is_available(void) +{ + return !!smem; +} +EXPORT_SYMBOL_GPL(qcom_smem_is_available); static int qcom_smem_alloc_private(struct qcom_smem *smem, - struct smem_partition_header *phdr, - unsigned int item, + struct smem_partition *part, + unsigned item, size_t size) { struct smem_private_entry *hdr, *end; + struct smem_partition_header *phdr; size_t alloc_size; void *cached; + void *p_end; + + phdr = (struct smem_partition_header __force *)part->virt_base; + p_end = (void *)phdr + part->size; hdr = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); cached = phdr_to_last_cached_entry(phdr); - while (hdr < end) { - if (hdr->canary != SMEM_PRIVATE_CANARY) { - dev_err(smem->dev, - "Found invalid canary in hosts %d:%d partition\n", - phdr->host0, phdr->host1); - return -EINVAL; - } + if (WARN_ON((void *)end > p_end || cached > p_end)) + return -EINVAL; + while (hdr < end) { + if (hdr->canary != SMEM_PRIVATE_CANARY) + goto bad_canary; if (le16_to_cpu(hdr->item) == item) return -EEXIST; hdr = uncached_entry_next(hdr); } + if (WARN_ON((void *)hdr > p_end)) + return -EINVAL; + /* Check that we don't grow into the cached region */ alloc_size = sizeof(*hdr) + ALIGN(size, 8); - if ((void *)hdr + alloc_size >= cached) { - dev_err(smem->dev, "Out of memory\n"); + if ((void *)hdr + alloc_size > cached) { + log_err("Out of memory\n"); return -ENOSPC; } @@ -381,14 +417,19 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, * that remote processors that does not take the remote spinlock still * gets a consistent view of the linked list. */ - dmb(); + wmb(); le32_add_cpu(&phdr->offset_free_uncached, alloc_size); return 0; +bad_canary: + log_err("Found invalid canary in hosts %hu:%hu partition\n", + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1)); + + return -EINVAL; } static int qcom_smem_alloc_global(struct qcom_smem *smem, - unsigned int item, + unsigned item, size_t size) { struct smem_global_entry *entry; @@ -411,7 +452,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, * so that remote processors will get a consistent view of the item * even though they do not take the spinlock on read. */ - dmb(); + wmb(); entry->allocated = cpu_to_le32(1); le32_add_cpu(&header->free_offset, size); @@ -429,45 +470,47 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, * Allocate space for a given smem item of size @size, given that the item is * not yet allocated. */ -static int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size) +int qcom_smem_alloc(unsigned host, unsigned item, size_t size) { - struct smem_partition_header *phdr; + struct smem_partition *part; int ret; - if (!__smem) - return -ENOMEM; + if (!smem) + return -EPROBE_DEFER; if (item < SMEM_ITEM_LAST_FIXED) { - dev_err(__smem->dev, - "Rejecting allocation of static entry %d\n", item); + log_err("Rejecting allocation of static entry %d\n", item); return -EINVAL; } - if (WARN_ON(item >= __smem->item_count)) + if (WARN_ON(item >= smem->item_count)) return -EINVAL; - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { - phdr = __smem->partitions[host]; - ret = qcom_smem_alloc_private(__smem, phdr, item, size); - } else if (__smem->global_partition) { - phdr = __smem->global_partition; - ret = qcom_smem_alloc_private(__smem, phdr, item, size); + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) { + part = &smem->partitions[host]; + ret = qcom_smem_alloc_private(smem, part, item, size); + } else if (smem->global_partition.virt_base) { + part = &smem->global_partition; + ret = qcom_smem_alloc_private(smem, part, item, size); } else { - ret = qcom_smem_alloc_global(__smem, item, size); + ret = qcom_smem_alloc_global(smem, item, size); } return ret; } +EXPORT_SYMBOL_GPL(qcom_smem_alloc); static void *qcom_smem_get_global(struct qcom_smem *smem, - unsigned int item, + unsigned item, size_t *size) { struct smem_header *header; - struct smem_region *area; + struct smem_region *region; struct smem_global_entry *entry; + u64 entry_offset; + u32 e_size; u32 aux_base; - unsigned int i; + unsigned i; header = smem->regions[0].virt_base; entry = &header->toc[item]; @@ -477,12 +520,19 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK; for (i = 0; i < smem->num_regions; i++) { - area = &smem->regions[i]; + region = &smem->regions[i]; + + if ((u32)region->aux_base == aux_base || !aux_base) { + e_size = le32_to_cpu(entry->size); + entry_offset = le32_to_cpu(entry->offset); + + if (WARN_ON(e_size + entry_offset > region->size)) + return ERR_PTR(-EINVAL); - if (area->aux_base == aux_base || !aux_base) { if (size != NULL) - *size = le32_to_cpu(entry->size); - return area->virt_base + le32_to_cpu(entry->offset); + *size = e_size; + + return region->virt_base + entry_offset; } } @@ -490,12 +540,18 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, } static void *qcom_smem_get_private(struct qcom_smem *smem, - struct smem_partition_header *phdr, - size_t cacheline, - unsigned int item, + struct smem_partition *part, + unsigned item, size_t *size) { struct smem_private_entry *e, *end; + struct smem_partition_header *phdr; + void *item_ptr, *p_end; + u32 padding_data; + u32 e_size; + + phdr = (struct smem_partition_header __force *)part->virt_base; + p_end = (void *)phdr + part->size; e = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); @@ -505,41 +561,70 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, goto invalid_canary; if (le16_to_cpu(e->item) == item) { - if (size != NULL) - *size = le32_to_cpu(e->size) - - le16_to_cpu(e->padding_data); + if (size != NULL) { + e_size = le32_to_cpu(e->size); + padding_data = le16_to_cpu(e->padding_data); + + if (WARN_ON(e_size > part->size || padding_data > e_size)) + return ERR_PTR(-EINVAL); - return uncached_entry_to_item(e); + *size = e_size - padding_data; + } + + item_ptr = uncached_entry_to_item(e); + if (WARN_ON(item_ptr > p_end)) + return ERR_PTR(-EINVAL); + + return item_ptr; } e = uncached_entry_next(e); } + if (WARN_ON((void *)e > p_end)) + return ERR_PTR(-EINVAL); + /* Item was not found in the uncached list, search the cached list */ - e = phdr_to_first_cached_entry(phdr, cacheline); + e = phdr_to_first_cached_entry(phdr, part->cacheline); end = phdr_to_last_cached_entry(phdr); + if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end)) + return ERR_PTR(-EINVAL); + while (e > end) { if (e->canary != SMEM_PRIVATE_CANARY) goto invalid_canary; if (le16_to_cpu(e->item) == item) { - if (size != NULL) - *size = le32_to_cpu(e->size) - - le16_to_cpu(e->padding_data); + if (size != NULL) { + e_size = le32_to_cpu(e->size); + padding_data = le16_to_cpu(e->padding_data); + + if (WARN_ON(e_size > part->size || padding_data > e_size)) + return ERR_PTR(-EINVAL); + + *size = e_size - padding_data; + } + + item_ptr = cached_entry_to_item(e); + if (WARN_ON(item_ptr < (void *)phdr)) + return ERR_PTR(-EINVAL); - return cached_entry_to_item(e); + return item_ptr; } - e = cached_entry_next(e, cacheline); + e = cached_entry_next(e, part->cacheline); } + if (WARN_ON((void *)e < (void *)phdr)) + return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOENT); invalid_canary: - dev_err(smem->dev, "Found invalid canary in hosts %d:%d partition\n", - phdr->host0, phdr->host1); + log_err("Found invalid canary in hosts %hu:%hu partition\n", + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1)); return ERR_PTR(-EINVAL); } @@ -553,33 +638,30 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, * Looks up smem item and returns pointer to it. Size of smem * item is returned in @size. */ -static void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size) +void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { - struct smem_partition_header *phdr; - size_t cacheln; - void *ptr = ERR_PTR(-ENOMEM); + struct smem_partition *part; + void *ptr = ERR_PTR(-EPROBE_DEFER); - if (!__smem) + if (!smem) return ptr; - if (WARN_ON(item >= __smem->item_count)) + if (WARN_ON(item >= smem->item_count)) return ERR_PTR(-EINVAL); - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { - phdr = __smem->partitions[host]; - cacheln = __smem->cacheline[host]; - ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size); - } else if (__smem->global_partition) { - phdr = __smem->global_partition; - cacheln = __smem->global_cacheline; - ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size); + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) { + part = &smem->partitions[host]; + ptr = qcom_smem_get_private(smem, part, item, size); + } else if (smem->global_partition.virt_base) { + part = &smem->global_partition; + ptr = qcom_smem_get_private(smem, part, item, size); } else { - ptr = qcom_smem_get_global(__smem, item, size); + ptr = qcom_smem_get_global(smem, item, size); } return ptr; - } +EXPORT_SYMBOL_GPL(qcom_smem_get); /** * qcom_smem_get_free_space() - retrieve amount of free space in a partition @@ -588,30 +670,94 @@ static void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size) * To be used by smem clients as a quick way to determine if any new * allocations has been made. */ -static int qcom_smem_get_free_space(unsigned int host) +int qcom_smem_get_free_space(unsigned host) { + struct smem_partition *part; struct smem_partition_header *phdr; struct smem_header *header; - unsigned int ret; + unsigned ret; - if (!__smem) - return -ENOMEM; + if (!smem) + return -EPROBE_DEFER; - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { - phdr = __smem->partitions[host]; + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) { + part = &smem->partitions[host]; + phdr = part->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); - } else if (__smem->global_partition) { - phdr = __smem->global_partition; + + if (ret > le32_to_cpu(part->size)) + return -EINVAL; + } else if (smem->global_partition.virt_base) { + part = &smem->global_partition; + phdr = part->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); + + if (ret > le32_to_cpu(part->size)) + return -EINVAL; } else { - header = __smem->regions[0].virt_base; + header = smem->regions[0].virt_base; ret = le32_to_cpu(header->available); + + if (ret > smem->regions[0].size) + return -EINVAL; } return ret; } +EXPORT_SYMBOL_GPL(qcom_smem_get_free_space); + +static bool addr_in_range(void __iomem *base, size_t size, void *addr) +{ + return base && ((void __iomem *)addr >= base && (void __iomem *)addr < base + size); +} + +/** + * qcom_smem_virt_to_phys() - return the physical address associated + * with an smem item pointer (previously returned by qcom_smem_get() + * @p: the virtual address to convert + * + * Returns 0 if the pointer provided is not within any smem region. + */ +phys_addr_t qcom_smem_virt_to_phys(void *p) +{ + struct smem_partition *part; + struct smem_region *area; + u64 offset; + u32 i; + + for (i = 0; i < SMEM_HOST_COUNT; i++) { + part = &smem->partitions[i]; + + if (addr_in_range(part->virt_base, part->size, p)) { + offset = p - part->virt_base; + + return (phys_addr_t)part->phys_base + offset; + } + } + + part = &smem->global_partition; + + if (addr_in_range(part->virt_base, part->size, p)) { + offset = p - part->virt_base; + + return (phys_addr_t)part->phys_base + offset; + } + + for (i = 0; i < smem->num_regions; i++) { + area = &smem->regions[i]; + + if (addr_in_range(area->virt_base, area->size, p)) { + offset = p - area->virt_base; + + return (phys_addr_t)area->aux_base + offset; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys); static int qcom_smem_get_sbl_version(struct qcom_smem *smem) { @@ -629,14 +775,13 @@ static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem) struct smem_ptable *ptable; u32 version; - ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; + ptable = smem->ptable; if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) return ERR_PTR(-ENOENT); version = le32_to_cpu(ptable->version); if (version != 1) { - dev_err(smem->dev, - "Unsupported partition header version %d\n", version); + log_err("Unsupported partition header version %d\n", version); return ERR_PTR(-EINVAL); } return ptable; @@ -658,82 +803,117 @@ static u32 qcom_smem_get_item_count(struct qcom_smem *smem) return le16_to_cpu(info->num_items); } -static int qcom_smem_set_global_partition(struct qcom_smem *smem) +/* + * Validate the partition header for a partition whose partition + * table entry is supplied. Returns a pointer to its header if + * valid, or a null pointer otherwise. + */ +static struct smem_partition_header * +qcom_smem_partition_header(struct qcom_smem *smem, + struct smem_ptable_entry *entry, u16 host0, u16 host1) { struct smem_partition_header *header; - struct smem_ptable_entry *entry = NULL; - struct smem_ptable *ptable; - u32 host0, host1, size; - int i; + u64 phys_addr; + u32 size; - ptable = qcom_smem_get_ptable(smem); - if (IS_ERR(ptable)) - return PTR_ERR(ptable); + phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset); + header = (void *)phys_addr; // devm_ioremap_wc() - for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { - entry = &ptable->entry[i]; - host0 = le16_to_cpu(entry->host0); - host1 = le16_to_cpu(entry->host1); + if (!header) + return NULL; - if (host0 == SMEM_GLOBAL_HOST && host0 == host1) - break; + if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { + log_err("bad partition magic %4ph\n", header->magic); + return NULL; } - if (!entry) { - dev_err(smem->dev, "Missing entry for global partition\n"); - return -EINVAL; + if (host0 != le16_to_cpu(header->host0)) { + log_err("bad host0 (%hu != %hu)\n", + host0, le16_to_cpu(header->host0)); + return NULL; + } + if (host1 != le16_to_cpu(header->host1)) { + log_err("bad host1 (%hu != %hu)\n", + host1, le16_to_cpu(header->host1)); + return NULL; } - if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) { - dev_err(smem->dev, "Invalid entry for global partition\n"); - return -EINVAL; + size = le32_to_cpu(header->size); + if (size != le32_to_cpu(entry->size)) { + log_err("bad partition size (%u != %u)\n", + size, le32_to_cpu(entry->size)); + return NULL; } - if (smem->global_partition) { - dev_err(smem->dev, "Already found the global partition\n"); - return -EINVAL; + if (le32_to_cpu(header->offset_free_uncached) > size) { + log_err("bad partition free uncached (%u > %u)\n", + le32_to_cpu(header->offset_free_uncached), size); + return NULL; } - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); - host0 = le16_to_cpu(header->host0); - host1 = le16_to_cpu(header->host1); + return header; +} - if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { - dev_err(smem->dev, "Global partition has invalid magic\n"); +static int qcom_smem_set_global_partition(struct qcom_smem *smem) +{ + struct smem_partition_header *header; + struct smem_ptable_entry *entry; + struct smem_ptable *ptable; + bool found = false; + int i; + + if (smem->global_partition.virt_base) { + log_err("Already found the global partition\n"); return -EINVAL; } - if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) { - dev_err(smem->dev, "Global partition hosts are invalid\n"); - return -EINVAL; + ptable = qcom_smem_get_ptable(smem); + if (IS_ERR(ptable)) + return PTR_ERR(ptable); + + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { + entry = &ptable->entry[i]; + if (!le32_to_cpu(entry->offset)) + continue; + if (!le32_to_cpu(entry->size)) + continue; + + if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST) + continue; + + if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) { + found = true; + break; + } } - if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { - dev_err(smem->dev, "Global partition has invalid size\n"); + if (!found) { + log_err("Missing entry for global partition\n"); return -EINVAL; } - size = le32_to_cpu(header->offset_free_uncached); - if (size > le32_to_cpu(header->size)) { - dev_err(smem->dev, - "Global partition has invalid free pointer\n"); + header = qcom_smem_partition_header(smem, entry, + SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST); + if (!header) return -EINVAL; - } - smem->global_partition = header; - smem->global_cacheline = le32_to_cpu(entry->cacheline); + smem->global_partition.virt_base = (void __iomem *)header; + smem->global_partition.phys_base = smem->regions[0].aux_base + + le32_to_cpu(entry->offset); + smem->global_partition.size = le32_to_cpu(entry->size); + smem->global_partition.cacheline = le32_to_cpu(entry->cacheline); return 0; } -static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, - unsigned int local_host) +static int +qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host) { struct smem_partition_header *header; struct smem_ptable_entry *entry; struct smem_ptable *ptable; - unsigned int remote_host; - u32 host0, host1; + u16 remote_host; + u16 host0, host1; int i; ptable = qcom_smem_get_ptable(smem); @@ -742,169 +922,164 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { entry = &ptable->entry[i]; - host0 = le16_to_cpu(entry->host0); - host1 = le16_to_cpu(entry->host1); - - if (host0 != local_host && host1 != local_host) - continue; - if (!le32_to_cpu(entry->offset)) continue; - if (!le32_to_cpu(entry->size)) continue; + host0 = le16_to_cpu(entry->host0); + host1 = le16_to_cpu(entry->host1); if (host0 == local_host) remote_host = host1; - else + else if (host1 == local_host) remote_host = host0; + else + continue; if (remote_host >= SMEM_HOST_COUNT) { - dev_err(smem->dev, - "Invalid remote host %d\n", - remote_host); + log_err("bad host %u\n", remote_host); return -EINVAL; } - if (smem->partitions[remote_host]) { - dev_err(smem->dev, - "Already found a partition for host %d\n", - remote_host); + if (smem->partitions[remote_host].virt_base) { + log_err("duplicate host %u\n", remote_host); return -EINVAL; } - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); - host0 = le16_to_cpu(header->host0); - host1 = le16_to_cpu(header->host1); - - if (memcmp(header->magic, SMEM_PART_MAGIC, - sizeof(header->magic))) { - dev_err(smem->dev, - "Partition %d has invalid magic\n", i); + header = qcom_smem_partition_header(smem, entry, host0, host1); + if (!header) return -EINVAL; - } - if (host0 != local_host && host1 != local_host) { - dev_err(smem->dev, - "Partition %d hosts are invalid\n", i); - return -EINVAL; - } + smem->partitions[remote_host].virt_base = (void __iomem *)header; + smem->partitions[remote_host].phys_base = smem->regions[0].aux_base + + le32_to_cpu(entry->offset); + smem->partitions[remote_host].size = le32_to_cpu(entry->size); + smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline); + } - if (host0 != remote_host && host1 != remote_host) { - dev_err(smem->dev, - "Partition %d hosts are invalid\n", i); - return -EINVAL; - } + return 0; +} - if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { - dev_err(smem->dev, - "Partition %d has invalid size\n", i); - return -EINVAL; - } +static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region) +{ + u32 ptable_start; - if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) { - dev_err(smem->dev, - "Partition %d has invalid free pointer\n", i); - return -EINVAL; - } + /* map starting 4K for smem header */ + region->virt_base = (void *)region->aux_base; + ptable_start = region->aux_base + region->size - SZ_4K; + /* map last 4k for toc */ + smem->ptable = (struct smem_ptable *)(u64)ptable_start; - smem->partitions[remote_host] = header; - smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline); - } + if (!region->virt_base || !smem->ptable) + return -ENOMEM; + + if (mmu_status()) + mmu_map_region(region->aux_base, region->size, false, false); return 0; } -static int qcom_smem_map_memory(struct qcom_smem *smem, struct udevice *dev, - const char *name, int i) +static int qcom_smem_map_global(struct qcom_smem *smem, u32 size) { - int ret; - struct ofnode_phandle_args args; - struct resource r; + u64 phys_addr; - if (!dev_read_prop(dev, name, NULL)) { - dev_err(dev, "%s prop not found\n", name); - return -EINVAL; - } + phys_addr = smem->regions[0].aux_base; - ret = dev_read_phandle_with_args(dev, name, NULL, 0, 0, &args); - if (ret) { - dev_err(dev, "%s phandle read failed\n", name); - return -EINVAL; - } + smem->regions[0].size = size; + smem->regions[0].virt_base = (void *)phys_addr; - if (!ofnode_valid(args.node)) { - dev_err(dev, "Invalid node from phandle args\n"); - return -EINVAL; + if (!smem->regions[0].virt_base) + return -ENOMEM; + + return 0; +} + +int qcom_socinfo_init(void) +{ + struct socinfo *info; + size_t item_size; + char buf[32] = { 0 }; + + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, + &item_size); + if (IS_ERR(info)) { + log_err("Couldn't find socinfo: %ld\n", PTR_ERR(info)); + return PTR_ERR(info); } - ret = ofnode_read_resource(args.node, 0, &r); - if (ret) { - dev_err(dev, "Can't get mmap base address(%d)\n", ret); - return ret; + if (offsetof(struct socinfo, serial_num) + sizeof(info->serial_num) <= item_size) { + snprintf(buf, sizeof(buf), "%u", le32_to_cpu(info->serial_num)); + env_set("serial#", buf); + } else { + return -ENOENT; } - smem->regions[i].aux_base = (u32)r.start; - smem->regions[i].size = resource_size(&r); - smem->regions[i].virt_base = devm_ioremap(dev, r.start, resource_size(&r)); - if (!smem->regions[i].virt_base) - return -ENOMEM; return 0; } -static int qcom_smem_probe(struct udevice *dev) +int qcom_smem_init(void) { struct smem_header *header; - struct qcom_smem *smem; - size_t array_size; int num_regions; + fdt_size_t reg_size = 0; + u32 phandle; + ofnode node, mem_node; u32 version; + u32 size; int ret; - fdt_addr_t addr; - fdt_size_t size; + int i; + + if (mmu_status() && smem) + mmu_map_region(smem->regions[0].aux_base, smem->regions[0].size, + false, false); - if (__smem) + if (smem) return 0; + smem = &__smem; + num_regions = 1; - if (dev_read_prop(dev, "qcom,rpm-msg-ram", NULL)) - num_regions++; - array_size = num_regions * sizeof(struct smem_region); - smem = devm_kzalloc(dev, sizeof(*smem) + array_size, GFP_KERNEL); - if (!smem) - return -ENOMEM; + node = ofnode_by_compatible(ofnode_root(), "qcom,smem"); + if (!ofnode_valid(node)) + return -ENODEV; + + if (ofnode_has_property(node, "memory-region")) { + ofnode_read_u32(node, "memory-region", &phandle); + mem_node = ofnode_get_by_phandle(phandle); + } else { + mem_node = node; + } - smem->dev = dev; smem->num_regions = num_regions; - addr = dev_read_addr_size(dev, &size); - if (addr == FDT_ADDR_T_NONE) { - ret = qcom_smem_map_memory(smem, dev, "memory-region", 0); - if (ret) - return ret; - } else { - smem->regions[0].aux_base = (u32)addr; - smem->regions[0].size = size; - smem->regions[0].virt_base = devm_ioremap(dev, addr, size); - if (!smem->regions[0].virt_base) - return -ENOMEM; + smem->regions[0].aux_base = ofnode_get_addr(mem_node); + reg_size = ofnode_get_size(mem_node); + if (smem->regions[0].aux_base == FDT_ADDR_T_NONE) { + log_err("Failed to get base address\n"); + return -EINVAL; } + smem->regions[0].size = reg_size; - if (num_regions > 1) { - ret = qcom_smem_map_memory(smem, dev, - "qcom,rpm-msg-ram", 1); - if (ret) - return ret; + ret = qcom_smem_map_toc(smem, &smem->regions[0]); + if (ret) { + log_err("Failed to map toc\n"); + return ret; + } + + for (i = 1; i < num_regions; i++) { + smem->regions[i].virt_base = (void *)smem->regions[i].aux_base; } header = smem->regions[0].virt_base; if (le32_to_cpu(header->initialized) != 1 || le32_to_cpu(header->reserved)) { - dev_err(dev, "SMEM is not initialized by SBL\n"); + log_err("SMEM is not initialized by SBL\n"); return -EINVAL; } + size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset); + version = qcom_smem_get_sbl_version(smem); switch (version >> 16) { case SMEM_GLOBAL_PART_VERSION: @@ -914,45 +1089,20 @@ static int qcom_smem_probe(struct udevice *dev) smem->item_count = qcom_smem_get_item_count(smem); break; case SMEM_GLOBAL_HEAP_VERSION: + qcom_smem_map_global(smem, size); smem->item_count = SMEM_ITEM_COUNT; break; default: - dev_err(dev, "Unsupported SMEM version 0x%x\n", version); + log_err("Unsupported SMEM version 0x%x\n", version); return -EINVAL; } + BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT); ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); - if (ret < 0 && ret != -ENOENT) + if (ret < 0 && ret != -ENOENT) { + log_err("Failed to enumerate partitions\n"); return ret; - - __smem = smem; - - return 0; -} - -static int qcom_smem_remove(struct udevice *dev) -{ - __smem = NULL; + } return 0; } - -const struct udevice_id qcom_smem_of_match[] = { - { .compatible = "qcom,smem" }, - { } -}; - -static const struct smem_ops msm_smem_ops = { - .alloc = qcom_smem_alloc, - .get = qcom_smem_get, - .get_free_space = qcom_smem_get_free_space, -}; - -U_BOOT_DRIVER(qcom_smem) = { - .name = "qcom_smem", - .id = UCLASS_SMEM, - .of_match = qcom_smem_of_match, - .ops = &msm_smem_ops, - .probe = qcom_smem_probe, - .remove = qcom_smem_remove, -}; diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c index b89dd0b406ba..da399685f7b2 100644 --- a/drivers/spmi/spmi-msm.c +++ b/drivers/spmi/spmi-msm.c @@ -59,6 +59,8 @@ #define SPMI_MAX_SLAVES 16 #define SPMI_MAX_PERIPH 256 +#define SDAM02_REBOOT_MODE_PID 0x71 + #define SPMI_CHANNEL_READ_ONLY BIT(31) #define SPMI_CHANNEL_VALID BIT(30) #define SPMI_CHANNEL_MASK 0xffff @@ -117,8 +119,11 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, return -EIO; if (!(priv->channel_map[usid][pid] & SPMI_CHANNEL_VALID)) return -EINVAL; - if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY) - return -EPERM; + /* Skip readonly check for SDAM02 reboot reason pid */ + if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY) { + if (!(usid == 0 && pid == SDAM02_REBOOT_MODE_PID)) + return -EPERM; + } channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK; diff --git a/drivers/ufs/ufs-qcom.c b/drivers/ufs/ufs-qcom.c index dc40ee62daf8..f5f5a6eb110c 100644 --- a/drivers/ufs/ufs-qcom.c +++ b/drivers/ufs/ufs-qcom.c @@ -30,6 +30,7 @@ #define UFS_CPU_MAX_BANDWIDTH 819200 static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_hba *hba, bool enable); +static u32 ufs_qcom_get_core_clk_unipro_max_freq(struct ufs_hba *hba); static int ufs_qcom_enable_clks(struct ufs_qcom_priv *priv) { @@ -47,17 +48,6 @@ static int ufs_qcom_enable_clks(struct ufs_qcom_priv *priv) return 0; } -static int ufs_qcom_init_clks(struct ufs_qcom_priv *priv) -{ - int err; - struct udevice *dev = priv->hba->dev; - - err = clk_get_bulk(dev, &priv->clks); - if (err) - return err; - - return 0; -} static int ufs_qcom_check_hibern8(struct ufs_hba *hba) { @@ -557,10 +547,45 @@ static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_hba *hba, bool enable) static int ufs_qcom_init(struct ufs_hba *hba) { struct ufs_qcom_priv *priv = dev_get_priv(hba->dev); + struct udevice *dev = hba->dev; + struct clk clk; + u32 max_freq; + long rate; int err; priv->hba = hba; + /* Get maximum frequency for core_clk_unipro from device tree */ + max_freq = ufs_qcom_get_core_clk_unipro_max_freq(hba); + + /* Get and configure core_clk_unipro */ + err = clk_get_by_name(dev, "core_clk_unipro", &clk); + if (err) { + dev_err(dev, "Failed to get core_clk_unipro: %d\n", err); + return err; + } + + rate = clk_set_rate(&clk, max_freq); + if (rate < 0) { + dev_err(dev, "Failed to set core_clk_unipro rate to %u Hz: %ld\n", + max_freq, rate); + } + + /* Get all clocks */ + err = clk_get_bulk(dev, &priv->clks); + if (err) { + dev_err(dev, "clk_get_bulk failed: %d\n", err); + return err; + } + + /* Enable clocks */ + err = ufs_qcom_enable_clks(priv); + if (err) { + dev_err(dev, "failed to enable clocks: %d\n", err); + clk_release_bulk(&priv->clks); + return err; + } + /* setup clocks */ ufs_qcom_setup_clocks(hba, true, PRE_CHANGE); @@ -579,14 +604,7 @@ static int ufs_qcom_init(struct ufs_hba *hba) priv->hw_ver.minor, priv->hw_ver.step); - err = ufs_qcom_init_clks(priv); - if (err) { - dev_err(hba->dev, "failed to initialize clocks, err:%d\n", err); - return err; - } - ufs_qcom_advertise_quirks(hba); - ufs_qcom_setup_clocks(hba, true, POST_CHANGE); return 0; } diff --git a/dts/upstream/src/arm64/qcom/qcs615-ride.dts b/dts/upstream/src/arm64/qcom/qcs615-ride.dts index 5a24c19c415e..c2c4db527f04 100644 --- a/dts/upstream/src/arm64/qcom/qcs615-ride.dts +++ b/dts/upstream/src/arm64/qcom/qcs615-ride.dts @@ -655,14 +655,12 @@ }; &usb_1 { - status = "okay"; -}; - -&usb_1_dwc3 { dr_mode = "peripheral"; + + status = "okay"; }; -&usb_hsphy_2 { +&usb_2_hsphy { vdd-supply = <&vreg_l5a>; vdda-pll-supply = <&vreg_l12a>; vdda-phy-dpdm-supply = <&vreg_l13a>; @@ -671,11 +669,9 @@ }; &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &ufs_mem_hc { diff --git a/dts/upstream/src/arm64/qcom/talos-evk-lvds-auo,g133han01.dtso b/dts/upstream/src/arm64/qcom/talos-evk-lvds-auo,g133han01.dtso new file mode 100644 index 000000000000..8d16ce4a61d2 --- /dev/null +++ b/dts/upstream/src/arm64/qcom/talos-evk-lvds-auo,g133han01.dtso @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; +/plugin/; + +#include + +&{/} { + backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&tlmm 115 GPIO_ACTIVE_HIGH>; + default-on; + }; + + panel-lvds { + compatible = "auo,g133han01"; + power-supply = <&vreg_v3p3>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* LVDS A (Odd pixels) */ + port@0 { + reg = <0>; + dual-lvds-odd-pixels; + + lvds_panel_out_a: endpoint { + remote-endpoint = <&sn65dsi84_out_a>; + }; + }; + + /* LVDS B (Even pixels) */ + port@1 { + reg = <1>; + dual-lvds-even-pixels; + + lvds_panel_out_b: endpoint { + remote-endpoint = <&sn65dsi84_out_b>; + }; + }; + }; + }; + + vreg_v3p3: regulator-v3p3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&hdmi_connector { + status = "disabled"; +}; + +&i2c1 { + clock-frequency = <400000>; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + hdmi_bridge: bridge@3d { + reg = <0x3d>; + status = "disabled"; + }; + + lvds_bridge: bridge@2c { + compatible = "ti,sn65dsi84"; + reg = <0x2c>; + enable-gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + sn65dsi84_in: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@2 { + reg = <2>; + + sn65dsi84_out_a: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&lvds_panel_out_a>; + }; + }; + + port@3 { + reg = <3>; + + sn65dsi84_out_b: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&lvds_panel_out_b>; + }; + }; + }; + }; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l11a>; + + status = "okay"; +}; + +&mdss_dsi0_out { + remote-endpoint = <&sn65dsi84_in>; + data-lanes = <0 1 2 3>; +}; + +&tlmm { + lcd_bklt_en: lcd-bklt-en-state { + pins = "gpio115"; + function = "gpio"; + bias-disable; + }; +}; diff --git a/dts/upstream/src/arm64/qcom/talos-evk-som.dtsi b/dts/upstream/src/arm64/qcom/talos-evk-som.dtsi new file mode 100644 index 000000000000..1720f175719a --- /dev/null +++ b/dts/upstream/src/arm64/qcom/talos-evk-som.dtsi @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; + +#include +#include +#include "talos.dtsi" +#include "pm8150.dtsi" +/ { + aliases { + i2c1 = &i2c1; + i2c5 = &i2c5; + mmc0 = &sdhc_1; + serial0 = &uart0; + serial1 = &uart7; + spi6 = &spi6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + clocks { + can_osc: can-oscillator { + compatible = "fixed-clock"; + clock-frequency = <20000000>; + #clock-cells = <0>; + }; + + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + clock-frequency = <32764>; + #clock-cells = <0>; + }; + + xo_board_clk: xo-board-clk { + compatible = "fixed-clock"; + clock-frequency = <38400000>; + #clock-cells = <0>; + }; + }; + + vreg_conn_1p8: regulator-conn-1p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_1p8"; + startup-delay-us = <4000>; + enable-active-high; + gpio = <&pm8150_gpios 1 GPIO_ACTIVE_HIGH>; + }; + + vreg_conn_pa: regulator-conn-pa { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_pa"; + startup-delay-us = <4000>; + enable-active-high; + gpio = <&pm8150_gpios 6 GPIO_ACTIVE_HIGH>; + }; + + regulator-usb2-vbus { + compatible = "regulator-fixed"; + regulator-name = "USB2_VBUS"; + gpio = <&pm8150_gpios 10 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&usb2_en>; + pinctrl-names = "default"; + enable-active-high; + regulator-always-on; + }; + + vreg_v3p3_can: regulator-v3p3-can { + compatible = "regulator-fixed"; + regulator-name = "vreg-v3p3-can"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_v5p0_can: regulator-v5p0-can { + compatible = "regulator-fixed"; + regulator-name = "vreg-v5p0-can"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + }; + + wcn6855-pmu { + compatible = "qcom,wcn6855-pmu"; + + pinctrl-0 = <&bt_en_state>, <&wlan_en_state>; + pinctrl-names = "default"; + + bt-enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>; + wlan-enable-gpios = <&tlmm 84 GPIO_ACTIVE_HIGH>; + + vddio-supply = <&vreg_conn_pa>; + vddaon-supply = <&vreg_s5a>; + vddpmu-supply = <&vreg_conn_1p8>; + vddpmumx-supply = <&vreg_conn_1p8>; + vddpmucx-supply = <&vreg_conn_pa>; + vddrfa0p95-supply = <&vreg_s5a>; + vddrfa1p3-supply = <&vreg_s6a>; + vddrfa1p9-supply = <&vreg_l15a>; + vddpcie1p3-supply = <&vreg_s6a>; + vddpcie1p9-supply = <&vreg_l15a>; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p7: ldo7 { + regulator-name = "vreg_pmu_rfa_1p7"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8150-rpmh-regulators"; + qcom,pmic-id = "a"; + + vreg_s3a: smps3 { + regulator-name = "vreg_s3a"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <650000>; + regulator-initial-mode = ; + }; + + vreg_s4a: smps4 { + regulator-name = "vreg_s4a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1829000>; + regulator-initial-mode = ; + }; + + vreg_s5a: smps5 { + regulator-name = "vreg_s5a"; + regulator-min-microvolt = <1896000>; + regulator-max-microvolt = <2040000>; + regulator-initial-mode = ; + }; + + vreg_s6a: smps6 { + regulator-name = "vreg_s6a"; + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1404000>; + regulator-initial-mode = ; + }; + + vreg_l1a: ldo1 { + regulator-name = "vreg_l1a"; + regulator-min-microvolt = <488000>; + regulator-max-microvolt = <852000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l2a: ldo2 { + regulator-name = "vreg_l2a"; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <3100000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l3a: ldo3 { + regulator-name = "vreg_l3a"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1248000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l5a: ldo5 { + regulator-name = "vreg_l5a"; + regulator-min-microvolt = <875000>; + regulator-max-microvolt = <975000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l7a: ldo7 { + regulator-name = "vreg_l7a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l8a: ldo8 { + regulator-name = "vreg_l8a"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1350000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l10a: ldo10 { + regulator-name = "vreg_l10a"; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l11a: ldo11 { + regulator-name = "vreg_l11a"; + regulator-min-microvolt = <1232000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l12a: ldo12 { + regulator-name = "vreg_l12a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1890000>; + regulator-initial-mode = ; + }; + + vreg_l13a: ldo13 { + regulator-name = "vreg_l13a"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3230000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l15a: ldo15 { + regulator-name = "vreg_l15a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1904000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l16a: ldo16 { + regulator-name = "vreg_l16a"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l17a: ldo17 { + regulator-name = "vreg_l17a"; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + }; + }; +}; + +&gpi_dma0 { + status = "okay"; +}; + +&gpi_dma1 { + status = "okay"; +}; + +&i2c5 { + clock-frequency = <400000>; + status = "okay"; + + eeprom@57 { + compatible = "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; + + eeprom@5f { + compatible = "atmel,24mac602"; + reg = <0x5f>; + pagesize = <16>; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000>; + remote-endpoint = <&dp0_connector_in>; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l11a>; + status = "okay"; +}; + +&mdss_dsi0_phy { + vcca-supply = <&vreg_l5a>; + status = "okay"; +}; + +&pcie { + perst-gpios = <&tlmm 89 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 100 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&pcie_default_state>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + + status = "okay"; +}; + +&pcie_port0 { + wifi@0 { + compatible = "pci17cb,1103"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + qcom,calibration-variant = "QC_QCS615_Ride"; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pm8150_gpios { + usb2_en: usb2-en-state { + pins = "gpio10"; + function = "normal"; + output-enable; + power-source = <0>; + }; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/qcs615/adsp.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/qcs615/cdsp.mbn"; + + status = "okay"; +}; + +&sdhc_1 { + pinctrl-0 = <&sdc1_state_on>; + pinctrl-1 = <&sdc1_state_off>; + pinctrl-names = "default", "sleep"; + + bus-width = <8>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + vmmc-supply = <&vreg_l17a>; + vqmmc-supply = <&vreg_s4a>; + + non-removable; + no-sd; + no-sdio; + + status = "okay"; +}; + +&spi6 { + status = "okay"; + + can@0 { + compatible = "microchip,mcp2515"; + reg = <0>; + clocks = <&can_osc>; + interrupts-extended = <&tlmm 87 IRQ_TYPE_LEVEL_LOW>; + spi-max-frequency = <10000000>; + vdd-supply = <&vreg_v3p3_can>; + xceiver-supply = <&vreg_v5p0_can>; + }; +}; + +&tlmm { + bt_en_state: bt-en-state { + pins = "gpio85"; + function = "gpio"; + bias-pull-down; + }; + + pcie_default_state: pcie-default-state { + clkreq-pins { + pins = "gpio90"; + function = "pcie_clk_req"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-pins { + pins = "gpio89"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + wake-pins { + pins = "gpio100"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + wifi_reg_en_pins_state: wifi-reg-en-pins-state { + pins = "gpio91"; + function = "gpio"; + drive-strength = <8>; + output-high; + bias-pull-up; + }; + + wlan_en_state: wlan-en-state { + pins = "gpio84"; + function = "gpio"; + drive-strength = <16>; + bias-pull-up; + }; +}; + +&uart0 { + status = "okay"; +}; + +&uart7 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn6855-bt"; + firmware-name = "QCA6698/hpnv21", "QCA6698/hpbtfw21.tlv"; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + }; +}; + +/* + * USB0 routing and EDL mode: + * + * The USB0 controller’s HS differential pair is switched (manually) + * between the Micro-USB port for EDL/ADB and the on-board USB 3.0 hub. + * + * During EDL (Emergency Download) mode, the HS lines are explicitly + * routed to the Micro-USB port to allow the SoC to enter device mode + * for flashing. + * + * After EDL the switch is normally toggled so the HS lines stay + * connected to the hub’s Type-A downstream ports, leaving no electrical + * path to the Micro-USB connector — therefore USB0 runs host-only in + * normal runtime and device mode must not be advertised. + * + * USB0 is configured host-only in the base device tree; a separate + * device-tree overlay enables the Micro-USB peripheral configuration for + * ADB. For ADB to work during normal runtime the DIP switch SW1 must be + * manually toggled to the off position (reconnecting the HS pair to the + * Micro-USB port). + */ + +&usb_1 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_hsphy { + vdd-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + vdda-phy-dpdm-supply = <&vreg_l13a>; + + status = "okay"; +}; + +&usb_2 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_2_hsphy { + vdd-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + vdda-phy-dpdm-supply = <&vreg_l13a>; + + status = "okay"; +}; + +&usb_qmpphy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + + status = "okay"; +}; + +&usb_qmpphy_2 { + vdda-phy-supply = <&vreg_l11a>; + vdda-pll-supply = <&vreg_l5a>; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 123 GPIO_ACTIVE_LOW>; + vcc-supply = <&vreg_l17a>; + vcc-max-microamp = <600000>; + vccq2-supply = <&vreg_s4a>; + vccq2-max-microamp = <600000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + + status = "okay"; +}; + +&venus { + status = "okay"; +}; diff --git a/dts/upstream/src/arm64/qcom/talos-evk-usb1-peripheral.dtso b/dts/upstream/src/arm64/qcom/talos-evk-usb1-peripheral.dtso new file mode 100644 index 000000000000..7552ecf9d7ee --- /dev/null +++ b/dts/upstream/src/arm64/qcom/talos-evk-usb1-peripheral.dtso @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; +/plugin/; + +/* + * USB0 Peripheral Mode Overlay + * + * This overlay switches USB0 from host mode to peripheral mode + * by configuring the USB controller node. + * + * Hardware requirement: + * The DIP switch SW1 must be toggled to reconnect the USB0 HS + * differential pair to the Micro-USB connector instead of the + * on-board USB 3.0 hub. + * + * Without toggling SW1, there is no electrical path to the + * Micro-USB connector and device mode will not function. + */ + +&usb_1 { + dr_mode = "peripheral"; + + status = "okay"; +}; diff --git a/dts/upstream/src/arm64/qcom/talos-evk.dts b/dts/upstream/src/arm64/qcom/talos-evk.dts new file mode 100644 index 000000000000..af100e22beee --- /dev/null +++ b/dts/upstream/src/arm64/qcom/talos-evk.dts @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; + +#include "talos-evk-som.dtsi" + +/ { + model = "Qualcomm QCS615 IQ 615 EVK"; + compatible = "qcom,talos-evk", "qcom,qcs615", "qcom,sm6150"; + chassis-type = "embedded"; + + aliases { + mmc1 = &sdhc_2; + }; + + dp0-connector { + compatible = "dp-connector"; + label = "DP0"; + type = "full-size"; + + hpd-gpios = <&tlmm 104 GPIO_ACTIVE_HIGH>; + + port { + dp0_connector_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; + }; + }; + }; + + hdmi_connector: hdmi-out { + compatible = "hdmi-connector"; + type = "d"; + + port { + hdmi_con_out: endpoint { + remote-endpoint = <&adv7535_out>; + }; + }; + }; + + vreg_v1p8_out: regulator-v1p8-out { + compatible = "regulator-fixed"; + regulator-name = "vreg-v1p8-out"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vreg_v5p0_out>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_v3p3_out: regulator-v3p3-out { + compatible = "regulator-fixed"; + regulator-name = "vreg-v3p3-out"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vreg_v5p0_out>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_v5p0_out: regulator-v5p0-out { + compatible = "regulator-fixed"; + regulator-name = "vreg-v5p0-out"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + /* Powered by system 20V rail (USBC_VBUS_IN) */ + }; +}; + +&i2c1 { + clock-frequency = <400000>; + status = "okay"; + + hdmi_bridge: bridge@3d { + compatible = "adi,adv7535"; + reg = <0x3d>; + avdd-supply = <&vreg_v1p8_out>; + dvdd-supply = <&vreg_v1p8_out>; + pvdd-supply = <&vreg_v1p8_out>; + a2vdd-supply = <&vreg_v1p8_out>; + v3p3-supply = <&vreg_v3p3_out>; + interrupts-extended = <&tlmm 26 IRQ_TYPE_LEVEL_LOW>; + adi,dsi-lanes = <4>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + adv7535_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + adv7535_out: endpoint { + remote-endpoint = <&hdmi_con_out>; + }; + }; + }; + }; +}; + +&mdss_dsi0_out { + remote-endpoint = <&adv7535_in>; + data-lanes = <0 1 2 3>; +}; + +&pon_pwrkey { + status = "okay"; +}; + +&pon_resin { + linux,code = ; + + status = "okay"; +}; + +&sdhc_2 { + pinctrl-0 = <&sdc2_state_on>; + pinctrl-1 = <&sdc2_state_off>; + pinctrl-names = "default", "sleep"; + + bus-width = <4>; + cd-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>; + + vmmc-supply = <&vreg_l10a>; + vqmmc-supply = <&vreg_s4a>; + + status = "okay"; +}; diff --git a/dts/upstream/src/arm64/qcom/talos.dtsi b/dts/upstream/src/arm64/qcom/talos.dtsi index 75716b4a58d6..79265436c718 100644 --- a/dts/upstream/src/arm64/qcom/talos.dtsi +++ b/dts/upstream/src/arm64/qcom/talos.dtsi @@ -4417,7 +4417,7 @@ status = "disabled"; }; - usb_hsphy_2: phy@88e3000 { + usb_2_hsphy: phy@88e3000 { compatible = "qcom,qcs615-qusb2-phy"; reg = <0x0 0x088e3000 0x0 0x180>; @@ -4486,9 +4486,9 @@ status = "disabled"; }; - usb_1: usb@a6f8800 { - compatible = "qcom,qcs615-dwc3", "qcom,dwc3"; - reg = <0x0 0x0a6f8800 0x0 0x400>; + usb_1: usb@a600000 { + compatible = "qcom,qcs615-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a600000 0x0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, @@ -4507,52 +4507,44 @@ <&gcc GCC_USB30_PRIM_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH 0>, + interrupts-extended = <&intc GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>, + <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH 0>, <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>, <&pdc 9 IRQ_TYPE_EDGE_BOTH>, <&pdc 8 IRQ_TYPE_EDGE_BOTH>, <&pdc 6 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "hs_phy_irq", "dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq"; + iommus = <&apps_smmu 0x140 0x0>; + + phys = <&usb_1_hsphy>, <&usb_qmpphy>; + phy-names = "usb2-phy", "usb3-phy"; + power-domains = <&gcc USB30_PRIM_GDSC>; required-opps = <&rpmhpd_opp_nom>; resets = <&gcc GCC_USB30_PRIM_BCR>; - #address-cells = <2>; - #size-cells = <2>; - ranges; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; + snps,usb3_lpm_capable; status = "disabled"; - - usb_1_dwc3: usb@a600000 { - compatible = "snps,dwc3"; - reg = <0x0 0x0a600000 0x0 0xcd00>; - - iommus = <&apps_smmu 0x140 0x0>; - interrupts = ; - - phys = <&usb_1_hsphy>, <&usb_qmpphy>; - phy-names = "usb2-phy", "usb3-phy"; - - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - snps,dis_u2_susphy_quirk; - snps,dis_u3_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,has-lpm-erratum; - snps,hird-threshold = /bits/ 8 <0x10>; - snps,usb3_lpm_capable; - }; }; - usb_2: usb@a8f8800 { - compatible = "qcom,qcs615-dwc3", "qcom,dwc3"; - reg = <0x0 0x0a8f8800 0x0 0x400>; + usb_2: usb@a800000 { + compatible = "qcom,qcs615-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a800000 0x0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB2_SEC_AXI_CLK>, <&gcc GCC_USB20_SEC_MASTER_CLK>, @@ -4571,15 +4563,22 @@ <&gcc GCC_USB20_SEC_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 663 IRQ_TYPE_LEVEL_HIGH 0>, + interrupts-extended = <&intc GIC_SPI 664 IRQ_TYPE_LEVEL_HIGH 0>, + <&intc GIC_SPI 663 IRQ_TYPE_LEVEL_HIGH 0>, <&intc GIC_SPI 662 IRQ_TYPE_LEVEL_HIGH 0>, <&pdc 11 IRQ_TYPE_EDGE_BOTH>, <&pdc 10 IRQ_TYPE_EDGE_BOTH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "hs_phy_irq", "dp_hs_phy_irq", "dm_hs_phy_irq"; + iommus = <&apps_smmu 0xe0 0x0>; + + phys = <&usb_2_hsphy>; + phy-names = "usb2-phy"; + power-domains = <&gcc USB20_SEC_GDSC>; required-opps = <&rpmhpd_opp_nom>; @@ -4587,30 +4586,15 @@ qcom,select-utmi-as-pipe-clk; - #address-cells = <2>; - #size-cells = <2>; - ranges; - - status = "disabled"; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; - usb_2_dwc3: usb@a800000 { - compatible = "snps,dwc3"; - reg = <0x0 0x0a800000 0x0 0xcd00>; + maximum-speed = "high-speed"; - iommus = <&apps_smmu 0xe0 0x0>; - interrupts = ; - - phys = <&usb_hsphy_2>; - phy-names = "usb2-phy"; - - snps,dis_u2_susphy_quirk; - snps,dis_u3_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,has-lpm-erratum; - snps,hird-threshold = /bits/ 8 <0x10>; - - maximum-speed = "high-speed"; - }; + status = "disabled"; }; tsens0: thermal-sensor@c263000 { diff --git a/env/Kconfig b/env/Kconfig index 7abd82ab6f38..f59b2acb7e6c 100644 --- a/env/Kconfig +++ b/env/Kconfig @@ -299,16 +299,20 @@ config ENV_IS_IN_SCSI The size of the partition where the environment is stored in bytes. Must be a multiple of the partition block size. - - CONFIG_ENV_SCSI_HW_PARTITION: + The partition selection method is configured via a choice statement: - Specifies which SCSI partition the environment is stored in. If not - set, defaults to partition 0, the user area. Common values might be - 1 (first SCSI boot partition), 2 (second SCSI boot partition). Ignored - if CONFIG_ENV_SCSI_PART_UUID is set to non-empty string. + - ENV_SCSI_PART_USE_UUID: Use the partition's unique UUID to identify + the SCSI partition for environment storage. - - CONFIG_ENV_SCSI_PART_UUID: + - ENV_SCSI_PART_USE_TYPE_GUID: Use the partition type GUID to identify + the SCSI partition for environment storage. The first partition + matching the specified type GUID will be used. - UUID of the SCSI partition where the environment is stored. + - ENV_SCSI_PART_USE_HW: Use the hardware device number to identify + the SCSI device for environment storage. Specifies which SCSI + partition the environment is stored in. If not set, defaults to + partition 0, the user area. Common values might be 1 (first SCSI + boot partition), 2 (second SCSI boot partition). config ENV_RANGE @@ -780,10 +784,52 @@ config ENV_MMC_USE_DT The 2 defines CONFIG_ENV_OFFSET, CONFIG_ENV_OFFSET_REDUND are not used as fallback. +choice + prompt "SCSI partition selection method" + depends on ENV_IS_IN_SCSI + default ENV_SCSI_PART_USE_UUID + help + Select the method to identify the SCSI partition for environment storage. + +config ENV_SCSI_PART_USE_UUID + bool "Use partition UUID" + help + Use the partition's unique UUID to identify the SCSI partition + for environment storage. + +config ENV_SCSI_PART_USE_TYPE_GUID + bool "Use partition type GUID" + select PARTITION_TYPE_GUID + help + Use the partition type GUID to identify the SCSI partition + for environment storage. The first partition matching the + specified type GUID will be used. + +config ENV_SCSI_PART_USE_HW + bool "Use hardware partition number" + help + Use the hardware device number to identify the SCSI device + for environment storage. + +endchoice + +config ENV_SCSI_PART_UUID + string "SCSI partition UUID for saving environment" + depends on ENV_SCSI_PART_USE_UUID + help + UUID of the SCSI partition that you want to store the environment in. + +config ENV_SCSI_PART_TYPE_GUID + string "SCSI partition type GUID for saving environment" + depends on ENV_SCSI_PART_USE_TYPE_GUID + help + Type GUID of the SCSI partition to store the environment in. + Uses the first partition matching this type GUID. + config ENV_SCSI_HW_PARTITION string "SCSI hardware partition number" - depends on ENV_IS_IN_SCSI - default 0 + depends on ENV_SCSI_PART_USE_HW + default "0" help SCSI hardware partition device number on the platform where the environment is stored. Note that this is not related to any software @@ -791,12 +837,6 @@ config ENV_SCSI_HW_PARTITION partition 0 or the first boot partition, which is 1 or some other defined partition. -config ENV_SCSI_PART_UUID - string "SCSI partition UUID for saving environment" - depends on ENV_IS_IN_SCSI - help - UUID of the SCSI partition that you want to store the environment in. - config ENV_USE_DEFAULT_ENV_TEXT_FILE bool "Create default environment from file" depends on !COMPILE_TEST diff --git a/env/scsi.c b/env/scsi.c index 91a6c430302c..b170f4ee0c7f 100644 --- a/env/scsi.c +++ b/env/scsi.c @@ -41,14 +41,17 @@ static inline struct env_scsi_info *env_scsi_get_part(void) is_scsi_scanned = true; } - if (CONFIG_ENV_SCSI_PART_UUID[0] == '\0') { - if (blk_get_device_part_str("scsi", CONFIG_ENV_SCSI_HW_PARTITION, - &ep->blk, &ep->part, true)) - return NULL; - } else { - if (scsi_get_blk_by_uuid(CONFIG_ENV_SCSI_PART_UUID, &ep->blk, &ep->part)) - return NULL; - } +#if defined(CONFIG_ENV_SCSI_PART_USE_TYPE_GUID) + if (scsi_get_blk_by_type_guid(CONFIG_ENV_SCSI_PART_TYPE_GUID, &ep->blk, &ep->part)) + return NULL; +#elif defined(CONFIG_ENV_SCSI_PART_USE_UUID) + if (scsi_get_blk_by_uuid(CONFIG_ENV_SCSI_PART_UUID, &ep->blk, &ep->part)) + return NULL; +#elif defined(CONFIG_ENV_SCSI_PART_USE_HW) + if (blk_get_device_part_str("scsi", CONFIG_ENV_SCSI_HW_PARTITION, + &ep->blk, &ep->part, true)) + return NULL; +#endif ep->count = CONFIG_ENV_SIZE / ep->part.blksz; @@ -95,20 +98,24 @@ static int env_scsi_load(void) int ret; if (!ep) { - if (CONFIG_ENV_SCSI_PART_UUID[0] == '\0') - env_set_default("SCSI partition " CONFIG_ENV_SCSI_HW_PARTITION " not found", 0); - else - env_set_default(CONFIG_ENV_SCSI_PART_UUID " partition not found", 0); - +#if defined(CONFIG_ENV_SCSI_PART_USE_TYPE_GUID) + env_set_default("partition type " CONFIG_ENV_SCSI_PART_TYPE_GUID " not found", 0); +#elif defined(CONFIG_ENV_SCSI_PART_USE_UUID) + env_set_default(CONFIG_ENV_SCSI_PART_UUID " partition not found", 0); +#elif defined(CONFIG_ENV_SCSI_PART_USE_HW) + env_set_default("SCSI partition " CONFIG_ENV_SCSI_HW_PARTITION " not found", 0); +#endif return -ENOENT; } if (blk_dread(ep->blk, ep->part.start, ep->count, &envbuf) != ep->count) { - if (CONFIG_ENV_SCSI_PART_UUID[0] == '\0') - env_set_default("SCSI partition " CONFIG_ENV_SCSI_HW_PARTITION " read failed", 0); - else - env_set_default(CONFIG_ENV_SCSI_PART_UUID " partition read failed", 0); - +#if defined(CONFIG_ENV_SCSI_PART_USE_TYPE_GUID) + env_set_default("partition type " CONFIG_ENV_SCSI_PART_TYPE_GUID " read failed", 0); +#elif defined(CONFIG_ENV_SCSI_PART_USE_UUID) + env_set_default(CONFIG_ENV_SCSI_PART_UUID " partition read failed", 0); +#elif defined(CONFIG_ENV_SCSI_PART_USE_HW) + env_set_default("SCSI partition " CONFIG_ENV_SCSI_HW_PARTITION " read failed", 0); +#endif return -EIO; } diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 36b5d87c304f..fe0aae2720ca 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -134,7 +134,6 @@ enum uclass_id { UCLASS_SCSI, /* SCSI device */ UCLASS_SERIAL, /* Serial UART */ UCLASS_SIMPLE_BUS, /* Bus with child devices */ - UCLASS_SMEM, /* Shared memory interface */ UCLASS_SOC, /* SOC Device */ UCLASS_SOUND, /* Playing simple sounds */ UCLASS_SPI, /* SPI bus */ diff --git a/include/fastboot-internal.h b/include/fastboot-internal.h index 610d4f914140..35683cdb642d 100644 --- a/include/fastboot-internal.h +++ b/include/fastboot-internal.h @@ -40,4 +40,34 @@ void fastboot_getvar_all(char *response); */ void fastboot_getvar(char *cmd_parameter, char *response); +#if CONFIG_IS_ENABLED(EFI_PARTITION) +/** + * fastboot_flash_gpt_partition_table() - Flash GPT partition table + * + * @interface: Block interface name (e.g., "mmc", "scsi") + * @device: Device number + * @download_buffer: Buffer containing GPT data + * @response: Pointer to fastboot response buffer + */ +void fastboot_flash_gpt_partition_table(const char *interface, + int device, + void *download_buffer, + char *response); +#endif + +#if CONFIG_IS_ENABLED(DOS_PARTITION) +/** + * fastboot_flash_mbr_partition_table() - Flash MBR partition table + * + * @interface: Block interface name (e.g., "mmc", "scsi") + * @device: Device number + * @download_buffer: Buffer containing MBR data + * @response: Pointer to fastboot response buffer + */ +void fastboot_flash_mbr_partition_table(const char *interface, + int device, + void *download_buffer, + char *response); +#endif + #endif diff --git a/include/nvmem.h b/include/nvmem.h index e6a8a98828b3..c3d845c3a7eb 100644 --- a/include/nvmem.h +++ b/include/nvmem.h @@ -26,11 +26,15 @@ * @nvmem: The backing storage device * @offset: The offset of the cell from the start of @nvmem * @size: The size of the cell, in bytes + * @bit_offset: Bit offset within the cell (0 for byte-level access) + * @nbits: Number of bits to use (0 for byte-level access) */ struct nvmem_cell { struct udevice *nvmem; unsigned int offset; size_t size; + unsigned int bit_offset; + unsigned int nbits; }; struct udevice; @@ -39,13 +43,27 @@ struct udevice; /** * nvmem_cell_read() - Read the value of an nvmem cell - * @cell: The nvmem cell to read + * @cell: The nvmem cell to read, containing: + * - @cell->offset: Byte offset within the NVMEM device + * - @cell->size: Size of the cell in bytes + * - @cell->nbits: Number of bits to extract (0 = read entire cell) + * - @cell->bit_offset: Starting bit position for extraction * @buf: The buffer to read into * @size: The size of @buf * + * For cells with bit fields (@cell->nbits > 0), this function: + * - Reads the raw bytes from @cell->offset in hardware + * - Extracts the bit field using @cell->bit_offset and @cell->nbits + * - Returns the extracted value in @buf + * - Requires @size == sizeof(u32) and @cell->size <= sizeof(u32) + * + * For cells without bit fields (@cell->nbits == 0): + * - Reads raw bytes directly from @cell->offset + * - Requires @size == @cell->size + * * Return: * * 0 on success - * * -EINVAL if @buf is not the same size as @cell. + * * -EINVAL if @size doesn't match requirements * * -ENOSYS if CONFIG_NVMEM is disabled * * A negative error if there was a problem reading the underlying storage */ @@ -53,13 +71,27 @@ int nvmem_cell_read(struct nvmem_cell *cell, void *buf, size_t size); /** * nvmem_cell_write() - Write a value to an nvmem cell - * @cell: The nvmem cell to write + * @cell: The nvmem cell to write, containing: + * - @cell->offset: Byte offset within the NVMEM device + * - @cell->size: Size of the cell in bytes + * - @cell->nbits: Number of bits to write (0 = write entire cell) + * - @cell->bit_offset: Starting bit position for insertion * @buf: The buffer to write from * @size: The size of @buf * + * For cells with bit fields (@cell->nbits > 0), this function: + * - Performs Read-Modify-Write to preserve other bits at @cell->offset + * - Masks and shifts the value to @cell->bit_offset position + * - Merges with existing bits outside the @cell->nbits field + * - Requires @size == sizeof(u32) and @cell->size <= sizeof(u32) + * + * For cells without bit fields (@cell->nbits == 0): + * - Writes raw bytes directly to @cell->offset + * - Requires @size == @cell->size + * * Return: * * 0 on success - * * -EINVAL if @buf is not the same size as @cell + * * -EINVAL if @size doesn't match requirements * * -ENOSYS if @cell is read-only, or if CONFIG_NVMEM is disabled * * A negative error if there was a problem writing the underlying storage */ diff --git a/include/part.h b/include/part.h index 15daacd7faaa..32614bd085bc 100644 --- a/include/part.h +++ b/include/part.h @@ -327,6 +327,20 @@ int part_get_info_by_name(struct blk_desc *desc, const char *name, int part_get_info_by_uuid(struct blk_desc *desc, const char *uuid, struct disk_partition *info); +/** + * part_get_info_by_type_guid() - Search for a partition by type GUID + * among all available registered partitions + * + * @desc: block device descriptor + * @type_guid: the specified partition type GUID + * @info: the disk partition info + * + * Return: the partition number on match (starting on 1), -ENOENT on no match, + * otherwise error + */ +int part_get_info_by_type_guid(struct blk_desc *desc, const char *type_guid, + struct disk_partition *info); + /** * part_get_info_by_dev_and_name_or_num() - Get partition info from dev number * and part name, or dev number and @@ -404,6 +418,13 @@ static inline int part_get_info_by_uuid(struct blk_desc *desc, const char *uuid, return -ENOENT; } +static inline int part_get_info_by_type_guid(struct blk_desc *desc, + const char *type_guid, + struct disk_partition *info) +{ + return -ENOENT; +} + static inline int part_get_info_by_dev_and_name_or_num(const char *dev_iface, const char *dev_part_str, diff --git a/include/scsi.h b/include/scsi.h index 2520a8b8fe63..83aaf0a70f63 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -366,6 +366,17 @@ int scsi_scan_dev(struct udevice *dev, bool verbose); int scsi_get_blk_by_uuid(const char *uuid, struct blk_desc **blk_desc_ptr, struct disk_partition *part_info_ptr); +/** + * scsi_get_blk_by_type_guid() - Provides SCSI partition information by type GUID. + * + * @type_guid: Type GUID of the partition for fetching its info + * @blk_desc_ptr: Provides the blk descriptor + * @part_info_ptr: Provides partition info + * Return: 0 if OK, -ve on error + */ +int scsi_get_blk_by_type_guid(const char *type_guid, struct blk_desc **blk_desc_ptr, + struct disk_partition *part_info_ptr); + #define SCSI_IDENTIFY 0xC0 /* not used */ /* Hardware errors */ diff --git a/include/smem.h b/include/smem.h deleted file mode 100644 index b19c534ebc43..000000000000 --- a/include/smem.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * The shared memory system is an allocate-only heap structure that - * consists of one of more memory areas that can be accessed by the processors - * in the SoC. - * - * Allocation can be done globally for all processors or to an individual processor. - * This is controlled by the @host parameter. - * - * Allocation and management of heap can be implemented in various ways, - * The @item parameter should be used as an index/hash to the memory region. - * - * Copyright (c) 2018 Ramon Fried - */ - -#ifndef _smemh_ -#define _smemh_ - -/* struct smem_ops: Operations for the SMEM uclass */ -struct smem_ops { - /** - * alloc() - allocate space for a smem item - * - * @host: remote processor id, or -1 for all processors. - * @item: smem item handle - * @size: number of bytes to be allocated - * @return 0 if OK, -ve on error - */ - int (*alloc)(unsigned int host, - unsigned int item, size_t size); - - /** - * get() - Resolve ptr of size of a smem item - * - * @host: the remote processor, of -1 for all processors. - * @item: smem item handle - * @size: pointer to be filled out with the size of the item - * @return pointer on success, NULL on error - */ - void *(*get)(unsigned int host, - unsigned int item, size_t *size); - - /** - * get_free_space() - Get free space in smem in bytes - * - * @host: the remote processor identifying a partition, or -1 - * for all processors. - * @return free space, -ve on error - */ - int (*get_free_space)(unsigned int host); -}; - -#define smem_get_ops(dev) ((struct smem_ops *)(dev)->driver->ops) - -/** - * smem_alloc() - allocate space for a smem item - * @host: remote processor id, or -1 - * @item: smem item handle - * @size: number of bytes to be allocated - * Return: 0 if OK, -ve on error - * - * Allocate space for a given smem item of size @size, given that the item is - * not yet allocated. - */ -int smem_alloc(struct udevice *dev, unsigned int host, unsigned int item, size_t size); - -/** - * smem_get() - resolve ptr of size of a smem item - * @host: the remote processor, or -1 for all processors. - * @item: smem item handle - * @size: pointer to be filled out with size of the item - * Return: pointer on success, NULL on error - * - * Looks up smem item and returns pointer to it. Size of smem - * item is returned in @size. - */ -void *smem_get(struct udevice *dev, unsigned int host, unsigned int item, size_t *size); - -/** - * smem_get_free_space() - retrieve amount of free space in a partition - * @host: the remote processor identifying a partition, or -1 - * for all processors. - * Return: size in bytes, -ve on error - * - * To be used by smem clients as a quick way to determine if any new - * allocations has been made. - */ -int smem_get_free_space(struct udevice *dev, unsigned int host); - -#endif /* _smem_h_ */ diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h new file mode 100644 index 000000000000..586432412eb8 --- /dev/null +++ b/include/soc/qcom/smem.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __QCOM_SMEM_H__ +#define __QCOM_SMEM_H__ + +#include +#include + +#define QCOM_SMEM_HOST_ANY -1 + +#if defined(CONFIG_QCOM_SMEM) +int qcom_smem_init(void); +int qcom_socinfo_init(void); + +bool qcom_smem_is_available(void); +int qcom_smem_alloc(unsigned host, unsigned item, size_t size); +void *qcom_smem_get(unsigned host, unsigned item, size_t *size); + +int qcom_smem_get_free_space(unsigned host); +#else +static int qcom_smem_init(void) { return -ENOSYS; } + +static bool qcom_smem_is_available(void) { return false; } +int qcom_smem_alloc(unsigned host, unsigned item, size_t size) +{ + return -ENOSYS; +} + +void *qcom_smem_get(unsigned host, unsigned item, size_t *size) +{ + return ERR_PTR(-ENOSYS); +} + +int qcom_smem_get_free_space(unsigned host); +#endif + +#endif diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h new file mode 100644 index 000000000000..608950443eee --- /dev/null +++ b/include/soc/qcom/socinfo.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QCOM_SOCINFO_H__ +#define __QCOM_SOCINFO_H__ + +#include + +/* + * SMEM item id, used to acquire handles to respective + * SMEM region. + */ +#define SMEM_HW_SW_BUILD_ID 137 + +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 +#define SMEM_SOCINFO_CHIP_ID_LENGTH 32 + +/* + * SoC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. + */ +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff) +#define SOCINFO_MINOR(ver) ((ver) & 0xffff) +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff)) + +/* Socinfo SMEM item structure */ +struct socinfo { + __le32 fmt; + __le32 id; + __le32 ver; + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH]; + /* Version 2 */ + __le32 raw_id; + __le32 raw_ver; + /* Version 3 */ + __le32 hw_plat; + /* Version 4 */ + __le32 plat_ver; + /* Version 5 */ + __le32 accessory_chip; + /* Version 6 */ + __le32 hw_plat_subtype; + /* Version 7 */ + __le32 pmic_model; + __le32 pmic_die_rev; + /* Version 8 */ + __le32 pmic_model_1; + __le32 pmic_die_rev_1; + __le32 pmic_model_2; + __le32 pmic_die_rev_2; + /* Version 9 */ + __le32 foundry_id; + /* Version 10 */ + __le32 serial_num; + /* Version 11 */ + __le32 num_pmics; + __le32 pmic_array_offset; + /* Version 12 */ + __le32 chip_family; + __le32 raw_device_family; + __le32 raw_device_num; + /* Version 13 */ + __le32 nproduct_id; + char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH]; + /* Version 14 */ + __le32 num_clusters; + __le32 ncluster_array_offset; + __le32 num_subset_parts; + __le32 nsubset_parts_array_offset; + /* Version 15 */ + __le32 nmodem_supported; + /* Version 16 */ + __le32 feature_code; + __le32 pcode; + __le32 npartnamemap_offset; + __le32 nnum_partname_mapping; + /* Version 17 */ + __le32 oem_variant; + /* Version 18 */ + __le32 num_kvps; + __le32 kvps_offset; + /* Version 19 */ + __le32 num_func_clusters; + __le32 boot_cluster; + __le32 boot_core; +}; + +/* Internal feature codes */ +enum qcom_socinfo_feature_code { + /* External feature codes */ + SOCINFO_FC_UNKNOWN = 0x0, + SOCINFO_FC_AA, + SOCINFO_FC_AB, + SOCINFO_FC_AC, + SOCINFO_FC_AD, + SOCINFO_FC_AE, + SOCINFO_FC_AF, + SOCINFO_FC_AG, + SOCINFO_FC_AH, +}; + +/* Internal feature codes */ +/* Valid values: 0 <= n <= 0xf */ +#define SOCINFO_FC_Yn(n) (0xf1 + (n)) +#define SOCINFO_FC_INT_MAX SOCINFO_FC_Yn(0xf) + +/* Product codes */ +#define SOCINFO_PC_UNKNOWN 0 +#define SOCINFO_PCn(n) ((n) + 1) +#define SOCINFO_PC_RESERVE (BIT(31) - 1) + +#endif diff --git a/test/dm/Makefile b/test/dm/Makefile index d69b0e08d66a..5995e06f5094 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_CMD_MUX) += mux-cmd.o obj-$(CONFIG_MULTIPLEXER) += mux-emul.o obj-$(CONFIG_MUX_MMIO) += mux-mmio.o obj-y += fdtdec.o +obj-$(CONFIG_NVMEM) += nvmem.o obj-$(CONFIG_MTD_RAW_NAND) += nand.o obj-$(CONFIG_UT_DM) += nop.o obj-y += ofnode.o @@ -113,7 +114,6 @@ obj-$(CONFIG_DM_SERIAL) += serial.o obj-$(CONFIG_DM_SPI_FLASH) += sf.o obj-$(CONFIG_SIMPLE_BUS) += simple-bus.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o -obj-$(CONFIG_SMEM) += smem.o obj-$(CONFIG_SOC_DEVICE) += soc.o obj-$(CONFIG_SOUND) += sound.o obj-$(CONFIG_DM_SPI) += spi.o diff --git a/test/dm/nvmem.c b/test/dm/nvmem.c new file mode 100644 index 000000000000..dd8d0151d2b9 --- /dev/null +++ b/test/dm/nvmem.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for NVMEM bit field support + */ + +#include +#include +#include +#include +#include +#include + +static int nvmem_test_write_raw(struct udevice *dev, uint offset, + const void *buf, uint size) +{ + return i2c_eeprom_write(dev, offset, buf, size); +} + +static int nvmem_test_read_raw(struct udevice *dev, uint offset, + void *buf, uint size) +{ + return i2c_eeprom_read(dev, offset, buf, size); +} + +/* Test NVMEM bit field operations */ +static int dm_test_nvmem_bitfield(struct unit_test_state *uts) +{ + struct udevice *nvmem_dev; + struct nvmem_cell cell; + u32 value; + u8 hw_value_u8; + u32 hw_value_u32; + + ut_assertok(uclass_get_device_by_name(UCLASS_I2C_EEPROM, + "nvmem-test@50", &nvmem_dev)); + + cell.nvmem = nvmem_dev; + + /* Test reg = <0x0 0x1>; bits = <1 7>: */ + cell.offset = 0x0; + cell.size = 1; + cell.bit_offset = 1; + cell.nbits = 7; + hw_value_u8 = 0x01; + ut_assertok(nvmem_test_write_raw(nvmem_dev, cell.offset, &hw_value_u8, 1)); + value = 0x7f; + ut_assertok(nvmem_cell_write(&cell, &value, sizeof(value))); + value = 0; + ut_assertok(nvmem_cell_read(&cell, &value, sizeof(value))); + ut_asserteq(0x7f, value); + ut_assertok(nvmem_test_read_raw(nvmem_dev, cell.offset, &hw_value_u8, 1)); + ut_asserteq(0xff, hw_value_u8); + + /* Test reg = <0x18 0x4>; bits = <4 12>: Spanning byte boundary */ + cell.offset = 0x18; + cell.size = 4; + cell.bit_offset = 4; + cell.nbits = 12; + hw_value_u32 = 0x0000000f; + ut_assertok(nvmem_test_write_raw(nvmem_dev, cell.offset, (u8 *)&hw_value_u32, 4)); + value = 0xfff; + ut_assertok(nvmem_cell_write(&cell, &value, sizeof(value))); + value = 0; + ut_assertok(nvmem_cell_read(&cell, &value, sizeof(value))); + ut_asserteq(0xfff, value); + ut_assertok(nvmem_test_read_raw(nvmem_dev, cell.offset, (u8 *)&hw_value_u32, 4)); + ut_asserteq(0x0000ffff, hw_value_u32); + + /* Test reg = <0x9 0x4>: Full 4-byte access without bit field */ + cell.offset = 0x9; + cell.bit_offset = 0; + cell.nbits = 0; + value = 0x12345678; + ut_assertok(nvmem_cell_write(&cell, &value, sizeof(value))); + value = 0; + ut_assertok(nvmem_cell_read(&cell, &value, sizeof(value))); + ut_asserteq(0x12345678, value); + + /* Test reg = <0xc 0x4>; bits = <16 16>: Upper 2 bytes */ + cell.offset = 0xc; + cell.bit_offset = 16; + cell.nbits = 16; + hw_value_u32 = 0x0000ffff; + ut_assertok(nvmem_test_write_raw(nvmem_dev, cell.offset, (u8 *)&hw_value_u32, 4)); + value = 0xffff; + ut_assertok(nvmem_cell_write(&cell, &value, sizeof(value))); + value = 0; + ut_assertok(nvmem_cell_read(&cell, &value, sizeof(value))); + ut_asserteq(0xffff, value); + ut_assertok(nvmem_test_read_raw(nvmem_dev, cell.offset, (u8 *)&hw_value_u32, 4)); + ut_asserteq(0xffffffff, hw_value_u32); + + return 0; +} +DM_TEST(dm_test_nvmem_bitfield, + UTF_PROBE_TEST | UTF_SCAN_FDT | UTF_FLAT_TREE); + +/* Test NVMEM error handling for invalid configurations */ +static int dm_test_nvmem_bitfield_errors(struct unit_test_state *uts) +{ + struct udevice *nvmem_dev; + struct nvmem_cell cell; + u32 value; + int ret; + + ut_assertok(uclass_get_device_by_name(UCLASS_I2C_EEPROM, + "nvmem-test@50", &nvmem_dev)); + + /* Test bit field exceeding cell size */ + cell.nvmem = nvmem_dev; + cell.offset = 0xd; + cell.size = 1; + cell.bit_offset = 0; + cell.nbits = 9; + + value = 0xff; + ret = nvmem_cell_write(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + ret = nvmem_cell_read(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + /* Test bit field exceeding 32 bits */ + cell.size = 4; + cell.bit_offset = 0; + cell.nbits = 33; + + ret = nvmem_cell_write(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + ret = nvmem_cell_read(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + /* Test invalid bit_offset + nbits */ + cell.size = 1; + cell.bit_offset = 7; + cell.nbits = 2; + + ret = nvmem_cell_write(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + ret = nvmem_cell_read(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + /* Test nbits=0 requires buffer size == cell size */ + cell.size = 1; + cell.bit_offset = 0; + cell.nbits = 0; + + value = 0xff; + ret = nvmem_cell_write(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + ret = nvmem_cell_read(&cell, &value, sizeof(value)); + ut_asserteq(-EINVAL, ret); + + return 0; +} +DM_TEST(dm_test_nvmem_bitfield_errors, + UTF_PROBE_TEST | UTF_SCAN_FDT | UTF_FLAT_TREE); diff --git a/test/dm/part.c b/test/dm/part.c index caae23bd4aad..7a427d3d41dd 100644 --- a/test/dm/part.c +++ b/test/dm/part.c @@ -8,9 +8,13 @@ #include #include #include +#include +#include #include #include +DECLARE_GLOBAL_DATA_PTR; + static int do_test(struct unit_test_state *uts, int expected, const char *part_str, bool whole) { @@ -195,3 +199,48 @@ static int dm_test_part_get_info_by_type(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_part_get_info_by_type, UTF_SCAN_PDATA | UTF_SCAN_FDT); + +static int dm_test_part_get_info_by_type_guid(struct unit_test_state *uts) +{ + struct udevice *dev, *blk_dev; + struct blk_desc *desc; + struct disk_partition info; + ofnode root, node; + int partnum; + + if (!IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) + return -EAGAIN; + + /* Bind the mmc5 node (ChromeOS image with type GUIDs) */ + root = oftree_root(oftree_default()); + node = ofnode_find_subnode(root, "mmc5"); + ut_assert(ofnode_valid(node)); + ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false)); + + /* Get the MMC device (probes it), then walk MMC -> BLK parent link */ + ut_assertok(uclass_get_device_by_seq(UCLASS_MMC, 5, &dev)); + ut_assertok(blk_get_from_parent(dev, &blk_dev)); + desc = dev_get_uclass_plat(blk_dev); + ut_assert(desc); + + /* + * Test: look up the first ChromeOS kernel partition by type GUID. + * In the ChromeOS image KERN_A is the first partition carrying the + * ChromeOS kernel type GUID (fe3a2a5d-...). This is partition 2. + */ + partnum = part_get_info_by_type_guid(desc, + "FE3A2A5D-4F32-41A7-B725-ACCC3285A309", + &info); + ut_asserteq(2, partnum); + ut_asserteq_str("KERN_A", info.name); + + /* Test: non-existent GUID must return -ENOENT */ + ut_asserteq(-ENOENT, + part_get_info_by_type_guid(desc, + "00000000-0000-0000-0000-000000000000", + &info)); + + return 0; +} + +DM_TEST(dm_test_part_get_info_by_type_guid, UTF_SCAN_PDATA | UTF_SCAN_FDT); diff --git a/test/dm/smem.c b/test/dm/smem.c deleted file mode 100644 index 89e74cccc574..000000000000 --- a/test/dm/smem.c +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2018 Ramon Fried - */ - -#include -#include -#include -#include -#include - -/* Basic test of the smem uclass */ -static int dm_test_smem_base(struct unit_test_state *uts) -{ - struct udevice *dev; - size_t size; - - ut_assertok(uclass_get_device(UCLASS_SMEM, 0, &dev)); - ut_assertnonnull(dev); - ut_assertok(smem_alloc(dev, -1, 0, 16)); - ut_asserteq(0, smem_get_free_space(dev, -1)); - ut_assertnull(smem_get(dev, -1, 0, &size)); - - return 0; -} -DM_TEST(dm_test_smem_base, UTF_SCAN_PDATA | UTF_SCAN_FDT); diff --git a/tools/efivar.py b/tools/efivar.py index 67729fa8505c..d248cd868ba5 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -79,6 +79,50 @@ def __init__(self, size, attrs, time, guid, name, data): def calc_crc32(buf): return zlib.crc32(buf) & 0xffffffff +def strip_auth_descriptor(data, attrs): + """ + Strip the EFI auth descriptor from authenticated variable data. + + This is used during efivar.py-based pre-seeding of ubootefi.var to + match U-Boot SetVariable() behavior, where the authentication header + is consumed during validation and only the payload is stored. + + For variables with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS: + - Input format: [EFI_VARIABLE_AUTHENTICATION_2 | payload] + - Stored format: [payload only] + + Auth header size calculation (aligned with U-Boot efi_variable_authenticate()): + auth_size = sizeof(EFI_TIME) + WIN_CERTIFICATE.dwLength + + Only the payload portion is retained after stripping the header. + """ + if not (attrs & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS): + return data + if not data: + return data + + efi = EfiStruct() + if len(data) < efi.var_time_size: + return data + + offset = efi.var_time_size + if len(data) < offset + efi.var_win_cert_size: + return data + + try: + cert_hdr = struct.unpack_from(efi.var_win_cert_fmt, data, offset) + dwLength = cert_hdr[0] + except struct.error: + return data + + auth_size = efi.var_time_size + dwLength + if auth_size <= 0 or auth_size > len(data): + return data + + if len(data) <= auth_size: + return b'' + return data[auth_size:] + class EfiVariableStore: def __init__(self, infile): self.infile = infile @@ -172,8 +216,12 @@ def set_var(self, guid, name, data, size, attrs): break offs = loffs + if data and (attrs & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS): + data = strip_auth_descriptor(data, attrs) + size = len(data) if data else 0 + tsec = int(time.time()) if attrs & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS else 0 - nd = name.encode('utf_16_le') + b"\x00\x00" + data + nd = name.encode('utf_16_le') + b"\x00\x00" + (data if data else b'') # U-Boot variable format requires the name + data blob to be 8-byte aligned pad = ((len(nd) + 7) & ~7) - len(nd) nd += bytes([0] * pad)