From e316aa5858b2acc3cfd5f2fc1d263f32e21d09c3 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 28 Jan 2021 20:42:33 +0900 Subject: [PATCH 01/35] board: warp: add power_max77696_init() function Add power_max77696_init() function. Since warp doesn't support DM, the keeping its code in board file is better than maintainig the file of driver. Signed-off-by: Jaehoon Chung --- board/warp/warp.c | 68 +++++++++++++++++++++++++++++++++++++++++- include/configs/warp.h | 1 - 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/board/warp/warp.c b/board/warp/warp.c index 0f1d038fab..c03b648850 100644 --- a/board/warp/warp.c +++ b/board/warp/warp.c @@ -26,7 +26,6 @@ #include #include #include -#include DECLARE_GLOBAL_DATA_PTR; @@ -45,6 +44,53 @@ DECLARE_GLOBAL_DATA_PTR; PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ PAD_CTL_ODE | PAD_CTL_SRE_FAST) +#define CONFIG_POWER_MAX77696_I2C_ADDR 0x3C + +enum { + L01_CNFG1 = 0x43, + L01_CNFG2, + L02_CNFG1, + L02_CNFG2, + L03_CNFG1, + L03_CNFG2, + L04_CNFG1, + L04_CNFG2, + L05_CNFG1, + L05_CNFG2, + L06_CNFG1, + L06_CNFG2, + L07_CNFG1, + L07_CNFG2, + L08_CNFG1, + L08_CNFG2, + L09_CNFG1, + L09_CNFG2, + L10_CNFG1, + L10_CNFG2, + LDO_INT1, + LDO_INT2, + LDO_INT1M, + LDO_INT2M, + LDO_CNFG3, + SW1_CNTRL, + SW2_CNTRL, + SW3_CNTRL, + SW4_CNTRL, + EPDCNFG, + EPDINTS, + EPDINT, + EPDINTM, + EPDVCOM, + EPDVEE, + EPDVNEG, + EPDVPOS, + EPDVDDH, + EPDSEQ, + EPDOKINTS, + CID = 0x9c, + PMIC_NUM_OF_REGS, +}; + int dram_init(void) { gd->ram_size = imx_ddr_size(); @@ -114,6 +160,26 @@ struct i2c_pads_info i2c_pad_info1 = { }, }; +static int power_max77696_init(unsigned char bus) +{ + static const char name[] = "MAX77696"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = CONFIG_POWER_MAX77696_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} + int power_init_board(void) { struct pmic *p; diff --git a/include/configs/warp.h b/include/configs/warp.h index f17eea117f..0f97804eb2 100644 --- a/include/configs/warp.h +++ b/include/configs/warp.h @@ -63,7 +63,6 @@ /* PMIC */ #define CONFIG_POWER #define CONFIG_POWER_I2C -#define CONFIG_POWER_MAX77696 #define CONFIG_EXTRA_ENV_SETTINGS \ "script=boot.scr\0" \ From 67696abf1f84f104d29fd979612adec5022a89f3 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 28 Jan 2021 20:42:34 +0900 Subject: [PATCH 02/35] power: pmic: remove pmic_max77696.c file Remove pmic_max77696.c file. The maintaining pmic_max77696.c file is useless. Signed-off-by: Jaehoon Chung --- drivers/power/pmic/Makefile | 1 - drivers/power/pmic/pmic_max77696.c | 31 ---------------- include/power/max77696_pmic.h | 59 ------------------------------ 3 files changed, 91 deletions(-) delete mode 100644 drivers/power/pmic/pmic_max77696.c delete mode 100644 include/power/max77696_pmic.h diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 7b4c0f02c6..89099fde57 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o -obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o obj-$(CONFIG_POWER_PCA9450) += pmic_pca9450.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o diff --git a/drivers/power/pmic/pmic_max77696.c b/drivers/power/pmic/pmic_max77696.c deleted file mode 100644 index f3a73d6405..0000000000 --- a/drivers/power/pmic/pmic_max77696.c +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2015 Freescale Semiconductor, Inc. - * Fabio Estevam - */ - -#include -#include -#include -#include -#include - -int power_max77696_init(unsigned char bus) -{ - static const char name[] = "MAX77696"; - struct pmic *p = pmic_alloc(); - - if (!p) { - printf("%s: POWER allocation error!\n", __func__); - return -ENOMEM; - } - - p->name = name; - p->interface = PMIC_I2C; - p->number_of_regs = PMIC_NUM_OF_REGS; - p->hw.i2c.addr = CONFIG_POWER_MAX77696_I2C_ADDR; - p->hw.i2c.tx_num = 1; - p->bus = bus; - - return 0; -} diff --git a/include/power/max77696_pmic.h b/include/power/max77696_pmic.h deleted file mode 100644 index 69bb7da349..0000000000 --- a/include/power/max77696_pmic.h +++ /dev/null @@ -1,59 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 Freescale Semiconductor, Inc. - * Fabio Estevam - */ - -#ifndef __MAX77696_PMIC_H__ -#define __MAX77696_PMIC_H__ - -#define CONFIG_POWER_MAX77696_I2C_ADDR 0x3C - -enum { - L01_CNFG1 = 0x43, - L01_CNFG2, - L02_CNFG1, - L02_CNFG2, - L03_CNFG1, - L03_CNFG2, - L04_CNFG1, - L04_CNFG2, - L05_CNFG1, - L05_CNFG2, - L06_CNFG1, - L06_CNFG2, - L07_CNFG1, - L07_CNFG2, - L08_CNFG1, - L08_CNFG2, - L09_CNFG1, - L09_CNFG2, - L10_CNFG1, - L10_CNFG2, - LDO_INT1, - LDO_INT2, - LDO_INT1M, - LDO_INT2M, - LDO_CNFG3, - SW1_CNTRL, - SW2_CNTRL, - SW3_CNTRL, - SW4_CNTRL, - EPDCNFG, - EPDINTS, - EPDINT, - EPDINTM, - EPDVCOM, - EPDVEE, - EPDVNEG, - EPDVPOS, - EPDVDDH, - EPDSEQ, - EPDOKINTS, - CID = 0x9c, - PMIC_NUM_OF_REGS, -}; - -int power_max77696_init(unsigned char bus); - -#endif From a2535243e0114a48a4225a38819aa7d9fdc3d02c Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Mon, 8 Feb 2021 13:54:31 +0100 Subject: [PATCH 03/35] lib: optee: migration optee_copy_fdt_nodes for OF_LIVE support The optee_copy_fdt_nodes is only used to copy op-tee nodes of U-Boot device tree (from gd->fdt_blob when OF_LIVE is not activated) to external device tree but it is not compatible with OF_LIVE. This patch migrates all used function fdt_ functions to read node on old_blob to ofnode functions, compatible with OF_LIVE and remove this parameter "old_blob". The generated "device tree" is checked on stm32mp platform with OF_LIVE activated. Signed-off-by: Patrick Delaunay --- common/image-fdt.c | 2 +- include/tee/optee.h | 4 ++-- lib/optee/optee.c | 45 ++++++++++++++++++--------------------------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/common/image-fdt.c b/common/image-fdt.c index a287b66392..d50e1ba3fe 100644 --- a/common/image-fdt.c +++ b/common/image-fdt.c @@ -562,7 +562,7 @@ int image_setup_libfdt(bootm_headers_t *images, void *blob, goto err; } - fdt_ret = optee_copy_fdt_nodes(gd->fdt_blob, blob); + fdt_ret = optee_copy_fdt_nodes(blob); if (fdt_ret) { printf("ERROR: transfer of optee nodes to new fdt failed: %s\n", fdt_strerror(fdt_ret)); diff --git a/include/tee/optee.h b/include/tee/optee.h index affa937da0..ebdfe5e98d 100644 --- a/include/tee/optee.h +++ b/include/tee/optee.h @@ -71,9 +71,9 @@ static inline int optee_verify_bootm_image(unsigned long image_addr, #endif #if defined(CONFIG_OPTEE) && defined(CONFIG_OF_LIBFDT) -int optee_copy_fdt_nodes(const void *old_blob, void *new_blob); +int optee_copy_fdt_nodes(void *new_blob); #else -static inline int optee_copy_fdt_nodes(const void *old_blob, void *new_blob) +static inline int optee_copy_fdt_nodes(void *new_blob) { return 0; } diff --git a/lib/optee/optee.c b/lib/optee/optee.c index 4dcf6f9309..672690dc53 100644 --- a/lib/optee/optee.c +++ b/lib/optee/optee.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -70,17 +72,11 @@ error: } #if defined(CONFIG_OF_LIBFDT) -static int optee_copy_firmware_node(const void *old_blob, void *fdt_blob) +static int optee_copy_firmware_node(ofnode node, void *fdt_blob) { - int old_offs, offs, ret, len; + int offs, ret, len; const void *prop; - old_offs = fdt_path_offset(old_blob, "/firmware/optee"); - if (old_offs < 0) { - debug("Original OP-TEE Device Tree node not found"); - return old_offs; - } - offs = fdt_path_offset(fdt_blob, "/firmware"); if (offs < 0) { offs = fdt_path_offset(fdt_blob, "/"); @@ -97,7 +93,7 @@ static int optee_copy_firmware_node(const void *old_blob, void *fdt_blob) return offs; /* copy the compatible property */ - prop = fdt_getprop(old_blob, old_offs, "compatible", &len); + prop = ofnode_get_property(node, "compatible", &len); if (!prop) { debug("missing OP-TEE compatible property"); return -EINVAL; @@ -108,7 +104,7 @@ static int optee_copy_firmware_node(const void *old_blob, void *fdt_blob) return ret; /* copy the method property */ - prop = fdt_getprop(old_blob, old_offs, "method", &len); + prop = ofnode_get_property(node, "method", &len); if (!prop) { debug("missing OP-TEE method property"); return -EINVAL; @@ -121,19 +117,18 @@ static int optee_copy_firmware_node(const void *old_blob, void *fdt_blob) return 0; } -int optee_copy_fdt_nodes(const void *old_blob, void *new_blob) +int optee_copy_fdt_nodes(void *new_blob) { - int nodeoffset, subnode, ret; - struct fdt_resource res; - - if (fdt_check_header(old_blob)) - return -EINVAL; + ofnode node, subnode; + int ret; + struct resource res; if (fdt_check_header(new_blob)) return -EINVAL; /* only proceed if there is an /firmware/optee node */ - if (fdt_path_offset(old_blob, "/firmware/optee") < 0) { + node = ofnode_path("/firmware/optee"); + if (!ofnode_valid(node)) { debug("No OP-TEE firmware node in old fdt, nothing to do"); return 0; } @@ -148,20 +143,17 @@ int optee_copy_fdt_nodes(const void *old_blob, void *new_blob) return 0; } - ret = optee_copy_firmware_node(old_blob, new_blob); + ret = optee_copy_firmware_node(node, new_blob); if (ret < 0) { printf("Failed to add OP-TEE firmware node\n"); return ret; } /* optee inserts its memory regions as reserved-memory nodes */ - nodeoffset = fdt_subnode_offset(old_blob, 0, "reserved-memory"); - if (nodeoffset >= 0) { - for (subnode = fdt_first_subnode(old_blob, nodeoffset); - subnode >= 0; - subnode = fdt_next_subnode(old_blob, subnode)) { - const char *name = fdt_get_name(old_blob, - subnode, NULL); + node = ofnode_path("/reserved-memory"); + if (ofnode_valid(node)) { + ofnode_for_each_subnode(subnode, node) { + const char *name = ofnode_get_name(subnode); if (!name) return -EINVAL; @@ -170,8 +162,7 @@ int optee_copy_fdt_nodes(const void *old_blob, void *new_blob) continue; /* check if this subnode has a reg property */ - ret = fdt_get_resource(old_blob, subnode, "reg", 0, - &res); + ret = ofnode_read_resource(subnode, 0, &res); if (!ret) { struct fdt_memory carveout = { .start = res.start, From ba139783110a1a6aee1b60d091744a26ecad4806 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 17 Feb 2021 13:50:40 +1030 Subject: [PATCH 04/35] hw_sha: Fix coding style errors Checkpatch complains about: ERROR: "foo * bar" should be "foo *bar" and CHECK: Alignment should match open parenthesis Signed-off-by: Joel Stanley --- include/hw_sha.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/hw_sha.h b/include/hw_sha.h index 991e496a3c..15b1a1c798 100644 --- a/include/hw_sha.h +++ b/include/hw_sha.h @@ -18,8 +18,8 @@ * should allocate at least 32 bytes at pOut in advance. * @param chunk_size chunk size for sha256 */ -void hw_sha256(const uchar * in_addr, uint buflen, - uchar * out_addr, uint chunk_size); +void hw_sha256(const uchar *in_addr, uint buflen, uchar *out_addr, + uint chunk_size); /** * Computes hash value of input pbuf using h/w acceleration @@ -31,8 +31,8 @@ void hw_sha256(const uchar * in_addr, uint buflen, * should allocate at least 32 bytes at pOut in advance. * @param chunk_size chunk_size for sha1 */ -void hw_sha1(const uchar * in_addr, uint buflen, - uchar * out_addr, uint chunk_size); +void hw_sha1(const uchar *in_addr, uint buflen, uchar *out_addr, + uint chunk_size); /* * Create the context for sha progressive hashing using h/w acceleration @@ -56,7 +56,7 @@ int hw_sha_init(struct hash_algo *algo, void **ctxp); * @return 0 if ok, -ve on error */ int hw_sha_update(struct hash_algo *algo, void *ctx, const void *buf, - unsigned int size, int is_last); + unsigned int size, int is_last); /* * Copy sha hash result at destination location @@ -70,6 +70,6 @@ int hw_sha_update(struct hash_algo *algo, void *ctx, const void *buf, * @return 0 if ok, -ve on error */ int hw_sha_finish(struct hash_algo *algo, void *ctx, void *dest_buf, - int size); + int size); #endif From a479f103dc1c801d0c6fe90bbaf99ae1700a84dc Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 17 Feb 2021 13:50:42 +1030 Subject: [PATCH 05/35] hash: Allow for SHA512 hardware implementations Similar to support for SHA1 and SHA256, allow the use of hardware hashing engine by enabling the algorithm and setting CONFIG_SHA_HW_ACCEL / CONFIG_SHA_PROG_HW_ACCEL. Signed-off-by: Joel Stanley --- common/hash.c | 24 ++++++++++++++++++++++-- include/hw_sha.h | 26 ++++++++++++++++++++++++++ lib/Kconfig | 15 +++++++-------- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/common/hash.c b/common/hash.c index fc64002f73..10dff7ddb0 100644 --- a/common/hash.c +++ b/common/hash.c @@ -97,7 +97,7 @@ static int hash_finish_sha256(struct hash_algo *algo, void *ctx, void } #endif -#if defined(CONFIG_SHA384) +#if defined(CONFIG_SHA384) && !defined(CONFIG_SHA_PROG_HW_ACCEL) static int hash_init_sha384(struct hash_algo *algo, void **ctxp) { sha512_context *ctx = malloc(sizeof(sha512_context)); @@ -125,7 +125,7 @@ static int hash_finish_sha384(struct hash_algo *algo, void *ctx, void } #endif -#if defined(CONFIG_SHA512) +#if defined(CONFIG_SHA512) && !defined(CONFIG_SHA_PROG_HW_ACCEL) static int hash_init_sha512(struct hash_algo *algo, void **ctxp) { sha512_context *ctx = malloc(sizeof(sha512_context)); @@ -260,10 +260,20 @@ static struct hash_algo hash_algo[] = { .name = "sha384", .digest_size = SHA384_SUM_LEN, .chunk_size = CHUNKSZ_SHA384, +#ifdef CONFIG_SHA_HW_ACCEL + .hash_func_ws = hw_sha384, +#else .hash_func_ws = sha384_csum_wd, +#endif +#ifdef CONFIG_SHA_PROG_HW_ACCEL + .hash_init = hw_sha_init, + .hash_update = hw_sha_update, + .hash_finish = hw_sha_finish, +#else .hash_init = hash_init_sha384, .hash_update = hash_update_sha384, .hash_finish = hash_finish_sha384, +#endif }, #endif #ifdef CONFIG_SHA512 @@ -271,10 +281,20 @@ static struct hash_algo hash_algo[] = { .name = "sha512", .digest_size = SHA512_SUM_LEN, .chunk_size = CHUNKSZ_SHA512, +#ifdef CONFIG_SHA_HW_ACCEL + .hash_func_ws = hw_sha512, +#else .hash_func_ws = sha512_csum_wd, +#endif +#ifdef CONFIG_SHA_PROG_HW_ACCEL + .hash_init = hw_sha_init, + .hash_update = hw_sha_update, + .hash_finish = hw_sha_finish, +#else .hash_init = hash_init_sha512, .hash_update = hash_update_sha512, .hash_finish = hash_finish_sha512, +#endif }, #endif { diff --git a/include/hw_sha.h b/include/hw_sha.h index 15b1a1c798..d4f3471c43 100644 --- a/include/hw_sha.h +++ b/include/hw_sha.h @@ -8,6 +8,32 @@ #define __HW_SHA_H #include +/** + * Computes hash value of input pbuf using h/w acceleration + * + * @param in_addr A pointer to the input buffer + * @param bufleni Byte length of input buffer + * @param out_addr A pointer to the output buffer. When complete + * 64 bytes are copied to pout[0]...pout[63]. Thus, a user + * should allocate at least 64 bytes at pOut in advance. + * @param chunk_size chunk size for sha512 + */ +void hw_sha512(const uchar *in_addr, uint buflen, uchar *out_addr, + uint chunk_size); + +/** + * Computes hash value of input pbuf using h/w acceleration + * + * @param in_addr A pointer to the input buffer + * @param bufleni Byte length of input buffer + * @param out_addr A pointer to the output buffer. When complete + * 48 bytes are copied to pout[0]...pout[47]. Thus, a user + * should allocate at least 48 bytes at pOut in advance. + * @param chunk_size chunk size for sha384 + */ +void hw_sha384(const uchar *in_addr, uint buflen, uchar *out_addr, + uint chunk_size); + /** * Computes hash value of input pbuf using h/w acceleration * diff --git a/lib/Kconfig b/lib/Kconfig index 80ff2443cb..ab8c9ccd60 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -391,19 +391,18 @@ config SHA384 config SHA_HW_ACCEL bool "Enable hashing using hardware" help - This option enables hardware acceleration - for SHA1/SHA256 hashing. - This affects the 'hash' command and also the - hash_lookup_algo() function. + This option enables hardware acceleration for SHA hashing. + This affects the 'hash' command and also the hash_lookup_algo() + function. config SHA_PROG_HW_ACCEL bool "Enable Progressive hashing support using hardware" depends on SHA_HW_ACCEL help - This option enables hardware-acceleration for - SHA1/SHA256 progressive hashing. - Data can be streamed in a block at a time and the hashing - is performed in hardware. + This option enables hardware-acceleration for SHA progressive + hashing. + Data can be streamed in a block at a time and the hashing is + performed in hardware. config MD5 bool "Support MD5 algorithm" From 0ca7c4d964dcce1ca3e0f7e1cdb8031561fdeab6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 17 Feb 2021 12:55:54 +0100 Subject: [PATCH 06/35] bootm: do not hang on failure On ARMv8 systems load mmc 0:1 $loadaddr vmlinuz-5.10.0-3-arm64 booti leads to a hanging system requiring to physically reset the system: FDT and ATAGS support not compiled in - hanging ### ERROR ### Please RESET the board ### For systems where physical access is difficult hanging is a poor choice. It is preferable to reset the system when U-Boot reaches a state that is not recoverable. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- arch/arm/lib/bootm.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 11af9e2fb9..f60ee3a7e6 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -249,8 +248,7 @@ static void boot_prep_linux(bootm_headers_t *images) #ifdef CONFIG_OF_LIBFDT debug("using: FDT\n"); if (image_setup_linux(images)) { - printf("FDT creation failed! hanging..."); - hang(); + panic("FDT creation failed!"); } #endif } else if (BOOTM_ENABLE_TAGS) { @@ -283,8 +281,7 @@ static void boot_prep_linux(bootm_headers_t *images) setup_board_tags(¶ms); setup_end_tag(gd->bd); } else { - printf("FDT and ATAGS support not compiled in - hanging\n"); - hang(); + panic("FDT and ATAGS support not compiled in\n"); } board_prep_linux(images); From bedab97a21d2c672a4ae96155bf3b53af86752f0 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Wed, 17 Feb 2021 17:06:53 +0000 Subject: [PATCH 07/35] Tegra: remove e2220-1170 board It's an old bringup board with out upstream Linux or L4T support Signed-off-by: Peter Robinson Cc: Stephen Warren Cc: Tom Warren Acked-by: Stephen Warren --- arch/arm/dts/Makefile | 1 - arch/arm/dts/tegra210-e2220-1170.dts | 63 ---------------------------- arch/arm/mach-tegra/tegra210/Kconfig | 9 ---- board/nvidia/e2220-1170/Kconfig | 12 ------ board/nvidia/e2220-1170/MAINTAINERS | 6 --- board/nvidia/e2220-1170/Makefile | 8 ---- board/nvidia/e2220-1170/e2220-1170.c | 32 -------------- configs/e2220-1170_defconfig | 48 --------------------- include/configs/e2220-1170.h | 28 ------------- 9 files changed, 207 deletions(-) delete mode 100644 arch/arm/dts/tegra210-e2220-1170.dts delete mode 100644 board/nvidia/e2220-1170/Kconfig delete mode 100644 board/nvidia/e2220-1170/MAINTAINERS delete mode 100644 board/nvidia/e2220-1170/Makefile delete mode 100644 board/nvidia/e2220-1170/e2220-1170.c delete mode 100644 configs/e2220-1170_defconfig delete mode 100644 include/configs/e2220-1170.h diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index d290c44257..b58f841472 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -201,7 +201,6 @@ dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \ tegra124-venice2.dtb \ tegra186-p2771-0000-000.dtb \ tegra186-p2771-0000-500.dtb \ - tegra210-e2220-1170.dtb \ tegra210-p2371-0000.dtb \ tegra210-p2371-2180.dtb \ tegra210-p2571.dtb \ diff --git a/arch/arm/dts/tegra210-e2220-1170.dts b/arch/arm/dts/tegra210-e2220-1170.dts deleted file mode 100644 index e6b06862d8..0000000000 --- a/arch/arm/dts/tegra210-e2220-1170.dts +++ /dev/null @@ -1,63 +0,0 @@ -/dts-v1/; - -#include "tegra210.dtsi" - -/ { - model = "NVIDIA E2220-1170"; - compatible = "nvidia,e2220-1170", "nvidia,tegra210"; - - chosen { - stdout-path = &uarta; - }; - - aliases { - i2c0 = "/i2c@7000d000"; - mmc0 = "/sdhci@700b0600"; - mmc1 = "/sdhci@700b0000"; - usb0 = "/usb@7d000000"; - }; - - memory { - reg = <0x0 0x80000000 0x0 0xc0000000>; - }; - - sdhci@700b0000 { - status = "okay"; - cd-gpios = <&gpio TEGRA_GPIO(Z, 1) GPIO_ACTIVE_LOW>; - power-gpios = <&gpio TEGRA_GPIO(Z, 4) GPIO_ACTIVE_HIGH>; - bus-width = <4>; - }; - - sdhci@700b0600 { - status = "okay"; - bus-width = <8>; - non-removable; - }; - - i2c@7000d000 { - status = "okay"; - clock-frequency = <400000>; - }; - - usb@7d000000 { - status = "okay"; - dr_mode = "peripheral"; - }; - - clocks { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; - - clk32k_in: clock@0 { - compatible = "fixed-clock"; - reg = <0>; - #clock-cells = <0>; - clock-frequency = <32768>; - }; - }; -}; - -&uarta { - status = "okay"; -}; diff --git a/arch/arm/mach-tegra/tegra210/Kconfig b/arch/arm/mach-tegra/tegra210/Kconfig index 97ed8e05f4..17f18cbf30 100644 --- a/arch/arm/mach-tegra/tegra210/Kconfig +++ b/arch/arm/mach-tegra/tegra210/Kconfig @@ -3,14 +3,6 @@ if TEGRA210 choice prompt "Tegra210 board select" -config TARGET_E2220_1170 - bool "NVIDIA Tegra210 E2220-1170 board" - select BOARD_LATE_INIT - help - E2220-1170 is a Tegra210 bringup board with onboard SoC, DRAM, - eMMC, SD card slot, HDMI, USB micro-B port, and sockets for various - expansion modules. - config TARGET_P2371_0000 bool "NVIDIA Tegra210 P2371-0000 board" select BOARD_LATE_INIT @@ -46,7 +38,6 @@ endchoice config SYS_SOC default "tegra210" -source "board/nvidia/e2220-1170/Kconfig" source "board/nvidia/p2371-0000/Kconfig" source "board/nvidia/p2371-2180/Kconfig" source "board/nvidia/p2571/Kconfig" diff --git a/board/nvidia/e2220-1170/Kconfig b/board/nvidia/e2220-1170/Kconfig deleted file mode 100644 index 0a30f49362..0000000000 --- a/board/nvidia/e2220-1170/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -if TARGET_E2220_1170 - -config SYS_BOARD - default "e2220-1170" - -config SYS_VENDOR - default "nvidia" - -config SYS_CONFIG_NAME - default "e2220-1170" - -endif diff --git a/board/nvidia/e2220-1170/MAINTAINERS b/board/nvidia/e2220-1170/MAINTAINERS deleted file mode 100644 index 0abb378840..0000000000 --- a/board/nvidia/e2220-1170/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -E2220-1170 BOARD -M: Tom Warren -S: Maintained -F: board/nvidia/e2220-1170/ -F: include/configs/e2220-1170.h -F: configs/e2220-1170_defconfig diff --git a/board/nvidia/e2220-1170/Makefile b/board/nvidia/e2220-1170/Makefile deleted file mode 100644 index c956b1972d..0000000000 --- a/board/nvidia/e2220-1170/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2013-2015 -# NVIDIA Corporation -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y += e2220-1170.o diff --git a/board/nvidia/e2220-1170/e2220-1170.c b/board/nvidia/e2220-1170/e2220-1170.c deleted file mode 100644 index b819b049f4..0000000000 --- a/board/nvidia/e2220-1170/e2220-1170.c +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2013-2019 - * NVIDIA Corporation - */ - -#include -#include -#include -#include -#include -#include "../p2571/max77620_init.h" - -void pin_mux_mmc(void) -{ - struct udevice *dev; - uchar val; - int ret; - - /* Turn on MAX77620 LDO2 to 3.3V for SD card power */ - debug("%s: Set LDO2 for VDDIO_SDMMC_AP power to 3.3V\n", __func__); - ret = i2c_get_chip_for_busnum(0, MAX77620_I2C_ADDR_7BIT, 1, &dev); - if (ret) { - printf("%s: Cannot find MAX77620 I2C chip\n", __func__); - return; - } - /* 0xF2 for 3.3v, enabled: bit7:6 = 11 = enable, bit5:0 = voltage */ - val = 0xF2; - ret = dm_i2c_write(dev, MAX77620_CNFG1_L2_REG, &val, 1); - if (ret) - printf("i2c_write 0 0x3c 0x27 failed: %d\n", ret); -} diff --git a/configs/e2220-1170_defconfig b/configs/e2220-1170_defconfig deleted file mode 100644 index efe3f8b574..0000000000 --- a/configs/e2220-1170_defconfig +++ /dev/null @@ -1,48 +0,0 @@ -CONFIG_ARM=y -CONFIG_ARCH_TEGRA=y -CONFIG_SYS_TEXT_BASE=0x80080000 -CONFIG_NR_DRAM_BANKS=2 -CONFIG_ENV_SIZE=0x2000 -CONFIG_ENV_OFFSET=0xFFFFE000 -CONFIG_TEGRA210=y -CONFIG_DEFAULT_DEVICE_TREE="tegra210-e2220-1170" -CONFIG_OF_SYSTEM_SETUP=y -CONFIG_CONSOLE_MUX=y -CONFIG_SYS_STDIO_DEREGISTER=y -CONFIG_SYS_PROMPT="Tegra210 (E2220-1170) # " -# CONFIG_CMD_IMI is not set -CONFIG_CMD_DFU=y -CONFIG_CMD_GPIO=y -CONFIG_CMD_I2C=y -CONFIG_CMD_MMC=y -CONFIG_CMD_SPI=y -CONFIG_CMD_USB=y -CONFIG_CMD_USB_MASS_STORAGE=y -# CONFIG_CMD_SETEXPR is not set -CONFIG_BOOTP_PREFER_SERVERIP=y -# CONFIG_CMD_NFS is not set -CONFIG_CMD_EXT4_WRITE=y -CONFIG_ENV_OVERWRITE=y -CONFIG_SYS_RELOC_GD_ENV_ADDR=y -CONFIG_SYS_MMC_ENV_PART=2 -CONFIG_DFU_MMC=y -CONFIG_DFU_RAM=y -CONFIG_DFU_SF=y -CONFIG_SYS_I2C_TEGRA=y -CONFIG_SF_DEFAULT_MODE=0 -CONFIG_SF_DEFAULT_SPEED=24000000 -CONFIG_SPI_FLASH_WINBOND=y -CONFIG_SYS_NS16550=y -CONFIG_TEGRA114_SPI=y -CONFIG_USB=y -CONFIG_DM_USB=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_TEGRA=y -CONFIG_USB_GADGET=y -CONFIG_USB_GADGET_MANUFACTURER="NVIDIA" -CONFIG_USB_GADGET_VENDOR_NUM=0x0955 -CONFIG_USB_GADGET_PRODUCT_NUM=0x701a -CONFIG_CI_UDC=y -CONFIG_USB_GADGET_DOWNLOAD=y -CONFIG_USB_HOST_ETHER=y -CONFIG_USB_ETHER_ASIX=y diff --git a/include/configs/e2220-1170.h b/include/configs/e2220-1170.h deleted file mode 100644 index 5ed62678ce..0000000000 --- a/include/configs/e2220-1170.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2013-2015 - * NVIDIA Corporation - */ - -#ifndef _E2220_1170_H -#define _E2220_1170_H - -#include - -#include "tegra210-common.h" - -/* High-level configuration options */ -#define CONFIG_TEGRA_BOARD_STRING "NVIDIA E2220-1170" - -/* Board-specific serial config */ -#define CONFIG_TEGRA_ENABLE_UARTA - -/* Environment in eMMC, at the end of 2nd "boot sector" */ - -/* SPI */ -#define CONFIG_SPI_FLASH_SIZE (4 << 20) - -#include "tegra-common-usb-gadget.h" -#include "tegra-common-post.h" - -#endif /* _E2220_1170_H */ From c146de48727da66ea7dc43f12bd41814cff2faa8 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Wed, 24 Feb 2021 15:25:53 -0800 Subject: [PATCH 08/35] mtd: Update fail_addr when erase fails due to bad blocks For all other erase failures, the fail_addr is updated with the failing address. Only in the case of erase failure due to bad block detection, the fail_addr is not updated. This change simply updates the fail_addr for this specific scenario so that it is consistent with the rest of the code. Signed-off-by: Farhan Ali --- drivers/mtd/nand/raw/nand_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 6557faddf9..3679ee727e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -3559,6 +3559,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; + instr->fail_addr = + ((loff_t)page << chip->page_shift); goto erase_exit; } From 9539f71675c40485e448efb3c4e06afc8d102f94 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Sun, 28 Feb 2021 16:29:51 -0500 Subject: [PATCH 09/35] hush: Fix assignments being misinterpreted as commands If there were no variable substitutions in a command, then initial assignments would be misinterpreted as commands, instead of being skipped over. This is demonstrated by the following example: => foo=bar echo baz Unknown command 'foo=bar' - try 'help' Signed-off-by: Sean Anderson --- common/cli_hush.c | 2 +- test/cmd/test_echo.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/cli_hush.c b/common/cli_hush.c index 9466651d1a..6cff3b1185 100644 --- a/common/cli_hush.c +++ b/common/cli_hush.c @@ -1673,7 +1673,7 @@ static int run_pipe_real(struct pipe *pi) return -1; } /* Process the command */ - return cmd_process(flag, child->argc, child->argv, + return cmd_process(flag, child->argc - i, child->argv + i, &flag_repeat, NULL); #endif } diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c index 9d60d7d1a0..091e4f823c 100644 --- a/test/cmd/test_echo.c +++ b/test/cmd/test_echo.c @@ -34,6 +34,8 @@ static struct test_data echo_data[] = { */ {"setenv jQx X; echo \"a)\" ${jQx} 'b)' '${jQx}' c) ${jQx}; setenv jQx", "a) X b) ${jQx} c) X"}, + /* Test shell variable assignments without substitutions */ + {"foo=bar echo baz", "baz"}, /* Test handling of shell variables. */ {"setenv jQx; for jQx in 1 2 3; do echo -n \"${jQx}, \"; done; echo;", "1, 2, 3, "}, From a62de442e41d85b0ab682c2d51ce9d24971dd9dd Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 3 Mar 2021 01:57:35 +0100 Subject: [PATCH 10/35] pci: Mark 64bit Memory BARs as such Just a bit more info to the reader. Signed-off-by: Phil Sutter Reviewed-by: Stefan Roese --- drivers/pci/pci_auto.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index da76148c58..05663c72b4 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -88,8 +88,9 @@ static void dm_pciauto_setup_device(struct udevice *dev, int bars_num, else bar_res = mem; - debug("PCI Autoconfig: BAR %d, %s, size=0x%llx, ", + debug("PCI Autoconfig: BAR %d, %s%s, size=0x%llx, ", bar_nr, bar_res == prefetch ? "Prf" : "Mem", + found_mem64 ? "64" : "", (unsigned long long)bar_size); } From b935d190662306b4174a8f7e3dbd6194c5f55ecb Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 4 Mar 2021 11:34:23 -0500 Subject: [PATCH 11/35] cmd: xtrace: Convert to bool This variable is a boolean, not a string. Signed-off-by: Sean Anderson Reviewed-by: Heinrich Schuchardt --- cmd/Kconfig | 2 +- configs/bcm_ns3_defconfig | 2 +- configs/taurus_defconfig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index c735e81b37..f4cff0c125 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -65,7 +65,7 @@ config SYS_PROMPT_HUSH_PS2 to complete a command. Usually "> ". config SYS_XTRACE - string "Command execution tracer" + bool "Command execution tracer" depends on CMDLINE default y if CMDLINE help diff --git a/configs/bcm_ns3_defconfig b/configs/bcm_ns3_defconfig index d060097d74..5fb82e9479 100644 --- a/configs/bcm_ns3_defconfig +++ b/configs/bcm_ns3_defconfig @@ -19,7 +19,7 @@ CONFIG_SILENT_U_BOOT_ONLY=y # CONFIG_DISPLAY_CPUINFO is not set CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="u-boot> " -CONFIG_SYS_XTRACE="n" +CONFIG_SYS_XTRACE=n CONFIG_CMD_GPT=y CONFIG_CMD_GPT_RENAME=y CONFIG_CMD_MMC=y diff --git a/configs/taurus_defconfig b/configs/taurus_defconfig index 6198159107..1f7e342229 100644 --- a/configs/taurus_defconfig +++ b/configs/taurus_defconfig @@ -44,7 +44,7 @@ CONFIG_SPL_NAND_BASE=y CONFIG_SPL_DM_SPI_FLASH=y CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="U-Boot> " -CONFIG_SYS_XTRACE="n" +CONFIG_SYS_XTRACE=n # CONFIG_CMD_BDI is not set CONFIG_CMD_BOOTZ=y # CONFIG_CMD_IMI is not set From d46933839f98d8cdb34fc87299b5f2a4ec4bbfec Mon Sep 17 00:00:00 2001 From: Stefan Herbrechtsmeier Date: Mon, 8 Mar 2021 16:07:11 +0000 Subject: [PATCH 12/35] disk: gpt: verify alternate LBA points to last usable LBA The gpt command require the GPT backup header at the standard location at the end of the device. Check the alternate LBA value before reading the GPT backup header from the last usable LBA of the device. Signed-off-by: Stefan Herbrechtsmeier Reviewed-by: Heinrich Schuchardt --- disk/part_efi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/disk/part_efi.c b/disk/part_efi.c index e5636ea7e6..0fb7ff0b6b 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -692,6 +692,15 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, /* Free pte before allocating again */ free(*gpt_pte); + /* + * Check that the alternate_lba entry points to the last LBA + */ + if (le64_to_cpu(gpt_head->alternate_lba) != (dev_desc->lba - 1)) { + printf("%s: *** ERROR: Misplaced Backup GPT ***\n", + __func__); + return -1; + } + if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), gpt_head, gpt_pte) != 1) { printf("%s: *** ERROR: Invalid Backup GPT ***\n", From 1f213ee4dbf249933c29d5f62fd53ec4754ab4aa Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 8 Mar 2021 22:38:06 +0100 Subject: [PATCH 13/35] firmware: scmi: voltage regulator Implement voltage regulators interfaced by the SCMI voltage domain protocol. The DT bindings are defined in the Linux kernel since SCMI voltage domain and regulators patches [1] and [2] integration in v5.11-rc7. Link: [1] https://github.com/torvalds/linux/commit/0f80fcec08e9c50b8d2992cf26495673765ebaba Link: [2] https://github.com/torvalds/linux/commit/2add5cacff3531e54c50b0832128299faa9f0563 Signed-off-by: Etienne Carriere Reviewed-by: Jaehoon Chung --- doc/device-tree-bindings/arm/arm,scmi.txt | 34 ++++ drivers/firmware/scmi/scmi_agent-uclass.c | 10 ++ drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/scmi_regulator.c | 195 ++++++++++++++++++++++ include/scmi_protocols.h | 113 +++++++++++++ 6 files changed, 361 insertions(+) create mode 100644 drivers/power/regulator/scmi_regulator.c diff --git a/doc/device-tree-bindings/arm/arm,scmi.txt b/doc/device-tree-bindings/arm/arm,scmi.txt index 1f293ea24c..a76124f4a3 100644 --- a/doc/device-tree-bindings/arm/arm,scmi.txt +++ b/doc/device-tree-bindings/arm/arm,scmi.txt @@ -62,6 +62,20 @@ Required properties: - #power-domain-cells : Should be 1. Contains the device or the power domain ID value used by SCMI commands. +Regulator bindings for the SCMI Regulator based on SCMI Message Protocol +------------------------------------------------------------ +An SCMI Regulator is permanently bound to a well defined SCMI Voltage Domain, +and should be always positioned as a root regulator. +It does not support any current operation. + +SCMI Regulators are grouped under a 'regulators' node which in turn is a child +of the SCMI Voltage protocol node inside the desired SCMI instance node. + +This binding uses the common regulator binding[6]. + +Required properties: + - reg : shall identify an existent SCMI Voltage Domain. + Sensor bindings for the sensors based on SCMI Message Protocol -------------------------------------------------------------- SCMI provides an API to access the various sensors on the SoC. @@ -105,6 +119,7 @@ Required sub-node properties: [3] Documentation/devicetree/bindings/thermal/thermal.txt [4] Documentation/devicetree/bindings/sram/sram.yaml [5] Documentation/devicetree/bindings/reset/reset.txt +[6] Documentation/devicetree/bindings/regulator/regulator.yaml Example: @@ -169,6 +184,25 @@ firmware { reg = <0x16>; #reset-cells = <1>; }; + + scmi_voltage: protocol@17 { + reg = <0x17>; + + regulators { + regulator_devX: regulator@0 { + reg = <0x0>; + regulator-max-microvolt = <3300000>; + }; + + regulator_devY: regulator@9 { + reg = <0x9>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <4200000>; + }; + + ... + }; + }; }; }; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 527163b5ce..4f5870b483 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -80,6 +80,16 @@ static int scmi_bind_protocols(struct udevice *dev) if (IS_ENABLED(CONFIG_RESET_SCMI)) drv = DM_DRIVER_GET(scmi_reset_domain); break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) { + node = ofnode_find_subnode(node, "regulators"); + if (!ofnode_valid(node)) { + dev_err(dev, "no regulators node\n"); + return -ENXIO; + } + drv = DM_DRIVER_GET(scmi_voltage_domain); + } + break; default: break; } diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index fbbea18c7d..5d6180229e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -353,3 +353,11 @@ config DM_REGULATOR_TPS65941 TPS65941 series of PMICs have 5 single phase BUCKs that can also be configured in multi phase modes & 4 LDOs. The driver implements get/set api for value and enable. + +config DM_REGULATOR_SCMI + bool "Enable driver for SCMI voltage domain regulators" + depends on DM_REGULATOR + select SCMI_AGENT + help + Enable this option if you want to support regulators exposed through + the SCMI voltage domain protocol by a SCMI server. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 9d58112dcb..b2f5972ea7 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o +obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c new file mode 100644 index 0000000000..b3142bf4e1 --- /dev/null +++ b/drivers/power/regulator/scmi_regulator.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020-2021 Linaro Limited + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct scmi_regulator_platdata - Platform data for a scmi voltage domain regulator + * @domain_id: ID representing the regulator for the related SCMI agent + */ +struct scmi_regulator_platdata { + u32 domain_id; +}; + +static int scmi_voltd_set_enable(struct udevice *dev, bool enable) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_set_in in = { + .domain_id = pdata->domain_id, + .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF, + }; + struct scmi_voltd_config_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret) + return ret; + + return ret; +} + +static int scmi_voltd_get_enable(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_config_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.config == SCMI_VOLTD_CONFIG_ON; +} + +static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_set_in in = { + .domain_id = pdata->domain_id, + .voltage_level = uV, + }; + struct scmi_voltd_level_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_voltd_get_voltage_level(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_level_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.voltage_level; +} + +static int scmi_regulator_of_to_plat(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + fdt_addr_t reg; + + reg = dev_read_addr(dev); + if (reg == FDT_ADDR_T_NONE) + return -EINVAL; + + pdata->domain_id = (u32)reg; + + return 0; +} + +static int scmi_regulator_probe(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_attr_in in = { 0 }; + struct scmi_voltd_attr_out out = { 0 }; + struct scmi_msg scmi_msg = { + .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + /* Check voltage domain is known from SCMI server */ + in.domain_id = pdata->domain_id; + + ret = devm_scmi_process_msg(dev->parent->parent, &scmi_msg); + if (ret) { + dev_err(dev, "Failed to query voltage domain %u: %d\n", + pdata->domain_id, ret); + return -ENXIO; + } + + return 0; +} + +static const struct dm_regulator_ops scmi_voltd_ops = { + .get_value = scmi_voltd_get_voltage_level, + .set_value = scmi_voltd_set_voltage_level, + .get_enable = scmi_voltd_get_enable, + .set_enable = scmi_voltd_set_enable, +}; + +U_BOOT_DRIVER(scmi_regulator) = { + .name = "scmi_regulator", + .id = UCLASS_REGULATOR, + .ops = &scmi_voltd_ops, + .probe = scmi_regulator_probe, + .of_to_plat = scmi_regulator_of_to_plat, + .plat_auto = sizeof(struct scmi_regulator_platdata), +}; + +static int scmi_regulator_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + drv = DM_DRIVER_GET(scmi_regulator); + + ofnode_for_each_subnode(node, dev_ofnode(dev)) { + ret = device_bind(dev, drv, ofnode_get_name(node), + NULL, node, NULL); + if (ret) + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(scmi_voltage_domain) = { + .name = "scmi_voltage_domain", + .id = UCLASS_NOP, + .bind = scmi_regulator_bind, +}; diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index ccab97c96c..2db71697e8 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -23,6 +23,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_ID_CLOCK = 0x14, SCMI_PROTOCOL_ID_SENSOR = 0x15, SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, + SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17, }; enum scmi_status_code { @@ -176,4 +177,116 @@ struct scmi_rd_reset_out { s32 status; }; +/* + * SCMI Voltage Domain Protocol + */ + +enum scmi_voltage_domain_message_id { + SCMI_VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET = 0x5, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET = 0x6, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET = 0x7, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET = 0x8, +}; + +#define SCMI_VOLTD_NAME_LEN 16 + +#define SCMI_VOLTD_CONFIG_MASK GENMASK(3, 0) +#define SCMI_VOLTD_CONFIG_OFF 0 +#define SCMI_VOLTD_CONFIG_ON 0x7 + +/** + * struct scmi_voltd_attr_in - Payload for VOLTAGE_DOMAIN_ATTRIBUTES message + * @domain_id: SCMI voltage domain ID + */ +struct scmi_voltd_attr_in { + u32 domain_id; +}; + +/** + * struct scmi_voltd_attr_out - Payload for VOLTAGE_DOMAIN_ATTRIBUTES response + * @status: SCMI command status + * @attributes: Retrieved attributes of the voltage domain + * @name: Voltage domain name + */ +struct scmi_voltd_attr_out { + s32 status; + u32 attributes; + char name[SCMI_VOLTD_NAME_LEN]; +}; + +/** + * struct scmi_voltd_config_set_in - Message payload for VOLTAGE_CONFIG_SET cmd + * @domain_id: SCMI voltage domain ID + * @config: Configuration data of the voltage domain + */ +struct scmi_voltd_config_set_in { + u32 domain_id; + u32 config; +}; + +/** + * struct scmi_voltd_config_set_out - Response for VOLTAGE_CONFIG_SET command + * @status: SCMI command status + */ +struct scmi_voltd_config_set_out { + s32 status; +}; + +/** + * struct scmi_voltd_config_get_in - Message payload for VOLTAGE_CONFIG_GET cmd + * @domain_id: SCMI voltage domain ID + */ +struct scmi_voltd_config_get_in { + u32 domain_id; +}; + +/** + * struct scmi_voltd_config_get_out - Response for VOLTAGE_CONFIG_GET command + * @status: SCMI command status + * @config: Configuration data of the voltage domain + */ +struct scmi_voltd_config_get_out { + s32 status; + u32 config; +}; + +/** + * struct scmi_voltd_level_set_in - Message payload for VOLTAGE_LEVEL_SET cmd + * @domain_id: SCMI voltage domain ID + * @flags: Parameter flags for configuring target level + * @voltage_level: Target voltage level in microvolts (uV) + */ +struct scmi_voltd_level_set_in { + u32 domain_id; + u32 flags; + s32 voltage_level; +}; + +/** + * struct scmi_voltd_level_set_out - Response for VOLTAGE_LEVEL_SET command + * @status: SCMI command status + */ +struct scmi_voltd_level_set_out { + s32 status; +}; + +/** + * struct scmi_voltd_level_get_in - Message payload for VOLTAGE_LEVEL_GET cmd + * @domain_id: SCMI voltage domain ID + */ +struct scmi_voltd_level_get_in { + u32 domain_id; +}; + +/** + * struct scmi_voltd_level_get_out - Response for VOLTAGE_LEVEL_GET command + * @status: SCMI command status + * @voltage_level: Voltage level in microvolts (uV) + */ +struct scmi_voltd_level_get_out { + s32 status; + s32 voltage_level; +}; + #endif /* _SCMI_PROTOCOLS_H */ From 0124218b8b4762621f5ff74bd03eb1bcb1d4e733 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 8 Mar 2021 22:38:07 +0100 Subject: [PATCH 14/35] firmware: scmi: sandbox test for voltage regulator Implement sandbox regulator devices for SCMI voltage domains and test them in DM scmi tests. Signed-off-by: Etienne Carriere Reviewed-by: Simon Glass Reviewed-by: Jaehoon Chung --- arch/sandbox/dts/test.dts | 23 +++ arch/sandbox/include/asm/scmi_test.h | 20 ++ configs/sandbox_defconfig | 1 + drivers/firmware/scmi/sandbox-scmi_agent.c | 203 ++++++++++++++++++- drivers/firmware/scmi/sandbox-scmi_devices.c | 25 ++- test/dm/scmi.c | 58 ++++++ 6 files changed, 327 insertions(+), 3 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 899e75f260..8c4c2bfddd 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -497,6 +497,27 @@ reg = <0x16>; #reset-cells = <1>; }; + + protocol@17 { + reg = <0x17>; + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + regul0_scmi0: reg@0 { + reg = <0>; + regulator-name = "sandbox-voltd0"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <3300000>; + }; + regul1_scmi0: reg@1 { + reg = <0x1>; + regulator-name = "sandbox-voltd1"; + regulator-min-microvolt = <1800000>; + }; + }; + }; }; sandbox-scmi-agent@1 { @@ -1264,6 +1285,8 @@ compatible = "sandbox,scmi-devices"; clocks = <&clk_scmi0 7>, <&clk_scmi0 3>, <&clk_scmi1 1>; resets = <&reset_scmi0 3>; + regul0-supply = <®ul0_scmi0>; + regul1-supply = <®ul1_scmi0>; }; pinctrl { diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h index 3e8b0068fd..9b70315314 100644 --- a/arch/sandbox/include/asm/scmi_test.h +++ b/arch/sandbox/include/asm/scmi_test.h @@ -31,6 +31,18 @@ struct sandbox_scmi_reset { bool asserted; }; +/** + * struct sandbox_scmi_voltd - Simulated voltage regulator exposed by SCMI + * @id: Identifier of the voltage domain used in the SCMI protocol + * @enabled: Regulator state: true if on, false if off + * @voltage_uv: Regulator current voltage in microvoltd (uV) + */ +struct sandbox_scmi_voltd { + uint id; + bool enabled; + int voltage_uv; +}; + /** * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent * @idx: Identifier for the SCMI agent, its index @@ -38,6 +50,8 @@ struct sandbox_scmi_reset { * @clk_count: Simulated clocks array size * @clk: Simulated reset domains * @clk_count: Simulated reset domains array size + * @voltd: Simulated voltage domains (regulators) + * @voltd_count: Simulated voltage domains array size */ struct sandbox_scmi_agent { uint idx; @@ -45,6 +59,8 @@ struct sandbox_scmi_agent { size_t clk_count; struct sandbox_scmi_reset *reset; size_t reset_count; + struct sandbox_scmi_voltd *voltd; + size_t voltd_count; }; /** @@ -63,12 +79,16 @@ struct sandbox_scmi_service { * @clk_count: Number of clock devices probed * @reset: Array the reset controller devices * @reset_count: Number of reset controller devices probed + * @regul: Array regulator devices + * @regul_count: Number of regulator devices probed */ struct sandbox_scmi_devices { struct clk *clk; size_t clk_count; struct reset_ctl *reset; size_t reset_count; + struct udevice **regul; + size_t regul_count; }; #ifdef CONFIG_SCMI_FIRMWARE diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5da8d1679e..c9318d4af5 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -219,6 +219,7 @@ CONFIG_DM_REGULATOR_FIXED=y CONFIG_REGULATOR_RK8XX=y CONFIG_REGULATOR_S5M8767=y CONFIG_DM_REGULATOR_SANDBOX=y +CONFIG_DM_REGULATOR_SCMI=y CONFIG_REGULATOR_TPS65090=y CONFIG_DM_PWM=y CONFIG_PWM_SANDBOX=y diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index 97a5dace15..45a82d6cc3 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -21,14 +21,15 @@ * SCMI protocols embedded in U-Boot. Currently: * - SCMI clock protocol: emulate 2 agents each exposing few clocks * - SCMI reset protocol: emulate 1 agents each exposing a reset + * - SCMI voltage domain protocol: emulate 1 agent exposing 2 regulators * - * Agent #0 simulates 2 clocks and 1 reset domain. + * Agent #0 simulates 2 clocks, 1 reset domain and 1 voltage domain. * See IDs in scmi0_clk[]/scmi0_reset[] and "sandbox-scmi-agent@0" in test.dts. * * Agent #1 simulates 1 clock. * See IDs in scmi1_clk[] and "sandbox-scmi-agent@1" in test.dts. * - * All clocks are default disabled and reset levels down. + * All clocks and regulators are default disabled and reset controller down. * * This Driver exports sandbox_scmi_service_ct() for the test sequence to * get the state of the simulated services (clock state, rate, ...) and @@ -47,6 +48,11 @@ static struct sandbox_scmi_reset scmi0_reset[] = { { .id = 3 }, }; +static struct sandbox_scmi_voltd scmi0_voltd[] = { + { .id = 0, .voltage_uv = 3300000 }, + { .id = 1, .voltage_uv = 1800000 }, +}; + static struct sandbox_scmi_clk scmi1_clk[] = { { .id = 1, .rate = 44 }, }; @@ -83,6 +89,13 @@ static void debug_print_agent_state(struct udevice *dev, char *str) agent->reset_count, agent->reset_count ? agent->reset[0].asserted : -1, agent->reset_count > 1 ? agent->reset[1].asserted : -1); + dev_dbg(dev, " scmi%u_voltd (%zu): %u/%d, %u/%d, ...\n", + agent->idx, + agent->voltd_count, + agent->voltd_count ? agent->voltd[0].enabled : -1, + agent->voltd_count ? agent->voltd[0].voltage_uv : -1, + agent->voltd_count ? agent->voltd[1].enabled : -1, + agent->voltd_count ? agent->voltd[1].voltage_uv : -1); }; static struct sandbox_scmi_clk *get_scmi_clk_state(uint agent_id, uint clock_id) @@ -125,6 +138,20 @@ static struct sandbox_scmi_reset *get_scmi_reset_state(uint agent_id, return NULL; } +static struct sandbox_scmi_voltd *get_scmi_voltd_state(uint agent_id, + uint domain_id) +{ + size_t n; + + if (agent_id == 0) { + for (n = 0; n < ARRAY_SIZE(scmi0_voltd); n++) + if (scmi0_voltd[n].id == domain_id) + return scmi0_voltd + n; + } + + return NULL; +} + /* * Sandbox SCMI agent ops */ @@ -292,6 +319,160 @@ static int sandbox_scmi_rd_reset(struct udevice *dev, struct scmi_msg *msg) return 0; } +static int sandbox_scmi_voltd_attribs(struct udevice *dev, struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_attr_in *in = NULL; + struct scmi_voltd_attr_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_attr_in *)msg->in_msg; + out = (struct scmi_voltd_attr_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + memset(out, 0, sizeof(*out)); + snprintf(out->name, sizeof(out->name), "regu%u", in->domain_id); + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_config_set(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_config_set_in *in = NULL; + struct scmi_voltd_config_set_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_config_set_in *)msg->in_msg; + out = (struct scmi_voltd_config_set_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else if (in->config & ~SCMI_VOLTD_CONFIG_MASK) { + dev_err(dev, "Invalid config value 0x%x\n", in->config); + + out->status = SCMI_INVALID_PARAMETERS; + } else if (in->config != SCMI_VOLTD_CONFIG_ON && + in->config != SCMI_VOLTD_CONFIG_OFF) { + dev_err(dev, "Unexpected custom value 0x%x\n", in->config); + + out->status = SCMI_INVALID_PARAMETERS; + } else { + voltd_state->enabled = in->config == SCMI_VOLTD_CONFIG_ON; + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_config_get(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_config_get_in *in = NULL; + struct scmi_voltd_config_get_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_config_get_in *)msg->in_msg; + out = (struct scmi_voltd_config_get_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + if (voltd_state->enabled) + out->config = SCMI_VOLTD_CONFIG_ON; + else + out->config = SCMI_VOLTD_CONFIG_OFF; + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_level_set(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_level_set_in *in = NULL; + struct scmi_voltd_level_set_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_level_set_in *)msg->in_msg; + out = (struct scmi_voltd_level_set_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + voltd_state->voltage_uv = in->voltage_level; + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_level_get(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_level_get_in *in = NULL; + struct scmi_voltd_level_get_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_level_get_in *)msg->in_msg; + out = (struct scmi_voltd_level_get_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + out->voltage_level = voltd_state->voltage_uv; + out->status = SCMI_SUCCESS; + } + + return 0; +} + static int sandbox_scmi_test_process_msg(struct udevice *dev, struct scmi_msg *msg) { @@ -318,6 +499,22 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, break; } break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + switch (msg->message_id) { + case SCMI_VOLTAGE_DOMAIN_ATTRIBUTES: + return sandbox_scmi_voltd_attribs(dev, msg); + case SCMI_VOLTAGE_DOMAIN_CONFIG_SET: + return sandbox_scmi_voltd_config_set(dev, msg); + case SCMI_VOLTAGE_DOMAIN_CONFIG_GET: + return sandbox_scmi_voltd_config_get(dev, msg); + case SCMI_VOLTAGE_DOMAIN_LEVEL_SET: + return sandbox_scmi_voltd_level_set(dev, msg); + case SCMI_VOLTAGE_DOMAIN_LEVEL_GET: + return sandbox_scmi_voltd_level_get(dev, msg); + default: + break; + } + break; case SCMI_PROTOCOL_ID_BASE: case SCMI_PROTOCOL_ID_POWER_DOMAIN: case SCMI_PROTOCOL_ID_SYSTEM: @@ -369,6 +566,8 @@ static int sandbox_scmi_test_probe(struct udevice *dev) .clk_count = ARRAY_SIZE(scmi0_clk), .reset = scmi0_reset, .reset_count = ARRAY_SIZE(scmi0_reset), + .voltd = scmi0_voltd, + .voltd_count = ARRAY_SIZE(scmi0_voltd), }; break; case '1': diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c index 69239a198f..66a6792881 100644 --- a/drivers/firmware/scmi/sandbox-scmi_devices.c +++ b/drivers/firmware/scmi/sandbox-scmi_devices.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020, Linaro Limited + * Copyright (C) 2020-2021, Linaro Limited */ #define LOG_CATEGORY UCLASS_MISC @@ -8,11 +8,13 @@ #include #include #include +#include #include #include #include #include #include +#include /* * Simulate to some extent a SCMI exchange. @@ -23,16 +25,19 @@ #define SCMI_TEST_DEVICES_CLK_COUNT 3 #define SCMI_TEST_DEVICES_RD_COUNT 1 +#define SCMI_TEST_DEVICES_VOLTD_COUNT 2 /* * struct sandbox_scmi_device_priv - Storage for device handles used by test * @clk: Array of clock instances used by tests * @reset_clt: Array of the reset controller instances used by tests + * @regulators: Array of regulator device references used by the tests * @devices: Resources exposed by sandbox_scmi_devices_ctx() */ struct sandbox_scmi_device_priv { struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT]; struct reset_ctl reset_ctl[SCMI_TEST_DEVICES_RD_COUNT]; + struct udevice *regulators[SCMI_TEST_DEVICES_VOLTD_COUNT]; struct sandbox_scmi_devices devices; }; @@ -76,6 +81,8 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) .clk_count = SCMI_TEST_DEVICES_CLK_COUNT, .reset = priv->reset_ctl, .reset_count = SCMI_TEST_DEVICES_RD_COUNT, + .regul = priv->regulators, + .regul_count = SCMI_TEST_DEVICES_VOLTD_COUNT, }; for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) { @@ -94,8 +101,24 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) } } + for (n = 0; n < SCMI_TEST_DEVICES_VOLTD_COUNT; n++) { + char name[32]; + + ret = snprintf(name, sizeof(name), "regul%zu-supply", n); + assert(ret >= 0 && ret < sizeof(name)); + + ret = device_get_supply_regulator(dev, name, + priv->devices.regul + n); + if (ret) { + dev_err(dev, "%s: Failed on voltd %zu\n", __func__, n); + goto err_regul; + } + } + return 0; +err_regul: + n = SCMI_TEST_DEVICES_RD_COUNT; err_reset: for (; n > 0; n--) reset_free(priv->devices.reset + n - 1); diff --git a/test/dm/scmi.c b/test/dm/scmi.c index be60b44b3b..7924103380 100644 --- a/test/dm/scmi.c +++ b/test/dm/scmi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include static int ut_assert_scmi_state_preprobe(struct unit_test_state *uts) @@ -47,6 +48,7 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, ut_asserteq(3, scmi_devices->clk_count); if (IS_ENABLED(CONFIG_RESET_SCMI)) ut_asserteq(1, scmi_devices->reset_count); + ut_asserteq(2, scmi_devices->regul_count); /* State of the simulated SCMI server exposed */ scmi_ctx = sandbox_scmi_service_ctx(); @@ -58,6 +60,8 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, ut_assertnonnull(scmi_ctx->agent[0]->clk); ut_asserteq(1, scmi_ctx->agent[0]->reset_count); ut_assertnonnull(scmi_ctx->agent[0]->reset); + ut_asserteq(2, scmi_ctx->agent[0]->voltd_count); + ut_assertnonnull(scmi_ctx->agent[0]->voltd); ut_assertnonnull(scmi_ctx->agent[1]); ut_assertnonnull(scmi_ctx->agent[1]->clk); @@ -201,3 +205,57 @@ static int dm_test_scmi_resets(struct unit_test_state *uts) } DM_TEST(dm_test_scmi_resets, UT_TESTF_SCAN_FDT); + +static int dm_test_scmi_voltage_domains(struct unit_test_state *uts) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_voltd *sandbox_voltd; + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + struct udevice *regul_dev; + + ut_assertok(load_sandbox_scmi_test_devices(uts, &dev)); + + scmi_devices = sandbox_scmi_devices_ctx(dev); + scmi_ctx = sandbox_scmi_service_ctx(); + + /* Set/Get an SCMI voltage domain level */ + sandbox_voltd = &scmi_ctx->agent[0]->voltd[0]; + regul_dev = scmi_devices->regul[0]; + ut_assert(regul_dev); + + uc_pdata = dev_get_uclass_plat(regul_dev); + ut_assert(uc_pdata); + + ut_assertok(regulator_set_value(regul_dev, uc_pdata->min_uV)); + ut_asserteq(sandbox_voltd->voltage_uv, uc_pdata->min_uV); + + ut_assert(regulator_get_value(regul_dev) == uc_pdata->min_uV); + + ut_assertok(regulator_set_value(regul_dev, uc_pdata->max_uV)); + ut_asserteq(sandbox_voltd->voltage_uv, uc_pdata->max_uV); + + ut_assert(regulator_get_value(regul_dev) == uc_pdata->max_uV); + + /* Enable/disable SCMI voltage domains */ + ut_assertok(regulator_set_enable(scmi_devices->regul[0], false)); + ut_assertok(regulator_set_enable(scmi_devices->regul[1], false)); + ut_assert(!scmi_ctx->agent[0]->voltd[0].enabled); + ut_assert(!scmi_ctx->agent[0]->voltd[1].enabled); + + ut_assertok(regulator_set_enable(scmi_devices->regul[0], true)); + ut_assert(scmi_ctx->agent[0]->voltd[0].enabled); + ut_assert(!scmi_ctx->agent[0]->voltd[1].enabled); + + ut_assertok(regulator_set_enable(scmi_devices->regul[1], true)); + ut_assert(scmi_ctx->agent[0]->voltd[0].enabled); + ut_assert(scmi_ctx->agent[0]->voltd[1].enabled); + + ut_assertok(regulator_set_enable(scmi_devices->regul[0], false)); + ut_assert(!scmi_ctx->agent[0]->voltd[0].enabled); + ut_assert(scmi_ctx->agent[0]->voltd[1].enabled); + + return release_sandbox_scmi_test_devices(uts, dev); +} +DM_TEST(dm_test_scmi_voltage_domains, UT_TESTF_SCAN_FDT); From c3bba708da5ec3066bfaaaede8783e891f984a1a Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 8 Mar 2021 22:38:08 +0100 Subject: [PATCH 15/35] firmware: scmi: fix inline comments and minor coding style issues Fix inline comments and empty line in scmi driver and test files. Remove test on IS_ENABLED(CONFIG_*_SCMI) in test/dm/scmi.c since these configuration are expected enabled when CONFIG_FIRMWARE_SCMI is enabled in sandbox configuration. Signed-off-by: Etienne Carriere Reviewed-by: Simon Glass Reviewed-by: Jaehoon Chung --- arch/sandbox/include/asm/scmi_test.h | 5 +++-- drivers/firmware/scmi/sandbox-scmi_agent.c | 4 ++-- test/dm/scmi.c | 15 ++------------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h index 9b70315314..2930e686d7 100644 --- a/arch/sandbox/include/asm/scmi_test.h +++ b/arch/sandbox/include/asm/scmi_test.h @@ -24,6 +24,7 @@ struct sandbox_scmi_clk { /** * struct sandbox_scmi_reset - Simulated reset controller exposed by SCMI + * @id: Identifier of the reset controller used in the SCMI protocol * @asserted: Reset control state: true if asserted, false if desasserted */ struct sandbox_scmi_reset { @@ -48,8 +49,8 @@ struct sandbox_scmi_voltd { * @idx: Identifier for the SCMI agent, its index * @clk: Simulated clocks * @clk_count: Simulated clocks array size - * @clk: Simulated reset domains - * @clk_count: Simulated reset domains array size + * @reset: Simulated reset domains + * @reset_count: Simulated reset domains array size * @voltd: Simulated voltage domains (regulators) * @voltd_count: Simulated voltage domains array size */ diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index 45a82d6cc3..4b968205c2 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -20,7 +20,7 @@ * processing. It simulates few of the SCMI services for some of the * SCMI protocols embedded in U-Boot. Currently: * - SCMI clock protocol: emulate 2 agents each exposing few clocks - * - SCMI reset protocol: emulate 1 agents each exposing a reset + * - SCMI reset protocol: emulate 1 agent exposing a reset controller * - SCMI voltage domain protocol: emulate 1 agent exposing 2 regulators * * Agent #0 simulates 2 clocks, 1 reset domain and 1 voltage domain. @@ -31,7 +31,7 @@ * * All clocks and regulators are default disabled and reset controller down. * - * This Driver exports sandbox_scmi_service_ct() for the test sequence to + * This Driver exports sandbox_scmi_service_ctx() for the test sequence to * get the state of the simulated services (clock state, rate, ...) and * check back-end device state reflects the request send through the * various uclass devices, as clocks and reset controllers. diff --git a/test/dm/scmi.c b/test/dm/scmi.c index 7924103380..2eaf06392e 100644 --- a/test/dm/scmi.c +++ b/test/dm/scmi.c @@ -44,10 +44,8 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, scmi_devices = sandbox_scmi_devices_ctx(dev); ut_assertnonnull(scmi_devices); - if (IS_ENABLED(CONFIG_CLK_SCMI)) - ut_asserteq(3, scmi_devices->clk_count); - if (IS_ENABLED(CONFIG_RESET_SCMI)) - ut_asserteq(1, scmi_devices->reset_count); + ut_asserteq(3, scmi_devices->clk_count); + ut_asserteq(1, scmi_devices->reset_count); ut_asserteq(2, scmi_devices->regul_count); /* State of the simulated SCMI server exposed */ @@ -110,7 +108,6 @@ static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts) return ret; } - DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT); static int dm_test_scmi_clocks(struct unit_test_state *uts) @@ -121,9 +118,6 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) int ret_dev; int ret; - if (!IS_ENABLED(CONFIG_CLK_SCMI)) - return 0; - ret = load_sandbox_scmi_test_devices(uts, &dev); if (ret) return ret; @@ -172,7 +166,6 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) return release_sandbox_scmi_test_devices(uts, dev); } - DM_TEST(dm_test_scmi_clocks, UT_TESTF_SCAN_FDT); static int dm_test_scmi_resets(struct unit_test_state *uts) @@ -182,9 +175,6 @@ static int dm_test_scmi_resets(struct unit_test_state *uts) struct udevice *dev = NULL; int ret; - if (!IS_ENABLED(CONFIG_RESET_SCMI)) - return 0; - ret = load_sandbox_scmi_test_devices(uts, &dev); if (ret) return ret; @@ -203,7 +193,6 @@ static int dm_test_scmi_resets(struct unit_test_state *uts) return release_sandbox_scmi_test_devices(uts, dev); } - DM_TEST(dm_test_scmi_resets, UT_TESTF_SCAN_FDT); static int dm_test_scmi_voltage_domains(struct unit_test_state *uts) From 3dfd7393709fd310e732b02796167bd012919cb7 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 8 Mar 2021 22:38:09 +0100 Subject: [PATCH 16/35] test: scmi: add local variables for scmi agent reference Add local variables agent0/agent1 to refer to SCMI sandbox context agent and ease readability of the test. For consistency, rename regul_dev to regul0_dev and remove sandbox_voltd in dm_test_scmi_voltage_domains(). Signed-off-by: Etienne Carriere Reviewed-by: Simon Glass --- test/dm/scmi.c | 100 +++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/test/dm/scmi.c b/test/dm/scmi.c index 2eaf06392e..c938e6d4fc 100644 --- a/test/dm/scmi.c +++ b/test/dm/scmi.c @@ -39,6 +39,8 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, { struct sandbox_scmi_devices *scmi_devices; struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; + struct sandbox_scmi_agent *agent1; /* Device references to check context against test sequence */ scmi_devices = sandbox_scmi_devices_ctx(dev); @@ -50,20 +52,22 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, /* State of the simulated SCMI server exposed */ scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; + agent1 = scmi_ctx->agent[1]; ut_asserteq(2, scmi_ctx->agent_count); - ut_assertnonnull(scmi_ctx->agent[0]); - ut_asserteq(2, scmi_ctx->agent[0]->clk_count); - ut_assertnonnull(scmi_ctx->agent[0]->clk); - ut_asserteq(1, scmi_ctx->agent[0]->reset_count); - ut_assertnonnull(scmi_ctx->agent[0]->reset); - ut_asserteq(2, scmi_ctx->agent[0]->voltd_count); - ut_assertnonnull(scmi_ctx->agent[0]->voltd); + ut_assertnonnull(agent0); + ut_asserteq(2, agent0->clk_count); + ut_assertnonnull(agent0->clk); + ut_asserteq(1, agent0->reset_count); + ut_assertnonnull(agent0->reset); + ut_asserteq(2, agent0->voltd_count); + ut_assertnonnull(agent0->voltd); - ut_assertnonnull(scmi_ctx->agent[1]); - ut_assertnonnull(scmi_ctx->agent[1]->clk); - ut_asserteq(1, scmi_ctx->agent[1]->clk_count); + ut_assertnonnull(agent1); + ut_assertnonnull(agent1->clk); + ut_asserteq(1, agent1->clk_count); return 0; } @@ -114,6 +118,8 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) { struct sandbox_scmi_devices *scmi_devices; struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; + struct sandbox_scmi_agent *agent1; struct udevice *dev = NULL; int ret_dev; int ret; @@ -124,6 +130,8 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) scmi_devices = sandbox_scmi_devices_ctx(dev); scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; + agent1 = scmi_ctx->agent[1]; /* Test SCMI clocks rate manipulation */ ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0])); @@ -133,9 +141,9 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) ret_dev = clk_set_rate(&scmi_devices->clk[1], 1088); ut_assert(!ret_dev || ret_dev == 1088); - ut_asserteq(1000, scmi_ctx->agent[0]->clk[0].rate); - ut_asserteq(1088, scmi_ctx->agent[0]->clk[1].rate); - ut_asserteq(44, scmi_ctx->agent[1]->clk[0].rate); + ut_asserteq(1000, agent0->clk[0].rate); + ut_asserteq(1088, agent0->clk[1].rate); + ut_asserteq(44, agent1->clk[0].rate); ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0])); ut_asserteq(1088, clk_get_rate(&scmi_devices->clk[1])); @@ -146,23 +154,23 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) ut_assert(!ret_dev || ret_dev == 333); /* Test SCMI clocks gating manipulation */ - ut_assert(!scmi_ctx->agent[0]->clk[0].enabled); - ut_assert(!scmi_ctx->agent[0]->clk[1].enabled); - ut_assert(!scmi_ctx->agent[1]->clk[0].enabled); + ut_assert(!agent0->clk[0].enabled); + ut_assert(!agent0->clk[1].enabled); + ut_assert(!agent1->clk[0].enabled); ut_asserteq(0, clk_enable(&scmi_devices->clk[1])); ut_asserteq(0, clk_enable(&scmi_devices->clk[2])); - ut_assert(!scmi_ctx->agent[0]->clk[0].enabled); - ut_assert(scmi_ctx->agent[0]->clk[1].enabled); - ut_assert(scmi_ctx->agent[1]->clk[0].enabled); + ut_assert(!agent0->clk[0].enabled); + ut_assert(agent0->clk[1].enabled); + ut_assert(agent1->clk[0].enabled); ut_assertok(clk_disable(&scmi_devices->clk[1])); ut_assertok(clk_disable(&scmi_devices->clk[2])); - ut_assert(!scmi_ctx->agent[0]->clk[0].enabled); - ut_assert(!scmi_ctx->agent[0]->clk[1].enabled); - ut_assert(!scmi_ctx->agent[1]->clk[0].enabled); + ut_assert(!agent0->clk[0].enabled); + ut_assert(!agent0->clk[1].enabled); + ut_assert(!agent1->clk[0].enabled); return release_sandbox_scmi_test_devices(uts, dev); } @@ -172,6 +180,7 @@ static int dm_test_scmi_resets(struct unit_test_state *uts) { struct sandbox_scmi_devices *scmi_devices; struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; struct udevice *dev = NULL; int ret; @@ -181,15 +190,16 @@ static int dm_test_scmi_resets(struct unit_test_state *uts) scmi_devices = sandbox_scmi_devices_ctx(dev); scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; /* Test SCMI resect controller manipulation */ - ut_assert(!scmi_ctx->agent[0]->reset[0].asserted) + ut_assert(!agent0->reset[0].asserted) ut_assertok(reset_assert(&scmi_devices->reset[0])); - ut_assert(scmi_ctx->agent[0]->reset[0].asserted) + ut_assert(agent0->reset[0].asserted) ut_assertok(reset_deassert(&scmi_devices->reset[0])); - ut_assert(!scmi_ctx->agent[0]->reset[0].asserted); + ut_assert(!agent0->reset[0].asserted); return release_sandbox_scmi_test_devices(uts, dev); } @@ -199,51 +209,51 @@ static int dm_test_scmi_voltage_domains(struct unit_test_state *uts) { struct sandbox_scmi_devices *scmi_devices; struct sandbox_scmi_service *scmi_ctx; - struct sandbox_scmi_voltd *sandbox_voltd; + struct sandbox_scmi_agent *agent0; struct dm_regulator_uclass_plat *uc_pdata; struct udevice *dev; - struct udevice *regul_dev; + struct udevice *regul0_dev; ut_assertok(load_sandbox_scmi_test_devices(uts, &dev)); scmi_devices = sandbox_scmi_devices_ctx(dev); scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; /* Set/Get an SCMI voltage domain level */ - sandbox_voltd = &scmi_ctx->agent[0]->voltd[0]; - regul_dev = scmi_devices->regul[0]; - ut_assert(regul_dev); + regul0_dev = scmi_devices->regul[0]; + ut_assert(regul0_dev); - uc_pdata = dev_get_uclass_plat(regul_dev); + uc_pdata = dev_get_uclass_plat(regul0_dev); ut_assert(uc_pdata); - ut_assertok(regulator_set_value(regul_dev, uc_pdata->min_uV)); - ut_asserteq(sandbox_voltd->voltage_uv, uc_pdata->min_uV); + ut_assertok(regulator_set_value(regul0_dev, uc_pdata->min_uV)); + ut_asserteq(agent0->voltd[0].voltage_uv, uc_pdata->min_uV); - ut_assert(regulator_get_value(regul_dev) == uc_pdata->min_uV); + ut_assert(regulator_get_value(regul0_dev) == uc_pdata->min_uV); - ut_assertok(regulator_set_value(regul_dev, uc_pdata->max_uV)); - ut_asserteq(sandbox_voltd->voltage_uv, uc_pdata->max_uV); + ut_assertok(regulator_set_value(regul0_dev, uc_pdata->max_uV)); + ut_asserteq(agent0->voltd[0].voltage_uv, uc_pdata->max_uV); - ut_assert(regulator_get_value(regul_dev) == uc_pdata->max_uV); + ut_assert(regulator_get_value(regul0_dev) == uc_pdata->max_uV); /* Enable/disable SCMI voltage domains */ ut_assertok(regulator_set_enable(scmi_devices->regul[0], false)); ut_assertok(regulator_set_enable(scmi_devices->regul[1], false)); - ut_assert(!scmi_ctx->agent[0]->voltd[0].enabled); - ut_assert(!scmi_ctx->agent[0]->voltd[1].enabled); + ut_assert(!agent0->voltd[0].enabled); + ut_assert(!agent0->voltd[1].enabled); ut_assertok(regulator_set_enable(scmi_devices->regul[0], true)); - ut_assert(scmi_ctx->agent[0]->voltd[0].enabled); - ut_assert(!scmi_ctx->agent[0]->voltd[1].enabled); + ut_assert(agent0->voltd[0].enabled); + ut_assert(!agent0->voltd[1].enabled); ut_assertok(regulator_set_enable(scmi_devices->regul[1], true)); - ut_assert(scmi_ctx->agent[0]->voltd[0].enabled); - ut_assert(scmi_ctx->agent[0]->voltd[1].enabled); + ut_assert(agent0->voltd[0].enabled); + ut_assert(agent0->voltd[1].enabled); ut_assertok(regulator_set_enable(scmi_devices->regul[0], false)); - ut_assert(!scmi_ctx->agent[0]->voltd[0].enabled); - ut_assert(scmi_ctx->agent[0]->voltd[1].enabled); + ut_assert(!agent0->voltd[0].enabled); + ut_assert(agent0->voltd[1].enabled); return release_sandbox_scmi_test_devices(uts, dev); } From 05a0776ed53ae6c9099595db17b72af2b6b25bae Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Wed, 10 Mar 2021 08:15:00 +0100 Subject: [PATCH 17/35] rtc: add support for rv3028 rtc Add support for rtc3028 rtc from microcrystal. based on linux dirver: commit a38fd8748464: ("Linux 5.12-rc2") Signed-off-by: Heiko Schocher Reviewed-by: Stefan Roese --- drivers/rtc/Kconfig | 6 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rv3028.c | 208 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/rtc/rv3028.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index dafba35279..c84a9d2b27 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -102,6 +102,12 @@ config RTC_PCF8563 If you say yes here you get support for the Philips PCF8563 RTC and compatible chips. +config RTC_RV3028 + bool "Enable RV3028 driver" + depends on DM_RTC + help + The MicroCrystal RV3028 is a I2C Real Time Clock (RTC) + config RTC_RV3029 bool "Enable RV3029 driver" depends on DM_RTC diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 15609e7b18..f668cf9050 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_PCF2127) += pcf2127.o obj-$(CONFIG_RTC_PL031) += pl031.o obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o obj-$(CONFIG_RTC_RS5C372A) += rs5c372.o +obj-$(CONFIG_RTC_RV3028) += rv3028.o obj-$(CONFIG_RTC_RV3029) += rv3029.o obj-$(CONFIG_RTC_RV8803) += rv8803.o obj-$(CONFIG_RTC_RX8025) += rx8025.o diff --git a/drivers/rtc/rv3028.c b/drivers/rtc/rv3028.c new file mode 100644 index 0000000000..9f63afc14a --- /dev/null +++ b/drivers/rtc/rv3028.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RTC driver for the Micro Crystal RV3028 + * + * based on linux driver from + * Copyright (C) 2019 Micro Crystal SA + * + * Alexandre Belloni + * + */ + +#include +#include +#include + +#define RV3028_SEC 0x00 +#define RV3028_MIN 0x01 +#define RV3028_HOUR 0x02 +#define RV3028_WDAY 0x03 +#define RV3028_DAY 0x04 +#define RV3028_MONTH 0x05 +#define RV3028_YEAR 0x06 +#define RV3028_ALARM_MIN 0x07 +#define RV3028_ALARM_HOUR 0x08 +#define RV3028_ALARM_DAY 0x09 +#define RV3028_STATUS 0x0E +#define RV3028_CTRL1 0x0F +#define RV3028_CTRL2 0x10 +#define RV3028_EVT_CTRL 0x13 +#define RV3028_TS_COUNT 0x14 +#define RV3028_TS_SEC 0x15 +#define RV3028_RAM1 0x1F +#define RV3028_EEPROM_ADDR 0x25 +#define RV3028_EEPROM_DATA 0x26 +#define RV3028_EEPROM_CMD 0x27 +#define RV3028_CLKOUT 0x35 +#define RV3028_OFFSET 0x36 +#define RV3028_BACKUP 0x37 + +#define RV3028_STATUS_PORF BIT(0) +#define RV3028_STATUS_EVF BIT(1) +#define RV3028_STATUS_AF BIT(2) +#define RV3028_STATUS_TF BIT(3) +#define RV3028_STATUS_UF BIT(4) +#define RV3028_STATUS_BSF BIT(5) +#define RV3028_STATUS_CLKF BIT(6) +#define RV3028_STATUS_EEBUSY BIT(7) + +#define RV3028_CLKOUT_FD_MASK GENMASK(2, 0) +#define RV3028_CLKOUT_PORIE BIT(3) +#define RV3028_CLKOUT_CLKSY BIT(6) +#define RV3028_CLKOUT_CLKOE BIT(7) + +#define RV3028_CTRL1_EERD BIT(3) +#define RV3028_CTRL1_WADA BIT(5) + +#define RV3028_CTRL2_RESET BIT(0) +#define RV3028_CTRL2_12_24 BIT(1) +#define RV3028_CTRL2_EIE BIT(2) +#define RV3028_CTRL2_AIE BIT(3) +#define RV3028_CTRL2_TIE BIT(4) +#define RV3028_CTRL2_UIE BIT(5) +#define RV3028_CTRL2_TSE BIT(7) + +#define RV3028_EVT_CTRL_TSR BIT(2) + +#define RV3028_EEPROM_CMD_UPDATE 0x11 +#define RV3028_EEPROM_CMD_WRITE 0x21 +#define RV3028_EEPROM_CMD_READ 0x22 + +#define RV3028_EEBUSY_POLL 10000 +#define RV3028_EEBUSY_TIMEOUT 100000 + +#define RV3028_BACKUP_TCE BIT(5) +#define RV3028_BACKUP_TCR_MASK GENMASK(1, 0) + +#define OFFSET_STEP_PPT 953674 + +#define RTC_RV3028_LEN 7 + +static int rv3028_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + u8 regs[RTC_RV3028_LEN]; + u8 status; + int ret; + + ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1); + if (ret < 0) { + printf("%s: error reading RTC status: %x\n", __func__, ret); + return -EIO; + } + + if (status & RV3028_STATUS_PORF) { + printf("Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = dm_i2c_read(dev, RV3028_SEC, regs, sizeof(regs)); + if (ret < 0) { + printf("%s: error reading RTC: %x\n", __func__, ret); + return -EIO; + } + + tm->tm_sec = bcd2bin(regs[RV3028_SEC] & 0x7f); + tm->tm_min = bcd2bin(regs[RV3028_MIN] & 0x7f); + tm->tm_hour = bcd2bin(regs[RV3028_HOUR] & 0x3f); + tm->tm_wday = regs[RV3028_WDAY] & 0x7; + tm->tm_mday = bcd2bin(regs[RV3028_DAY] & 0x3f); + tm->tm_mon = bcd2bin(regs[RV3028_MONTH] & 0x1f); + tm->tm_year = bcd2bin(regs[RV3028_YEAR]) + 2000; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + debug("%s: %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; +} + +static int rv3028_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + u8 regs[RTC_RV3028_LEN]; + u8 status; + int ret; + + debug("%s: %4d-%02d-%02d (wday=%d( %2d:%02d:%02d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + if (tm->tm_year < 2000) { + printf("%s: year %d (before 2000) not supported\n", + __func__, tm->tm_year); + return -EINVAL; + } + + regs[RV3028_SEC] = bin2bcd(tm->tm_sec); + regs[RV3028_MIN] = bin2bcd(tm->tm_min); + regs[RV3028_HOUR] = bin2bcd(tm->tm_hour); + regs[RV3028_WDAY] = tm->tm_wday; + regs[RV3028_DAY] = bin2bcd(tm->tm_mday); + regs[RV3028_MONTH] = bin2bcd(tm->tm_mon); + regs[RV3028_YEAR] = bin2bcd(tm->tm_year - 2000); + + ret = dm_i2c_write(dev, RV3028_SEC, regs, sizeof(regs)); + if (ret) { + printf("%s: set rtc error: %d\n", __func__, ret); + return ret; + } + + ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1); + if (ret < 0) { + printf("%s: error reading RTC status: %x\n", __func__, ret); + return -EIO; + } + status |= RV3028_STATUS_PORF; + return dm_i2c_write(dev, RV3028_STATUS, &status, 1); +} + +static int rv3028_rtc_reset(struct udevice *dev) +{ + return 0; +} + +static int rv3028_rtc_read8(struct udevice *dev, unsigned int reg) +{ + u8 data; + int ret; + + ret = dm_i2c_read(dev, reg, &data, sizeof(data)); + return ret < 0 ? ret : data; +} + +static int rv3028_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + u8 data = val; + + return dm_i2c_write(dev, reg, &data, 1); +} + +static int rv3028_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | + DM_I2C_CHIP_WR_ADDRESS); + + return 0; +} + +static const struct rtc_ops rv3028_rtc_ops = { + .get = rv3028_rtc_get, + .set = rv3028_rtc_set, + .read8 = rv3028_rtc_read8, + .write8 = rv3028_rtc_write8, + .reset = rv3028_rtc_reset, +}; + +static const struct udevice_id rv3028_rtc_ids[] = { + { .compatible = "microcrystal,rv3028" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv3028) = { + .name = "rtc-rv3028", + .id = UCLASS_RTC, + .probe = rv3028_probe, + .of_match = rv3028_rtc_ids, + .ops = &rv3028_rtc_ops, +}; From 5c935eb6f7075b227178ddd3a3a231bef4b7238d Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Wed, 10 Mar 2021 22:39:23 +1100 Subject: [PATCH 18/35] terminal: correct stdio_dev invocations stdio_dev methods have taken a pointer to themselves since 709ea543 (nearly 7 years ago). Signed-off-by: Asherah Connor Reviewed-by: Simon Glass --- cmd/terminal.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/terminal.c b/cmd/terminal.c index f6e4d2539e..733701e059 100644 --- a/cmd/terminal.c +++ b/cmd/terminal.c @@ -33,8 +33,8 @@ int do_terminal(struct cmd_tbl *cmd, int flag, int argc, char *const argv[]) int c; /* read from console and display on serial port */ - if (stdio_devices[0]->tstc()) { - c = stdio_devices[0]->getc(); + if (stdio_devices[0]->tstc(stdio_devices[0])) { + c = stdio_devices[0]->getc(stdio_devices[0]); if (last_tilde == 1) { if (c == '.') { putc(c); @@ -43,7 +43,7 @@ int do_terminal(struct cmd_tbl *cmd, int flag, int argc, char *const argv[]) } else { last_tilde = 0; /* write the delayed tilde */ - dev->putc('~'); + dev->putc(dev, '~'); /* fall-through to print current * character */ } @@ -53,12 +53,12 @@ int do_terminal(struct cmd_tbl *cmd, int flag, int argc, char *const argv[]) puts("[u-boot]"); putc(c); } - dev->putc(c); + dev->putc(dev, c); } /* read from serial port and display on console */ - if (dev->tstc()) { - c = dev->getc(); + if (dev->tstc(dev)) { + c = dev->getc(dev); putc(c); } } From ac3821430e0e2dc6ddb766950b0098c67d4969d3 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Wed, 10 Mar 2021 22:39:24 +1100 Subject: [PATCH 19/35] terminal: only serial_reinit_all if available serial_reinit_all() is only available if CONFIG_SERIAL is defined (i.e. !CONFIG_DM_SERIAL). Signed-off-by: Asherah Connor Reviewed-by: Simon Glass --- cmd/terminal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/terminal.c b/cmd/terminal.c index 733701e059..9e32a4191e 100644 --- a/cmd/terminal.c +++ b/cmd/terminal.c @@ -25,7 +25,9 @@ int do_terminal(struct cmd_tbl *cmd, int flag, int argc, char *const argv[]) if (!dev) return -1; - serial_reinit_all(); + if (IS_ENABLED(CONFIG_SERIAL)) + serial_reinit_all(); + printf("Entering terminal mode for port %s\n", dev->name); puts("Use '~.' to leave the terminal and get back to u-boot\n"); From 92e84896112037833e429d629f87cedbeb255d5a Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Wed, 10 Mar 2021 15:35:11 +0200 Subject: [PATCH 20/35] tee: optee: Change printing during optee_probe Right now the error messages when optee has a version mismatch or shared memory is not configured are done with a debug(). That's not very convenient since you have to enable debugging to figure out what's going on, although this is an actual error. So let's switch the debug() -> dev_err() and report those explicitly. Signed-off-by: Ilias Apalodimas Reviewed-by: Patrick Delaunay --- drivers/tee/optee/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index b898c32edc..73dbb22ba0 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -624,14 +624,14 @@ static int optee_probe(struct udevice *dev) u32 sec_caps; if (!is_optee_api(pdata->invoke_fn)) { - debug("%s: OP-TEE api uid mismatch\n", __func__); + dev_err(dev, "OP-TEE api uid mismatch\n"); return -ENOENT; } print_os_revision(dev, pdata->invoke_fn); if (!api_revision_is_compatible(pdata->invoke_fn)) { - debug("%s: OP-TEE api revision mismatch\n", __func__); + dev_err(dev, "OP-TEE api revision mismatch\n"); return -ENOENT; } @@ -642,7 +642,7 @@ static int optee_probe(struct udevice *dev) */ if (!exchange_capabilities(pdata->invoke_fn, &sec_caps) || !(sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)) { - debug("%s: OP-TEE capabilities mismatch\n", __func__); + dev_err(dev, "OP-TEE capabilities mismatch\n"); return -ENOENT; } From d3358ecc54be0bc3b4dd11f7a63eab0a2842f772 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 11 Mar 2021 00:15:41 -0500 Subject: [PATCH 21/35] lib: string: Fix strlcpy return value strlcpy should always return the number of bytes copied. We were accidentally missing the nul-terminator. We also always used to return a non-zero value, even if we did not actually copy anything. Fixes: 23cd138503 ("Integrate USB gadget layer and USB CDC driver layer") Signed-off-by: Sean Anderson --- lib/string.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/string.c b/lib/string.c index 73b984123d..1b867ac09d 100644 --- a/lib/string.c +++ b/lib/string.c @@ -114,17 +114,21 @@ char * strncpy(char * dest,const char *src,size_t count) * NUL-terminated string that fits in the buffer (unless, * of course, the buffer size is zero). It does not pad * out the result like strncpy() does. + * + * Return: the number of bytes copied */ size_t strlcpy(char *dest, const char *src, size_t size) { - size_t ret = strlen(src); - if (size) { - size_t len = (ret >= size) ? size - 1 : ret; + size_t srclen = strlen(src); + size_t len = (srclen >= size) ? size - 1 : srclen; + memcpy(dest, src, len); dest[len] = '\0'; + return len + 1; } - return ret; + + return 0; } #endif From 9af869c4145a668b6db9accdea554eb57895a25e Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 11 Mar 2021 00:15:42 -0500 Subject: [PATCH 22/35] lib: string: Implement strlcat This introduces strlcat, which provides a safer interface than strncat. It never copies more than its size bytes, including the terminating nul. In addition, it never reads past dest[size - 1], even if dest is not nul-terminated. This also removes the stub for dwc3 now that we have a proper implementation. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- drivers/usb/dwc3/linux-compat.h | 6 ------ include/linux/string.h | 3 +++ lib/string.c | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h index 82793765be..3bb0bda5a6 100644 --- a/drivers/usb/dwc3/linux-compat.h +++ b/drivers/usb/dwc3/linux-compat.h @@ -13,10 +13,4 @@ #define dev_WARN(dev, format, arg...) debug(format, ##arg) -static inline size_t strlcat(char *dest, const char *src, size_t n) -{ - strcat(dest, src); - return strlen(dest) + strlen(src); -} - #endif diff --git a/include/linux/string.h b/include/linux/string.h index d67998e5c4..dd255f2163 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -35,6 +35,9 @@ extern char * strcat(char *, const char *); #ifndef __HAVE_ARCH_STRNCAT extern char * strncat(char *, const char *, __kernel_size_t); #endif +#ifndef __HAVE_ARCH_STRLCAT +size_t strlcat(char *, const char *, size_t); +#endif #ifndef __HAVE_ARCH_STRCMP extern int strcmp(const char *,const char *); #endif diff --git a/lib/string.c b/lib/string.c index 1b867ac09d..a0cff8fe88 100644 --- a/lib/string.c +++ b/lib/string.c @@ -180,6 +180,25 @@ char * strncat(char *dest, const char *src, size_t count) } #endif +#ifndef __HAVE_ARCH_STRLCAT +/** + * strlcat - Append a length-limited, %NUL-terminated string to another + * @dest: The string to be appended to + * @src: The string to append to it + * @size: The size of @dest + * + * Compatible with *BSD: the result is always a valid NUL-terminated string that + * fits in the buffer (unless, of course, the buffer size is zero). It does not + * write past @size like strncat() does. + */ +size_t strlcat(char *dest, const char *src, size_t size) +{ + size_t len = strnlen(dest, size); + + return len + strlcpy(dest + len, src, size - len); +} +#endif + #ifndef __HAVE_ARCH_STRCMP /** * strcmp - Compare two strings From c4ac52f55d90bd46ade9db6f761638fdfdf26e16 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 11 Mar 2021 00:15:43 -0500 Subject: [PATCH 23/35] test: Add test for strlcat This test is adapted from glibc, which is very concerned about alignment. It also tests strlcpy by dependency. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- test/lib/Makefile | 1 + test/lib/strlcat.c | 126 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 test/lib/strlcat.c diff --git a/test/lib/Makefile b/test/lib/Makefile index a30f615aa9..aa2e66bc7f 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -11,6 +11,7 @@ obj-y += longjmp.o obj-$(CONFIG_CONSOLE_RECORD) += test_print.o obj-$(CONFIG_SSCANF) += sscanf.o obj-y += string.o +obj-y += strlcat.o obj-$(CONFIG_ERRNO_STR) += test_errno_str.o obj-$(CONFIG_UT_LIB_ASN1) += asn1.o obj-$(CONFIG_UT_LIB_RSA) += rsa.o diff --git a/test/lib/strlcat.c b/test/lib/strlcat.c new file mode 100644 index 0000000000..ee61684c40 --- /dev/null +++ b/test/lib/strlcat.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.1+ +/* + * Copyright (C) 2021 Sean Anderson + * Copyright (C) 2011-2021 Free Software Foundation, Inc. + * + * These tests adapted from glibc's string/test-strncat.c + */ + +#include +#include +#include +#include + +#define BUF_SIZE 4096 +char buf1[BUF_SIZE], buf2[BUF_SIZE]; + +static int do_test_strlcat(struct unit_test_state *uts, int line, size_t align1, + size_t align2, size_t len1, size_t len2, size_t n) +{ + char *s1, *s2; + size_t i, len, expected, actual; + + align1 &= 7; + if (align1 + len1 >= BUF_SIZE) + return 0; + if (align1 + n > BUF_SIZE) + return 0; + + align2 &= 7; + if (align2 + len1 + len2 >= BUF_SIZE) + return 0; + if (align2 + len1 + n > BUF_SIZE) + return 0; + + s1 = buf1 + align1; + s2 = buf2 + align2; + + for (i = 0; i < len1 - 1; i++) + s1[i] = 32 + 23 * i % (127 - 32); + s1[len1 - 1] = '\0'; + + for (i = 0; i < len2 - 1; i++) + s2[i] = 32 + 23 * i % (127 - 32); + s2[len2 - 1] = '\0'; + + expected = len2 < n ? min(len1 + len2 - 1, n) : n; + actual = strlcat(s2, s1, n); + if (expected != actual) { + ut_failf(uts, __FILE__, line, __func__, + "strlcat(s2, s1, 2) == len2 < n ? min(len1 + len2, n) : n", + "Expected %#lx (%ld), got %#lx (%ld)", + expected, expected, actual, actual); + return CMD_RET_FAILURE; + } + + len = min3(len1, n - len2, (size_t)0); + if (memcmp(s2 + len2, s1, len)) { + ut_failf(uts, __FILE__, line, __func__, + "s2 + len1 == s1", + "Expected \"%.*s\", got \"%.*s\"", + (int)len, s1, (int)len, s2 + len2); + return CMD_RET_FAILURE; + } + + i = min(n, len1 + len2 - 1) - 1; + if (len2 < n && s2[i] != '\0') { + ut_failf(uts, __FILE__, line, __func__, + "n < len1 && s2[len2 + n] == '\\0'", + "Expected s2[%ld] = '\\0', got %d ('%c')", + i, s2[i], s2[i]); + return CMD_RET_FAILURE; + } + + return 0; +} + +#define test_strlcat(align1, align2, len1, len2, n) do { \ + int ret = do_test_strlcat(uts, __LINE__, align1, align2, len1, len2, \ + n); \ + if (ret) \ + return ret; \ +} while (0) + +static int lib_test_strlcat(struct unit_test_state *uts) +{ + size_t i, n; + + test_strlcat(0, 2, 2, 2, SIZE_MAX); + test_strlcat(0, 0, 4, 4, SIZE_MAX); + test_strlcat(4, 0, 4, 4, SIZE_MAX); + test_strlcat(0, 0, 8, 8, SIZE_MAX); + test_strlcat(0, 8, 8, 8, SIZE_MAX); + + for (i = 1; i < 8; i++) { + test_strlcat(0, 0, 8 << i, 8 << i, SIZE_MAX); + test_strlcat(8 - i, 2 * i, 8 << i, 8 << i, SIZE_MAX); + test_strlcat(0, 0, 8 << i, 2 << i, SIZE_MAX); + test_strlcat(8 - i, 2 * i, 8 << i, 2 << i, SIZE_MAX); + + test_strlcat(i, 2 * i, 8 << i, 1, SIZE_MAX); + test_strlcat(2 * i, i, 8 << i, 1, SIZE_MAX); + test_strlcat(i, i, 8 << i, 10, SIZE_MAX); + } + + for (n = 2; n <= 2048; n *= 4) { + test_strlcat(0, 2, 2, 2, n); + test_strlcat(0, 0, 4, 4, n); + test_strlcat(4, 0, 4, 4, n); + test_strlcat(0, 0, 8, 8, n); + test_strlcat(0, 8, 8, 8, n); + + for (i = 1; i < 8; i++) { + test_strlcat(0, 0, 8 << i, 8 << i, n); + test_strlcat(8 - i, 2 * i, 8 << i, 8 << i, n); + test_strlcat(0, 0, 8 << i, 2 << i, n); + test_strlcat(8 - i, 2 * i, 8 << i, 2 << i, n); + + test_strlcat(i, 2 * i, 8 << i, 1, n); + test_strlcat(2 * i, i, 8 << i, 1, n); + test_strlcat(i, i, 8 << i, 10, n); + } + } + + return 0; +} +LIB_TEST(lib_test_strlcat, 0); From 69a752983171c2983fc1f29c7cfa1844f41e5d8b Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 11 Mar 2021 00:15:44 -0500 Subject: [PATCH 24/35] fastboot: Fix possible buffer overrun This fixes several uses of strn(cpy|cat) which did not terminate their destinations properly. Fixes de1728ce4c ("fastboot: Allow u-boot-style partitions") Reported-by: Coverity Scan Signed-off-by: Sean Anderson --- drivers/fastboot/fb_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index 8e74e50e91..2f3837e559 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -40,7 +40,7 @@ static int raw_part_get_info_by_name(struct blk_desc *dev_desc, /* check for raw partition descriptor */ strcpy(env_desc_name, "fastboot_raw_partition_"); - strncat(env_desc_name, name, PART_NAME_LEN); + strlcat(env_desc_name, name, PART_NAME_LEN); raw_part_desc = strdup(env_get(env_desc_name)); if (raw_part_desc == NULL) return -ENODEV; @@ -61,7 +61,7 @@ static int raw_part_get_info_by_name(struct blk_desc *dev_desc, info->start = simple_strtoul(argv[0], NULL, 0); info->size = simple_strtoul(argv[1], NULL, 0); info->blksz = dev_desc->blksz; - strncpy((char *)info->name, name, PART_NAME_LEN); + strlcpy((char *)info->name, name, PART_NAME_LEN); if (raw_part_desc) { if (strcmp(strsep(&raw_part_desc, " "), "mmcpart") == 0) { @@ -114,7 +114,7 @@ static int part_get_info_by_name_or_alias(struct blk_desc **dev_desc, /* check for alias */ strcpy(env_alias_name, "fastboot_partition_alias_"); - strncat(env_alias_name, name, PART_NAME_LEN); + strlcat(env_alias_name, name, PART_NAME_LEN); aliased_part_name = env_get(env_alias_name); if (aliased_part_name != NULL) ret = do_get_part_info(dev_desc, aliased_part_name, From d9c305071069164feceaff3e543c61693bc2c401 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 11 Mar 2021 00:15:45 -0500 Subject: [PATCH 25/35] checkpatch: Add warnings for using strn(cat|cpy) strn(cat|cpy) has a bad habit of not nul-terminating the destination, resulting in constructions like strncpy(foo, bar, sizeof(foo) - 1); foo[sizeof(foo) - 1] = '\0'; However, it is very easy to forget about this behavior and accidentally leave a string unterminated. This has shown up in some recent coverity scans [1, 2] (including code recently touched by yours truly). Fortunately, the guys at OpenBSD came up with strl(cat|cpy), which always nul-terminate strings. These functions are already in U-Boot, so we should encourage new code to use them instead of strn(cat|cpy). [1] https://lists.denx.de/pipermail/u-boot/2021-March/442888.html [2] https://lists.denx.de/pipermail/u-boot/2021-January/438073.html Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- scripts/checkpatch.pl | 6 ++++++ tools/patman/test_checkpatch.py | 14 +++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 755f4802a4..4e047586a6 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2365,6 +2365,12 @@ sub u_boot_line { "Use 'if (IS_ENABLED(CONFIG...))' instead of '#if or #ifdef' where possible\n" . $herecurr); } + # prefer strl(cpy|cat) over strn(cpy|cat) + if ($line =~ /\bstrn(cpy|cat)\s*\(/) { + WARN("STRL", + "strl$1 is preferred over strn$1 because it always produces a nul-terminated string\n" . $herecurr); + } + # use defconfig to manage CONFIG_CMD options if ($line =~ /\+\s*#\s*(define|undef)\s+(CONFIG_CMD\w*)\b/) { ERROR("DEFINE_CONFIG_CMD", diff --git a/tools/patman/test_checkpatch.py b/tools/patman/test_checkpatch.py index a4fec1d4c1..56af5265cc 100644 --- a/tools/patman/test_checkpatch.py +++ b/tools/patman/test_checkpatch.py @@ -353,7 +353,7 @@ index 0000000..2234c87 Args: pm: PatchMaker object to use - msg" Expected message (e.g. 'LIVETREE') + msg: Expected message (e.g. 'LIVETREE') pmtype: Type of problem ('error', 'warning') """ result = pm.run_checkpatch() @@ -439,6 +439,18 @@ index 0000000..2234c87 self.check_struct('per_device_auto', '_priv', 'DEVICE_PRIV_AUTO') self.check_struct('per_device_plat_auto', '_plat', 'DEVICE_PLAT_AUTO') + def check_strl(self, func): + """Check one of the checks for strn(cpy|cat)""" + pm = PatchMaker() + pm.add_line('common/main.c', "strn%s(foo, bar, sizeof(foo));" % func) + self.checkSingleMessage(pm, "STRL", + "strl%s is preferred over strn%s because it always produces a nul-terminated string\n" + % (func, func)) + + def testStrl(self): + """Check for uses of strn(cat|cpy)""" + self.check_strl("cat"); + self.check_strl("cpy"); if __name__ == "__main__": unittest.main() From dc514d7ea2d03cf06d31f084ee356c01d35f9a83 Mon Sep 17 00:00:00 2001 From: Trevor Woerner Date: Mon, 15 Mar 2021 12:01:33 -0400 Subject: [PATCH 26/35] moveconfig.py: add to the "do not process" list Skip the processing of *.aml and *.dat files while iterating through the source in order to process header files. Signed-off-by: Trevor Woerner --- tools/moveconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/moveconfig.py b/tools/moveconfig.py index 1ac30c0028..41dd803c4e 100755 --- a/tools/moveconfig.py +++ b/tools/moveconfig.py @@ -679,7 +679,7 @@ def cleanup_headers(configs, options): continue for filename in filenames: if not filename.endswith(('~', '.dts', '.dtsi', '.bin', - '.elf')): + '.elf','.aml','.dat')): header_path = os.path.join(dirpath, filename) # This file contains UTF-16 data and no CONFIG symbols if header_path == 'include/video_font_data.h': From 2a3f161c8b16ed4fe4bd215dddfa21f4ddbd3e37 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Tue, 16 Mar 2021 09:29:40 +0100 Subject: [PATCH 27/35] scmi: correctly configure MMU for SCMI buffer Align the MMU area for SCMI shared buffer on section size; use the ALIGN macro in mmu_set_region_dcache_behaviour call. Since commit d877f8fd0f09 ("arm: provide a function for boards init code to modify MMU virtual-physical map") the parameter of mmu_set_region_dcache_behaviour need to be MMU_SECTION_SIZE aligned. Fixes: 240720e9052f ("firmware: scmi: mailbox/smt agent device") Signed-off-by: Patrick Delaunay Reviewed-by: Etienne Carriere --- drivers/firmware/scmi/smt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 60b9d499b7..4954355c1a 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -56,8 +56,10 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) #ifdef CONFIG_ARM if (dcache_status()) - mmu_set_region_dcache_behaviour((uintptr_t)smt->buf, - smt->size, DCACHE_OFF); + mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE), + ALIGN(smt->size, MMU_SECTION_SIZE), + DCACHE_OFF); + #endif return 0; From 5b0b43e0e25dbeed62a2155e7f3be562b9ceb9bd Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Fri, 19 Mar 2021 18:21:40 +1100 Subject: [PATCH 28/35] x86: qemu: move QFW to its own uclass We move qfw into its own uclass and split the PIO functions into a specific driver for that uclass. The PIO driver is selected in the qemu-x86 board config (this covers x86 and x86_64). include/qfw.h is cleaned up and documentation added. Signed-off-by: Asherah Connor Reviewed-by: Simon Glass Reviewed-by: Bin Meng Tested-by: Bin Meng --- arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 49 +------ arch/x86/cpu/qfw_cpu.c | 11 +- board/emulation/qemu-x86/Kconfig | 1 + cmd/qfw.c | 56 ++++--- common/Makefile | 2 + common/qfw.c | 104 +++++++++++++ drivers/misc/Kconfig | 11 +- drivers/misc/Makefile | 5 +- drivers/misc/qfw.c | 245 +++++++++++-------------------- drivers/misc/qfw_pio.c | 69 +++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 204 ++++++++++++++++++++----- 13 files changed, 492 insertions(+), 275 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_pio.c diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c index 9ce86b379c..735b656084 100644 --- a/arch/x86/cpu/qemu/cpu.c +++ b/arch/x86/cpu/qemu/cpu.c @@ -22,7 +22,14 @@ int cpu_qemu_get_desc(const struct udevice *dev, char *buf, int size) static int cpu_qemu_get_count(const struct udevice *dev) { - return qemu_fwcfg_online_cpus(); + int ret; + struct udevice *qfw_dev; + + ret = qfw_get_dev(&qfw_dev); + if (ret) + return ret; + + return qfw_online_cpus(qfw_dev); } static const struct cpu_ops cpu_qemu_ops = { diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 044a429c13..e54082df7f 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,47 +17,9 @@ static bool i440fx; -#ifdef CONFIG_QFW - -/* on x86, the qfw registers are all IO ports */ -#define FW_CONTROL_PORT 0x510 -#define FW_DATA_PORT 0x511 -#define FW_DMA_PORT_LOW 0x514 -#define FW_DMA_PORT_HIGH 0x518 - -static void qemu_x86_fwcfg_read_entry_pio(uint16_t entry, - uint32_t size, void *address) -{ - uint32_t i = 0; - uint8_t *data = address; - - /* - * writting FW_CFG_INVALID will cause read operation to resume at - * last offset, otherwise read will start at offset 0 - * - * Note: on platform where the control register is IO port, the - * endianness is little endian. - */ - if (entry != FW_CFG_INVALID) - outw(cpu_to_le16(entry), FW_CONTROL_PORT); - - /* the endianness of data register is string-preserving */ - while (size--) - data[i++] = inb(FW_DATA_PORT); -} - -static void qemu_x86_fwcfg_read_entry_dma(struct fw_cfg_dma_access *dma) -{ - /* the DMA address register is big endian */ - outl(cpu_to_be32((uintptr_t)dma), FW_DMA_PORT_HIGH); - - while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR) - __asm__ __volatile__ ("pause"); -} - -static struct fw_cfg_arch_ops fwcfg_x86_ops = { - .arch_read_pio = qemu_x86_fwcfg_read_entry_pio, - .arch_read_dma = qemu_x86_fwcfg_read_entry_dma +#if CONFIG_IS_ENABLED(QFW_PIO) +U_BOOT_DRVINFO(x86_qfw_pio) = { + .name = "qfw_pio", }; #endif @@ -132,10 +95,6 @@ static void qemu_chipset_init(void) enable_pm_ich9(); } - -#ifdef CONFIG_QFW - qemu_fwcfg_init(&fwcfg_x86_ops); -#endif } #if !CONFIG_IS_ENABLED(SPL_X86_32BIT_INIT) diff --git a/arch/x86/cpu/qfw_cpu.c b/arch/x86/cpu/qfw_cpu.c index b959eaddde..ee00b8fe73 100644 --- a/arch/x86/cpu/qfw_cpu.c +++ b/arch/x86/cpu/qfw_cpu.c @@ -18,7 +18,7 @@ int qemu_cpu_fixup(void) int cpu_num; int cpu_online; struct uclass *uc; - struct udevice *dev, *pdev; + struct udevice *dev, *pdev, *qfwdev; struct cpu_plat *plat; char *cpu; @@ -39,6 +39,13 @@ int qemu_cpu_fixup(void) return -ENODEV; } + /* get qfw dev */ + ret = qfw_get_dev(&qfwdev); + if (ret) { + printf("unable to find qfw device\n"); + return ret; + } + /* calculate cpus that are already bound */ cpu_num = 0; for (uclass_find_first_device(UCLASS_CPU, &dev); @@ -48,7 +55,7 @@ int qemu_cpu_fixup(void) } /* get actual cpu number */ - cpu_online = qemu_fwcfg_online_cpus(); + cpu_online = qfw_online_cpus(qfwdev); if (cpu_online < 0) { printf("unable to get online cpu number: %d\n", cpu_online); return cpu_online; diff --git a/board/emulation/qemu-x86/Kconfig b/board/emulation/qemu-x86/Kconfig index 6d19299d8b..9bb8a726ed 100644 --- a/board/emulation/qemu-x86/Kconfig +++ b/board/emulation/qemu-x86/Kconfig @@ -20,6 +20,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select X86_RESET_VECTOR select QEMU + select QFW_PIO select BOARD_ROMSIZE_KB_1024 imply VIRTIO_PCI imply VIRTIO_NET diff --git a/cmd/qfw.c b/cmd/qfw.c index bb571487f0..e6a9fdb2af 100644 --- a/cmd/qfw.c +++ b/cmd/qfw.c @@ -8,19 +8,22 @@ #include #include #include +#include + +static struct udevice *qfw_dev; /* * This function prepares kernel for zboot. It loads kernel data * to 'load_addr', initrd to 'initrd_addr' and kernel command * line using qemu fw_cfg interface. */ -static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) +static int qemu_fwcfg_cmd_setup_kernel(void *load_addr, void *initrd_addr) { char *data_addr; uint32_t setup_size, kernel_size, cmdline_size, initrd_size; - qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size); - qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size); + qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size); + qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size); if (setup_size == 0 || kernel_size == 0) { printf("warning: no kernel available\n"); @@ -28,28 +31,28 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) } data_addr = load_addr; - qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA, - le32_to_cpu(setup_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA, + le32_to_cpu(setup_size), data_addr); data_addr += le32_to_cpu(setup_size); - qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA, - le32_to_cpu(kernel_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA, + le32_to_cpu(kernel_size), data_addr); data_addr += le32_to_cpu(kernel_size); data_addr = initrd_addr; - qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size); + qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size); if (initrd_size == 0) { printf("warning: no initrd available\n"); } else { - qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA, - le32_to_cpu(initrd_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA, + le32_to_cpu(initrd_size), data_addr); data_addr += le32_to_cpu(initrd_size); } - qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); + qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); if (cmdline_size) { - qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA, - le32_to_cpu(cmdline_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA, + le32_to_cpu(cmdline_size), data_addr); /* * if kernel cmdline only contains '\0', (e.g. no -append * when invoking qemu), do not update bootargs @@ -72,21 +75,20 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) return 0; } -static int qemu_fwcfg_list_firmware(void) +static int qemu_fwcfg_cmd_list_firmware(void) { int ret; struct fw_cfg_file_iter iter; struct fw_file *file; /* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(); + ret = qfw_read_firmware_list(qfw_dev); if (ret) return ret; - - for (file = qemu_fwcfg_file_iter_init(&iter); - !qemu_fwcfg_file_iter_end(&iter); - file = qemu_fwcfg_file_iter_next(&iter)) { + for (file = qfw_file_iter_init(qfw_dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { printf("%-56s\n", file->cfg.name); } @@ -96,7 +98,7 @@ static int qemu_fwcfg_list_firmware(void) static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - if (qemu_fwcfg_list_firmware() < 0) + if (qemu_fwcfg_cmd_list_firmware() < 0) return CMD_RET_FAILURE; return 0; @@ -105,14 +107,7 @@ static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, static int qemu_fwcfg_do_cpus(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - int ret = qemu_fwcfg_online_cpus(); - if (ret < 0) { - printf("QEMU fw_cfg interface not found\n"); - return CMD_RET_FAILURE; - } - - printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus()); - + printf("%d cpu(s) online\n", qfw_online_cpus(qfw_dev)); return 0; } @@ -153,7 +148,7 @@ static int qemu_fwcfg_do_load(struct cmd_tbl *cmdtp, int flag, return CMD_RET_FAILURE; } - return qemu_fwcfg_setup_kernel(load_addr, initrd_addr); + return qemu_fwcfg_cmd_setup_kernel(load_addr, initrd_addr); } static struct cmd_tbl fwcfg_commands[] = { @@ -168,7 +163,8 @@ static int do_qemu_fw(struct cmd_tbl *cmdtp, int flag, int argc, int ret; struct cmd_tbl *fwcfg_cmd; - if (!qemu_fwcfg_present()) { + ret = qfw_get_dev(&qfw_dev); + if (ret) { printf("QEMU fw_cfg interface not found\n"); return CMD_RET_USAGE; } diff --git a/common/Makefile b/common/Makefile index 215b8b26fd..0952ae23f8 100644 --- a/common/Makefile +++ b/common/Makefile @@ -138,3 +138,5 @@ obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += xyzModem.o obj-$(CONFIG_AVB_VERIFY) += avb_verify.o obj-$(CONFIG_SCP03) += scp03.o + +obj-$(CONFIG_QFW) += qfw.o diff --git a/common/qfw.c b/common/qfw.c new file mode 100644 index 0000000000..90cbb8c5dd --- /dev/null +++ b/common/qfw.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Miao Yan + * (C) Copyright 2021 Asherah Connor + */ + +#include +#include +#include +#include + +int qfw_get_dev(struct udevice **devp) +{ + return uclass_first_device_err(UCLASS_QFW, devp); +} + +int qfw_online_cpus(struct udevice *dev) +{ + u16 nb_cpus; + + qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus); + + return le16_to_cpu(nb_cpus); +} + +int qfw_read_firmware_list(struct udevice *dev) +{ + int i; + u32 count; + struct fw_file *file; + struct list_head *entry; + + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + /* don't read it twice */ + if (!list_empty(&qdev->fw_list)) + return 0; + + qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count); + if (!count) + return 0; + + count = be32_to_cpu(count); + for (i = 0; i < count; i++) { + file = malloc(sizeof(*file)); + if (!file) { + printf("error: allocating resource\n"); + goto err; + } + qfw_read_entry(dev, FW_CFG_INVALID, + sizeof(struct fw_cfg_file), &file->cfg); + file->addr = 0; + list_add_tail(&file->list, &qdev->fw_list); + } + + return 0; + +err: + list_for_each(entry, &qdev->fw_list) { + file = list_entry(entry, struct fw_file, list); + free(file); + } + + return -ENOMEM; +} + +struct fw_file *qfw_find_file(struct udevice *dev, const char *name) +{ + struct list_head *entry; + struct fw_file *file; + + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + list_for_each(entry, &qdev->fw_list) { + file = list_entry(entry, struct fw_file, list); + if (!strcmp(file->cfg.name, name)) + return file; + } + + return NULL; +} + +struct fw_file *qfw_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter) +{ + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + iter->entry = qdev->fw_list.next; + iter->end = &qdev->fw_list; + return list_entry((struct list_head *)iter->entry, + struct fw_file, list); +} + +struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter) +{ + iter->entry = ((struct list_head *)iter->entry)->next; + return list_entry((struct list_head *)iter->entry, + struct fw_file, list); +} + +bool qfw_file_iter_end(struct fw_cfg_file_iter *iter) +{ + return iter->entry == iter->end; +} diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7d2a299779..3a254eba4b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -368,8 +368,15 @@ config WINBOND_W83627 config QFW bool help - Hidden option to enable QEMU fw_cfg interface. This will be selected by - either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE. + Hidden option to enable QEMU fw_cfg interface and uclass. This will + be selected by either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE. + +config QFW_PIO + bool + depends on QFW + help + Hidden option to enable PIO QEMU fw_cfg interface. This will be + selected by the appropriate QEMU board. config I2C_EEPROM bool "Enable driver for generic I2C-attached EEPROMs" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 1a49396007..53bff72428 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,7 +55,10 @@ obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o -obj-$(CONFIG_QFW) += qfw.o +ifdef CONFIG_QFW +obj-y += qfw.o +obj-$(CONFIG_QFW_PIO) += qfw_pio.o +endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index f6eb6583ed..ea00be88a8 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -1,25 +1,22 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 Miao Yan + * (C) Copyright 2021 Asherah Connor */ +#define LOG_CATEGORY UCLASS_QFW + #include #include #include #include #include #include -#include +#include +#include #ifdef CONFIG_GENERATE_ACPI_TABLE #include #endif -#include - -static bool fwcfg_present; -static bool fwcfg_dma_present; -static struct fw_cfg_arch_ops *fwcfg_arch_ops; - -static LIST_HEAD(fw_list); #ifdef CONFIG_GENERATE_ACPI_TABLE /* @@ -32,7 +29,8 @@ static LIST_HEAD(fw_list); * be ignored. * @return: 0 on success, or negative value on failure */ -static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) +static int bios_linker_allocate(struct udevice *dev, + struct bios_linker_entry *entry, ulong *addr) { uint32_t size, align; struct fw_file *file; @@ -45,7 +43,7 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) return -EINVAL; } - file = qemu_fwcfg_find_file(entry->alloc.file); + file = qfw_find_file(dev, entry->alloc.file); if (!file) { printf("error: can't find file %s\n", entry->alloc.file); return -ENOENT; @@ -75,8 +73,8 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", file->cfg.name, size, entry->alloc.zone, align, aligned_addr); - qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), - size, (void *)aligned_addr); + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, + (void *)aligned_addr); file->addr = aligned_addr; /* adjust address for low memory allocation */ @@ -94,16 +92,17 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) * ACPI tables * @return: 0 on success, or negative value on failure */ -static int bios_linker_add_pointer(struct bios_linker_entry *entry) +static int bios_linker_add_pointer(struct udevice *dev, + struct bios_linker_entry *entry) { struct fw_file *dest, *src; uint32_t offset = le32_to_cpu(entry->pointer.offset); uint64_t pointer = 0; - dest = qemu_fwcfg_find_file(entry->pointer.dest_file); + dest = qfw_find_file(dev, entry->pointer.dest_file); if (!dest || !dest->addr) return -ENOENT; - src = qemu_fwcfg_find_file(entry->pointer.src_file); + src = qfw_find_file(dev, entry->pointer.src_file); if (!src || !src->addr) return -ENOENT; @@ -127,13 +126,14 @@ static int bios_linker_add_pointer(struct bios_linker_entry *entry) * checksums * @return: 0 on success, or negative value on failure */ -static int bios_linker_add_checksum(struct bios_linker_entry *entry) +static int bios_linker_add_checksum(struct udevice *dev, + struct bios_linker_entry *entry) { struct fw_file *file; uint8_t *data, cksum = 0; uint8_t *cksum_start; - file = qemu_fwcfg_find_file(entry->cksum.file); + file = qfw_find_file(dev, entry->cksum.file); if (!file || !file->addr) return -ENOENT; @@ -149,20 +149,27 @@ static int bios_linker_add_checksum(struct bios_linker_entry *entry) /* This function loads and patches ACPI tables provided by QEMU */ ulong write_acpi_tables(ulong addr) { - int i, ret = 0; + int i, ret; struct fw_file *file; struct bios_linker_entry *table_loader; struct bios_linker_entry *entry; uint32_t size; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return addr; + } /* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(); + ret = qfw_read_firmware_list(dev); if (ret) { printf("error: can't read firmware file list\n"); return addr; } - file = qemu_fwcfg_find_file("etc/table-loader"); + file = qfw_find_file(dev, "etc/table-loader"); if (!file) { printf("error: can't find etc/table-loader\n"); return addr; @@ -180,24 +187,23 @@ ulong write_acpi_tables(ulong addr) return addr; } - qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), - size, table_loader); + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); for (i = 0; i < (size / sizeof(*entry)); i++) { entry = table_loader + i; switch (le32_to_cpu(entry->command)) { case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(entry, &addr); + ret = bios_linker_allocate(dev, entry, &addr); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: - ret = bios_linker_add_pointer(entry); + ret = bios_linker_add_pointer(dev, entry); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: - ret = bios_linker_add_checksum(entry); + ret = bios_linker_add_checksum(dev, entry); if (ret) goto out; break; @@ -209,9 +215,9 @@ ulong write_acpi_tables(ulong addr) out: if (ret) { struct fw_cfg_file_iter iter; - for (file = qemu_fwcfg_file_iter_init(&iter); - !qemu_fwcfg_file_iter_end(&iter); - file = qemu_fwcfg_file_iter_next(&iter)) { + for (file = qfw_file_iter_init(dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { if (file->addr) { free((void *)file->addr); file->addr = 0; @@ -225,170 +231,89 @@ out: ulong acpi_get_rsdp_addr(void) { + int ret; struct fw_file *file; + struct udevice *dev; - file = qemu_fwcfg_find_file("etc/acpi/rsdp"); + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return 0; + } + + file = qfw_find_file(dev, "etc/acpi/rsdp"); return file->addr; } #endif -/* Read configuration item using fw_cfg PIO interface */ -static void qemu_fwcfg_read_entry_pio(uint16_t entry, - uint32_t size, void *address) +static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, + void *address) { - debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", - entry, size, address); + struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev); - return fwcfg_arch_ops->arch_read_pio(entry, size, address); + debug("%s: entry 0x%x, size %u address %p\n", __func__, entry, size, + address); + + ops->read_entry_io(qdev->dev, entry, size, address); } -/* Read configuration item using fw_cfg DMA interface */ -static void qemu_fwcfg_read_entry_dma(uint16_t entry, - uint32_t size, void *address) +static void qfw_read_entry_dma(struct qfw_dev *qdev, u16 entry, u32 size, + void *address) { - struct fw_cfg_dma_access dma; + struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev); - dma.length = cpu_to_be32(size); - dma.address = cpu_to_be64((uintptr_t)address); - dma.control = cpu_to_be32(FW_CFG_DMA_READ); + struct qfw_dma dma = { + .length = cpu_to_be32(size), + .address = cpu_to_be64((uintptr_t)address), + .control = cpu_to_be32(FW_CFG_DMA_READ), + }; /* - * writting FW_CFG_INVALID will cause read operation to resume at - * last offset, otherwise read will start at offset 0 + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 */ if (entry != FW_CFG_INVALID) dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); - barrier(); - - debug("qemu_fwcfg_read_entry_dma: entry 0x%x, size %u address %p, control 0x%x\n", + debug("%s: entry 0x%x, size %u address %p, control 0x%x\n", __func__, entry, size, address, be32_to_cpu(dma.control)); - fwcfg_arch_ops->arch_read_dma(&dma); + barrier(); + + ops->read_entry_dma(qdev->dev, &dma); } -bool qemu_fwcfg_present(void) +void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address) { - return fwcfg_present; -} + struct qfw_dev *qdev = dev_get_uclass_priv(dev); -bool qemu_fwcfg_dma_present(void) -{ - return fwcfg_dma_present; -} - -void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address) -{ - if (fwcfg_dma_present) - qemu_fwcfg_read_entry_dma(entry, length, address); + if (qdev->dma_present) + qfw_read_entry_dma(qdev, entry, size, address); else - qemu_fwcfg_read_entry_pio(entry, length, address); + qfw_read_entry_io(qdev, entry, size, address); } -int qemu_fwcfg_online_cpus(void) +int qfw_register(struct udevice *dev) { - uint16_t nb_cpus; + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + u32 qemu, dma_enabled; - if (!fwcfg_present) + qdev->dev = dev; + INIT_LIST_HEAD(&qdev->fw_list); + + qfw_read_entry_io(qdev, FW_CFG_SIGNATURE, 4, &qemu); + if (be32_to_cpu(qemu) != QEMU_FW_CFG_SIGNATURE) return -ENODEV; - qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); - - return le16_to_cpu(nb_cpus); -} - -int qemu_fwcfg_read_firmware_list(void) -{ - int i; - uint32_t count; - struct fw_file *file; - struct list_head *entry; - - /* don't read it twice */ - if (!list_empty(&fw_list)) - return 0; - - qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); - if (!count) - return 0; - - count = be32_to_cpu(count); - for (i = 0; i < count; i++) { - file = malloc(sizeof(*file)); - if (!file) { - printf("error: allocating resource\n"); - goto err; - } - qemu_fwcfg_read_entry(FW_CFG_INVALID, - sizeof(struct fw_cfg_file), &file->cfg); - file->addr = 0; - list_add_tail(&file->list, &fw_list); - } + qfw_read_entry_io(qdev, FW_CFG_ID, 1, &dma_enabled); + if (dma_enabled & FW_CFG_DMA_ENABLED) + qdev->dma_present = true; return 0; - -err: - list_for_each(entry, &fw_list) { - file = list_entry(entry, struct fw_file, list); - free(file); - } - - return -ENOMEM; } -struct fw_file *qemu_fwcfg_find_file(const char *name) -{ - struct list_head *entry; - struct fw_file *file; - - list_for_each(entry, &fw_list) { - file = list_entry(entry, struct fw_file, list); - if (!strcmp(file->cfg.name, name)) - return file; - } - - return NULL; -} - -struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter) -{ - iter->entry = fw_list.next; - return list_entry((struct list_head *)iter->entry, - struct fw_file, list); -} - -struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) -{ - iter->entry = ((struct list_head *)iter->entry)->next; - return list_entry((struct list_head *)iter->entry, - struct fw_file, list); -} - -bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) -{ - return iter->entry == &fw_list; -} - -void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops) -{ - uint32_t qemu; - uint32_t dma_enabled; - - fwcfg_present = false; - fwcfg_dma_present = false; - fwcfg_arch_ops = NULL; - - if (!ops || !ops->arch_read_pio || !ops->arch_read_dma) - return; - fwcfg_arch_ops = ops; - - qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); - if (be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE) - fwcfg_present = true; - - if (fwcfg_present) { - qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); - if (dma_enabled & FW_CFG_DMA_ENABLED) - fwcfg_dma_present = true; - } -} +UCLASS_DRIVER(qfw) = { + .id = UCLASS_QFW, + .name = "qfw", + .per_device_auto = sizeof(struct qfw_dev), +}; diff --git a/drivers/misc/qfw_pio.c b/drivers/misc/qfw_pio.c new file mode 100644 index 0000000000..e2f628d338 --- /dev/null +++ b/drivers/misc/qfw_pio.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PIO interface for QFW + * + * (C) Copyright 2015 Miao Yan + * (C) Copyright 2021 Asherah Connor + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include +#include +#include + +/* + * PIO ports are correct for x86, which appears to be the only arch that uses + * PIO. + */ +#define FW_CONTROL_PORT 0x510 +#define FW_DATA_PORT 0x511 +#define FW_DMA_PORT_LOW 0x514 +#define FW_DMA_PORT_HIGH 0x518 + +static void qfw_pio_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + /* + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 + * + * Note: on platform where the control register is IO port, the + * endianness is little endian. + */ + if (entry != FW_CFG_INVALID) + outw(cpu_to_le16(entry), FW_CONTROL_PORT); + + /* the endianness of data register is string-preserving */ + u32 i = 0; + u8 *data = address; + + while (size--) + data[i++] = inb(FW_DATA_PORT); +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qfw_pio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + /* the DMA address register is big-endian */ + outl(cpu_to_be32((uintptr_t)dma), FW_DMA_PORT_HIGH); + + while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); +} + +static int qfw_pio_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_pio_ops = { + .read_entry_io = qfw_pio_read_entry_io, + .read_entry_dma = qfw_pio_read_entry_dma, +}; + +U_BOOT_DRIVER(qfw_pio) = { + .name = "qfw_pio", + .id = UCLASS_QFW, + .probe = qfw_pio_probe, + .ops = &qfw_pio_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d75de368c5..d800f679d5 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -90,6 +90,7 @@ enum uclass_id { UCLASS_POWER_DOMAIN, /* (SoC) Power domains */ UCLASS_PWM, /* Pulse-width modulator */ UCLASS_PWRSEQ, /* Power sequence device */ + UCLASS_QFW, /* QEMU firmware config device */ UCLASS_RAM, /* RAM controller */ UCLASS_REGULATOR, /* Regulator device */ UCLASS_REMOTEPROC, /* Remote Processor device */ diff --git a/include/qfw.h b/include/qfw.h index cea8e11d44..7ca132e66a 100644 --- a/include/qfw.h +++ b/include/qfw.h @@ -8,7 +8,12 @@ #include -enum qemu_fwcfg_items { +/* + * List of firmware configuration item selectors. The official source of truth + * for these is the QEMU source itself; see + * https://github.com/qemu/qemu/blob/master/hw/nvram/fw_cfg.c + */ +enum { FW_CFG_SIGNATURE = 0x00, FW_CFG_ID = 0x01, FW_CFG_UUID = 0x02, @@ -66,8 +71,10 @@ enum { #define FW_CFG_DMA_SKIP (1 << 2) #define FW_CFG_DMA_SELECT (1 << 3) +/* Bit set in FW_CFG_ID response to indicate DMA interface availability. */ #define FW_CFG_DMA_ENABLED (1 << 1) +/* Structs read from FW_CFG_FILE_DIR. */ struct fw_cfg_file { __be32 size; __be16 select; @@ -82,19 +89,7 @@ struct fw_file { }; struct fw_cfg_file_iter { - struct list_head *entry; /* structure to iterate file list */ -}; - -struct fw_cfg_dma_access { - __be32 control; - __be32 length; - __be64 address; -}; - -struct fw_cfg_arch_ops { - void (*arch_read_pio)(uint16_t selector, uint32_t size, - void *address); - void (*arch_read_dma)(struct fw_cfg_dma_access *dma); + struct list_head *entry, *end; /* structures to iterate file list */ }; struct bios_linker_entry { @@ -146,37 +141,178 @@ struct bios_linker_entry { }; } __packed; +/* DMA transfer control data between UCLASS_QFW and QEMU. */ +struct qfw_dma { + __be32 control; + __be32 length; + __be64 address; +}; + +/* uclass per-device configuration information */ +struct qfw_dev { + struct udevice *dev; /* Transport device */ + bool dma_present; /* DMA interface usable? */ + struct list_head fw_list; /* Cached firmware file list */ +}; + +/* Ops used internally between UCLASS_QFW and its driver implementations. */ +struct dm_qfw_ops { + /** + * read_entry_io() - Read a firmware config entry using the regular + * IO interface for the platform (either PIO or MMIO) + * + * Supply %FW_CFG_INVALID as the entry to continue a previous read. In + * this case, no selector will be issued before reading. + * + * @dev: Device to use + * @entry: Firmware config entry number (e.g. %FW_CFG_SIGNATURE) + * @size: Number of bytes to read + * @address: Target location for read + */ + void (*read_entry_io)(struct udevice *dev, u16 entry, u32 size, + void *address); + + /** + * read_entry_dma() - Read a firmware config entry using the DMA + * interface + * + * Supply FW_CFG_INVALID as the entry to continue a previous read. In + * this case, no selector will be issued before reading. + * + * This method assumes DMA availability has already been confirmed. + * + * @dev: Device to use + * @dma: DMA transfer control struct + */ + void (*read_entry_dma)(struct udevice *dev, struct qfw_dma *dma); +}; + +#define dm_qfw_get_ops(dev) \ + ((struct dm_qfw_ops *)(dev)->driver->ops) + /** - * Initialize QEMU fw_cfg interface + * qfw_register() - Called by a qfw driver after successful probe. + * @dev: Device registering itself with the uclass. * - * @ops: arch specific read operations + * Used internally by driver implementations on successful probe. + * + * Return: 0 on success, negative otherwise. */ -void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops); +int qfw_register(struct udevice *dev); -void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address); -int qemu_fwcfg_read_firmware_list(void); -struct fw_file *qemu_fwcfg_find_file(const char *name); +struct udevice; /** - * Get system cpu number + * qfw_get_dev() - Get QEMU firmware config device. + * @devp: Pointer to be filled with address of the qfw device. * - * @return: cpu number in system + * Gets the active QEMU firmware config device, for use with qfw_read_entry() + * and others. + * + * Return: 0 on success, -ENODEV if the device is not available. */ -int qemu_fwcfg_online_cpus(void); - -/* helper functions to iterate firmware file list */ -struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter); -struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter); -bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter); - -bool qemu_fwcfg_present(void); -bool qemu_fwcfg_dma_present(void); +int qfw_get_dev(struct udevice **devp); /** - * qemu_cpu_fixup() - Fix up the CPUs for QEMU + * qfw_read_entry() - Read a QEMU firmware config entry + * @dev: QFW device to use. + * @entry: Firmware config entry number (e.g. %FW_CFG_SIGNATURE). + * @size: Number of bytes to read. + * @address: Target location for read. * - * @return 0 if OK, -ENODEV if no CPUs, -ENOMEM if out of memory, other -ve on - * on other error + * Reads a QEMU firmware config entry using @dev. DMA will be used if the QEMU + * machine supports it, otherwise PIO/MMIO. + */ +void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address); + +/** + * qfw_read_firmware_list() - Read and cache the QEMU firmware config file + * list. + * @dev: QFW device to use. + * + * Reads the QEMU firmware config file list, caching it against @dev for later + * use with qfw_find_file(). + * + * If the list has already been read, does nothing and returns 0 (success). + * + * Return: 0 on success, -ENOMEM if unable to allocate. + */ +int qfw_read_firmware_list(struct udevice *dev); + +/** + * qfw_find_file() - Find a file by name in the QEMU firmware config file + * list. + * @dev: QFW device to use. + * @name: Name of file to locate (e.g. "etc/table-loader"). + * + * Finds a file by name in the QEMU firmware config file list cached against + * @dev. You must call qfw_read_firmware_list() successfully first for this to + * succeed. + * + * Return: Pointer to &struct fw_file if found, %NULL if not present. + */ +struct fw_file *qfw_find_file(struct udevice *dev, const char *name); + +/** + * qfw_online_cpus() - Get number of CPUs in system from QEMU firmware config. + * @dev: QFW device to use. + * + * Asks QEMU to report how many CPUs it is emulating for the machine. + * + * Return: Number of CPUs in the system. + */ +int qfw_online_cpus(struct udevice *dev); + +/** + * qfw_file_iter_init() - Start iterating cached firmware file list. + * @dev: QFW device to use. + * @iter: Iterator to be initialised. + * + * Starts iterating the cached firmware file list in @dev. You must call + * qfw_read_firmware_list() successfully first, otherwise you will always get + * an empty list. + * + * qfw_file_iter_init() returns the first &struct fw_file, but it may be + * invalid if the list is empty. Check that ``!qfw_file_iter_end(&iter)`` + * first. + * + * Return: The first &struct fw_file item in the firmware file list, if any. + * Only valid when qfw_file_iter_end() is not true after the call. + */ +struct fw_file *qfw_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter); + +/** + * qfw_file_iter_next() - Iterate cached firmware file list. + * @iter: Iterator to use. + * + * Continues iterating the cached firmware file list in @dev. You must call + * qfw_file_iter_init() first to initialise it. Check that + * ``!qfw_file_iter_end(&iter)`` before using the return value of this + * function. + * + * Return: The next &struct fw_file item in the firmware file list. Only valid + * when qfw_file_iter_end() is not true after the call. + */ +struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter); + +/** + * qfw_file_iter_end() - Check if iter is at end of list. + * @iter: Iterator to use. + * + * Checks whether or not the iterator is at its end position. If so, the + * qfw_file_iter_init() or qfw_file_iter_next() call that immediately preceded + * returned invalid data. + * + * Return: True if the iterator is at its end; false otherwise. + */ +bool qfw_file_iter_end(struct fw_cfg_file_iter *iter); + +/** + * qemu_cpu_fixup() - Fix up the CPUs for QEMU. + * + * Return: 0 on success, -ENODEV if no CPUs, -ENOMEM if out of memory, other < + * 0 on on other error. */ int qemu_cpu_fixup(void); From 69512551aa8453b8ea9d1c5c896de9b3ad9cbfdf Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Fri, 19 Mar 2021 18:21:41 +1100 Subject: [PATCH 29/35] test: qemu: add qfw sandbox driver, dm tests, qemu tests A sandbox driver and test are added for the qfw uclass, and a test in QEMU added for qfw functionality to confirm it doesn't break in real world use. Signed-off-by: Asherah Connor Reviewed-by: Simon Glass Reviewed-by: Bin Meng --- configs/sandbox_noinst_defconfig | 1 - configs/sandbox_spl_defconfig | 1 - drivers/misc/Makefile | 1 + drivers/misc/qfw_sandbox.c | 127 +++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/qfw.c | 42 ++++++++++ test/py/tests/test_qfw.py | 26 +++++++ 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 drivers/misc/qfw_sandbox.c create mode 100644 test/dm/qfw.c create mode 100644 test/py/tests/test_qfw.py diff --git a/configs/sandbox_noinst_defconfig b/configs/sandbox_noinst_defconfig index 1ac7e025b6..01905900cb 100644 --- a/configs/sandbox_noinst_defconfig +++ b/configs/sandbox_noinst_defconfig @@ -73,7 +73,6 @@ CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_TIME=y CONFIG_CMD_TIMER=y CONFIG_CMD_SOUND=y -CONFIG_CMD_QFW=y CONFIG_CMD_BOOTSTAGE=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 0e40b17c62..7f1a5ef850 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -75,7 +75,6 @@ CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_TIME=y CONFIG_CMD_TIMER=y CONFIG_CMD_SOUND=y -CONFIG_CMD_QFW=y CONFIG_CMD_BOOTSTAGE=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 53bff72428..2864b84af9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o ifdef CONFIG_QFW obj-y += qfw.o obj-$(CONFIG_QFW_PIO) += qfw_pio.o +obj-$(CONFIG_SANDBOX) += qfw_sandbox.o endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o diff --git a/drivers/misc/qfw_sandbox.c b/drivers/misc/qfw_sandbox.c new file mode 100644 index 0000000000..b09974d33b --- /dev/null +++ b/drivers/misc/qfw_sandbox.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sandbox interface for QFW + * + * (C) Copyright 2015 Miao Yan + * (C) Copyright 2021 Asherah Connor + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include +#include +#include +#include +#include + +struct qfw_sandbox_plat { + u8 file_dir_offset; +}; + +static void qfw_sandbox_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + debug("%s: entry 0x%x size %u address %p\n", __func__, entry, size, + address); + + switch (entry) { + case FW_CFG_SIGNATURE: + if (size == 4) + *((u32 *)address) = cpu_to_be32(QEMU_FW_CFG_SIGNATURE); + break; + case FW_CFG_ID: + /* Advertise DMA support */ + if (size == 1) + *((u8 *)address) = FW_CFG_DMA_ENABLED; + break; + default: + debug("%s got unsupported entry 0x%x\n", __func__, entry); + /* + * Sandbox driver doesn't support other entries here, assume we use DMA + * to read them -- the uclass driver will exclusively use it when + * advertised. + */ + } +} + +static void qfw_sandbox_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + u16 entry; + u32 control = be32_to_cpu(dma->control); + void *address = (void *)be64_to_cpu(dma->address); + u32 length = be32_to_cpu(dma->length); + struct qfw_sandbox_plat *plat = dev_get_plat(dev); + struct fw_cfg_file *file; + + debug("%s\n", __func__); + + if (!(control & FW_CFG_DMA_READ)) + return; + + if (control & FW_CFG_DMA_SELECT) { + /* Start new read. */ + entry = control >> 16; + + /* Arbitrary values to be used by tests. */ + switch (entry) { + case FW_CFG_NB_CPUS: + if (length == 2) + *((u16 *)address) = cpu_to_le16(5); + break; + case FW_CFG_FILE_DIR: + if (length == 4) { + *((u32 *)address) = cpu_to_be32(2); + plat->file_dir_offset = 1; + } + break; + default: + debug("%s got unsupported entry 0x%x\n", __func__, + entry); + } + } else if (plat->file_dir_offset && length == 64) { + file = address; + switch (plat->file_dir_offset) { + case 1: + file->size = cpu_to_be32(8); + file->select = cpu_to_be16(FW_CFG_FILE_FIRST); + strcpy(file->name, "test-one"); + plat->file_dir_offset++; + break; + case 2: + file->size = cpu_to_be32(8); + file->select = cpu_to_be16(FW_CFG_FILE_FIRST + 1); + strcpy(file->name, "test-two"); + plat->file_dir_offset++; + break; + } + } + + /* + * Signal that we are finished. No-one checks this in sandbox -- + * normally the platform-specific driver looks for it -- but let's + * replicate the behaviour in case someone relies on it later. + */ + dma->control = 0; +} + +static int qfw_sandbox_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_sandbox_ops = { + .read_entry_io = qfw_sandbox_read_entry_io, + .read_entry_dma = qfw_sandbox_read_entry_dma, +}; + +U_BOOT_DRIVER(qfw_sandbox) = { + .name = "qfw_sandbox", + .id = UCLASS_QFW, + .plat_auto = sizeof(struct qfw_sandbox_plat), + .probe = qfw_sandbox_probe, + .ops = &qfw_sandbox_ops, +}; + +U_BOOT_DRVINFO(qfw_sandbox) = { + .name = "qfw_sandbox", +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index f5cc5540e8..d54abb7341 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -98,5 +98,6 @@ endif ifneq ($(CONFIG_EFI_PARTITION),) obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o endif +obj-$(CONFIG_QFW) += qfw.o endif endif # !SPL diff --git a/test/dm/qfw.c b/test/dm/qfw.c new file mode 100644 index 0000000000..f3f3568983 --- /dev/null +++ b/test/dm/qfw.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Asherah Connor + */ + +#include +#include +#include +#include +#include +#include + +/* + * Exercise the device enough to be satisfied the initialisation and DMA + * interfaces work. + */ + +static int dm_test_qfw_cpus(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev)); + ut_asserteq(5, qfw_online_cpus(dev)); + + return 0; +} + +DM_TEST(dm_test_qfw_cpus, UT_TESTF_SCAN_PDATA); + +static int dm_test_qfw_firmware_list(struct unit_test_state *uts) +{ + struct udevice *dev; + struct fw_file *file; + + ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev)); + ut_assertok(qfw_read_firmware_list(dev)); + ut_assertok_ptr((file = qfw_find_file(dev, "test-one"))); + + return 0; +} + +DM_TEST(dm_test_qfw_firmware_list, UT_TESTF_SCAN_PDATA); diff --git a/test/py/tests/test_qfw.py b/test/py/tests/test_qfw.py new file mode 100644 index 0000000000..8b668c9721 --- /dev/null +++ b/test/py/tests/test_qfw.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2021, Asherah Connor + +# Test qfw command implementation + +import pytest + +@pytest.mark.buildconfigspec('cmd_qfw') +def test_qfw_cpus(u_boot_console): + "Test QEMU firmware config reports the CPU count." + + output = u_boot_console.run_command('qfw cpus') + # The actual number varies depending on the board under test, so only + # assert a non-zero output. + assert 'cpu(s) online' in output + assert '0 cpu(s) online' not in output + +@pytest.mark.buildconfigspec('cmd_qfw') +def test_qfw_list(u_boot_console): + "Test QEMU firmware config lists devices." + + output = u_boot_console.run_command('qfw list') + # Assert either: + # 1) 'test-one', from the sandbox driver, or + # 2) 'bootorder', found in every real QEMU implementation. + assert ("bootorder" in output) or ("test-one" in output) From 5830b57bf9aeed1b072a54faa7d74f6d62cac4c6 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Fri, 19 Mar 2021 18:21:42 +1100 Subject: [PATCH 30/35] qemu: add MMIO driver for QFW Add MMIO driver for QFW. Note that there is no consumer as of this patch. Signed-off-by: Asherah Connor Reviewed-by: Simon Glass Reviewed-by: Bin Meng --- drivers/misc/Kconfig | 7 +++ drivers/misc/Makefile | 1 + drivers/misc/qfw_mmio.c | 119 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 drivers/misc/qfw_mmio.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3a254eba4b..c650471ff7 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -378,6 +378,13 @@ config QFW_PIO Hidden option to enable PIO QEMU fw_cfg interface. This will be selected by the appropriate QEMU board. +config QFW_MMIO + bool + depends on QFW + help + Hidden option to enable MMIO QEMU fw_cfg interface. This will be + selected by the appropriate QEMU board. + config I2C_EEPROM bool "Enable driver for generic I2C-attached EEPROMs" depends on MISC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2864b84af9..0c67d43a5d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o ifdef CONFIG_QFW obj-y += qfw.o obj-$(CONFIG_QFW_PIO) += qfw_pio.o +obj-$(CONFIG_QFW_MMIO) += qfw_mmio.o obj-$(CONFIG_SANDBOX) += qfw_sandbox.o endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o diff --git a/drivers/misc/qfw_mmio.c b/drivers/misc/qfw_mmio.c new file mode 100644 index 0000000000..f397384054 --- /dev/null +++ b/drivers/misc/qfw_mmio.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MMIO interface for QFW + * + * (C) Copyright 2015 Miao Yan + * (C) Copyright 2021 Asherah Connor + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include +#include +#include +#include +#include + +struct qfw_mmio { + /* + * Each access to the 64-bit data register can be 8/16/32/64 bits wide. + */ + union { + u8 data8; + u16 data16; + u32 data32; + u64 data64; + }; + u16 selector; + u8 padding[6]; + u64 dma; +}; + +struct qfw_mmio_plat { + volatile struct qfw_mmio *mmio; +}; + +static void qfw_mmio_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + /* + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 + * + * Note: on platform where the control register is MMIO, the register + * is big endian. + */ + if (entry != FW_CFG_INVALID) + plat->mmio->selector = cpu_to_be16(entry); + + /* the endianness of data register is string-preserving */ + while (size >= 8) { + *(u64 *)address = plat->mmio->data64; + address += 8; + size -= 8; + } + while (size >= 4) { + *(u32 *)address = plat->mmio->data32; + address += 4; + size -= 4; + } + while (size >= 2) { + *(u16 *)address = plat->mmio->data16; + address += 2; + size -= 2; + } + while (size >= 1) { + *(u8 *)address = plat->mmio->data8; + address += 1; + size -= 1; + } +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qfw_mmio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + /* the DMA address register is big-endian */ + plat->mmio->dma = cpu_to_be64((uintptr_t)dma); + + while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); +} + +static int qfw_mmio_of_to_plat(struct udevice *dev) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + plat->mmio = map_physmem(dev_read_addr(dev), + sizeof(struct qfw_mmio), + MAP_NOCACHE); + + return 0; +} + +static int qfw_mmio_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_mmio_ops = { + .read_entry_io = qfw_mmio_read_entry_io, + .read_entry_dma = qfw_mmio_read_entry_dma, +}; + +static const struct udevice_id qfw_mmio_ids[] = { + { .compatible = "qemu,fw-cfg-mmio" }, + {} +}; + +U_BOOT_DRIVER(qfw_mmio) = { + .name = "qfw_mmio", + .id = UCLASS_QFW, + .of_match = qfw_mmio_ids, + .plat_auto = sizeof(struct qfw_mmio_plat), + .of_to_plat = qfw_mmio_of_to_plat, + .probe = qfw_mmio_probe, + .ops = &qfw_mmio_ops, +}; From 0219279381bcba7ad57c438d96dcee27643fc294 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Fri, 19 Mar 2021 18:21:43 +1100 Subject: [PATCH 31/35] qemu: arm: select QFW, MMIO on qemu-arm Select CMD_QFW and QFW_MMIO in the qemu-arm board (covers arm and arm64). Signed-off-by: Asherah Connor Reviewed-by: Simon Glass Reviewed-by: Bin Meng --- board/emulation/qemu-arm/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/board/emulation/qemu-arm/Kconfig b/board/emulation/qemu-arm/Kconfig index fb8d38f5b8..95dbefa78b 100644 --- a/board/emulation/qemu-arm/Kconfig +++ b/board/emulation/qemu-arm/Kconfig @@ -5,6 +5,8 @@ config SYS_TEXT_BASE config BOARD_SPECIFIC_OPTIONS # dummy def_bool y + select CMD_QFW + select QFW_MMIO imply VIRTIO_MMIO imply VIRTIO_PCI imply VIRTIO_NET From a12aa7995f19ba612a0ab0a9b13da38183250973 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Mar 2021 14:52:06 +1300 Subject: [PATCH 32/35] gpio: i2c-gpio: Drop use of dm_gpio_set_dir() This is the only driver that uses this function. Update it to use the alternative which is dm_gpio_clrset_flags(). Signed-off-by: Simon Glass Tested-by: Harm Berntsen --- drivers/i2c/i2c-gpio.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index a301a4460b..cf8f8f4035 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -48,12 +48,13 @@ static int i2c_gpio_sda_get(struct i2c_gpio_bus *bus) static void i2c_gpio_sda_set(struct i2c_gpio_bus *bus, int bit) { struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + ulong flags; if (bit) - sda->flags = (sda->flags & ~GPIOD_IS_OUT) | GPIOD_IS_IN; + flags = GPIOD_IS_IN; else - sda->flags = (sda->flags & (~GPIOD_IS_IN & ~GPIOD_IS_OUT_ACTIVE)) | GPIOD_IS_OUT; - dm_gpio_set_dir(sda); + flags = GPIOD_IS_OUT; + dm_gpio_clrset_flags(sda, GPIOD_MASK_DIR, flags); } static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) @@ -62,16 +63,14 @@ static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) int count = 0; if (bit) { - scl->flags = (scl->flags & ~GPIOD_IS_OUT) | GPIOD_IS_IN; - dm_gpio_set_dir(scl); + dm_gpio_clrset_flags(scl, GPIOD_MASK_DIR, GPIOD_IS_IN); while (!dm_gpio_get_value(scl) && count++ < 100000) udelay(1); if (!dm_gpio_get_value(scl)) pr_err("timeout waiting on slave to release scl\n"); } else { - scl->flags = (scl->flags & (~GPIOD_IS_IN & ~GPIOD_IS_OUT_ACTIVE)) | GPIOD_IS_OUT; - dm_gpio_set_dir(scl); + dm_gpio_clrset_flags(scl, GPIOD_MASK_DIR, GPIOD_IS_OUT); } } @@ -79,11 +78,11 @@ static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) static void i2c_gpio_scl_set_output_only(struct i2c_gpio_bus *bus, int bit) { struct gpio_desc *scl = &bus->gpios[PIN_SCL]; - scl->flags = (scl->flags & (~GPIOD_IS_IN & ~GPIOD_IS_OUT_ACTIVE)) | GPIOD_IS_OUT; + ulong flags = GPIOD_IS_OUT; if (bit) - scl->flags |= GPIOD_IS_OUT_ACTIVE; - dm_gpio_set_dir(scl); + flags |= GPIOD_IS_OUT_ACTIVE; + dm_gpio_clrset_flags(scl, GPIOD_MASK_DIR, flags); } static void i2c_gpio_write_bit(struct i2c_gpio_bus *bus, int delay, uchar bit) From 34f8fc7c859926174b02734b35e5d5112a8c9fe7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Mar 2021 14:52:07 +1300 Subject: [PATCH 33/35] gpio: Drop dm_gpio_set_dir() This function is not used. Drop it. Signed-off-by: Simon Glass Reviewed-by: Patrick Delaunay --- drivers/gpio/gpio-uclass.c | 11 ----------- include/asm-generic/gpio.h | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index f24db87ef0..e4e7f58c39 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -713,17 +713,6 @@ int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags) return dm_gpio_clrset_flags(desc, GPIOD_MASK_DIR, flags); } -int dm_gpio_set_dir(struct gpio_desc *desc) -{ - int ret; - - ret = check_reserved(desc, "set_dir"); - if (ret) - return ret; - - return _dm_gpio_set_flags(desc, desc->flags); -} - int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr, ulong set) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2cb0500aec..e33cde7abd 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -706,17 +706,6 @@ int dm_gpio_get_value(const struct gpio_desc *desc); int dm_gpio_set_value(const struct gpio_desc *desc, int value); -/** - * dm_gpio_set_dir() - Set the direction for a GPIO - * - * This sets up the direction according to the GPIO flags: desc->flags. - * - * @desc: GPIO description containing device, offset and flags, - * previously returned by gpio_request_by_name() - * @return 0 if OK, -ve on error - */ -int dm_gpio_set_dir(struct gpio_desc *desc); - /** * dm_gpio_clrset_flags() - Update flags * From 7062d4e815d255891d96aafe617a264c4c4a8a02 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Tue, 30 Mar 2021 15:29:03 +0200 Subject: [PATCH 34/35] scmi: translate the resource only when livetree is not activated Call the translation function on the ofnode_read_resource result only when the livetree is not activated. Today of_address_to_resource() calls ofnode_read_resource() for livetree support and fdt_get_resource() when livetree is not supported. The fdt_get_resource() doesn't do the address translation so when it is required when livetree is activated but this address translation is already done by ofnode_read_resource(). Fixes: 240720e9052f ("firmware: scmi: mailbox/smt agent device") Signed-off-by: Patrick Delaunay --- drivers/firmware/scmi/smt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 4954355c1a..f1915c0074 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -41,8 +41,13 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (ret) return ret; - faddr = cpu_to_fdt32(resource.start); - paddr = ofnode_translate_address(args.node, &faddr); + /* TEMP workaround for ofnode_read_resource translation issue */ + if (of_live_active()) { + paddr = resource.start; + } else { + faddr = cpu_to_fdt32(resource.start); + paddr = ofnode_translate_address(args.node, &faddr); + } smt->size = resource_size(&resource); if (smt->size < sizeof(struct scmi_smt_header)) { From 8c4e3b79bd0bb76eea16869e9666e19047c0d005 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 6 Apr 2021 14:00:24 +0200 Subject: [PATCH 35/35] cmd: exit: Fix return value In case exit is called in a script without parameter, the command returns -2 ; in case exit is called with a numerical parameter, the command returns -2 and lower. This leads to the following problem: => setenv foo 'echo bar ; exit 1' ; run foo ; echo $? bar 0 => setenv foo 'echo bar ; exit 0' ; run foo ; echo $? bar 0 => setenv foo 'echo bar ; exit -2' ; run foo ; echo $? bar 0 That is, no matter what the 'exit' command argument is, the return value is always 0 and so it is not possible to use script return value in subsequent tests. Fix this and simplify the exit command such that if exit is called with no argument, the command returns 0, just like 'true' in cmd/test.c. In case the command is called with any argument that is positive integer, the argument is set as return value. => setenv foo 'echo bar ; exit 1' ; run foo ; echo $? bar 1 => setenv foo 'echo bar ; exit 0' ; run foo ; echo $? bar 0 => setenv foo 'echo bar ; exit -2' ; run foo ; echo $? bar 0 Note that this does change ABI established in 2004 , although it is unclear whether that ABI was originally OK or not. Fixes: c26e454dfc6 Signed-off-by: Marek Vasut Cc: Pantelis Antoniou Cc: Tom Rini --- cmd/exit.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/exit.c b/cmd/exit.c index 7bf241ec73..923f0870fb 100644 --- a/cmd/exit.c +++ b/cmd/exit.c @@ -10,13 +10,10 @@ static int do_exit(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - int r; - - r = 0; if (argc > 1) - r = simple_strtoul(argv[1], NULL, 10); + return simple_strtoul(argv[1], NULL, 10); - return -r - 2; + return 0; } U_BOOT_CMD(