diff options
68 files changed, 1999 insertions, 135 deletions
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 9c58f69dbd0..9cef363fbaa 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -58,6 +58,7 @@ config ARCH_LS1043A select ARM_ERRATA_855873 if !TFABOOT select FSL_LAYERSCAPE select FSL_LSCH2 + select HAS_FSL_XHCI_USB if USB_HOST select SYS_FSL_SRDS_1 select SYS_HAS_SERDES select SYS_FSL_DDR @@ -89,6 +90,7 @@ config ARCH_LS1046A select ARMV8_SET_SMPEN select FSL_LAYERSCAPE select FSL_LSCH2 + select HAS_FSL_XHCI_USB if USB_HOST select SYS_FSL_SRDS_1 select SYS_HAS_SERDES select SYS_FSL_DDR @@ -245,6 +247,7 @@ config ARCH_LX2160A bool select ARMV8_SET_SMPEN select FSL_LSCH3 + select HAS_FSL_XHCI_USB if USB_HOST select NXP_LSCH3_2 select SYS_HAS_SERDES select SYS_FSL_SRDS_1 @@ -642,9 +645,8 @@ config SPL_LDSCRIPT config HAS_FSL_XHCI_USB bool - default y if ARCH_LS1043A || ARCH_LS1046A help - For some SoC(such as LS1043A and LS1046A), USB and QE-HDLC multiplex use + For some SoC (such as LS1043A and LS1046A), USB and QE-HDLC multiplex use pins, select it when the pins are assigned to USB. config SYS_FSL_BOOTROM_BASE diff --git a/arch/arm/cpu/armv8/fsl-layerscape/soc.c b/arch/arm/cpu/armv8/fsl-layerscape/soc.c index 1641b657990..42a09685462 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/soc.c +++ b/arch/arm/cpu/armv8/fsl-layerscape/soc.c @@ -953,12 +953,15 @@ int board_late_init(void) #endif #ifdef CONFIG_TFABOOT /* - * Set bootcmd and mcinitcmd if they don't exist in the environment. + * Set bootcmd and mcinitcmd if "fsl_bootcmd_mcinitcmd_set" does + * not exists in env */ - if (!env_get("bootcmd")) + if (env_get_yesno("fsl_bootcmd_mcinitcmd_set") <= 0) { + // Set bootcmd and mcinitcmd as per boot source fsl_setenv_bootcmd(); - if (!env_get("mcinitcmd")) fsl_setenv_mcinitcmd(); + env_set("fsl_bootcmd_mcinitcmd_set", "y"); + } #endif #ifdef CONFIG_QSPI_AHB_INIT qspi_ahb_init(); diff --git a/arch/arm/dts/fsl-ls1028a-kontron-sl28.dts b/arch/arm/dts/fsl-ls1028a-kontron-sl28.dts index ea77a83d2fe..7f237c39ec0 100644 --- a/arch/arm/dts/fsl-ls1028a-kontron-sl28.dts +++ b/arch/arm/dts/fsl-ls1028a-kontron-sl28.dts @@ -72,61 +72,6 @@ /* The following setting enables 1-1-2 (CMD-ADDR-DATA) mode */ spi-rx-bus-width = <2>; /* 2 SPI Rx lines */ spi-tx-bus-width = <1>; /* 1 SPI Tx line */ - - partition@0 { - reg = <0x000000 0x010000>; - label = "rcw"; - read-only; - }; - - partition@10000 { - reg = <0x010000 0x0f0000>; - label = "failsafe bootloader"; - read-only; - }; - - partition@100000 { - reg = <0x100000 0x040000>; - label = "failsafe DP firmware"; - read-only; - }; - - partition@140000 { - reg = <0x140000 0x0a0000>; - label = "failsafe trusted firmware"; - read-only; - }; - - partition@1e0000 { - reg = <0x1e0000 0x020000>; - label = "reserved"; - read-only; - }; - - partition@200000 { - reg = <0x200000 0x010000>; - label = "configuration store"; - }; - - partition@210000 { - reg = <0x210000 0x0f0000>; - label = "bootloader"; - }; - - partition@300000 { - reg = <0x300000 0x040000>; - label = "DP firmware"; - }; - - partition@340000 { - reg = <0x340000 0x0a0000>; - label = "trusted firmware"; - }; - - partition@3e0000 { - reg = <0x3e0000 0x020000>; - label = "bootloader environment"; - }; }; }; diff --git a/arch/riscv/cpu/fu740/spl.c b/arch/riscv/cpu/fu740/spl.c index 55e30346ff1..c6816e9ed4c 100644 --- a/arch/riscv/cpu/fu740/spl.c +++ b/arch/riscv/cpu/fu740/spl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2020-201 SiFive, Inc + * Copyright (C) 2020-2021 SiFive, Inc * Pragnesh Patel <pragnesh.patel@sifive.com> */ diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d5976318d1c..962bdbe5567 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -731,6 +731,20 @@ i2c-eeprom = <&bootcount_i2c>; }; + bootcount_4@0 { + compatible = "u-boot,bootcount-syscon"; + syscon = <&syscon0>; + reg = <0x0 0x04>, <0x0 0x04>; + reg-names = "syscon_reg", "offset"; + }; + + bootcount_2@0 { + compatible = "u-boot,bootcount-syscon"; + syscon = <&syscon0>; + reg = <0x0 0x04>, <0x0 0x02> ; + reg-names = "syscon_reg", "offset"; + }; + adc: adc@0 { compatible = "sandbox,adc"; #io-channel-cells = <1>; diff --git a/board/freescale/common/fsl_validate.c b/board/freescale/common/fsl_validate.c index 066aa9a7c37..c90afe2e210 100644 --- a/board/freescale/common/fsl_validate.c +++ b/board/freescale/common/fsl_validate.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2015 Freescale Semiconductor, Inc. + * Copyright 2021 NXP */ #include <common.h> @@ -498,8 +499,11 @@ static int calc_img_key_hash(struct fsl_secboot_img_priv *img) return ret; ret = algo->hash_init(algo, &ctx); - if (ret) + if (ret) { + if (ctx) + free(ctx); return ret; + } /* Update hash for ESBC key */ #ifdef CONFIG_KEY_REVOCATION @@ -518,8 +522,11 @@ static int calc_img_key_hash(struct fsl_secboot_img_priv *img) /* Copy hash at destination buffer */ ret = algo->hash_finish(algo, ctx, hash_val, algo->digest_size); - if (ret) + if (ret) { + if (ctx) + free(ctx); return ret; + } for (i = 0; i < SHA256_BYTES; i++) img->img_key_hash[i] = hash_val[i]; @@ -547,14 +554,18 @@ static int calc_esbchdr_esbc_hash(struct fsl_secboot_img_priv *img) ret = algo->hash_init(algo, &ctx); /* Copy hash at destination buffer */ - if (ret) + if (ret) { + free(ctx); return ret; + } /* Update hash for CSF Header */ ret = algo->hash_update(algo, ctx, (u8 *)&img->hdr, sizeof(struct fsl_secboot_img_hdr), 0); - if (ret) + if (ret) { + free(ctx); return ret; + } /* Update the hash with that of srk table if srk flag is 1 * If IE Table is selected, key is not added in the hash @@ -581,22 +592,29 @@ static int calc_esbchdr_esbc_hash(struct fsl_secboot_img_priv *img) key_hash = 1; } #endif - if (ret) + if (ret) { + free(ctx); return ret; - if (!key_hash) + } + if (!key_hash) { + free(ctx); return ERROR_KEY_TABLE_NOT_FOUND; + } /* Update hash for actual Image */ ret = algo->hash_update(algo, ctx, (u8 *)(*(img->img_addr_ptr)), img->img_size, 1); - if (ret) + if (ret) { + free(ctx); return ret; + } /* Copy hash at destination buffer */ ret = algo->hash_finish(algo, ctx, hash_val, algo->digest_size); - if (ret) + if (ret) { + free(ctx); return ret; - + } return 0; } diff --git a/board/freescale/ls2080ardb/ls2080ardb.c b/board/freescale/ls2080ardb/ls2080ardb.c index 6504cf768f1..e8722f20c13 100644 --- a/board/freescale/ls2080ardb/ls2080ardb.c +++ b/board/freescale/ls2080ardb/ls2080ardb.c @@ -33,6 +33,9 @@ #endif #include "../common/vid.h" +#define CORTINA_FW_ADDR_IFCNOR 0x580980000 +#define CORTINA_FW_ADDR_IFCNOR_ALTBANK 0x584980000 +#define CORTINA_FW_ADDR_QSPI 0x980000 #define PIN_MUX_SEL_SDHC 0x00 #define PIN_MUX_SEL_DSPI 0x0a @@ -235,6 +238,41 @@ int config_board_mux(int ctrl_type) return 0; } +ulong *cs4340_get_fw_addr(void) +{ +#ifdef CONFIG_TFABOOT + struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); + u32 svr = gur_in32(&gur->svr); +#endif + ulong cortina_fw_addr = CONFIG_CORTINA_FW_ADDR; + +#ifdef CONFIG_TFABOOT + /* LS2088A TFA boot */ + if (SVR_SOC_VER(svr) == SVR_LS2088A) { + enum boot_src src = get_boot_src(); + u8 sw; + + switch (src) { + case BOOT_SOURCE_IFC_NOR: + sw = QIXIS_READ(brdcfg[0]); + sw = (sw & 0x0f); + if (sw == 0) + cortina_fw_addr = CORTINA_FW_ADDR_IFCNOR; + else if (sw == 4) + cortina_fw_addr = CORTINA_FW_ADDR_IFCNOR_ALTBANK; + break; + case BOOT_SOURCE_QSPI_NOR: + /* Only one bank in QSPI */ + cortina_fw_addr = CORTINA_FW_ADDR_QSPI; + break; + default: + printf("WARNING: Boot source not found\n"); + } + } +#endif + return (ulong *)cortina_fw_addr; +} + int board_init(void) { #ifdef CONFIG_FSL_MC_ENET diff --git a/board/freescale/t208xrdb/cpld.h b/board/freescale/t208xrdb/cpld.h index a623b1811fa..3139c2b85fd 100644 --- a/board/freescale/t208xrdb/cpld.h +++ b/board/freescale/t208xrdb/cpld.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright 2014 Freescale Semiconductor + * Copyright 2021 NXP */ /* @@ -42,3 +43,6 @@ void cpld_write(unsigned int reg, u8 value); /* RSTCON Register */ #define CPLD_RSTCON_EDC_RST 0x04 + +/* MISCCSR Register */ +#define CPLD_MISC_POR_EN 0x30 diff --git a/board/freescale/t208xrdb/t208xrdb.c b/board/freescale/t208xrdb/t208xrdb.c index 1f0cdee0b86..73ebb4a55bf 100644 --- a/board/freescale/t208xrdb/t208xrdb.c +++ b/board/freescale/t208xrdb/t208xrdb.c @@ -128,6 +128,13 @@ int misc_init_r(void) reg |= CPLD_RSTCON_EDC_RST; CPLD_WRITE(reset_ctl, reg); + /* Enable POR for boards revisions D and up */ + if (get_hw_revision() >= 'D') { + reg = CPLD_READ(misc_csr); + reg |= CPLD_MISC_POR_EN; + CPLD_WRITE(misc_csr, reg); + } + return 0; } @@ -158,3 +165,23 @@ int ft_board_setup(void *blob, struct bd_info *bd) return 0; } + +ulong *cs4340_get_fw_addr(void) +{ + ulong cortina_fw_addr = CONFIG_CORTINA_FW_ADDR; + +#ifdef CONFIG_SYS_CORTINA_FW_IN_NOR + u8 reg; + + reg = CPLD_READ(flash_csr); + if (!(reg & CPLD_BOOT_SEL)) { + reg = ((reg & CPLD_LBMAP_MASK) >> CPLD_LBMAP_SHIFT); + if (reg == 0) + cortina_fw_addr = CORTINA_FW_ADDR_IFCNOR; + else if (reg == 4) + cortina_fw_addr = CORTINA_FW_ADDR_IFCNOR_ALTBANK; + } +#endif + + return (ulong *)cortina_fw_addr; +} diff --git a/board/freescale/t208xrdb/t208xrdb.h b/board/freescale/t208xrdb/t208xrdb.h index edbc860c9d0..26998898e82 100644 --- a/board/freescale/t208xrdb/t208xrdb.h +++ b/board/freescale/t208xrdb/t208xrdb.h @@ -7,6 +7,9 @@ #ifndef __CORENET_DS_H__ #define __CORENET_DS_H__ +#define CORTINA_FW_ADDR_IFCNOR 0xefe00000 +#define CORTINA_FW_ADDR_IFCNOR_ALTBANK 0xebe00000 + void fdt_fixup_board_enet(void *blob); void pci_of_setup(void *blob, struct bd_info *bd); void fdt_fixup_board_fman_ethernet(void *blob); diff --git a/board/freescale/t4rdb/t4240rdb.c b/board/freescale/t4rdb/t4240rdb.c index 6ab35ca9185..20ce7523e57 100644 --- a/board/freescale/t4rdb/t4240rdb.c +++ b/board/freescale/t4rdb/t4240rdb.c @@ -151,3 +151,22 @@ void board_detail(void) break; } } + +ulong *cs4340_get_fw_addr(void) +{ + ulong cortina_fw_addr = CONFIG_CORTINA_FW_ADDR; + +#ifdef CONFIG_SYS_CORTINA_FW_IN_NOR + u8 sw; + + sw = CPLD_READ(vbank); + sw = sw & CPLD_BANK_SEL_MASK; + + if (sw == 0) + cortina_fw_addr = CORTINA_FW_ADDR_IFCNOR; + else if (sw == 4) + cortina_fw_addr = CORTINA_FW_ADDR_IFCNOR_ALTBANK; +#endif + + return (ulong *)cortina_fw_addr; +} diff --git a/board/freescale/t4rdb/t4rdb.h b/board/freescale/t4rdb/t4rdb.h index 3f1fa7bbd24..06779f552fa 100644 --- a/board/freescale/t4rdb/t4rdb.h +++ b/board/freescale/t4rdb/t4rdb.h @@ -11,6 +11,9 @@ #define CONFIG_SYS_NUM_FM1_DTSEC 4 #define CONFIG_SYS_NUM_FM2_DTSEC 4 +#define CORTINA_FW_ADDR_IFCNOR 0xefe00000 +#define CORTINA_FW_ADDR_IFCNOR_ALTBANK 0xebf00000 + void fdt_fixup_board_enet(void *blob); void pci_of_setup(void *blob, struct bd_info *bd); diff --git a/board/sifive/unleashed/Makefile b/board/sifive/unleashed/Makefile index 5821679dd92..98e9111cbcc 100644 --- a/board/sifive/unleashed/Makefile +++ b/board/sifive/unleashed/Makefile @@ -2,8 +2,8 @@ # # Copyright (c) 2019 Western Digital Corporation or its affiliates. -obj-y += unleashed.o - ifdef CONFIG_SPL_BUILD obj-y += spl.o +else +obj-y += unleashed.o endif diff --git a/board/sifive/unleashed/unleashed.c b/board/sifive/unleashed/unleashed.c index a4e78220cba..fa65fcade08 100644 --- a/board/sifive/unleashed/unleashed.c +++ b/board/sifive/unleashed/unleashed.c @@ -16,6 +16,7 @@ #include <misc.h> #include <spl.h> #include <asm/arch/cache.h> +#include <asm/sections.h> /* * This define is a value used for error/unknown serial. @@ -113,6 +114,16 @@ int misc_init_r(void) #endif +void *board_fdt_blob_setup(void) +{ + if (IS_ENABLED(CONFIG_OF_SEPARATE)) { + if (gd->arch.firmware_fdt_addr) + return (ulong *)gd->arch.firmware_fdt_addr; + else + return (ulong *)&_end; + } +} + int board_init(void) { int ret; diff --git a/board/sifive/unmatched/Makefile b/board/sifive/unmatched/Makefile index e00b330e8ce..13453300896 100644 --- a/board/sifive/unmatched/Makefile +++ b/board/sifive/unmatched/Makefile @@ -2,9 +2,10 @@ # # Copyright (c) 2020-2021 SiFive, Inc -obj-y += unmatched.o obj-$(CONFIG_ID_EEPROM) += hifive-platform-i2c-eeprom.o ifdef CONFIG_SPL_BUILD obj-y += spl.o +else +obj-y += unmatched.o endif diff --git a/board/sifive/unmatched/unmatched.c b/board/sifive/unmatched/unmatched.c index 6d605595889..da23a6ce246 100644 --- a/board/sifive/unmatched/unmatched.c +++ b/board/sifive/unmatched/unmatched.c @@ -9,6 +9,17 @@ #include <common.h> #include <dm.h> #include <asm/arch/cache.h> +#include <asm/sections.h> + +void *board_fdt_blob_setup(void) +{ + if (IS_ENABLED(CONFIG_OF_SEPARATE)) { + if (gd->arch.firmware_fdt_addr) + return (ulong *)gd->arch.firmware_fdt_addr; + else + return (ulong *)&_end; + } +} int board_init(void) { diff --git a/board/toradex/apalis-tk1/apalis-tk1.c b/board/toradex/apalis-tk1/apalis-tk1.c index b97617cfca3..2769b546010 100644 --- a/board/toradex/apalis-tk1/apalis-tk1.c +++ b/board/toradex/apalis-tk1/apalis-tk1.c @@ -38,8 +38,24 @@ int arch_misc_init(void) { if (readl(NV_PA_BASE_SRAM + NVBOOTINFOTABLE_BOOTTYPE) == - NVBOOTTYPE_RECOVERY) - printf("USB recovery mode\n"); + NVBOOTTYPE_RECOVERY) { + printf("USB recovery mode, attempting to boot Toradex Easy " + "Installer\n"); + env_set("bootdelay", "-2"); + env_set("defargs", "pcie_aspm=off user_debug=30"); + env_set("fdt_high", ""); + env_set("initrd_high", ""); + + env_set("setup", "env set setupargs igb_mac=${ethaddr} " + "consoleblank=0 no_console_suspend=1 " + "console=${console},${baudrate}n8 ${memargs}"); + env_set("teziargs", "rootfstype=squashfs root=/dev/ram quiet " + "autoinstall"); + env_set("vidargs", "video=HDMI-A-1:640x480-16@60D"); + env_set("bootcmd", "run setup; env set bootargs ${defargs} " + "${setupargs} ${vidargs} ${teziargs}; bootm 0x80208000" + "#config@${soc}-${fdt_module}-${fdt_board}.dtb"); + } /* PCB Version Indication: V1.2 and later have GPIO_PV0 wired to GND */ gpio_request(TEGRA_GPIO(V, 0), "PCB Version Indication"); diff --git a/configs/T2080RDB_NAND_defconfig b/configs/T2080RDB_NAND_defconfig index 95a2c778fc0..93d8d4ba56e 100644 --- a/configs/T2080RDB_NAND_defconfig +++ b/configs/T2080RDB_NAND_defconfig @@ -69,6 +69,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_FW_IN_NAND=y +CONFIG_CORTINA_FW_ADDR=0x200000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_SDCARD_defconfig b/configs/T2080RDB_SDCARD_defconfig index 21d22df4eb3..10598804a1f 100644 --- a/configs/T2080RDB_SDCARD_defconfig +++ b/configs/T2080RDB_SDCARD_defconfig @@ -66,6 +66,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_FW_IN_MMC=y +CONFIG_CORTINA_FW_ADDR=0x114000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_SPIFLASH_defconfig b/configs/T2080RDB_SPIFLASH_defconfig index 393b6db2863..59963fdf374 100644 --- a/configs/T2080RDB_SPIFLASH_defconfig +++ b/configs/T2080RDB_SPIFLASH_defconfig @@ -68,6 +68,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_FW_IN_SPIFLASH=y +CONFIG_CORTINA_FW_ADDR=0x120000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_defconfig b/configs/T2080RDB_defconfig index 24e927c7350..466e91743f5 100644 --- a/configs/T2080RDB_defconfig +++ b/configs/T2080RDB_defconfig @@ -53,6 +53,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0xEFE00000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_revD_NAND_defconfig b/configs/T2080RDB_revD_NAND_defconfig index 250c2d5e962..f6eeade2a39 100644 --- a/configs/T2080RDB_revD_NAND_defconfig +++ b/configs/T2080RDB_revD_NAND_defconfig @@ -70,6 +70,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_FW_IN_NAND=y +CONFIG_CORTINA_FW_ADDR=0x200000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_revD_SDCARD_defconfig b/configs/T2080RDB_revD_SDCARD_defconfig index d5eea40797a..0286610cb03 100644 --- a/configs/T2080RDB_revD_SDCARD_defconfig +++ b/configs/T2080RDB_revD_SDCARD_defconfig @@ -67,6 +67,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_FW_IN_MMC=y +CONFIG_CORTINA_FW_ADDR=0x114000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_revD_SPIFLASH_defconfig b/configs/T2080RDB_revD_SPIFLASH_defconfig index 4d38f4b978f..eb073ce4be5 100644 --- a/configs/T2080RDB_revD_SPIFLASH_defconfig +++ b/configs/T2080RDB_revD_SPIFLASH_defconfig @@ -69,6 +69,7 @@ CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y CONFIG_SYS_CORTINA_FW_IN_SPIFLASH=y +CONFIG_CORTINA_FW_ADDR=0x120000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T2080RDB_revD_defconfig b/configs/T2080RDB_revD_defconfig index 2ecbabf99ec..ab7096e5202 100644 --- a/configs/T2080RDB_revD_defconfig +++ b/configs/T2080RDB_revD_defconfig @@ -54,6 +54,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0xEFE00000 CONFIG_PHY_REALTEK=y CONFIG_DM_ETH=y CONFIG_DM_MDIO=y diff --git a/configs/T4240RDB_SDCARD_defconfig b/configs/T4240RDB_SDCARD_defconfig index 2230e674fcb..c1ca2565e26 100644 --- a/configs/T4240RDB_SDCARD_defconfig +++ b/configs/T4240RDB_SDCARD_defconfig @@ -57,6 +57,7 @@ CONFIG_SPI_FLASH_SST=y CONFIG_PHYLIB=y CONFIG_PHYLIB_10G=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x77f000 CONFIG_PHY_TERANETICS=y CONFIG_PHY_VITESSE=y CONFIG_DM_ETH=y diff --git a/configs/T4240RDB_defconfig b/configs/T4240RDB_defconfig index abb2137d91e..14594b05791 100644 --- a/configs/T4240RDB_defconfig +++ b/configs/T4240RDB_defconfig @@ -45,6 +45,7 @@ CONFIG_SPI_FLASH_SST=y CONFIG_PHYLIB=y CONFIG_PHYLIB_10G=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0xefe00000 CONFIG_PHY_TERANETICS=y CONFIG_PHY_VITESSE=y CONFIG_DM_ETH=y diff --git a/configs/ls2080ardb_SECURE_BOOT_defconfig b/configs/ls2080ardb_SECURE_BOOT_defconfig index b1477eac141..365ee87bdbc 100644 --- a/configs/ls2080ardb_SECURE_BOOT_defconfig +++ b/configs/ls2080ardb_SECURE_BOOT_defconfig @@ -47,6 +47,7 @@ CONFIG_DM_SPI_FLASH=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x580980000 CONFIG_E1000=y CONFIG_MII=y CONFIG_NVME=y diff --git a/configs/ls2080ardb_defconfig b/configs/ls2080ardb_defconfig index c107bcddad7..cb46f4e4bbc 100644 --- a/configs/ls2080ardb_defconfig +++ b/configs/ls2080ardb_defconfig @@ -50,6 +50,7 @@ CONFIG_DM_SPI_FLASH=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x580980000 CONFIG_E1000=y CONFIG_MII=y CONFIG_NVME=y diff --git a/configs/ls2080ardb_nand_defconfig b/configs/ls2080ardb_nand_defconfig index 6615958ae4e..d371fa5e69b 100644 --- a/configs/ls2080ardb_nand_defconfig +++ b/configs/ls2080ardb_nand_defconfig @@ -58,6 +58,7 @@ CONFIG_SYS_FLASH_CFI=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x980000 CONFIG_E1000=y CONFIG_MII=y CONFIG_NVME=y diff --git a/configs/ls2081ardb_defconfig b/configs/ls2081ardb_defconfig index da02de270fb..26692b2e73e 100644 --- a/configs/ls2081ardb_defconfig +++ b/configs/ls2081ardb_defconfig @@ -45,6 +45,7 @@ CONFIG_DM_SPI_FLASH=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x980000 CONFIG_E1000=y CONFIG_MII=y CONFIG_NVME=y diff --git a/configs/ls2088ardb_qspi_SECURE_BOOT_defconfig b/configs/ls2088ardb_qspi_SECURE_BOOT_defconfig index 0201fce1a72..bd16602413f 100644 --- a/configs/ls2088ardb_qspi_SECURE_BOOT_defconfig +++ b/configs/ls2088ardb_qspi_SECURE_BOOT_defconfig @@ -44,6 +44,7 @@ CONFIG_SPI_FLASH_SPANSION=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x980000 CONFIG_DM_ETH=y CONFIG_DM_MDIO=y CONFIG_E1000=y diff --git a/configs/ls2088ardb_qspi_defconfig b/configs/ls2088ardb_qspi_defconfig index 336a0a7b32b..3a426031b91 100644 --- a/configs/ls2088ardb_qspi_defconfig +++ b/configs/ls2088ardb_qspi_defconfig @@ -51,6 +51,7 @@ CONFIG_SPI_FLASH_SPANSION=y CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y CONFIG_PHY_CORTINA=y +CONFIG_CORTINA_FW_ADDR=0x980000 CONFIG_DM_ETH=y CONFIG_DM_MDIO=y CONFIG_E1000=y diff --git a/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig b/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig index ed4304c7048..91fba196182 100644 --- a/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig +++ b/configs/lx2160aqds_tfa_SECURE_BOOT_defconfig @@ -51,6 +51,7 @@ CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_EON=y CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_MT35XU=y CONFIG_SPI_FLASH_SST=y # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHYLIB=y diff --git a/configs/lx2160aqds_tfa_defconfig b/configs/lx2160aqds_tfa_defconfig index faa8da770b5..d52063c7a87 100644 --- a/configs/lx2160aqds_tfa_defconfig +++ b/configs/lx2160aqds_tfa_defconfig @@ -58,6 +58,7 @@ CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_EON=y CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_MT35XU=y CONFIG_SPI_FLASH_SST=y # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHYLIB=y diff --git a/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig b/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig index f8511cb193d..94e103c5d1d 100644 --- a/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig +++ b/configs/lx2160ardb_tfa_SECURE_BOOT_defconfig @@ -47,6 +47,7 @@ CONFIG_FSL_ESDHC=y CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_MT35XU=y # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y diff --git a/configs/lx2160ardb_tfa_defconfig b/configs/lx2160ardb_tfa_defconfig index 004eb7de74f..d09bcde92e7 100644 --- a/configs/lx2160ardb_tfa_defconfig +++ b/configs/lx2160ardb_tfa_defconfig @@ -56,6 +56,7 @@ CONFIG_FSL_ESDHC=y CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_MT35XU=y # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y diff --git a/configs/lx2160ardb_tfa_stmm_defconfig b/configs/lx2160ardb_tfa_stmm_defconfig index 140f851ba27..93b1e49cf2d 100644 --- a/configs/lx2160ardb_tfa_stmm_defconfig +++ b/configs/lx2160ardb_tfa_stmm_defconfig @@ -56,6 +56,7 @@ CONFIG_FSL_ESDHC=y CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_MT35XU=y # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHYLIB=y CONFIG_PHY_AQUANTIA=y diff --git a/configs/qemu-riscv64_smode_defconfig b/configs/qemu-riscv64_smode_defconfig index 0000564e41e..4a6416e2540 100644 --- a/configs/qemu-riscv64_smode_defconfig +++ b/configs/qemu-riscv64_smode_defconfig @@ -6,6 +6,8 @@ CONFIG_ARCH_RV64I=y CONFIG_RISCV_SMODE=y CONFIG_DISTRO_DEFAULTS=y CONFIG_FIT=y +CONFIG_USE_PREBOOT=y +CONFIG_PREBOOT="setenv fdt_addr ${fdtcontroladdr}; fdt addr ${fdtcontroladdr};" CONFIG_DISPLAY_CPUINFO=y CONFIG_DISPLAY_BOARDINFO=y CONFIG_CMD_BOOTEFI_SELFTEST=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 952d430304c..4658f18dfa7 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -131,6 +131,7 @@ CONFIG_AXI=y CONFIG_AXI_SANDBOX=y CONFIG_BOOTCOUNT_LIMIT=y CONFIG_DM_BOOTCOUNT=y +CONFIG_DM_BOOTCOUNT_SYSCON=y CONFIG_DM_BOOTCOUNT_RTC=y CONFIG_DM_BOOTCOUNT_I2C_EEPROM=y CONFIG_BUTTON=y diff --git a/doc/device-tree-bindings/bootcount-syscon.txt b/doc/device-tree-bindings/bootcount-syscon.txt new file mode 100644 index 00000000000..e124f7b6142 --- /dev/null +++ b/doc/device-tree-bindings/bootcount-syscon.txt @@ -0,0 +1,24 @@ +Bootcount Configuration +This is the implementation of the feature as described in +https://www.denx.de/wiki/DULG/UBootBootCountLimit. + +Required Properties: +- compatible: must be "u-boot,bootcount-syscon". +- syscon: reference to the syscon device used. +- reg: contains address and size of the register and the location and size of the bootcount value. + The driver supports a 4 bytes register length and 2 and 4 bytes bootcount value length. +- reg-names: must be "syscon_reg", "offset"; + +Example: + ... + syscon0: syscon@0 { + compatible = "sandbox,syscon0"; + reg = <0x10 16>; + }; + ... + bootcount@0 { + compatible = "u-boot,bootcount-syscon"; + syscon = <&syscon0>; + reg = <0x0 0x04>, <0x0 0x04>; + reg-names = "syscon_reg", "offset"; + }; diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 0de2b7bd78c..607027c968d 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -144,6 +144,18 @@ config BOOTCOUNT_MEM is not cleared on softreset. compatible = "u-boot,bootcount"; +config DM_BOOTCOUNT_SYSCON + bool "Support SYSCON devices as a backing store for bootcount" + select REGMAP + select SYSCON + help + Enable reading/writing the bootcount value in a DM SYSCON device. + The driver supports a fixed 32 bits size register using the native + endianness. However, this can be controlled from the SYSCON DT node + configuration. + + Accessing the backend is done using the regmap interface. + endmenu endif diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile index 12658ffdcec..3a784bb0a64 100644 --- a/drivers/bootcount/Makefile +++ b/drivers/bootcount/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o +obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o diff --git a/drivers/bootcount/bootcount_syscon.c b/drivers/bootcount/bootcount_syscon.c new file mode 100644 index 00000000000..413fd5bb9df --- /dev/null +++ b/drivers/bootcount/bootcount_syscon.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Vaisala Oyj. All rights reserved. + */ + +#include <common.h> +#include <bootcount.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/ioport.h> +#include <regmap.h> +#include <syscon.h> + +#define BYTES_TO_BITS(bytes) ((bytes) << 3) +#define GEN_REG_MASK(val_size, val_addr) \ + (GENMASK(BYTES_TO_BITS(val_size) - 1, 0) \ + << (!!((val_addr) == 0x02) * BYTES_TO_BITS(2))) +#define GET_DEFAULT_VALUE(val_size) \ + (CONFIG_SYS_BOOTCOUNT_MAGIC >> \ + (BYTES_TO_BITS((sizeof(u32) - (val_size))))) + +/** + * struct bootcount_syscon_priv - driver's private data + * + * @regmap: syscon regmap + * @reg_addr: register address used to store the bootcount value + * @size: size of the bootcount value (2 or 4 bytes) + * @magic: magic used to validate/save the bootcount value + * @magic_mask: magic value bitmask + * @reg_mask: mask used to identify the location of the bootcount value + * in the register when 2 bytes length is used + * @shift: value used to extract the botcount value from the register + */ +struct bootcount_syscon_priv { + struct regmap *regmap; + fdt_addr_t reg_addr; + fdt_size_t size; + u32 magic; + u32 magic_mask; + u32 reg_mask; + int shift; +}; + +static int bootcount_syscon_set(struct udevice *dev, const u32 val) +{ + struct bootcount_syscon_priv *priv = dev_get_priv(dev); + u32 regval; + + if ((val & priv->magic_mask) != 0) + return -EINVAL; + + regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask); + + if (priv->size == 2) { + regval &= 0xffff; + regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size); + } + + debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x\n", + __func__, regval, priv->reg_mask); + + return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask, + regval); +} + +static int bootcount_syscon_get(struct udevice *dev, u32 *val) +{ + struct bootcount_syscon_priv *priv = dev_get_priv(dev); + u32 regval; + int ret; + + ret = regmap_read(priv->regmap, priv->reg_addr, ®val); + if (ret) + return ret; + + regval &= priv->reg_mask; + regval >>= priv->shift; + + if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) { + *val = regval & ~priv->magic_mask; + } else { + dev_err(dev, "%s: Invalid bootcount magic\n", __func__); + return -EINVAL; + } + + debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x\n", + __func__, *val, regval); + return 0; +} + +static int bootcount_syscon_of_to_plat(struct udevice *dev) +{ + struct bootcount_syscon_priv *priv = dev_get_priv(dev); + fdt_addr_t bootcount_offset; + fdt_size_t reg_size; + + priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon"); + if (IS_ERR(priv->regmap)) { + dev_err(dev, "%s: Unable to find regmap (%ld)\n", __func__, + PTR_ERR(priv->regmap)); + return PTR_ERR(priv->regmap); + } + + priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", ®_size); + if (priv->reg_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s: syscon_reg address not found\n", __func__); + return -EINVAL; + } + if (reg_size != 4) { + dev_err(dev, "%s: Unsupported register size: %d\n", __func__, + reg_size); + return -EINVAL; + } + + bootcount_offset = dev_read_addr_size_name(dev, "offset", &priv->size); + if (bootcount_offset == FDT_ADDR_T_NONE) { + dev_err(dev, "%s: offset configuration not found\n", __func__); + return -EINVAL; + } + if (bootcount_offset + priv->size > reg_size) { + dev_err(dev, + "%s: Bootcount value doesn't fit in the reserved space\n", + __func__); + return -EINVAL; + } + if (priv->size != 2 && priv->size != 4) { + dev_err(dev, + "%s: Driver supports only 2 and 4 bytes bootcount size\n", + __func__); + return -EINVAL; + } + + priv->magic = GET_DEFAULT_VALUE(priv->size); + priv->magic_mask = GENMASK(BYTES_TO_BITS(priv->size) - 1, + BYTES_TO_BITS(priv->size >> 1)); + priv->shift = !!(bootcount_offset == 0x02) * BYTES_TO_BITS(priv->size); + priv->reg_mask = GEN_REG_MASK(priv->size, bootcount_offset); + + return 0; +} + +static const struct bootcount_ops bootcount_syscon_ops = { + .get = bootcount_syscon_get, + .set = bootcount_syscon_set, +}; + +static const struct udevice_id bootcount_syscon_ids[] = { + { .compatible = "u-boot,bootcount-syscon" }, + {} +}; + +U_BOOT_DRIVER(bootcount_syscon) = { + .name = "bootcount-syscon", + .id = UCLASS_BOOTCOUNT, + .of_to_plat = bootcount_syscon_of_to_plat, + .priv_auto = sizeof(struct bootcount_syscon_priv), + .of_match = bootcount_syscon_ids, + .ops = &bootcount_syscon_ops, +}; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index e07c6dd78a6..baac8d281e4 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -220,4 +220,13 @@ config SANDBOX_CLK_CCF Enable this option if you want to test the Linux kernel's Common Clock Framework [CCF] code in U-Boot's Sandbox clock driver. +config CLK_VERSACLOCK + tristate "Enable VersaClock 5/6 devices" + depends on CLK + depends on CLK_CCF + depends on OF_CONTROL + help + This driver supports the IDT VersaClock 5 and VersaClock 6 + programmable clock generators. + endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 6e9c2d54853..711ae5bc29d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o obj-$(CONFIG_STM32H7) += clk_stm32h7.o obj-$(CONFIG_CLK_VERSAL) += clk_versal.o obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o +obj-$(CONFIG_CLK_VERSACLOCK) += clk_versaclock.o diff --git a/drivers/clk/clk_versaclock.c b/drivers/clk/clk_versaclock.c new file mode 100644 index 00000000000..578668bcf83 --- /dev/null +++ b/drivers/clk/clk_versaclock.c @@ -0,0 +1,1100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for IDT Versaclock 5/6 + * + * Derived from code Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <common.h> +#include <clk.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <dm/device_compat.h> +#include <log.h> +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/math64.h> + +#include <dt-bindings/clk/versaclock.h> + +/* VersaClock5 registers */ +#define VC5_OTP_CONTROL 0x00 + +/* Factory-reserved register block */ +#define VC5_RSVD_DEVICE_ID 0x01 +#define VC5_RSVD_ADC_GAIN_7_0 0x02 +#define VC5_RSVD_ADC_GAIN_15_8 0x03 +#define VC5_RSVD_ADC_OFFSET_7_0 0x04 +#define VC5_RSVD_ADC_OFFSET_15_8 0x05 +#define VC5_RSVD_TEMPY 0x06 +#define VC5_RSVD_OFFSET_TBIN 0x07 +#define VC5_RSVD_GAIN 0x08 +#define VC5_RSVD_TEST_NP 0x09 +#define VC5_RSVD_UNUSED 0x0a +#define VC5_RSVD_BANDGAP_TRIM_UP 0x0b +#define VC5_RSVD_BANDGAP_TRIM_DN 0x0c +#define VC5_RSVD_CLK_R_12_CLK_AMP_4 0x0d +#define VC5_RSVD_CLK_R_34_CLK_AMP_4 0x0e +#define VC5_RSVD_CLK_AMP_123 0x0f + +/* Configuration register block */ +#define VC5_PRIM_SRC_SHDN 0x10 +#define VC5_PRIM_SRC_SHDN_EN_XTAL BIT(7) +#define VC5_PRIM_SRC_SHDN_EN_CLKIN BIT(6) +#define VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ BIT(3) +#define VC5_PRIM_SRC_SHDN_SP BIT(1) +#define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN BIT(0) + +#define VC5_VCO_BAND 0x11 +#define VC5_XTAL_X1_LOAD_CAP 0x12 +#define VC5_XTAL_X2_LOAD_CAP 0x13 +#define VC5_REF_DIVIDER 0x15 +#define VC5_REF_DIVIDER_SEL_PREDIV2 BIT(7) +#define VC5_REF_DIVIDER_REF_DIV(n) ((n) & 0x3f) + +#define VC5_VCO_CTRL_AND_PREDIV 0x16 +#define VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV BIT(7) + +#define VC5_FEEDBACK_INT_DIV 0x17 +#define VC5_FEEDBACK_INT_DIV_BITS 0x18 +#define VC5_FEEDBACK_FRAC_DIV(n) (0x19 + (n)) +#define VC5_RC_CONTROL0 0x1e +#define VC5_RC_CONTROL1 0x1f +/* Register 0x20 is factory reserved */ + +/* Output divider control for divider 1,2,3,4 */ +#define VC5_OUT_DIV_CONTROL(idx) (0x21 + ((idx) * 0x10)) +#define VC5_OUT_DIV_CONTROL_RESET BIT(7) +#define VC5_OUT_DIV_CONTROL_SELB_NORM BIT(3) +#define VC5_OUT_DIV_CONTROL_SEL_EXT BIT(2) +#define VC5_OUT_DIV_CONTROL_INT_MODE BIT(1) +#define VC5_OUT_DIV_CONTROL_EN_FOD BIT(0) + +#define VC5_OUT_DIV_FRAC(idx, n) (0x22 + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_FRAC4_OD_SCEE BIT(1) + +#define VC5_OUT_DIV_STEP_SPREAD(idx, n) (0x26 + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_SPREAD_MOD(idx, n) (0x29 + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_SKEW_INT(idx, n) (0x2b + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_INT(idx, n) (0x2d + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_SKEW_FRAC(idx) (0x2f + ((idx) * 0x10)) +/* Registers 0x30, 0x40, 0x50 are factory reserved */ + +/* Clock control register for clock 1,2 */ +#define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) +#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 +#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) + +#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) +#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) + +#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 +#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0 << VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2 << VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3 << VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0 +#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0 << VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1 << VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2 << VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3 << VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) + +#define VC5_CLK_OE_SHDN 0x68 +#define VC5_CLK_OS_SHDN 0x69 + +#define VC5_GLOBAL_REGISTER 0x76 +#define VC5_GLOBAL_REGISTER_GLOBAL_RESET BIT(5) + +/* PLL/VCO runs between 2.5 GHz and 3.0 GHz */ +#define VC5_PLL_VCO_MIN 2500000000UL +#define VC5_PLL_VCO_MAX 3000000000UL + +/* VC5 Input mux settings */ +#define VC5_MUX_IN_XIN BIT(0) +#define VC5_MUX_IN_CLKIN BIT(1) + +/* Maximum number of clk_out supported by this driver */ +#define VC5_MAX_CLK_OUT_NUM 5 + +/* Maximum number of FODs supported by this driver */ +#define VC5_MAX_FOD_NUM 4 + +/* flags to describe chip features */ +/* chip has built-in oscilator */ +#define VC5_HAS_INTERNAL_XTAL BIT(0) +/* chip has PFD requency doubler */ +#define VC5_HAS_PFD_FREQ_DBL BIT(1) + +/* Supported IDT VC5 models. */ +enum vc5_model { + IDT_VC5_5P49V5923, + IDT_VC5_5P49V5925, + IDT_VC5_5P49V5933, + IDT_VC5_5P49V5935, + IDT_VC6_5P49V6901, + IDT_VC6_5P49V6965, +}; + +/* Structure to describe features of a particular VC5 model */ +struct vc5_chip_info { + const enum vc5_model model; + const unsigned int clk_fod_cnt; + const unsigned int clk_out_cnt; + const u32 flags; +}; + +struct vc5_driver_data; + +struct vc5_hw_data { + struct clk hw; + struct vc5_driver_data *vc5; + u32 div_int; + u32 div_frc; + unsigned int num; +}; + +struct vc5_out_data { + struct clk hw; + struct vc5_driver_data *vc5; + unsigned int num; + unsigned int clk_output_cfg0; + unsigned int clk_output_cfg0_mask; +}; + +struct vc5_driver_data { + struct udevice *i2c; + const struct vc5_chip_info *chip_info; + + struct clk *pin_xin; + struct clk *pin_clkin; + unsigned char clk_mux_ins; + struct clk clk_mux; + struct clk clk_mul; + struct clk clk_pfd; + struct vc5_hw_data clk_pll; + struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM]; + struct vc5_out_data clk_out[VC5_MAX_CLK_OUT_NUM]; +}; + +static const struct vc5_chip_info idt_5p49v5923_info = { + .model = IDT_VC5_5P49V5923, + .clk_fod_cnt = 2, + .clk_out_cnt = 3, + .flags = 0, +}; + +static const struct vc5_chip_info idt_5p49v5925_info = { + .model = IDT_VC5_5P49V5925, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = 0, +}; + +static const struct vc5_chip_info idt_5p49v5933_info = { + .model = IDT_VC5_5P49V5933, + .clk_fod_cnt = 2, + .clk_out_cnt = 3, + .flags = VC5_HAS_INTERNAL_XTAL, +}; + +static const struct vc5_chip_info idt_5p49v5935_info = { + .model = IDT_VC5_5P49V5935, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = VC5_HAS_INTERNAL_XTAL, +}; + +static const struct vc5_chip_info idt_5p49v6901_info = { + .model = IDT_VC6_5P49V6901, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = VC5_HAS_PFD_FREQ_DBL, +}; + +static const struct vc5_chip_info idt_5p49v6965_info = { + .model = IDT_VC6_5P49V6965, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = 0, +}; + +static int vc5_update_bits(struct udevice *dev, unsigned int reg, unsigned int mask, + unsigned int src) +{ + int ret; + unsigned char cache; + + ret = dm_i2c_read(dev, reg, &cache, 1); + if (ret < 0) + return ret; + + cache &= ~mask; + cache |= mask & src; + ret = dm_i2c_write(dev, reg, (uchar *)&cache, 1); + + return ret; +} + +static unsigned long vc5_mux_get_rate(struct clk *hw) +{ + return clk_get_rate(clk_get_parent(hw)); +} + +static int vc5_mux_set_parent(struct clk *hw, unsigned char index) +{ + struct vc5_driver_data *vc5 = container_of(hw, struct vc5_driver_data, clk_mux); + const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN; + u8 src; + + if (index > 1 || !vc5->clk_mux_ins) + return -EINVAL; + + if (vc5->clk_mux_ins == (VC5_MUX_IN_CLKIN | VC5_MUX_IN_XIN)) { + if (index == 0) + src = VC5_PRIM_SRC_SHDN_EN_XTAL; + if (index == 1) + src = VC5_PRIM_SRC_SHDN_EN_CLKIN; + } else { + if (index != 0) + return -EINVAL; + + if (vc5->clk_mux_ins == VC5_MUX_IN_XIN) + src = VC5_PRIM_SRC_SHDN_EN_XTAL; + else if (vc5->clk_mux_ins == VC5_MUX_IN_CLKIN) + src = VC5_PRIM_SRC_SHDN_EN_CLKIN; + else /* Invalid; should have been caught by vc5_probe() */ + return -EINVAL; + } + + return vc5_update_bits(vc5->i2c, VC5_PRIM_SRC_SHDN, mask, src); +} + +static const struct clk_ops vc5_mux_ops = { + .get_rate = vc5_mux_get_rate, +}; + +static unsigned long vc5_pfd_round_rate(struct clk *hw, unsigned long rate) +{ + struct clk *clk_parent = clk_get_parent(hw); + unsigned long parent_rate = clk_get_rate(clk_parent); + unsigned long idiv; + + /* PLL cannot operate with input clock above 50 MHz. */ + if (rate > 50000000) + return -EINVAL; + + /* CLKIN within range of PLL input, feed directly to PLL. */ + if (parent_rate <= 50000000) + return parent_rate; + + idiv = DIV_ROUND_UP(parent_rate, rate); + if (idiv > 127) + return -EINVAL; + + return parent_rate / idiv; +} + +static unsigned long vc5_pfd_recalc_rate(struct clk *hw) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_pfd); + unsigned int prediv, div; + struct clk *clk_parent = clk_get_parent(hw); + unsigned long parent_rate = clk_get_rate(clk_parent); + + dm_i2c_read(vc5->i2c, VC5_VCO_CTRL_AND_PREDIV, (uchar *)&prediv, 1); + + /* The bypass_prediv is set, PLL fed from Ref_in directly. */ + if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV) + return parent_rate; + + dm_i2c_read(vc5->i2c, VC5_REF_DIVIDER, (uchar *)&div, 1); + + /* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */ + if (div & VC5_REF_DIVIDER_SEL_PREDIV2) + return parent_rate / 2; + else + return parent_rate / VC5_REF_DIVIDER_REF_DIV(div); +} + +static unsigned long vc5_pfd_set_rate(struct clk *hw, unsigned long rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_pfd); + unsigned long idiv; + u8 div; + struct clk *clk_parent = clk_get_parent(hw); + unsigned long parent_rate = clk_get_rate(clk_parent); + + /* CLKIN within range of PLL input, feed directly to PLL. */ + if (parent_rate <= 50000000) { + vc5_update_bits(vc5->i2c, VC5_VCO_CTRL_AND_PREDIV, + VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, + VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV); + vc5_update_bits(vc5->i2c, VC5_REF_DIVIDER, 0xff, 0x00); + return 0; + } + + idiv = DIV_ROUND_UP(parent_rate, rate); + + /* We have dedicated div-2 predivider. */ + if (idiv == 2) + div = VC5_REF_DIVIDER_SEL_PREDIV2; + else + div = VC5_REF_DIVIDER_REF_DIV(idiv); + + vc5_update_bits(vc5->i2c, VC5_REF_DIVIDER, 0xff, div); + vc5_update_bits(vc5->i2c, VC5_VCO_CTRL_AND_PREDIV, + VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, 0); + + return 0; +} + +static const struct clk_ops vc5_pfd_ops = { + .round_rate = vc5_pfd_round_rate, + .get_rate = vc5_pfd_recalc_rate, + .set_rate = vc5_pfd_set_rate, +}; + +/* + * VersaClock5 PLL/VCO + */ +static unsigned long vc5_pll_recalc_rate(struct clk *hw) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc = hwdata->vc5; + struct clk *clk_parent = clk_get_parent(hw); + unsigned long parent_rate = clk_get_rate(clk_parent); + u32 div_int, div_frc; + u8 fb[5]; + + dm_i2c_read(vc->i2c, VC5_FEEDBACK_INT_DIV, fb, 5); + + div_int = (fb[0] << 4) | (fb[1] >> 4); + div_frc = (fb[2] << 16) | (fb[3] << 8) | fb[4]; + + /* The PLL divider has 12 integer bits and 24 fractional bits */ + return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24); +} + +static unsigned long vc5_pll_round_rate(struct clk *hw, unsigned long rate) +{ + struct clk *clk_parent = clk_get_parent(hw); + unsigned long parent_rate = clk_get_rate(clk_parent); + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + u32 div_int; + u64 div_frc; + + if (rate < VC5_PLL_VCO_MIN) + rate = VC5_PLL_VCO_MIN; + if (rate > VC5_PLL_VCO_MAX) + rate = VC5_PLL_VCO_MAX; + + /* Determine integer part, which is 12 bit wide */ + div_int = rate / parent_rate; + if (div_int > 0xfff) + rate = parent_rate * 0xfff; + + /* Determine best fractional part, which is 24 bit wide */ + div_frc = rate % parent_rate; + div_frc *= BIT(24) - 1; + do_div(div_frc, parent_rate); + + hwdata->div_int = div_int; + hwdata->div_frc = (u32)div_frc; + + return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24); +} + +static unsigned long vc5_pll_set_rate(struct clk *hw, unsigned long rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + u8 fb[5]; + + fb[0] = hwdata->div_int >> 4; + fb[1] = hwdata->div_int << 4; + fb[2] = hwdata->div_frc >> 16; + fb[3] = hwdata->div_frc >> 8; + fb[4] = hwdata->div_frc; + + return dm_i2c_write(vc5->i2c, VC5_FEEDBACK_INT_DIV, fb, 5); +} + +static const struct clk_ops vc5_pll_ops = { + .round_rate = vc5_pll_round_rate, + .get_rate = vc5_pll_recalc_rate, + .set_rate = vc5_pll_set_rate, +}; + +static unsigned long vc5_fod_recalc_rate(struct clk *hw) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc = hwdata->vc5; + struct clk *parent = &vc->clk_pll.hw; + unsigned long parent_rate = vc5_pll_recalc_rate(parent); + + /* VCO frequency is divided by two before entering FOD */ + u32 f_in = parent_rate / 2; + u32 div_int, div_frc; + u8 od_int[2]; + u8 od_frc[4]; + + dm_i2c_read(vc->i2c, VC5_OUT_DIV_INT(hwdata->num, 0), od_int, 2); + dm_i2c_read(vc->i2c, VC5_OUT_DIV_FRAC(hwdata->num, 0), od_frc, 4); + + div_int = (od_int[0] << 4) | (od_int[1] >> 4); + div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) | + (od_frc[2] << 6) | (od_frc[3] >> 2); + + /* Avoid division by zero if the output is not configured. */ + if (div_int == 0 && div_frc == 0) + return 0; + + /* The PLL divider has 12 integer bits and 30 fractional bits */ + return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); +} + +static unsigned long vc5_fod_round_rate(struct clk *hw, unsigned long rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc = hwdata->vc5; + struct clk *parent = &vc->clk_pll.hw; + unsigned long parent_rate = vc5_pll_recalc_rate(parent); + + /* VCO frequency is divided by two before entering FOD */ + u32 f_in = parent_rate / 2; + u32 div_int; + u64 div_frc; + + /* Determine integer part, which is 12 bit wide */ + div_int = f_in / rate; + + /* + * WARNING: The clock chip does not output signal if the integer part + * of the divider is 0xfff and fractional part is non-zero. + * Clamp the divider at 0xffe to keep the code simple. + */ + if (div_int > 0xffe) { + div_int = 0xffe; + rate = f_in / div_int; + } + + /* Determine best fractional part, which is 30 bit wide */ + div_frc = f_in % rate; + div_frc <<= 24; + do_div(div_frc, rate); + + hwdata->div_int = div_int; + hwdata->div_frc = (u32)div_frc; + + return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); +} + +static unsigned long vc5_fod_set_rate(struct clk *hw, unsigned long rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + + u8 data[14] = { + hwdata->div_frc >> 22, hwdata->div_frc >> 14, + hwdata->div_frc >> 6, hwdata->div_frc << 2, + 0, 0, 0, 0, 0, + 0, 0, + hwdata->div_int >> 4, hwdata->div_int << 4, + 0 + }; + + dm_i2c_write(vc5->i2c, VC5_OUT_DIV_FRAC(hwdata->num, 0), data, 14); + + /* + * Toggle magic bit in undocumented register for unknown reason. + * This is what the IDT timing commander tool does and the chip + * datasheet somewhat implies this is needed, but the register + * and the bit is not documented. + */ + vc5_update_bits(vc5->i2c, VC5_GLOBAL_REGISTER, + VC5_GLOBAL_REGISTER_GLOBAL_RESET, 0); + vc5_update_bits(vc5->i2c, VC5_GLOBAL_REGISTER, + VC5_GLOBAL_REGISTER_GLOBAL_RESET, + VC5_GLOBAL_REGISTER_GLOBAL_RESET); + + return 0; +} + +static const struct clk_ops vc5_fod_ops = { + .round_rate = vc5_fod_round_rate, + .get_rate = vc5_fod_recalc_rate, + .set_rate = vc5_fod_set_rate, +}; + +static int vc5_clk_out_prepare(struct clk *hw) +{ + struct udevice *dev; + struct vc5_driver_data *vc5; + struct vc5_out_data *hwdata; + + const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT | + VC5_OUT_DIV_CONTROL_EN_FOD; + unsigned int src; + int ret; + + uclass_get_device_by_name(UCLASS_CLK, clk_hw_get_name(hw), &dev); + vc5 = dev_get_priv(dev); + hwdata = &vc5->clk_out[hw->id]; + + /* + * If the input mux is disabled, enable it first and + * select source from matching FOD. + */ + + dm_i2c_read(vc5->i2c, VC5_OUT_DIV_CONTROL(hwdata->num), (uchar *)&src, 1); + + if ((src & mask) == 0) { + src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD; + ret = vc5_update_bits(vc5->i2c, + VC5_OUT_DIV_CONTROL(hwdata->num), + mask | VC5_OUT_DIV_CONTROL_RESET, src); + if (ret) + return ret; + } + + /* Enable the clock buffer */ + vc5_update_bits(vc5->i2c, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), + VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, + VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); + if (hwdata->clk_output_cfg0_mask) { + vc5_update_bits(vc5->i2c, VC5_CLK_OUTPUT_CFG(hwdata->num, 0), + hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + } + + return 0; +} + +static int vc5_clk_out_unprepare(struct clk *hw) +{ + struct udevice *dev; + struct vc5_driver_data *vc5; + struct vc5_out_data *hwdata; + int ret; + + uclass_get_device_by_name(UCLASS_CLK, clk_hw_get_name(hw), &dev); + vc5 = dev_get_priv(dev); + hwdata = &vc5->clk_out[hw->id]; + + /* Disable the clock buffer */ + ret = vc5_update_bits(vc5->i2c, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), + VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0); + + return ret; +} + +static int vc5_clk_out_set_parent(struct vc5_driver_data *vc, u8 num, u8 index) +{ + const u8 mask = VC5_OUT_DIV_CONTROL_RESET | + VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT | + VC5_OUT_DIV_CONTROL_EN_FOD; + const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT; + u8 src = VC5_OUT_DIV_CONTROL_RESET; + + if (index == 0) + src |= VC5_OUT_DIV_CONTROL_EN_FOD; + else + src |= extclk; + + return vc5_update_bits(vc->i2c, VC5_OUT_DIV_CONTROL(num), mask, src); +} + +/* + * The device references to the Versaclock point to the head, so xlate needs to + * redirect it to clk_out[idx] + */ +static int vc5_clk_out_xlate(struct clk *hw, struct ofnode_phandle_args *args) +{ + unsigned int idx = args->args[0]; + + if (args->args_count != 1) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + hw->id = idx; + + return 0; +} + +static unsigned long vc5_clk_out_set_rate(struct clk *hw, unsigned long rate) +{ + struct udevice *dev; + struct vc5_driver_data *vc; + struct clk *parent; + + uclass_get_device_by_name(UCLASS_CLK, clk_hw_get_name(hw), &dev); + vc = dev_get_priv(dev); + parent = clk_get_parent(&vc->clk_out[hw->id].hw); + + /* setting the output rate really means setting the parent FOD rate */ + return clk_set_rate(parent, clk_round_rate(parent, rate)); +} + +static unsigned long vc5_clk_out_get_rate(struct clk *hw) +{ + return clk_get_parent_rate(hw); +} + +static const struct clk_ops vc5_clk_out_ops = { + .enable = vc5_clk_out_prepare, + .disable = vc5_clk_out_unprepare, + .set_rate = vc5_clk_out_set_rate, + .get_rate = vc5_clk_out_get_rate, +}; + +static const struct clk_ops vc5_clk_out_sel_ops = { + .enable = vc5_clk_out_prepare, + .disable = vc5_clk_out_unprepare, + .get_rate = vc5_clk_out_get_rate, +}; + +static const struct clk_ops vc5_clk_ops = { + .enable = vc5_clk_out_prepare, + .disable = vc5_clk_out_unprepare, + .of_xlate = vc5_clk_out_xlate, + .set_rate = vc5_clk_out_set_rate, + .get_rate = vc5_clk_out_get_rate, +}; + +static int vc5_map_index_to_output(const enum vc5_model model, + const unsigned int n) +{ + switch (model) { + case IDT_VC5_5P49V5933: + return (n == 0) ? 0 : 3; + case IDT_VC5_5P49V5923: + case IDT_VC5_5P49V5925: + case IDT_VC5_5P49V5935: + case IDT_VC6_5P49V6901: + case IDT_VC6_5P49V6965: + default: + return n; + } +} + +static int vc5_update_mode(ofnode np_output, + struct vc5_out_data *clk_out) +{ + u32 value; + + if (!ofnode_read_u32(np_output, "idt,mode", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; + switch (value) { + case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: + case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: + clk_out->clk_output_cfg0 |= + value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int vc5_update_power(ofnode np_output, struct vc5_out_data *clk_out) +{ + u32 value; + + if (!ofnode_read_u32(np_output, "idt,voltage-microvolt", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; + switch (value) { + case 1800000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; + break; + case 2500000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; + break; + case 3300000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_map_cap_value(u32 femtofarads) +{ + int mapped_value; + + /* + * The datasheet explicitly states 9000 - 25000 with 0.5pF + * steps, but the Programmer's guide shows the steps are 0.430pF. + * After getting feedback from Renesas, the .5pF steps were the + * goal, but 430nF was the actual values. + * Because of this, the actual range goes to 22760 instead of 25000 + */ + if (femtofarads < 9000 || femtofarads > 22760) + return -EINVAL; + + /* + * The Programmer's guide shows XTAL[5:0] but in reality, + * XTAL[0] and XTAL[1] are both LSB which makes the math + * strange. With clarfication from Renesas, setting the + * values should be simpler by ignoring XTAL[0] + */ + mapped_value = DIV_ROUND_CLOSEST(femtofarads - 9000, 430); + + /* + * Since the calculation ignores XTAL[0], there is one + * special case where mapped_value = 32. In reality, this means + * the real mapped value should be 111111b. In other cases, + * the mapped_value needs to be shifted 1 to the left. + */ + if (mapped_value > 31) + mapped_value = 0x3f; + else + mapped_value <<= 1; + + return mapped_value; +} + +static int vc5_update_cap_load(ofnode node, struct vc5_driver_data *vc5) +{ + u32 value; + int mapped_value; + + if (!ofnode_read_u32(node, "idt,xtal-load-femtofarads", &value)) { + mapped_value = vc5_map_cap_value(value); + + if (mapped_value < 0) + return mapped_value; + + /* + * The mapped_value is really the high 6 bits of + * VC5_XTAL_X1_LOAD_CAP and VC5_XTAL_X2_LOAD_CAP, so + * shift the value 2 places. + */ + vc5_update_bits(vc5->i2c, VC5_XTAL_X1_LOAD_CAP, ~0x03, mapped_value << 2); + vc5_update_bits(vc5->i2c, VC5_XTAL_X2_LOAD_CAP, ~0x03, mapped_value << 2); + } + + return 0; +} + +static int vc5_update_slew(ofnode np_output, struct vc5_out_data *clk_out) +{ + u32 value; + + if (!ofnode_read_u32(np_output, "idt,slew-percent", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; + + switch (value) { + case 80: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; + break; + case 85: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; + break; + case 90: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; + break; + case 100: + clk_out->clk_output_cfg0 |= + VC5_CLK_OUTPUT_CFG0_SLEW_100; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_get_output_config(struct udevice *dev, + struct vc5_out_data *clk_out) +{ + ofnode np_output; + char child_name[5]; + int ret = 0; + + sprintf(child_name, "OUT%d", clk_out->num + 1); + + np_output = dev_read_subnode(dev, child_name); + + if (!ofnode_valid(np_output)) { + dev_dbg(dev, "Invalid clock output configuration OUT%d\n", + clk_out->num + 1); + return 0; + } + + ret = vc5_update_mode(np_output, clk_out); + if (ret) + return ret; + + ret = vc5_update_power(np_output, clk_out); + if (ret) + return ret; + + ret = vc5_update_slew(np_output, clk_out); + + return ret; +} + +static char *versaclock_get_name(const char *dev_name, const char *clk_name, int index) +{ + int length; + char *buf; + + if (index < 0) + length = snprintf(NULL, 0, "%s.%s", dev_name, clk_name) + 1; + else + length = snprintf(NULL, 0, "%s.%s%d", dev_name, clk_name, index) + 1; + + buf = malloc(length); + if (!buf) + ERR_PTR(-ENOMEM); + + if (index < 0) + snprintf(buf, length, "%s.%s", dev_name, clk_name); + else + snprintf(buf, length, "%s.%s%d", dev_name, clk_name, index); + + return buf; +} + +int versaclock_probe(struct udevice *dev) +{ + struct vc5_driver_data *vc5 = dev_get_priv(dev); + struct vc5_chip_info *chip = (void *)dev_get_driver_data(dev); + unsigned int n, idx = 0; + char *mux_name, *pfd_name, *pll_name, *outsel_name; + char *out_name[VC5_MAX_CLK_OUT_NUM]; + char *fod_name[VC5_MAX_FOD_NUM]; + int ret; + u64 val; + + val = (u64)dev_read_addr_ptr(dev); + ret = i2c_get_chip(dev->parent, val, 1, &vc5->i2c); + + if (ret) { + dev_dbg(dev, "I2C probe failed.\n"); + return ret; + } + + vc5->chip_info = chip; + vc5->pin_xin = devm_clk_get(dev, "xin"); + + if (IS_ERR(vc5->pin_xin)) + dev_dbg(dev, "failed to get xin clock\n"); + + ret = clk_enable(vc5->pin_xin); + if (ret) + dev_dbg(dev, "failed to enable XIN clock\n"); + + vc5->pin_clkin = devm_clk_get(dev, "clkin"); + + /* Register clock input mux */ + if (!IS_ERR(vc5->pin_xin)) { + vc5->clk_mux_ins |= VC5_MUX_IN_XIN; + } else if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) { + if (IS_ERR(vc5->pin_xin)) + return PTR_ERR(vc5->pin_xin); + vc5->clk_mux_ins |= VC5_MUX_IN_XIN; + } + + mux_name = versaclock_get_name(dev->name, "mux", -1); + if (IS_ERR(mux_name)) + return PTR_ERR(mux_name); + + clk_register(&vc5->clk_mux, "versaclock-mux", mux_name, vc5->pin_xin->dev->name); + + if (!IS_ERR(vc5->pin_xin)) + vc5_mux_set_parent(&vc5->clk_mux, 1); + else + vc5_mux_set_parent(&vc5->clk_mux, 0); + + /* Configure Optional Loading Capacitance for external XTAL */ + if (!(vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)) { + ret = vc5_update_cap_load(dev_ofnode(dev), vc5); + if (ret) + dev_dbg(dev, "failed to vc5_update_cap_load\n"); + } + + /* Register PFD */ + pfd_name = versaclock_get_name(dev->name, "pfd", -1); + if (IS_ERR(pfd_name)) { + ret = PTR_ERR(pfd_name); + goto free_mux; + } + + ret = clk_register(&vc5->clk_pfd, "versaclock-pfd", pfd_name, vc5->clk_mux.dev->name); + if (ret) + goto free_pfd; + + /* Register PLL */ + vc5->clk_pll.num = 0; + vc5->clk_pll.vc5 = vc5; + pll_name = versaclock_get_name(dev->name, "pll", -1); + if (IS_ERR(pll_name)) { + ret = PTR_ERR(pll_name); + goto free_pfd; + } + + ret = clk_register(&vc5->clk_pll.hw, "versaclock-pll", pll_name, vc5->clk_pfd.dev->name); + if (ret) + goto free_pll; + + /* Register FODs */ + for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { + fod_name[n] = versaclock_get_name(dev->name, "fod", n); + if (IS_ERR(pll_name)) { + ret = PTR_ERR(fod_name[n]); + goto free_fod; + } + idx = vc5_map_index_to_output(vc5->chip_info->model, n); + vc5->clk_fod[n].num = idx; + vc5->clk_fod[n].vc5 = vc5; + ret = clk_register(&vc5->clk_fod[n].hw, "versaclock-fod", fod_name[n], + vc5->clk_pll.hw.dev->name); + if (ret) + goto free_fod; + } + + /* Register MUX-connected OUT0_I2C_SELB output */ + vc5->clk_out[0].num = idx; + vc5->clk_out[0].vc5 = vc5; + outsel_name = versaclock_get_name(dev->name, "out0_sel_i2cb", -1); + if (IS_ERR(outsel_name)) { + ret = PTR_ERR(outsel_name); + goto free_fod; + }; + + ret = clk_register(&vc5->clk_out[0].hw, "versaclock-outsel", outsel_name, + vc5->clk_mux.dev->name); + if (ret) + goto free_selb; + + /* Register FOD-connected OUTx outputs */ + for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { + idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); + out_name[n] = versaclock_get_name(dev->name, "out", n); + if (IS_ERR(out_name[n])) { + ret = PTR_ERR(out_name[n]); + goto free_selb; + } + vc5->clk_out[n].num = idx; + vc5->clk_out[n].vc5 = vc5; + ret = clk_register(&vc5->clk_out[n].hw, "versaclock-out", out_name[n], + vc5->clk_fod[idx].hw.dev->name); + if (ret) + goto free_out; + vc5_clk_out_set_parent(vc5, idx, 0); + + /* Fetch Clock Output configuration from DT (if specified) */ + ret = vc5_get_output_config(dev, &vc5->clk_out[n]); + if (ret) { + dev_dbg(dev, "failed to vc5_get_output_config()\n"); + goto free_out; + } + } + + return 0; + +free_out: + for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { + clk_free(&vc5->clk_out[n].hw); + free(out_name[n]); + } +free_selb: + clk_free(&vc5->clk_out[0].hw); + free(outsel_name); +free_fod: + for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { + clk_free(&vc5->clk_fod[n].hw); + free(fod_name[n]); + } +free_pll: + clk_free(&vc5->clk_pll.hw); + free(pll_name); +free_pfd: + clk_free(&vc5->clk_pfd); + free(pfd_name); +free_mux: + clk_free(&vc5->clk_mux); + free(mux_name); + + return ret; +} + +static const struct udevice_id versaclock_ids[] = { + { .compatible = "idt,5p49v5923", .data = (ulong)&idt_5p49v5923_info }, + { .compatible = "idt,5p49v5925", .data = (ulong)&idt_5p49v5925_info }, + { .compatible = "idt,5p49v5933", .data = (ulong)&idt_5p49v5933_info }, + { .compatible = "idt,5p49v5935", .data = (ulong)&idt_5p49v5935_info }, + { .compatible = "idt,5p49v6901", .data = (ulong)&idt_5p49v6901_info }, + { .compatible = "idt,5p49v6965", .data = (ulong)&idt_5p49v6965_info }, + {}, +}; + +U_BOOT_DRIVER(versaclock) = { + .name = "versaclock", + .id = UCLASS_CLK, + .ops = &vc5_clk_ops, + .of_match = versaclock_ids, + .probe = versaclock_probe, + .priv_auto = sizeof(struct vc5_driver_data), +}; + +U_BOOT_DRIVER(versaclock_mux) = { + .name = "versaclock-mux", + .id = UCLASS_CLK, + .ops = &vc5_mux_ops, +}; + +U_BOOT_DRIVER(versaclock_pfd) = { + .name = "versaclock-pfd", + .id = UCLASS_CLK, + .ops = &vc5_pfd_ops, +}; + +U_BOOT_DRIVER(versaclock_pll) = { + .name = "versaclock-pll", + .id = UCLASS_CLK, + .ops = &vc5_pll_ops, +}; + +U_BOOT_DRIVER(versaclock_fod) = { + .name = "versaclock-fod", + .id = UCLASS_CLK, + .ops = &vc5_fod_ops, +}; + +U_BOOT_DRIVER(versaclock_out) = { + .name = "versaclock-out", + .id = UCLASS_CLK, + .ops = &vc5_clk_out_ops, +}; + +U_BOOT_DRIVER(versaclock_outsel) = { + .name = "versaclock-outsel", + .id = UCLASS_CLK, + .ops = &vc5_clk_out_sel_ops, +}; diff --git a/drivers/crypto/fsl/jobdesc.c b/drivers/crypto/fsl/jobdesc.c index d2354155318..c350b328561 100644 --- a/drivers/crypto/fsl/jobdesc.c +++ b/drivers/crypto/fsl/jobdesc.c @@ -300,7 +300,7 @@ void inline_cnstr_jobdesc_rng_deinstantiation(u32 *desc, int handle) void inline_cnstr_jobdesc_rng(u32 *desc, void *data_out, u32 size) { - dma_addr_t dma_data_out = virt_to_phys(data_out); + caam_dma_addr_t dma_data_out = virt_to_phys(data_out); init_job_desc(desc, 0); append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG | diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index 2f3837e5591..cbb3f7b1dea 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -512,7 +512,7 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, u32 download_bytes, char *response) { struct blk_desc *dev_desc; - struct disk_partition info; + struct disk_partition info = {0}; #ifdef CONFIG_FASTBOOT_MMC_BOOT_SUPPORT if (strcmp(cmd, CONFIG_FASTBOOT_MMC_BOOT1_NAME) == 0) { @@ -525,19 +525,14 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, if (strcmp(cmd, CONFIG_FASTBOOT_MMC_BOOT2_NAME) == 0) { dev_desc = fastboot_mmc_get_dev(response); if (dev_desc) - fb_mmc_boot_ops(dev_desc, download_buffer, 1, + fb_mmc_boot_ops(dev_desc, download_buffer, 2, download_bytes, response); return; } #endif #if CONFIG_IS_ENABLED(EFI_PARTITION) -#ifndef CONFIG_FASTBOOT_MMC_USER_SUPPORT if (strcmp(cmd, CONFIG_FASTBOOT_GPT_NAME) == 0) { -#else - if (strcmp(cmd, CONFIG_FASTBOOT_GPT_NAME) == 0 || - strcmp(cmd, CONFIG_FASTBOOT_MMC_USER_NAME) == 0) { -#endif dev_desc = fastboot_mmc_get_dev(response); if (!dev_desc) return; @@ -599,7 +594,20 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, } #endif - if (fastboot_mmc_get_part_info(cmd, &dev_desc, &info, response) < 0) +#if CONFIG_IS_ENABLED(FASTBOOT_MMC_USER_SUPPORT) + if (strcmp(cmd, CONFIG_FASTBOOT_MMC_USER_NAME) == 0) { + dev_desc = fastboot_mmc_get_dev(response); + if (!dev_desc) + return; + + strlcpy((char *)&info.name, cmd, sizeof(info.name)); + info.size = dev_desc->lba; + info.blksz = dev_desc->blksz; + } +#endif + + if (!info.name[0] && + fastboot_mmc_get_part_info(cmd, &dev_desc, &info, response) < 0) return; if (is_sparse_image(download_buffer)) { @@ -655,7 +663,7 @@ void fastboot_mmc_erase(const char *cmd, char *response) /* erase EMMC boot2 */ dev_desc = fastboot_mmc_get_dev(response); if (dev_desc) - fb_mmc_boot_ops(dev_desc, NULL, 1, 0, response); + fb_mmc_boot_ops(dev_desc, NULL, 2, 0, response); return; } #endif diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 09695f6c2b0..4a89c1a62b7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -179,6 +179,16 @@ config LPC32XX_GPIO help Support for the LPC32XX GPIO driver. +config MCP230XX_GPIO + bool "MCP230XX GPIO driver" + depends on DM + help + Support for Microchip's MCP230XX I2C connected GPIO devices. + The following chips are supported: + - MCP23008 + - MCP23017 + - MCP23018 + config MSCC_SGPIO bool "Microsemi Serial GPIO driver" depends on DM_GPIO && SOC_VCOREIII diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 16b09fb1b5b..58f4704f6bc 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o obj-$(CONFIG_KONA_GPIO) += kona_gpio.o obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o obj-$(CONFIG_MARVELL_MFP) += mvmfp.o +obj-$(CONFIG_MCP230XX_GPIO) += mcp230xx_gpio.o obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o obj-$(CONFIG_PCA953X) += pca953x.o diff --git a/drivers/gpio/mcp230xx_gpio.c b/drivers/gpio/mcp230xx_gpio.c new file mode 100644 index 00000000000..9f02fd42b35 --- /dev/null +++ b/drivers/gpio/mcp230xx_gpio.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021, Collabora Ltd. + * Copyright (C) 2021, General Electric Company + * Author(s): Sebastian Reichel <sebastian.reichel@collabora.com> + */ + +#define LOG_CATEGORY UCLASS_GPIO + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/gpio.h> +#include <dm/device_compat.h> +#include <dt-bindings/gpio/gpio.h> + +enum mcp230xx_type { + UNKNOWN = 0, + MCP23008, + MCP23017, + MCP23018, +}; + +#define MCP230XX_IODIR 0x00 +#define MCP230XX_GPPU 0x06 +#define MCP230XX_GPIO 0x09 +#define MCP230XX_OLAT 0x0a + +#define BANKSIZE 8 + +static int mcp230xx_read(struct udevice *dev, uint reg, uint offset) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + int bank = offset / BANKSIZE; + int mask = 1 << (offset % BANKSIZE); + int shift = (uc_priv->gpio_count / BANKSIZE) - 1; + int ret; + + ret = dm_i2c_reg_read(dev, (reg << shift) | bank); + if (ret < 0) + return ret; + + return !!(ret & mask); +} + +static int mcp230xx_write(struct udevice *dev, uint reg, uint offset, bool val) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + int bank = offset / BANKSIZE; + int mask = 1 << (offset % BANKSIZE); + int shift = (uc_priv->gpio_count / BANKSIZE) - 1; + + return dm_i2c_reg_clrset(dev, (reg << shift) | bank, mask, val ? mask : 0); +} + +static int mcp230xx_get_value(struct udevice *dev, uint offset) +{ + int ret; + + ret = mcp230xx_read(dev, MCP230XX_GPIO, offset); + if (ret < 0) { + dev_err(dev, "%s error: %d\n", __func__, ret); + return ret; + } + + return ret; +} + +static int mcp230xx_set_value(struct udevice *dev, uint offset, int val) +{ + int ret; + + ret = mcp230xx_write(dev, MCP230XX_GPIO, offset, val); + if (ret < 0) { + dev_err(dev, "%s error: %d\n", __func__, ret); + return ret; + } + + return ret; +} + +static int mcp230xx_get_flags(struct udevice *dev, unsigned int offset, + ulong *flags) +{ + int direction, pullup; + + pullup = mcp230xx_read(dev, MCP230XX_GPPU, offset); + if (pullup < 0) { + dev_err(dev, "%s error: %d\n", __func__, pullup); + return pullup; + } + + direction = mcp230xx_read(dev, MCP230XX_IODIR, offset); + if (direction < 0) { + dev_err(dev, "%s error: %d\n", __func__, direction); + return direction; + } + + *flags = direction ? GPIOD_IS_IN : GPIOD_IS_OUT; + + if (pullup) + *flags |= GPIOD_PULL_UP; + + return 0; +} + +static int mcp230xx_set_flags(struct udevice *dev, uint offset, ulong flags) +{ + bool input = !(flags & (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE)); + bool pullup = flags & GPIOD_PULL_UP; + ulong supported_mask; + int ret; + + /* Note: active-low is ignored (handled by core) */ + supported_mask = GPIOD_ACTIVE_LOW | GPIOD_MASK_DIR | GPIOD_PULL_UP; + if (flags & ~supported_mask) { + dev_err(dev, "%s unsupported flag(s): %lx\n", __func__, flags); + return -EINVAL; + } + + ret = mcp230xx_write(dev, MCP230XX_OLAT, offset, !!(flags & GPIOD_IS_OUT_ACTIVE)); + if (ret) { + dev_err(dev, "%s failed to setup output latch: %d\n", __func__, ret); + return ret; + } + + ret = mcp230xx_write(dev, MCP230XX_GPPU, offset, pullup); + if (ret) { + dev_err(dev, "%s failed to setup pull-up: %d\n", __func__, ret); + return ret; + } + + ret = mcp230xx_write(dev, MCP230XX_IODIR, offset, input); + if (ret) { + dev_err(dev, "%s failed to setup direction: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int mcp230xx_direction_input(struct udevice *dev, uint offset) +{ + return mcp230xx_set_flags(dev, offset, GPIOD_IS_IN); +} + +static int mcp230xx_direction_output(struct udevice *dev, uint offset, int val) +{ + int ret = mcp230xx_set_value(dev, offset, val); + if (ret < 0) { + dev_err(dev, "%s error: %d\n", __func__, ret); + return ret; + } + return mcp230xx_set_flags(dev, offset, GPIOD_IS_OUT); +} + +static int mcp230xx_get_function(struct udevice *dev, uint offset) +{ + int ret; + + ret = mcp230xx_read(dev, MCP230XX_IODIR, offset); + if (ret < 0) { + dev_err(dev, "%s error: %d\n", __func__, ret); + return ret; + } + + return ret ? GPIOF_INPUT : GPIOF_OUTPUT; +} + +static const struct dm_gpio_ops mcp230xx_ops = { + .direction_input = mcp230xx_direction_input, + .direction_output = mcp230xx_direction_output, + .get_value = mcp230xx_get_value, + .set_value = mcp230xx_set_value, + .get_function = mcp230xx_get_function, + .set_flags = mcp230xx_set_flags, + .get_flags = mcp230xx_get_flags, +}; + +static int mcp230xx_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + char name[32], label[8], *str; + int addr, gpio_count, size; + const u8 *tmp; + + switch (dev_get_driver_data(dev)) { + case MCP23008: + gpio_count = 8; + break; + case MCP23017: + case MCP23018: + gpio_count = 16; + break; + default: + return -ENODEV; + } + + addr = dev_read_addr(dev); + tmp = dev_read_prop(dev, "label", &size); + if (tmp) { + memcpy(label, tmp, sizeof(label) - 1); + label[sizeof(label) - 1] = '\0'; + snprintf(name, sizeof(name), "%s@%x_", label, addr); + } else { + snprintf(name, sizeof(name), "gpio@%x_", addr); + } + + str = strdup(name); + if (!str) + return -ENOMEM; + + uc_priv->bank_name = str; + uc_priv->gpio_count = gpio_count; + + dev_dbg(dev, "%s is ready\n", str); + + return 0; +} + +static const struct udevice_id mcp230xx_ids[] = { + { .compatible = "microchip,mcp23008", .data = MCP23008, }, + { .compatible = "microchip,mcp23017", .data = MCP23017, }, + { .compatible = "microchip,mcp23018", .data = MCP23018, }, + { } +}; + +U_BOOT_DRIVER(mcp230xx) = { + .name = "mcp230xx", + .id = UCLASS_GPIO, + .ops = &mcp230xx_ops, + .probe = mcp230xx_probe, + .of_match = mcp230xx_ids, +}; diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index cf8f8f40359..1aedad5c8ed 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -336,8 +336,17 @@ static int i2c_gpio_of_to_plat(struct udevice *dev) struct i2c_gpio_bus *bus = dev_get_priv(dev); int ret; + /* "gpios" is deprecated and replaced by "sda-gpios" + "scl-gpios". */ ret = gpio_request_list_by_name(dev, "gpios", bus->gpios, ARRAY_SIZE(bus->gpios), 0); + if (ret == -ENOENT) { + ret = gpio_request_by_name(dev, "sda-gpios", 0, + &bus->gpios[PIN_SDA], 0); + if (ret < 0) + goto error; + ret = gpio_request_by_name(dev, "scl-gpios", 0, + &bus->gpios[PIN_SCL], 0); + } if (ret < 0) goto error; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 04c88503a2f..db1c9d94624 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -247,6 +247,21 @@ int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value) return dm_i2c_write(dev, offset, &val, 1); } +int dm_i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set) +{ + uint8_t val; + int ret; + + ret = dm_i2c_read(dev, offset, &val, 1); + if (ret < 0) + return ret; + + val &= ~clr; + val |= set; + + return dm_i2c_write(dev, offset, &val, 1); +} + /** * i2c_probe_chip() - probe for a chip on a bus * diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 405bf767530..64d5ddf2385 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -131,6 +131,16 @@ config SYS_CORTINA_FW_IN_SPIFLASH endchoice +config CORTINA_FW_ADDR + hex "Cortina Firmware Address" + depends on PHY_CORTINA && !SYS_CORTINA_NO_FW_UPLOAD + default 0x0 + +config CORTINA_FW_LENGTH + hex "Cortina Firmware Length" + depends on PHY_CORTINA && !SYS_CORTINA_NO_FW_UPLOAD + default 0x40000 + config PHY_CORTINA_ACCESS bool "Cortina Access Ethernet PHYs support" default y diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index b381a431fd9..2ac02952450 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -17,12 +17,11 @@ #include <linux/err.h> #include <phy.h> #include <cortina.h> -#ifdef CONFIG_SYS_CORTINA_FW_IN_NAND #include <nand.h> -#elif defined(CONFIG_SYS_CORTINA_FW_IN_SPIFLASH) #include <spi_flash.h> -#elif defined(CONFIG_SYS_CORTINA_FW_IN_MMC) #include <mmc.h> +#ifdef CONFIG_ARM64 +#include <asm/arch/cpu.h> #endif #ifndef CONFIG_PHYLIB_10G @@ -124,6 +123,11 @@ struct cortina_reg_config cortina_reg_cfg[] = { {VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB, 0xc01E}, }; +__weak ulong *cs4340_get_fw_addr(void) +{ + return (ulong *)CONFIG_CORTINA_FW_ADDR; +} + void cs4340_upload_firmware(struct phy_device *phydev) { char line_temp[0x50] = {0}; @@ -132,22 +136,76 @@ void cs4340_upload_firmware(struct phy_device *phydev) int i, line_cnt = 0, column_cnt = 0; struct cortina_reg_config fw_temp; char *addr = NULL; + ulong cortina_fw_addr = (ulong)cs4340_get_fw_addr(); + +#ifdef CONFIG_TFABOOT + enum boot_src src = get_boot_src(); + + if (src == BOOT_SOURCE_IFC_NOR) { + addr = (char *)cortina_fw_addr; + } else if (src == BOOT_SOURCE_IFC_NAND) { + int ret; + size_t fw_length = CONFIG_CORTINA_FW_LENGTH; + + addr = malloc(CONFIG_CORTINA_FW_LENGTH); + ret = nand_read(get_nand_dev_by_index(0), + (loff_t)cortina_fw_addr, &fw_length, (u_char *)addr); + if (ret == -EUCLEAN) { + printf("NAND read of Cortina firmware at 0x%lx failed %d\n", + cortina_fw_addr, ret); + } + } else if (src == BOOT_SOURCE_QSPI_NOR) { + int ret; + struct spi_flash *ucode_flash; + addr = malloc(CONFIG_CORTINA_FW_LENGTH); + ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!ucode_flash) { + puts("SF: probe for Cortina ucode failed\n"); + } else { + ret = spi_flash_read(ucode_flash, cortina_fw_addr, + CONFIG_CORTINA_FW_LENGTH, addr); + if (ret) + puts("SF: read for Cortina ucode failed\n"); + spi_flash_free(ucode_flash); + } + } else if (src == BOOT_SOURCE_SD_MMC) { + int dev = CONFIG_SYS_MMC_ENV_DEV; + u32 cnt = CONFIG_CORTINA_FW_LENGTH / 512; + u32 blk = cortina_fw_addr / 512; + struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); + + if (!mmc) { + puts("Failed to find MMC device for Cortina ucode\n"); + } else { + addr = malloc(CONFIG_CORTINA_FW_LENGTH); + printf("MMC read: dev # %u, block # %u, count %u ...\n", + dev, blk, cnt); + mmc_init(mmc); +#ifdef CONFIG_BLK + (void)blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr); +#else + (void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt, addr); +#endif + } + } +#else /* CONFIG_TFABOOT */ #if defined(CONFIG_SYS_CORTINA_FW_IN_NOR) || \ defined(CONFIG_SYS_CORTINA_FW_IN_REMOTE) - addr = (char *)CONFIG_CORTINA_FW_ADDR; + addr = (char *)cortina_fw_addr; #elif defined(CONFIG_SYS_CORTINA_FW_IN_NAND) int ret; size_t fw_length = CONFIG_CORTINA_FW_LENGTH; addr = malloc(CONFIG_CORTINA_FW_LENGTH); ret = nand_read(get_nand_dev_by_index(0), - (loff_t)CONFIG_CORTINA_FW_ADDR, + (loff_t)cortina_fw_addr, &fw_length, (u_char *)addr); if (ret == -EUCLEAN) { - printf("NAND read of Cortina firmware at 0x%x failed %d\n", - CONFIG_CORTINA_FW_ADDR, ret); + printf("NAND read of Cortina firmware at 0x%lx failed %d\n", + cortina_fw_addr, ret); } #elif defined(CONFIG_SYS_CORTINA_FW_IN_SPIFLASH) int ret; @@ -159,7 +217,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) if (!ucode_flash) { puts("SF: probe for Cortina ucode failed\n"); } else { - ret = spi_flash_read(ucode_flash, CONFIG_CORTINA_FW_ADDR, + ret = spi_flash_read(ucode_flash, cortina_fw_addr, CONFIG_CORTINA_FW_LENGTH, addr); if (ret) puts("SF: read for Cortina ucode failed\n"); @@ -168,7 +226,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) #elif defined(CONFIG_SYS_CORTINA_FW_IN_MMC) int dev = CONFIG_SYS_MMC_ENV_DEV; u32 cnt = CONFIG_CORTINA_FW_LENGTH / 512; - u32 blk = CONFIG_CORTINA_FW_ADDR / 512; + u32 blk = cortina_fw_addr / 512; struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); if (!mmc) { @@ -187,6 +245,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) #endif } #endif +#endif while (*addr != 'Q') { i = 0; @@ -195,7 +254,7 @@ void cs4340_upload_firmware(struct phy_device *phydev) line_temp[i++] = *addr++; if (0x50 < i) { printf("Not found Cortina PHY ucode at 0x%p\n", - (char *)CONFIG_CORTINA_FW_ADDR); + (char *)cortina_fw_addr); return; } } diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index 6c5bad4c2ca..bba7a330e0c 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -428,7 +428,7 @@ static bool nxp_fspi_supports_op(struct spi_slave *slave, op->data.nbytes > f->devtype_data->txfifo) return false; - return true; + return spi_mem_default_supports_op(slave, op); } /* Instead of busy looping invoke readl_poll_sleep_timeout functionality. */ diff --git a/env/Kconfig b/env/Kconfig index 67ff172e3a9..c0dff1fd819 100644 --- a/env/Kconfig +++ b/env/Kconfig @@ -616,7 +616,7 @@ config SYS_RELOC_GD_ENV_ADDR config SYS_MMC_ENV_DEV int "mmc device number" depends on ENV_IS_IN_MMC || ENV_IS_IN_FAT || SYS_LS_PPA_FW_IN_MMC || \ - CMD_MVEBU_BUBT || FMAN_ENET || QE + CMD_MVEBU_BUBT || FMAN_ENET || QE || PHY_CORTINA default 0 help MMC device number on the platform where the environment is stored. diff --git a/include/configs/T208xRDB.h b/include/configs/T208xRDB.h index 63cc5af2c6d..601e67c80c3 100644 --- a/include/configs/T208xRDB.h +++ b/include/configs/T208xRDB.h @@ -479,7 +479,6 @@ unsigned long get_board_ddr_clk(void); * env, so we got 0x110000. */ #define CONFIG_SYS_FMAN_FW_ADDR 0x110000 -#define CONFIG_CORTINA_FW_ADDR 0x120000 #elif defined(CONFIG_SDCARD) /* @@ -488,11 +487,9 @@ unsigned long get_board_ddr_clk(void); * 0x2000 (16 blocks), 8 + 2048 + 16 = 2072, enlarge it to 2080. */ #define CONFIG_SYS_FMAN_FW_ADDR (512 * 0x820) -#define CONFIG_CORTINA_FW_ADDR (512 * 0x8a0) #elif defined(CONFIG_MTD_RAW_NAND) #define CONFIG_SYS_FMAN_FW_ADDR (3 * CONFIG_SYS_NAND_BLOCK_SIZE) -#define CONFIG_CORTINA_FW_ADDR (4 * CONFIG_SYS_NAND_BLOCK_SIZE) #elif defined(CONFIG_SRIO_PCIE_BOOT_SLAVE) /* * Slave has no ucode locally, it can fetch this from remote. When implementing @@ -502,17 +499,14 @@ unsigned long get_board_ddr_clk(void); * master LAW->the ucode address in master's memory space. */ #define CONFIG_SYS_FMAN_FW_ADDR 0xFFE00000 -#define CONFIG_CORTINA_FW_ADDR 0xFFE10000 #else #define CONFIG_SYS_FMAN_FW_ADDR 0xEFF00000 -#define CONFIG_CORTINA_FW_ADDR 0xEFE00000 #endif #define CONFIG_SYS_QE_FMAN_FW_LENGTH 0x10000 #define CONFIG_SYS_FDT_PAD (0x3000 + CONFIG_SYS_QE_FMAN_FW_LENGTH) #endif /* CONFIG_NOBQFMAN */ #ifdef CONFIG_SYS_DPAA_FMAN -#define CONFIG_CORTINA_FW_LENGTH 0x40000 #define RGMII_PHY1_ADDR 0x01 /* RealTek RTL8211E */ #define RGMII_PHY2_ADDR 0x02 #define CORTINA_PHY_ADDR1 0x0c /* Cortina CS4315 */ diff --git a/include/configs/T4240RDB.h b/include/configs/T4240RDB.h index 57a39fa970f..c796b1d7ed0 100644 --- a/include/configs/T4240RDB.h +++ b/include/configs/T4240RDB.h @@ -517,8 +517,6 @@ unsigned long get_board_ddr_clk(void); #endif /* CONFIG_NOBQFMAN */ #ifdef CONFIG_SYS_DPAA_FMAN -#define CONFIG_CORTINA_FW_ADDR 0xefe00000 -#define CONFIG_CORTINA_FW_LENGTH 0x40000 #define SGMII_PHY_ADDR1 0x0 #define SGMII_PHY_ADDR2 0x1 #define SGMII_PHY_ADDR3 0x2 diff --git a/include/configs/ls2080ardb.h b/include/configs/ls2080ardb.h index 49c2cc573bc..bfbde1da978 100644 --- a/include/configs/ls2080ardb.h +++ b/include/configs/ls2080ardb.h @@ -560,14 +560,6 @@ unsigned long get_board_sys_clk(void); #endif /* MAC/PHY configuration */ -#ifdef CONFIG_FSL_MC_ENET -#ifdef CONFIG_QSPI_BOOT -#define CONFIG_CORTINA_FW_ADDR 0x20980000 -#else -#define CONFIG_CORTINA_FW_ADDR 0x580980000 -#endif -#define CONFIG_CORTINA_FW_LENGTH 0x40000 - #define CORTINA_PHY_ADDR1 0x10 #define CORTINA_PHY_ADDR2 0x11 #define CORTINA_PHY_ADDR3 0x12 @@ -577,9 +569,7 @@ unsigned long get_board_sys_clk(void); #define AQ_PHY_ADDR3 0x02 #define AQ_PHY_ADDR4 0x03 #define AQR405_IRQ_MASK 0x36 - #define CONFIG_ETHPRIME "DPMAC1@xgmii" -#endif #include <asm/fsl_secure_boot.h> diff --git a/include/configs/lx2160a_common.h b/include/configs/lx2160a_common.h index 1338ee3cda3..1ae7d37dd9f 100644 --- a/include/configs/lx2160a_common.h +++ b/include/configs/lx2160a_common.h @@ -143,7 +143,6 @@ /* USB */ #ifdef CONFIG_USB_HOST -#define CONFIG_HAS_FSL_XHCI_USB #ifndef CONFIG_TARGET_LX2162AQDS #define CONFIG_USB_MAX_CONTROLLER_COUNT 2 #endif @@ -181,6 +180,7 @@ unsigned long get_board_ddr_clk(void); #define XSPI_MC_INIT_CMD \ "sf probe 0:0 && " \ "sf read 0x80640000 0x640000 0x80000 && " \ + "sf read $fdt_addr_r 0xf00000 0x100000 && " \ "env exists secureboot && " \ "esbc_validate 0x80640000 && " \ "esbc_validate 0x80680000; " \ @@ -191,6 +191,7 @@ unsigned long get_board_ddr_clk(void); #define SD_MC_INIT_CMD \ "mmc read 0x80a00000 0x5000 0x1200;" \ "mmc read 0x80e00000 0x7000 0x800;" \ + "mmc read $fdt_addr_r 0x7800 0x800;" \ "env exists secureboot && " \ "mmc read 0x80640000 0x3200 0x20 && " \ "mmc read 0x80680000 0x3400 0x20 && " \ @@ -201,6 +202,7 @@ unsigned long get_board_ddr_clk(void); #define SD2_MC_INIT_CMD \ "mmc dev 1; mmc read 0x80a00000 0x5000 0x1200;" \ "mmc read 0x80e00000 0x7000 0x800;" \ + "mmc read $fdt_addr_r 0x7800 0x800;" \ "env exists secureboot && " \ "mmc read 0x80640000 0x3200 0x20 && " \ "mmc read 0x80680000 0x3400 0x20 && " \ diff --git a/include/i2c.h b/include/i2c.h index 8db34a67fee..3d9ecaba0b6 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -243,6 +243,20 @@ int dm_i2c_reg_read(struct udevice *dev, uint offset); int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val); /** + * dm_i2c_reg_clrset() - Apply bitmask to an I2C register + * + * Read value, apply bitmask and write modified value back to the + * given address in an I2C chip + * + * @dev: Device to use for transfer + * @offset: Address for the R/W operation + * @clr: Bitmask of bits that should be cleared + * @set: Bitmask of bits that should be set + * @return 0 on success, -ve on error + */ +int dm_i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set); + +/** * dm_i2c_xfer() - Transfer messages over I2C * * This transfers a raw message. It is best to use dm_i2c_reg_read/write() diff --git a/lib/display_options.c b/lib/display_options.c index c08a87e3162..4da1f5244f3 100644 --- a/lib/display_options.c +++ b/lib/display_options.c @@ -107,7 +107,12 @@ void print_size(uint64_t size, const char *s) } if (!c) { - printf("%llu Bytes%s", size, s); + /* + * SPL tiny-printf is not capable for printing uint64_t. + * We have just checked that the size is small enought to fit + * unsigned int safely. + */ + printf("%u Bytes%s", (unsigned int)size, s); return; } diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c index 8fc7e48d994..89aaa854771 100644 --- a/lib/tiny-printf.c +++ b/lib/tiny-printf.c @@ -9,8 +9,9 @@ */ #include <common.h> -#include <stdarg.h> +#include <log.h> #include <serial.h> +#include <stdarg.h> #include <linux/ctype.h> struct printf_info { @@ -269,20 +270,19 @@ static int _vprintf(struct printf_info *info, const char *fmt, va_list va) } break; case 'p': -#ifdef DEBUG - pointer(info, fmt, va_arg(va, void *)); - /* - * Skip this because it pulls in _ctype which is - * 256 bytes, and we don't generally implement - * pointer anyway - */ - while (isalnum(fmt[0])) - fmt++; - break; -#else + if (CONFIG_IS_ENABLED(NET_SUPPORT) || _DEBUG) { + pointer(info, fmt, va_arg(va, void *)); + /* + * Skip this because it pulls in _ctype which is + * 256 bytes, and we don't generally implement + * pointer anyway + */ + while (isalnum(fmt[0])) + fmt++; + break; + } islong = true; /* no break */ -#endif case 'x': if (islong) { num = va_arg(va, unsigned long); diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt index 2d70bf5da7c..d86f35856f7 100644 --- a/scripts/config_whitelist.txt +++ b/scripts/config_whitelist.txt @@ -190,8 +190,6 @@ CONFIG_CONS_SCIF1 CONFIG_CONS_SCIF2 CONFIG_CONS_SCIF4 CONFIG_CON_ROT -CONFIG_CORTINA_FW_ADDR -CONFIG_CORTINA_FW_LENGTH CONFIG_CPLD_BR_PRELIM CONFIG_CPLD_OR_PRELIM CONFIG_CPM2 diff --git a/test/dm/bootcount.c b/test/dm/bootcount.c index e0c47b5d7a6..b77b472d1f2 100644 --- a/test/dm/bootcount.c +++ b/test/dm/bootcount.c @@ -12,12 +12,13 @@ #include <test/test.h> #include <test/ut.h> -static int dm_test_bootcount(struct unit_test_state *uts) +static int dm_test_bootcount_rtc(struct unit_test_state *uts) { struct udevice *dev; u32 val; - ut_assertok(uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount@0", + &dev)); ut_assertok(dm_bootcount_set(dev, 0)); ut_assertok(dm_bootcount_get(dev, &val)); ut_assert(val == 0); @@ -36,5 +37,46 @@ static int dm_test_bootcount(struct unit_test_state *uts) return 0; } -DM_TEST(dm_test_bootcount, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +DM_TEST(dm_test_bootcount_rtc, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_bootcount_syscon_four_bytes(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val; + + sandbox_set_enable_memio(true); + ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount_4@0", + &dev)); + ut_assertok(dm_bootcount_set(dev, 0xab)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0xab); + ut_assertok(dm_bootcount_set(dev, 0)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0); + + return 0; +} + +DM_TEST(dm_test_bootcount_syscon_four_bytes, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_bootcount_syscon_two_bytes(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val; + + sandbox_set_enable_memio(true); + ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount_2@0", + &dev)); + ut_assertok(dm_bootcount_set(dev, 0xab)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0xab); + ut_assertok(dm_bootcount_set(dev, 0)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0); + + return 0; +} + +DM_TEST(dm_test_bootcount_syscon_two_bytes, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/test/dm/i2c.c b/test/dm/i2c.c index d74f5f9fbc7..74b20971956 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -304,3 +304,32 @@ static int dm_test_i2c_addr_offset(struct unit_test_state *uts) } DM_TEST(dm_test_i2c_addr_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_reg_clrset(struct unit_test_state *uts) +{ + struct udevice *eeprom; + struct udevice *dev; + u8 buf[5]; + + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); + + /* Do a transfer so we can find the emulator */ + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); + + /* Dummy data for the test */ + ut_assertok(dm_i2c_write(dev, 0, "\xff\x00\xff\x00\x10", 5)); + + /* Do some clrset tests */ + ut_assertok(dm_i2c_reg_clrset(dev, 0, 0xff, 0x10)); + ut_assertok(dm_i2c_reg_clrset(dev, 1, 0x00, 0x11)); + ut_assertok(dm_i2c_reg_clrset(dev, 2, 0xed, 0x00)); + ut_assertok(dm_i2c_reg_clrset(dev, 3, 0xff, 0x13)); + ut_assertok(dm_i2c_reg_clrset(dev, 4, 0x00, 0x14)); + + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem("\x10\x11\x12\x13\x14", buf, sizeof(buf)); + + return 0; +} +DM_TEST(dm_test_i2c_reg_clrset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); |