Merge tag 'for-v2021.10-rc3' of https://source.denx.de/u-boot/custodians/u-boot-i2c

i2c changes for for-v2021.10-rc3

new driver:
- Introduce mcp230xx support
  from Sebastian Reichel

new feature:
- i2c-gpio: add support for "sda-gpios" + "scl-gpios" i2c-gpio bindings.
  from Samuel Holland

- bootcount: add a new driver with syscon as backend
  from Nandor Han
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index d597631..962bdbe 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/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 952d430..4658f18 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -131,6 +131,7 @@
 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 0000000..e124f7b
--- /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 0de2b7b..607027c 100644
--- a/drivers/bootcount/Kconfig
+++ b/drivers/bootcount/Kconfig
@@ -144,6 +144,18 @@
 	  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 12658ff..3a784bb 100644
--- a/drivers/bootcount/Makefile
+++ b/drivers/bootcount/Makefile
@@ -14,3 +14,4 @@
 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 0000000..413fd5b
--- /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, &regval);
+	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", &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/gpio/Kconfig b/drivers/gpio/Kconfig
index 09695f6..4a89c1a 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -179,6 +179,16 @@
 	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 16b09fb..58f4704 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -24,6 +24,7 @@
 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 0000000..9f02fd4
--- /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 cf8f8f4..1aedad5 100644
--- a/drivers/i2c/i2c-gpio.c
+++ b/drivers/i2c/i2c-gpio.c
@@ -336,8 +336,17 @@
 	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 04c8850..db1c9d9 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -247,6 +247,21 @@
 	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/include/i2c.h b/include/i2c.h
index 8db34a6..3d9ecab 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -243,6 +243,20 @@
 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/test/dm/bootcount.c b/test/dm/bootcount.c
index e0c47b5..b77b472 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 @@
 	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 d74f5f9..74b2097 100644
--- a/test/dm/i2c.c
+++ b/test/dm/i2c.c
@@ -304,3 +304,32 @@
 }
 
 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);