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.
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig
index 570252d..3c56253 100644
--- a/drivers/bootcount/Kconfig
+++ b/drivers/bootcount/Kconfig
@@ -79,14 +79,6 @@
 	  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 @@
 	  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 @@
 	  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 @@
 	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 @@
 	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 b65959a..e7771f5 100644
--- a/drivers/bootcount/Makefile
+++ b/drivers/bootcount/Makefile
@@ -6,7 +6,6 @@
 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_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 0000000..e27034c
--- /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 b3ac67e..0000000
--- 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 e54de42..215ce01 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 @@
 
 	/*
 	 * 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 @@
 	 * 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 8867a56..5405067 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -388,6 +388,81 @@
 	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 ea4ef53..b867b6c 100644
--- a/drivers/i2c/npcm_i2c.c
+++ b/drivers/i2c/npcm_i2c.c
@@ -517,11 +517,6 @@
 	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 ef3820e..4e59009 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -538,6 +538,18 @@
 			    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