Add support for: Reading DDR training data from eMMC during Shikra boot flow and sending it to QcLIB via interface table.
Saving DDR training data in eMMC when QcLIB performs DDR training and sends the data via interface table. Signed-off-by: KR Sowmiya <[email protected]> --- arch/arm/mach-snapdragon/Kconfig | 67 +++++++++ arch/arm/mach-snapdragon/board_spl.c | 128 ++++++++++++++++++ arch/arm/mach-snapdragon/include/mach/qclib.h | 3 + arch/arm/mach-snapdragon/include/mach/spl.h | 2 + board/qualcomm/shikra/spl-shikra.c | 35 ++++- 5 files changed, 228 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig index 9e23d6d8948..64976c275f6 100644 --- a/arch/arm/mach-snapdragon/Kconfig +++ b/arch/arm/mach-snapdragon/Kconfig @@ -93,6 +93,73 @@ config QCOM_XBL_SEC_ELF The image is copied to the build output as xbl_sec.elf and embedded by the platform SPL linker script as a Qualcomm XBL-sec segment. +config QCOM_DDR_TRAINING_SUPPORTED + bool "Enable DDR training data save/restore support in SPL" + depends on SPL && (SPL_PARTITIONS || SPL_PARTITION_TYPE_GUID) + help + Enable support for saving and restoring QCLIB's DDR training data + to/from a dedicated storage partition during the SPL boot flow. + +if QCOM_DDR_TRAINING_SUPPORTED + +choice + prompt "DDR training partition lookup method" + default QCOM_DDR_TRAINING_USE_PARTITION_NAME + help + Locate the DDR training-data partition on the boot medium either + using partition name or partition type GUID. + +config QCOM_DDR_TRAINING_USE_PARTITION_NAME + bool "Look up the DDR training partition by GPT partition name" + depends on SPL_PARTITIONS + help + Locate the DDR training-data partition on the boot medium by its + GPT partition name (CONFIG_QCOM_DDR_TRAINING_PARTITION_NAME). + +config QCOM_DDR_TRAINING_USE_PARTITION_GUID + bool "Look up the DDR training partition by GPT partition type GUID" + depends on SPL_PARTITION_TYPE_GUID + help + Locate the DDR training-data partition on the boot medium by its + GPT partition type GUID (CONFIG_QCOM_DDR_TRAINING_PARTITION_GUID). + +endchoice + +config QCOM_DDR_TRAINING_PARTITION_NAME + string "DDR training GPT partition name" + depends on QCOM_DDR_TRAINING_USE_PARTITION_NAME + default "ddr" + help + Name of the GPT partition on the boot medium used to persist DDR + training data across boots. Locates the DDR training-data + partition by GPT partition name. + +config QCOM_DDR_TRAINING_PARTITION_GUID + string "DDR training GPT partition type GUID" + depends on QCOM_DDR_TRAINING_USE_PARTITION_GUID + default "20a0c19c-286a-42fa-9ce7-f64c3226a794" + help + GPT partition type GUID identifying the partition on the boot + medium used to persist DDR training data across boots. Locates + the DDR training-data partition by GPT partition type GUID. + +config QCOM_SPL_BOOTIMEM_DDR_TRAINING_BASE + hex "IMEM address to stage DDR training data at" + default 0x0c368000 + help + Address within BOOTIMEM where DDR training data is staged, to be + consumed/produced by QCLIB via the interface table. + +config QCOM_SPL_BOOTIMEM_DDR_TRAINING_SIZE + hex "Maximum number of bytes to stage for DDR training data" + default 0x8000 + help + Maximum number of bytes read from/written to the DDR training + partition and staged at + CONFIG_QCOM_SPL_BOOTIMEM_DDR_TRAINING_BASE. + +endif # QCOM_DDR_TRAINING_SUPPORTED + choice prompt "Qualcomm boot0.h workaround" optional diff --git a/arch/arm/mach-snapdragon/board_spl.c b/arch/arm/mach-snapdragon/board_spl.c index 31a06b1e002..62db86fe628 100644 --- a/arch/arm/mach-snapdragon/board_spl.c +++ b/arch/arm/mach-snapdragon/board_spl.c @@ -77,6 +77,134 @@ int qcom_spl_get_boot_blk_desc(struct blk_desc **descp) return 0; } +#if (IS_ENABLED(CONFIG_QCOM_DDR_TRAINING_SUPPORTED)) +/** + * qcom_spl_ddr_training_restore() - Restore the DDR training data from the boot + * medium. + * + * This function reads the boot medium for DDR training data which gets saved + * in IMEM location. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_ddr_training_restore(void) +{ + struct blk_desc *desc; + struct disk_partition info; + lbaint_t blocks; + int ret; + void *blk_buf = NULL; + + ret = qcom_spl_get_boot_blk_desc(&desc); + if (ret) { + pr_debug("DDR training: boot device not supported (%d)\n", ret); + return ret; + } + +#if defined(CONFIG_QCOM_DDR_TRAINING_USE_PARTITION_NAME) + ret = part_get_info_by_name(desc, CONFIG_QCOM_DDR_TRAINING_PARTITION_NAME, + &info); +#elif defined(CONFIG_QCOM_DDR_TRAINING_USE_PARTITION_GUID) + ret = part_get_info_by_type_guid(desc, CONFIG_QCOM_DDR_TRAINING_PARTITION_GUID, + &info); +#else + ret = -EINVAL; +#endif + if (ret < 0) { + pr_debug("DDR training: partition not found (%d)\n", ret); + return ret; + } + + blocks = DIV_ROUND_UP(CONFIG_QCOM_SPL_BOOTIMEM_DDR_TRAINING_SIZE, + info.blksz); + if (blocks > info.size) + blocks = info.size; + + { + blk_buf = malloc_cache_aligned(blocks * info.blksz); + + if (!blk_buf) { + return -ENOMEM; + } + + size_t read_size = blocks * info.blksz; + size_t copy_size = min_t(size_t, CONFIG_QCOM_SPL_BOOTIMEM_DDR_TRAINING_SIZE, + read_size); + + if (blk_dread(desc, info.start, blocks, + (void *)blk_buf) != blocks) { + pr_err("DDR training: failed to read partition '%s'\n", + CONFIG_QCOM_DDR_TRAINING_PARTITION_NAME); + free(blk_buf); + return -EIO; + } + + memcpy((u8 *)CONFIG_QCOM_SPL_BOOTIMEM_DDR_TRAINING_BASE, blk_buf, copy_size); + free(blk_buf); + } + + return 0; +} + +/** + * qcom_spl_ddr_training_save() - Persist DDR training data QCLIB asked to + * save. + * @address: Address of the blob to save (as updated by QCLIB on return). + * @size: Size of the blob to save, in bytes. + * + * Return: 0 on success, or a negative error code on failure. + */ +int qcom_spl_ddr_training_save(u64 address, u32 size) +{ + struct blk_desc *desc; + struct disk_partition info; + lbaint_t blocks; + int ret; + + if (!address || !size) { + pr_err("DDR training: QCLIB requested save with no data\n"); + return -EINVAL; + } + + ret = qcom_spl_get_boot_blk_desc(&desc); + if (ret) { + pr_err("DDR training: boot device not supported for save (%d)\n", + ret); + return ret; + } + +#if defined(CONFIG_QCOM_DDR_TRAINING_USE_PARTITION_NAME) + ret = part_get_info_by_name(desc, CONFIG_QCOM_DDR_TRAINING_PARTITION_NAME, + &info); +#elif defined(CONFIG_QCOM_DDR_TRAINING_USE_PARTITION_GUID) + ret = part_get_info_by_type_guid(desc, CONFIG_QCOM_DDR_TRAINING_PARTITION_GUID, + &info); +#else + ret = -EINVAL; +#endif + if (ret < 0) { + pr_err("DDR training: partition not found for save (%d)\n", ret); + return ret; + } + + blocks = DIV_ROUND_UP(size, info.blksz); + if (blocks > info.size) { + pr_err("DDR training: data (%u bytes) too large for partition\n", + size); + return -ENOSPC; + } + + if (blk_dwrite(desc, info.start, blocks, + (void *)(uintptr_t)address) != blocks) { + pr_err("DDR training: failed to write partition '%s'\n", + CONFIG_QCOM_DDR_TRAINING_PARTITION_NAME); + return -EIO; + } + + return 0; +} +#endif /*CONFIG_QCOM_DDR_TRAINING_SUPPORTED) */ + #if IS_ENABLED(CONFIG_SPL_SMEM) /** * qcom_spl_populate_smem() - Populate shared memory (SMEM) information. diff --git a/arch/arm/mach-snapdragon/include/mach/qclib.h b/arch/arm/mach-snapdragon/include/mach/qclib.h index 10865b03ea1..a643f3240c5 100644 --- a/arch/arm/mach-snapdragon/include/mach/qclib.h +++ b/arch/arm/mach-snapdragon/include/mach/qclib.h @@ -15,6 +15,9 @@ /* interface_table.global_attributes bits */ #define QCLIB_GA_ENABLE_UART_LOGGING BIT(0) +/* interface_table per entry attribute bit */ +#define QCLIB_ATTR_SAVE_TO_STORAGE BIT(0) + /** * struct interface_table_entry - Meta data for blobs in QCLIB interface * @entry_name: Name of the data blob (e.g., "dcb_settings"). diff --git a/arch/arm/mach-snapdragon/include/mach/spl.h b/arch/arm/mach-snapdragon/include/mach/spl.h index a32a85c48fd..868e19cfcd7 100644 --- a/arch/arm/mach-snapdragon/include/mach/spl.h +++ b/arch/arm/mach-snapdragon/include/mach/spl.h @@ -12,5 +12,7 @@ void qcom_spl_malloc_init_f(void); int qcom_spl_loader_pre_ddr(u8 boot_device); void qcom_spl_error_handler(void *arg); int qcom_spl_get_boot_blk_desc(struct blk_desc **descp); +int qcom_spl_ddr_training_restore(void); +int qcom_spl_ddr_training_save(u64 address, u32 size); #endif /* __SPL_H__ */ diff --git a/board/qualcomm/shikra/spl-shikra.c b/board/qualcomm/shikra/spl-shikra.c index c2a5d2d692b..76b69932062 100644 --- a/board/qualcomm/shikra/spl-shikra.c +++ b/board/qualcomm/shikra/spl-shikra.c @@ -119,8 +119,6 @@ int arm_reserve_mmu(void) #define SHIKRA_DCB_LP5 "dcb_lp5" #define SHIKRA_ECC "ecc" -#define SHIKRA_DDR_TRAINING_DATA_ADDR 0x0c368000 -#define SHIKRA_DDR_TRAINING_DATA_SIZE 0x8000 #define SHIKRA_PMIC_COCOS_SIZE 0xbc00 #define SHIKRA_PMIC_WAILUA_SIZE 0x6000 #define SHIKRA_DCB_LP4_SIZE 0x4800 @@ -357,11 +355,14 @@ int qcom_spl_soc_qclib_override(struct interface_table *table, return ret; } - ret = qclib_add_iftbl_entry(table, SHIKRA_DDR_TRAINING_DATA, - SHIKRA_DDR_TRAINING_DATA_ADDR, - SHIKRA_DDR_TRAINING_DATA_SIZE); - if (ret) - return ret; +#if IS_ENABLED(CONFIG_QCOM_DDR_TRAINING_SUPPORTED) + ret = qclib_add_iftbl_entry(table, SHIKRA_DDR_TRAINING_DATA, + CONFIG_QCOM_SPL_BOOTIMEM_DDR_TRAINING_BASE, + CONFIG_QCOM_SPL_BOOTIMEM_DDR_TRAINING_SIZE); + if (ret) + return ret; +#endif + ret = qclib_add_iftbl_entry(table, SHIKRA_DDR_INFORMATION, 0, 0); if (ret) return ret; @@ -383,7 +384,11 @@ int qcom_spl_soc_pre_qclib_routine(void) { qcom_spl_soc_shrm_reset(); + if (IS_ENABLED(CONFIG_QCOM_DDR_TRAINING_SUPPORTED)) + return qcom_spl_ddr_training_restore(); + return 0; + } void qcom_spl_soc_rpm_reset(void) @@ -417,6 +422,22 @@ int qcom_spl_soc_post_qclib_routine(struct interface_table *table) return ret; qclib_set_ddr_sr_exit_address(entry.address); + + if (IS_ENABLED(CONFIG_QCOM_DDR_TRAINING_SUPPORTED)) { + ret = qcom_spl_get_iftbl_entry_by_name(table, SHIKRA_DDR_TRAINING_DATA, + &entry); + if (ret) + return ret; + + if (entry.attributes & QCLIB_ATTR_SAVE_TO_STORAGE) { + ret = qcom_spl_ddr_training_save(entry.address, entry.size); + if (ret) { + pr_err("DDR training: save failed (%d)\n", ret); + return ret; + } + } + } + return 0; } -- 2.43.0
