diff --git a/arch/arm/dts/lemans-evk-u-boot.dtsi b/arch/arm/dts/lemans-evk-u-boot.dtsi new file mode 100644 index 000000000000..cdd3d32f61a5 --- /dev/null +++ b/arch/arm/dts/lemans-evk-u-boot.dtsi @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2026, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/ { + /* Will be removed when bootloader updates later */ + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x3ee00000>, + <0x0 0xc0000000 0x0 0x0fd00000>, + <0xD 0x00000000 0x2 0x54100000>, + <0xA 0x80000000 0x1 0x80000000>, + <0x9 0x00000000 0x1 0x80000000>, + <0x1 0x00000000 0x3 0x00000000>, + <0x0 0xd0000000 0x0 0x01900000>, + <0x0 0xd3500000 0x0 0x2cb00000>; + }; +}; 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/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile index 343e825c6fdd..d1ebe75c4f4f 100644 --- a/arch/arm/mach-snapdragon/Makefile +++ b/arch/arm/mach-snapdragon/Makefile @@ -4,4 +4,5 @@ obj-y += board.o obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o +obj-$(CONFIG_OF_LIVE) += qcom_fixup_ddrinfo.o obj-$(CONFIG_OF_LIVE) += of_fixup.o diff --git a/arch/arm/mach-snapdragon/of_fixup.c b/arch/arm/mach-snapdragon/of_fixup.c index 374e6262db43..c1a0a92834b4 100644 --- a/arch/arm/mach-snapdragon/of_fixup.c +++ b/arch/arm/mach-snapdragon/of_fixup.c @@ -30,6 +30,7 @@ #include #include #include +#include "qcom_fixup_handlers.h" /** * find_ssphy_node() - Find the super-speed PHY node referenced by DWC3 @@ -283,5 +284,86 @@ EVENT_SPY_FULL(EVT_OF_LIVE_BUILT, qcom_of_fixup_nodes); int ft_board_setup(void __maybe_unused *blob, struct bd_info __maybe_unused *bd) { + struct fdt_header *fdt = blob; + + ddrinfo_fixup_handler(fdt); + return 0; } + +int fixup_dt_node(void *fdt_ptr, int node_offset, + const char *property_name, + void *property_value, + enum fdt_fixup_type type) +{ + int ret; + + if ((!fdt_ptr || node_offset < 0) || + (!property_value && type != ADD_SUBNODE)) + return -1; + + switch (type) { + case APPEND_PROP_U32: + fdt_set_totalsize(fdt_ptr, + (fdt_totalsize(fdt_ptr) + + sizeof(struct fdt_property) + + strlen(property_name) + 3 + + sizeof(u32))); + ret = fdt_appendprop_u32(fdt_ptr, node_offset, + property_name, + *(u32 *)property_value); + break; + case APPEND_PROP_U64: + fdt_set_totalsize(fdt_ptr, + (fdt_totalsize(fdt_ptr) + + sizeof(struct fdt_property) + + strlen(property_name) + 3 + + sizeof(u64))); + ret = fdt_appendprop_u64(fdt_ptr, node_offset, + property_name, + *(u64 *)property_value); + break; + case SET_PROP_U32: + fdt_set_totalsize(fdt_ptr, + (fdt_totalsize(fdt_ptr) + + sizeof(struct fdt_property) + + strlen(property_name) + 3 + + sizeof(u32))); + ret = fdt_setprop_u32(fdt_ptr, node_offset, + property_name, + *(u32 *)property_value); + break; + case SET_PROP_U64: + fdt_set_totalsize(fdt_ptr, + (fdt_totalsize(fdt_ptr) + + sizeof(struct fdt_property) + + strlen(property_name) + 3 + + sizeof(u64))); + ret = fdt_setprop_u64(fdt_ptr, node_offset, + property_name, + *(u64 *)property_value); + break; + case SET_PROP_STRING: + fdt_set_totalsize(fdt_ptr, + (fdt_totalsize(fdt_ptr) + + sizeof(struct fdt_property) + + strlen(property_name) + 3 + + strlen((char *)property_value))); + ret = fdt_setprop_string(fdt_ptr, node_offset, + property_name, + (char *)property_value); + break; + case ADD_SUBNODE: + fdt_set_totalsize(fdt_ptr, + (fdt_totalsize(fdt_ptr) + + sizeof(struct fdt_property) + + strlen(property_name) + 3)); + ret = fdt_add_subnode(fdt_ptr, node_offset, + property_name); + break; + default: + ret = -1; + } + + return ret; +} diff --git a/arch/arm/mach-snapdragon/qcom_fixup_ddrinfo.c b/arch/arm/mach-snapdragon/qcom_fixup_ddrinfo.c new file mode 100644 index 000000000000..59a0e3c5d82a --- /dev/null +++ b/arch/arm/mach-snapdragon/qcom_fixup_ddrinfo.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DDRInfo Fixup + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include +#include +#include +#include +#include "qcom_fixup_handlers.h" +#include "qcom-priv.h" + +/* With the new SMEM architecture, SMEM IDs need to be defined in individual + * driver files + */ +#define SMEM_ID_DDRINFO 0x25B // 603 + +struct ddr_details_entry { + u8 manufacturer_id; + u8 device_type; +}; + +/** + * get_ddr_details() - Retrieves the DDR details entry from the SMEM. + * @ddr_detail: The DDR details entry to retrieve. + * + * This function retrieves the DDR details entry from the SMEM and prints it + * out. + * + * Return: 0 on success, -1 on failure + */ +static int get_ddr_details(struct ddr_details_entry *ddr_detail) +{ + void *ddr_table_ptr; + struct udevice *dev; + size_t size; + + if (uclass_get_device(UCLASS_SMEM, 0, &dev) != 0) + return log_msg_ret("Error: uclass_get_device\n", -1); + ddr_table_ptr = smem_get(dev, 0, SMEM_ID_DDRINFO, &size); + + if (!ddr_table_ptr) + return log_msg_ret("Error: invalid DDR Entry\n", -1); + memcpy((void *)ddr_detail, ddr_table_ptr, sizeof(struct ddr_details_entry)); + + return 0; +} + +/** + * set_mem_reg_node() - Sets the ram partition info to memory node + * @fdt_ptr: Pointer to the device tree + * @path_offset: Offset to the memory node + * @start_addr: Start address of the mem partition + * @mem_size: Size of the mem partition + * + * This function is responsible for setting the reg property of memory node + * in the device tree + * + * Return: 0 on success, -1 on failure + */ +static int set_mem_reg_node(struct fdt_header *fdt_ptr, u32 path_offset, + u64 start_addr, u64 mem_size) +{ + int ret; + + log_debug("Mem info start addr: %llx ,size %llx\n", start_addr, mem_size); + ret = fixup_dt_node(fdt_ptr, path_offset, "reg", + (void *)(&start_addr), APPEND_PROP_U64); + if (ret) + return log_msg_ret("Failed to append start_addr details in Reg prop\n", -1); + + ret = fixup_dt_node(fdt_ptr, path_offset, "reg", + (void *)(&mem_size), APPEND_PROP_U64); + if (ret) + return log_msg_ret("Failed to append mem_size details in Reg prop\n", -1); + + return 0; +} + +/** + * set_ram_part_info() - Gets the ram partition info from smem and sets + * it to memory node + * @fdt_ptr: Pointer to the device tree + * @path_offset: Offset to the memory node + * + * This function is responsible for obtaining ram partition information info + * from smem and sets it to the device tree + * tree. + * + * Return: 0 on success, -1 on failure + */ +static int set_ram_part_info(struct fdt_header *fdt_ptr, u32 path_offset) +{ + int ret = 0, res, part; + struct usable_ram_partition_table *rpt; + struct ram_partition_entry *rpe; + + rpt = qcom_get_ram_partitions(); + if (!rpt) + return -1; + rpe = &rpt->ram_part_entry[0]; + + for (part = 0; part < rpt->num_partitions; part++, rpe++) + if (rpe->partition_category == RAM_PARTITION_SDRAM && + rpe->partition_type == RAM_PARTITION_SYS_MEMORY) { + res = set_mem_reg_node(fdt_ptr, path_offset, + rpe->start_address, + rpe->available_length); + if (res) { + log_err("Failed to set Mem info start addr: %llx ,size %llx\n", + rpe->start_address, rpe->available_length); + ret = -1; + } + } + + return ret; +} + +/** + * ddrinfo_fixup_handler() - DDRInfo Fixup handler function + * @fdt_ptr: Pointer to the device tree + * + * This function is responsible for updating the DDR information in the device + * tree. + */ +void ddrinfo_fixup_handler(struct fdt_header *fdt_ptr) +{ + u32 path_offset, ret; + u64 prop_value; + struct ddr_details_entry ddr_details; + + ret = get_ddr_details(&ddr_details); + if (ret) { + log_err("Error getting DDR details\n"); + return; + } + + path_offset = fdt_path_offset(fdt_ptr, "/memory"); + if (path_offset < 0) { + log_err("Error getting memory offset: %d\n", path_offset); + return; + } + + prop_value = (u64)ddr_details.device_type; + ret = fixup_dt_node(fdt_ptr, path_offset, "ddr_device_type", + (void *)(&prop_value), APPEND_PROP_U64); + if (ret) + log_err("Failed to append DDR device type data : %d\n", ret); + + ret = fdt_delprop(fdt_ptr, path_offset, "reg"); + if (!ret) { + ret = set_ram_part_info(fdt_ptr, path_offset); + if (ret) + log_err("set_ram_part_info failed"); + } +} + +/* End of File */ diff --git a/arch/arm/mach-snapdragon/qcom_fixup_handlers.h b/arch/arm/mach-snapdragon/qcom_fixup_handlers.h new file mode 100644 index 000000000000..60c5cf6f0996 --- /dev/null +++ b/arch/arm/mach-snapdragon/qcom_fixup_handlers.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * SUBSET Parts Fixup: A tool for fixing up subset parts in a system + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + */ + +#include + +enum fdt_fixup_type { + APPEND_PROP_U32 = 0, + APPEND_PROP_U64 = 1, + SET_PROP_U32 = 2, + SET_PROP_U64 = 3, + SET_PROP_STRING = 4, + ADD_SUBNODE = 5, +}; + +/** + * ddrinfo_fixup_handler() - DDRInfo Fixup handler function + * @fdt_ptr: Pointer to the device tree + * + * This function is responsible for updating the DDR information in + * the device tree. + * + * Return: None + */ +void ddrinfo_fixup_handler(struct fdt_header *fdt_ptr); + +/** + * fixup_dt_node() - Exports a property to the firmware DT node. + * @fdt_ptr: The firmware DT node to update. + * @node_offset: The offset in the DT node where the property should be set. + * @property_name: The name of the property to set. + * @property_value: The value of the property to set. + * @type: Fixup type + * This function sets a property in the firmware DT node with the given name and + * value. + * + * Return: 0 on success, negative on failure. + */ +int fixup_dt_node(void *fdt_ptr, int node_offset, + const char *property_name, + void *property_value, + enum fdt_fixup_type type); diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index ac92ebf1afd7..383b5ccf6df4 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -961,6 +961,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"; }; @@ -978,6 +983,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 { diff --git a/board/qualcomm/default.env b/board/qualcomm/default.env index dbf6f4e72600..429d71d57f3c 100644 --- a/board/qualcomm/default.env +++ b/board/qualcomm/default.env @@ -1,7 +1,7 @@ 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 bootmenu_0=Boot first available device=run do_boot diff --git a/configs/qcm6490_defconfig b/configs/qcm6490_defconfig index b088367f86c0..c06b0f9548d7 100644 --- a/configs/qcm6490_defconfig +++ b/configs/qcm6490_defconfig @@ -16,3 +16,8 @@ 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" diff --git a/configs/qcom_defconfig b/configs/qcom_defconfig index 3c8982dafd93..e7dcb71672b2 100644 --- a/configs/qcom_defconfig +++ b/configs/qcom_defconfig @@ -79,8 +79,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 +99,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 @@ -118,6 +120,7 @@ CONFIG_PINCONF=y CONFIG_PINCTRL_QCOM_GENERIC=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 +133,9 @@ CONFIG_SOC_QCOM=y CONFIG_QCOM_COMMAND_DB=y CONFIG_QCOM_RPMH_POWER_DOMAIN=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 69% rename from configs/qcom_qcs9100_defconfig rename to configs/qcom_lemans_defconfig index 082106157bbc..c88e1c9242a3 100644 --- a/configs/qcom_qcs9100_defconfig +++ b/configs/qcom_lemans_defconfig @@ -9,8 +9,10 @@ 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 diff --git a/configs/qcom_qcs615_defconfig b/configs/qcom_qcs615_defconfig index 27666a8129d7..021d9d672846 100644 --- a/configs/qcom_qcs615_defconfig +++ b/configs/qcom_qcs615_defconfig @@ -22,3 +22,8 @@ CONFIG_REMAKE_ELF=y CONFIG_TEXT_BASE=0x9fc00000 CONFIG_FASTBOOT_BUF_ADDR=0xa1600000 + +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" 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/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 66f3cf2de4f0..13fd4ae8a7d7 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -189,6 +189,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; @@ -252,6 +253,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/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/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/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/test/dm/Makefile b/test/dm/Makefile index 771b703b737d..c60bb077e10a 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 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);