diff options
author | Tom Rini <trini@konsulko.com> | 2023-11-02 10:12:33 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-11-02 10:12:33 -0400 |
commit | 07fe79c93c5caba181f37844ca95fbda4db3f613 (patch) | |
tree | 9766b3de49837bae1d4ea6c6b69fc33710333fd2 | |
parent | b0c391ce0c01064a96711965e22f5d745e73edc3 (diff) | |
parent | 5b6ee512ceb8d990e010646c4fe7b8a3633fad68 (diff) | |
download | u-boot-07fe79c93c5caba181f37844ca95fbda4db3f613.tar.gz |
Merge tag 'i2cfixes-for-v2024-01-rc2' of https://source.denx.de/u-boot/custodians/u-boot-i2c
i2c updates for v2024.01-rc2
- nuvoton: support standard/fast/fast plus mode
- bootcount: remove legacy i2c driver and implement
DM based version
Bugfixes:
- designware_i2c: adjust timing calculation
SPL probing failed on the StarFive VisionFive 2 board
Heinrich fixed this, by syncing timing calculation with
linux implementation.
-rw-r--r-- | drivers/bootcount/Kconfig | 34 | ||||
-rw-r--r-- | drivers/bootcount/Makefile | 2 | ||||
-rw-r--r-- | drivers/bootcount/bootcount_dm_i2c.c | 102 | ||||
-rw-r--r-- | drivers/bootcount/bootcount_i2c.c | 43 | ||||
-rw-r--r-- | drivers/i2c/designware_i2c.c | 19 | ||||
-rw-r--r-- | drivers/i2c/i2c-uclass.c | 75 | ||||
-rw-r--r-- | drivers/i2c/npcm_i2c.c | 5 | ||||
-rw-r--r-- | include/i2c.h | 12 |
8 files changed, 218 insertions, 74 deletions
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 570252d186a..3c56253b1ea 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -79,14 +79,6 @@ config BOOTCOUNT_RAM Store the bootcount in DRAM protected against bit errors due to short power loss or holding a system in RESET. -config BOOTCOUNT_I2C - bool "Boot counter on I2C device" - help - Enable support for the bootcounter on an i2c (like RTC) device. - CFG_SYS_I2C_RTC_ADDR = i2c chip address - CONFIG_SYS_BOOTCOUNT_ADDR = i2c addr which is used for - the bootcounter. - config BOOTCOUNT_AT91 bool "Boot counter for Atmel AT91SAM9XE" depends on AT91SAM9XE @@ -117,6 +109,16 @@ config DM_BOOTCOUNT_RTC Accesses to the backing store are performed using the write16 and read16 ops of DM RTC devices. +config DM_BOOTCOUNT_I2C + bool "Driver Model boot counter on I2C device" + depends on DM_I2C + help + Enable support for the bootcounter on a generic i2c device, like a RTC + or PMIC. The bootcounter is configured in the device tree using the + "u-boot,bootcount-i2c" compatible string. It requires a phandle + 'i2cbcdev' for the i2c device and an 'offset' property used within the + device. + config DM_BOOTCOUNT_I2C_EEPROM bool "Support i2c eeprom devices as a backing store for bootcount" depends on I2C_EEPROM @@ -175,14 +177,6 @@ config BOOTCOUNT_BOOTLIMIT counter being cleared. If set to 0, do not set a boot limit in the environment. -config BOOTCOUNT_ALEN - int "I2C address length" - default 1 - depends on BOOTCOUNT_I2C - help - Length of the the I2C address at SYS_BOOTCOUNT_ADDR for storing - the boot counter. - config SYS_BOOTCOUNT_SINGLEWORD bool "Use single word to pack boot count and magic value" depends on BOOTCOUNT_GENERIC @@ -218,7 +212,7 @@ config SYS_BOOTCOUNT_ADDR default 0x44E3E000 if BOOTCOUNT_AM33XX || BOOTCOUNT_AM33XX_NVMEM default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ - BOOTCOUNT_I2C || BOOTCOUNT_AM33XX_NVMEM + BOOTCOUNT_AM33XX_NVMEM help Set the address used for reading and writing the boot counter. @@ -226,13 +220,11 @@ config SYS_BOOTCOUNT_MAGIC hex "Magic value for the boot counter" default 0xB001C041 if BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \ - BOOTCOUNT_RAM || BOOTCOUNT_I2C || \ - BOOTCOUNT_AT91 || DM_BOOTCOUNT + BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT default 0xB0 if BOOTCOUNT_AM33XX_NVMEM depends on BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \ - BOOTCOUNT_RAM || BOOTCOUNT_I2C || \ - BOOTCOUNT_AT91 || DM_BOOTCOUNT || \ + BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT || \ BOOTCOUNT_AM33XX_NVMEM help Set the magic value used for the boot counter. diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile index b65959a384b..e7771f5b36d 100644 --- a/drivers/bootcount/Makefile +++ b/drivers/bootcount/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_BOOTCOUNT_AT91) += bootcount_at91.o obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o -obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o obj-$(CONFIG_BOOTCOUNT_AM33XX_NVMEM) += bootcount_nvmem.o @@ -14,5 +13,6 @@ obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o obj-$(CONFIG_DM_BOOTCOUNT_PMIC_PFUZE100) += pmic_pfuze100.o obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o +obj-$(CONFIG_DM_BOOTCOUNT_I2C) += bootcount_dm_i2c.o obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o diff --git a/drivers/bootcount/bootcount_dm_i2c.c b/drivers/bootcount/bootcount_dm_i2c.c new file mode 100644 index 00000000000..e27034cbeb0 --- /dev/null +++ b/drivers/bootcount/bootcount_dm_i2c.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 + * Philip Richard Oberfichtner <pro@denx.de> + * + * Based on previous work from Heiko Schocher (legacy bootcount_i2c.c driver) + */ + +#include <bootcount.h> +#include <dm.h> +#include <i2c.h> + +#define BC_MAGIC 0x55 + +struct bootcount_i2c_priv { + struct udevice *bcdev; + unsigned int offset; +}; + +static int bootcount_i2c_set(struct udevice *dev, const u32 val) +{ + int ret; + struct bootcount_i2c_priv *priv = dev_get_priv(dev); + + ret = dm_i2c_reg_write(priv->bcdev, priv->offset, BC_MAGIC); + if (ret < 0) + goto err_exit; + + ret = dm_i2c_reg_write(priv->bcdev, priv->offset + 1, val & 0xff); + if (ret < 0) + goto err_exit; + + return 0; + +err_exit: + log_debug("%s: Error writing to I2C device (%d)\n", __func__, ret); + return ret; +} + +static int bootcount_i2c_get(struct udevice *dev, u32 *val) +{ + int ret; + struct bootcount_i2c_priv *priv = dev_get_priv(dev); + + ret = dm_i2c_reg_read(priv->bcdev, priv->offset); + if (ret < 0) + goto err_exit; + + if ((ret & 0xff) != BC_MAGIC) { + log_debug("%s: Invalid Magic, reset bootcounter.\n", __func__); + *val = 0; + return bootcount_i2c_set(dev, 0); + } + + ret = dm_i2c_reg_read(priv->bcdev, priv->offset + 1); + if (ret < 0) + goto err_exit; + + *val = ret; + return 0; + +err_exit: + log_debug("%s: Error reading from I2C device (%d)\n", __func__, ret); + return ret; +} + +static int bootcount_i2c_probe(struct udevice *dev) +{ + struct bootcount_i2c_priv *priv = dev_get_priv(dev); + int ret; + + ret = dev_read_u32(dev, "offset", &priv->offset); + if (ret) + goto exit; + + ret = i2c_get_chip_by_phandle(dev, "i2cbcdev", &priv->bcdev); + +exit: + if (ret) + log_debug("%s failed, ret = %d\n", __func__, ret); + + return ret; +} + +static const struct bootcount_ops bootcount_i2c_ops = { + .get = bootcount_i2c_get, + .set = bootcount_i2c_set, +}; + +static const struct udevice_id bootcount_i2c_ids[] = { + { .compatible = "u-boot,bootcount-i2c" }, + { } +}; + +U_BOOT_DRIVER(bootcount_i2c) = { + .name = "bootcount-i2c", + .id = UCLASS_BOOTCOUNT, + .priv_auto = sizeof(struct bootcount_i2c_priv), + .probe = bootcount_i2c_probe, + .of_match = bootcount_i2c_ids, + .ops = &bootcount_i2c_ops, +}; diff --git a/drivers/bootcount/bootcount_i2c.c b/drivers/bootcount/bootcount_i2c.c deleted file mode 100644 index b3ac67ea35d..00000000000 --- a/drivers/bootcount/bootcount_i2c.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2013 - * Heiko Schocher, DENX Software Engineering, hs@denx.de. - */ - -#include <bootcount.h> -#include <linux/compiler.h> -#include <i2c.h> - -#define BC_MAGIC 0xbc - -void bootcount_store(ulong a) -{ - unsigned char buf[3]; - int ret; - - buf[0] = BC_MAGIC; - buf[1] = (a & 0xff); - ret = i2c_write(CFG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR, - CONFIG_BOOTCOUNT_ALEN, buf, 2); - if (ret != 0) - puts("Error writing bootcount\n"); -} - -ulong bootcount_load(void) -{ - unsigned char buf[3]; - int ret; - - ret = i2c_read(CFG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR, - CONFIG_BOOTCOUNT_ALEN, buf, 2); - if (ret != 0) { - puts("Error loading bootcount\n"); - return 0; - } - if (buf[0] == BC_MAGIC) - return buf[1]; - - bootcount_store(0); - - return 0; -} diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index e54de42abc3..215ce010cb7 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -24,6 +24,17 @@ */ #define DW_I2C_COMP_TYPE 0x44570140 +/* + * This constant is used to calculate when during the clock high phase the data + * bit shall be read. The value was copied from the Linux v6.5 function + * i2c_dw_scl_hcnt() which provides the following explanation: + * + * "This is just an experimental rule: the tHD;STA period turned out to be + * proportinal to (_HCNT + 3). With this setting, we could meet both tHIGH and + * tHD;STA timing specs." + */ +#define T_HD_STA_OFFSET 3 + static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) { u32 ena = enable ? IC_ENABLE_0B : 0; @@ -155,10 +166,10 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, /* * Back-solve for hcnt and lcnt according to the following equations: - * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time + * SCL_High_time = [(HCNT + IC_*_SPKLEN + T_HD_STA_OFFSET) * ic_clk] + SCL_Fall_time * SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time */ - hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt; + hcnt = min_thigh_cnt - fall_cnt - T_HD_STA_OFFSET - spk_cnt; lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1; if (hcnt < 0 || lcnt < 0) { @@ -170,13 +181,13 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode, * Now add things back up to ensure the period is hit. If it is off, * split the difference and bias to lcnt for remainder */ - tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1; if (tot < period_cnt) { diff = (period_cnt - tot) / 2; hcnt += diff; lcnt += diff; - tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1; lcnt += period_cnt - tot; } diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 8867a560bd8..5405067861e 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -388,6 +388,81 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, return 0; } +/* Find and probe I2C bus based on a chip attached to it */ +static int i2c_get_parent_bus(ofnode chip, struct udevice **devp) +{ + ofnode node; + struct udevice *dev; + int ret; + + node = ofnode_get_parent(chip); + if (!ofnode_valid(node)) + return -ENODEV; + + ret = uclass_get_device_by_ofnode(UCLASS_I2C, node, &dev); + if (ret) { + *devp = NULL; + return ret; + } + + *devp = dev; + return 0; +} + +int i2c_get_chip_by_phandle(const struct udevice *parent, const char *prop_name, + struct udevice **devp) +{ + ofnode node; + uint phandle; + struct udevice *bus, *chip; + char *dev_name; + int ret; + + debug("%s: Searching I2C chip for phandle \"%s\"\n", + __func__, prop_name); + + dev_name = strdup(prop_name); + if (!dev_name) { + ret = -ENOMEM; + goto err_exit; + } + + ret = dev_read_u32(parent, "i2cbcdev", &phandle); + if (ret) + goto err_exit; + + node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(node)) { + ret = -ENODEV; + goto err_exit; + } + + ret = i2c_get_parent_bus(node, &bus); + if (ret) + goto err_exit; + + ret = device_bind_driver_to_node(bus, "i2c_generic_chip_drv", + dev_name, node, &chip); + if (ret) + goto err_exit; + + ret = device_probe(chip); + if (ret) { + device_unbind(chip); + goto err_exit; + } + + debug("%s succeeded\n", __func__); + *devp = chip; + return 0; + +err_exit: + free(dev_name); + debug("%s failed, ret = %d\n", __func__, ret); + *devp = NULL; + return ret; +} + int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, struct udevice **devp) { diff --git a/drivers/i2c/npcm_i2c.c b/drivers/i2c/npcm_i2c.c index ea4ef532565..b867b6c8e91 100644 --- a/drivers/i2c/npcm_i2c.c +++ b/drivers/i2c/npcm_i2c.c @@ -517,11 +517,6 @@ static int npcm_i2c_init_clk(struct npcm_i2c_bus *bus, u32 bus_freq) u32 sclfrq; u8 hldt, val; - if (bus_freq > I2C_FREQ_100K) { - printf("Support standard mode only\n"); - return -EINVAL; - } - /* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) */ sclfrq = freq / (bus_freq * 4); if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX) diff --git a/include/i2c.h b/include/i2c.h index ef3820eaba7..4e59009cd93 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -538,6 +538,18 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, struct udevice **devp); /** + * i2c_get_chip_by_phandle() - get a device to use to access a chip + * based on a phandle property pointing to it + * + * @parent: Parent device containing the phandle pointer + * @name: Name of phandle property in the parent device node + * @devp: Returns pointer to new device or NULL if not found + * Return: 0 on success, -ve on failure + */ +int i2c_get_chip_by_phandle(const struct udevice *parent, const char *prop_name, + struct udevice **devp); + +/** * i2c_chip_of_to_plat() - Decode standard I2C platform data * * This decodes the chip address from a device tree node and puts it into |