rtc: rv3029: update to support DM and sync with Linux 4.17

The "Flamingo" carrier-board for the RK3399-Q7 has a RV3029 populated
and the application will use the off-module RV3029 RTC including the
battery backed SRAM.

To support this use case, this commit includes the following changes:
 * updates the rv3029 driver to use DM
 * implements the read8/write8 operations

This syncs the implementation with the Linux code (based on 4.17),
porting the trickle-charger support from there (with improvements to
avoid unnecessary EEPROM updates) and adheres to the Linux DTS
binding.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Tested-by: Klaus Goger <klaus.goger@theobroma-systems.com>
diff --git a/drivers/rtc/rv3029.c b/drivers/rtc/rv3029.c
index dd4d060..38acb9c 100644
--- a/drivers/rtc/rv3029.c
+++ b/drivers/rtc/rv3029.c
@@ -1,189 +1,495 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * (C) Copyright 2010
- * Heiko Schocher, DENX Software Engineering, hs@denx.de
+ * (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH
+ *
+ * Based on a the Linux rtc-rv3029c2.c driver written by:
+ *   Gregory Hermant <gregory.hermant@calao-systems.com>
+ *   Michael Buesch <m@bues.ch>
  */
+
 #include <common.h>
 #include <command.h>
+#include <dm.h>
 #include <i2c.h>
 #include <rtc.h>
 
-#define RTC_RV3029_CTRL1	0x00
-#define RTC_RV3029_CTRL1_EERE	(1 << 3)
+#define RTC_RV3029_PAGE_LEN             7
 
-#define RTC_RV3029_CTRL_STATUS	0x03
-#define RTC_RV3029_CTRLS_EEBUSY	(1 << 7)
+/* control section */
+#define RV3029_ONOFF_CTRL		0x00
+#define RV3029_ONOFF_CTRL_WE		BIT(0)
+#define RV3029_ONOFF_CTRL_TE		BIT(1)
+#define RV3029_ONOFF_CTRL_TAR		BIT(2)
+#define RV3029_ONOFF_CTRL_EERE		BIT(3)
+#define RV3029_ONOFF_CTRL_SRON		BIT(4)
+#define RV3029_ONOFF_CTRL_TD0		BIT(5)
+#define RV3029_ONOFF_CTRL_TD1		BIT(6)
+#define RV3029_ONOFF_CTRL_CLKINT	BIT(7)
+#define RV3029_IRQ_CTRL			0x01
+#define RV3029_IRQ_CTRL_AIE		BIT(0)
+#define RV3029_IRQ_CTRL_TIE		BIT(1)
+#define RV3029_IRQ_CTRL_V1IE		BIT(2)
+#define RV3029_IRQ_CTRL_V2IE		BIT(3)
+#define RV3029_IRQ_CTRL_SRIE		BIT(4)
+#define RV3029_IRQ_FLAGS		0x02
+#define RV3029_IRQ_FLAGS_AF		BIT(0)
+#define RV3029_IRQ_FLAGS_TF		BIT(1)
+#define RV3029_IRQ_FLAGS_V1IF		BIT(2)
+#define RV3029_IRQ_FLAGS_V2IF		BIT(3)
+#define RV3029_IRQ_FLAGS_SRF		BIT(4)
+#define RV3029_STATUS			0x03
+#define RV3029_STATUS_VLOW1		BIT(2)
+#define RV3029_STATUS_VLOW2		BIT(3)
+#define RV3029_STATUS_SR		BIT(4)
+#define RV3029_STATUS_PON		BIT(5)
+#define RV3029_STATUS_EEBUSY		BIT(7)
+#define RV3029_RST_CTRL			0x04
+#define RV3029_RST_CTRL_SYSR		BIT(4)
+#define RV3029_CONTROL_SECTION_LEN	0x05
 
-#define RTC_RV3029_CTRL_RESET	0x04
-#define RTC_RV3029_CTRL_SYS_R	(1 << 4)
+/* watch section */
+#define RV3029_W_SEC			0x08
+#define RV3029_W_MINUTES		0x09
+#define RV3029_W_HOURS			0x0A
+#define RV3029_REG_HR_12_24		BIT(6) /* 24h/12h mode */
+#define RV3029_REG_HR_PM		BIT(5) /* PM/AM bit in 12h mode */
+#define RV3029_W_DATE			0x0B
+#define RV3029_W_DAYS			0x0C
+#define RV3029_W_MONTHS			0x0D
+#define RV3029_W_YEARS			0x0E
 
-#define RTC_RV3029_CLOCK_PAGE	0x08
-#define RTC_RV3029_PAGE_LEN	7
+/* eeprom control section */
+#define RV3029_CONTROL_E2P_EECTRL	0x30
+#define RV3029_TRICKLE_1K		BIT(4) /* 1.5K resistance */
+#define RV3029_TRICKLE_5K		BIT(5) /* 5K   resistance */
+#define RV3029_TRICKLE_20K		BIT(6) /* 20K  resistance */
+#define RV3029_TRICKLE_80K		BIT(7) /* 80K  resistance */
+#define RV3029_TRICKLE_MASK		(RV3029_TRICKLE_1K |\
+					 RV3029_TRICKLE_5K |\
+					 RV3029_TRICKLE_20K |\
+					 RV3029_TRICKLE_80K)
+#define RV3029_TRICKLE_SHIFT		4
 
-#define RV3029C2_W_SECONDS	0x00
-#define RV3029C2_W_MINUTES	0x01
-#define RV3029C2_W_HOURS	0x02
-#define RV3029C2_W_DATE		0x03
-#define RV3029C2_W_DAYS		0x04
-#define RV3029C2_W_MONTHS	0x05
-#define RV3029C2_W_YEARS	0x06
 
-#define RV3029C2_REG_HR_12_24          (1 << 6)  /* 24h/12h mode */
-#define RV3029C2_REG_HR_PM             (1 << 5)  /* PM/AM bit in 12h mode */
-
-#define RTC_RV3029_EEPROM_CTRL	0x30
-#define RTC_RV3029_TRICKLE_1K	(1 << 4)
-#define RTC_RV3029_TRICKLE_5K	(1 << 5)
-#define RTC_RV3029_TRICKLE_20K	(1 << 6)
-#define RTC_RV3029_TRICKLE_80K	(1 << 7)
-
-int rtc_get( struct rtc_time *tmp )
+static int rv3029_rtc_get(struct udevice *dev, struct rtc_time *tm)
 {
-	int	ret;
-	unsigned char buf[RTC_RV3029_PAGE_LEN];
+	u8 regs[RTC_RV3029_PAGE_LEN];
+	int ret;
 
-	ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, buf, \
-			RTC_RV3029_PAGE_LEN);
-	if (ret) {
+	ret = dm_i2c_read(dev, RV3029_W_SEC, regs, sizeof(regs));
+	if (ret < 0) {
 		printf("%s: error reading RTC: %x\n", __func__, ret);
-		return -1;
+		return -EIO;
 	}
-	tmp->tm_sec  = bcd2bin( buf[RV3029C2_W_SECONDS] & 0x7f);
-	tmp->tm_min  = bcd2bin( buf[RV3029C2_W_MINUTES] & 0x7f);
-	if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_12_24) {
-		/* 12h format */
-		tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x1f);
-		if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_PM)
-			/* PM flag set */
-			tmp->tm_hour += 12;
-	} else
-		tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x3f);
 
-	tmp->tm_mday = bcd2bin( buf[RV3029C2_W_DATE] & 0x3F );
-	tmp->tm_mon  = bcd2bin( buf[RV3029C2_W_MONTHS] & 0x1F );
-	tmp->tm_wday = bcd2bin( buf[RV3029C2_W_DAYS] & 0x07 );
+	tm->tm_sec = bcd2bin(regs[RV3029_W_SEC - RV3029_W_SEC]);
+	tm->tm_min = bcd2bin(regs[RV3029_W_MINUTES - RV3029_W_SEC]);
+
+	/* HR field has a more complex interpretation */
+	{
+		const u8 _hr = regs[RV3029_W_HOURS - RV3029_W_SEC];
+
+		if (_hr & RV3029_REG_HR_12_24) {
+			/* 12h format */
+			tm->tm_hour = bcd2bin(_hr & 0x1f);
+			if (_hr & RV3029_REG_HR_PM)	/* PM flag set */
+				tm->tm_hour += 12;
+		} else {
+			/* 24h format */
+			tm->tm_hour = bcd2bin(_hr & 0x3f);
+		}
+	}
+
+	tm->tm_mday = bcd2bin(regs[RV3029_W_DATE - RV3029_W_SEC]);
+	tm->tm_mon = bcd2bin(regs[RV3029_W_MONTHS - RV3029_W_SEC]) - 1;
 	/* RTC supports only years > 1999 */
-	tmp->tm_year = bcd2bin( buf[RV3029C2_W_YEARS]) + 2000;
-	tmp->tm_yday = 0;
-	tmp->tm_isdst = 0;
+	tm->tm_year = bcd2bin(regs[RV3029_W_YEARS - RV3029_W_SEC]) + 2000;
+	tm->tm_wday = bcd2bin(regs[RV3029_W_DAYS - RV3029_W_SEC]) - 1;
 
-	debug( "Get DATE: %4d-%02d-%02d (wday=%d)  TIME: %2d:%02d:%02d\n",
-		tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
-		tmp->tm_hour, tmp->tm_min, tmp->tm_sec );
+	tm->tm_yday = 0;
+	tm->tm_isdst = 0;
+
+	debug("%s: %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n",
+	      __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+	      tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
 
 	return 0;
 }
 
-int rtc_set( struct rtc_time *tmp )
+static int rv3029_rtc_set(struct udevice *dev, const struct rtc_time *tm)
 {
-	int	ret;
-	unsigned char buf[RTC_RV3029_PAGE_LEN];
+	u8 regs[RTC_RV3029_PAGE_LEN];
 
-	debug( "Set DATE: %4d-%02d-%02d (wday=%d)  TIME: %2d:%02d:%02d\n",
-		tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
-		tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+	debug("%s: %4d-%02d-%02d (wday=%d( %2d:%02d:%02d\n",
+	      __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+	      tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
 
-	if (tmp->tm_year < 2000) {
-		printf("RTC: year %d < 2000 not possible\n", tmp->tm_year);
-		return -1;
+
+	if (tm->tm_year < 2000) {
+		printf("%s: year %d (before 2000) not supported\n",
+		       __func__, tm->tm_year);
+		return -EINVAL;
 	}
-	buf[RV3029C2_W_SECONDS] = bin2bcd(tmp->tm_sec);
-	buf[RV3029C2_W_MINUTES] = bin2bcd(tmp->tm_min);
-	buf[RV3029C2_W_HOURS] = bin2bcd(tmp->tm_hour);
-	/* set 24h format */
-	buf[RV3029C2_W_HOURS] &= ~RV3029C2_REG_HR_12_24;
-	buf[RV3029C2_W_DATE] = bin2bcd(tmp->tm_mday);
-	buf[RV3029C2_W_DAYS] = bin2bcd(tmp->tm_wday);
-	buf[RV3029C2_W_MONTHS] = bin2bcd(tmp->tm_mon);
-	tmp->tm_year -= 2000;
-	buf[RV3029C2_W_YEARS] = bin2bcd(tmp->tm_year);
-	ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1,
-			buf, RTC_RV3029_PAGE_LEN);
 
-	/* give the RTC some time to update */
-	udelay(1000);
-	return ret;
+	regs[RV3029_W_SEC - RV3029_W_SEC] = bin2bcd(tm->tm_sec);
+	regs[RV3029_W_MINUTES - RV3029_W_SEC] = bin2bcd(tm->tm_min);
+	regs[RV3029_W_HOURS - RV3029_W_SEC] = bin2bcd(tm->tm_hour);
+	regs[RV3029_W_DATE - RV3029_W_SEC] = bin2bcd(tm->tm_mday);
+	regs[RV3029_W_MONTHS - RV3029_W_SEC] = bin2bcd(tm->tm_mon + 1);
+	regs[RV3029_W_DAYS - RV3029_W_SEC] = bin2bcd(tm->tm_wday + 1) & 0x7;
+	regs[RV3029_W_YEARS - RV3029_W_SEC] = bin2bcd(tm->tm_year - 2000);
+
+	return dm_i2c_write(dev, RV3029_W_SEC, regs, sizeof(regs));
 }
 
-/* sets EERE-Bit  (automatic EEPROM refresh) */
-static void set_eere_bit(int state)
+static int rv3029_rtc_reset(struct udevice *dev)
 {
-	unsigned char reg_ctrl1;
+	u8 ctrl = RV3029_RST_CTRL_SYSR;
+	unsigned long start;
+	const unsigned long timeout_ms = 10000;
+	int ret;
 
-	(void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1,
-			&reg_ctrl1, 1);
+	/* trigger the system-reset */
+	ret = dm_i2c_write(dev, RV3029_RST_CTRL, &ctrl, 1);
+	if (ret < 0)
+		return -EIO;
 
-	if (state)
-		reg_ctrl1 |= RTC_RV3029_CTRL1_EERE;
-	else
-		reg_ctrl1 &= (~RTC_RV3029_CTRL1_EERE);
+	/* wait for the system-reset to complete */
+	start = get_timer(0);
+	do {
+		if (get_timer(start) > timeout_ms)
+			return -ETIMEDOUT;
 
-	(void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1,
-		&reg_ctrl1, 1);
+		ret = dm_i2c_read(dev, RV3029_RST_CTRL, &ctrl, 1);
+		if (ret < 0)
+			return -EIO;
+	} while (ctrl & RV3029_RST_CTRL_SYSR);
+
+	return 0;
 }
 
-/* waits until EEPROM page is no longer busy (times out after 10ms*loops) */
-static int wait_eebusy(int loops)
+static int rv3029_rtc_read8(struct udevice *dev, unsigned int reg)
 {
-	int i;
-	unsigned char ctrl_status;
+	u8 data;
+	int ret;
 
-	for (i = 0; i < loops; i++) {
-		(void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_STATUS,
-			1, &ctrl_status, 1);
+	ret = dm_i2c_read(dev, reg, &data, sizeof(data));
+	return ret < 0 ? ret : data;
+}
 
-		if ((ctrl_status & RTC_RV3029_CTRLS_EEBUSY) == 0)
+static int rv3029_rtc_write8(struct udevice *dev, unsigned int reg, int val)
+{
+	u8 data = val;
+
+	return dm_i2c_write(dev, reg, &data, 1);
+}
+
+#if defined(OF_CONTROL)
+static int rv3029_get_sr(struct udevice *dev, u8 *buf)
+{
+	int ret = dm_i2c_read(dev, RV3029_STATUS, buf, 1);
+
+	if (ret < 0)
+		return -EIO;
+
+	dev_dbg(dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]);
+	return 0;
+}
+
+static int rv3029_set_sr(struct udevice *dev, u8 val)
+{
+	int ret;
+
+	ret = dm_i2c_read(dev, RV3029_STATUS, &val, 1);
+	if (ret < 0)
+		return -EIO;
+
+	dev_dbg(dev, "status = 0x%.2x (%d)\n", val, val);
+	return 0;
+}
+
+static int rv3029_eeprom_busywait(struct udevice *dev)
+{
+	int i, ret;
+	u8 sr;
+
+	for (i = 100; i > 0; i--) {
+		ret = rv3029_get_sr(dev, &sr);
+		if (ret < 0)
+			break;
+		if (!(sr & RV3029_STATUS_EEBUSY))
 			break;
 		udelay(10000);
 	}
-	return i;
-}
-
-void rtc_reset (void)
-{
-	unsigned char buf[RTC_RV3029_PAGE_LEN];
-
-	buf[0] = RTC_RV3029_CTRL_SYS_R;
-	(void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_RESET, 1,
-			buf, 1);
-
-#if defined(CONFIG_SYS_RV3029_TCR)
-	/*
-	 * because EEPROM_CTRL register is in EEPROM page it is necessary to
-	 * disable automatic EEPROM refresh and check if EEPROM is busy
-	 * before EEPORM_CTRL register may be accessed
-	 */
-	set_eere_bit(0);
-	wait_eebusy(100);
-	/* read current trickle charger setting */
-	(void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL,
-			1, buf, 1);
-	/* enable automatic EEPROM refresh again */
-	set_eere_bit(1);
-
-	/*
-	 * to minimize EEPROM access write trickle charger setting only if it
-	 * differs from current value
-	 */
-	if ((buf[0] & 0xF0) != CONFIG_SYS_RV3029_TCR) {
-		buf[0] = (buf[0] & 0x0F) | CONFIG_SYS_RV3029_TCR;
-		/*
-		 * write trickle charger setting (disable autom. EEPROM
-		 * refresh and wait until EEPROM is idle)
-		 */
-		set_eere_bit(0);
-		wait_eebusy(100);
-		(void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR,
-				RTC_RV3029_EEPROM_CTRL, 1, buf, 1);
-		/*
-		 * it is necessary to wait 10ms before EEBUSY-Bit may be read
-		 * (this is not documented in the data sheet yet, but the
-		 * manufacturer recommends it)
-		 */
-		udelay(10000);
-		/* wait until EEPROM write access is finished */
-		wait_eebusy(100);
-		set_eere_bit(1);
+	if (i <= 0) {
+		dev_err(dev, "EEPROM busy wait timeout.\n");
+		return -ETIMEDOUT;
 	}
-#endif
+
+	return ret;
 }
+
+static int rv3029_update_bits(struct udevice *dev, u8 reg, u8 mask, u8 set)
+{
+	u8 buf;
+	int ret;
+
+	ret = dm_i2c_read(dev, reg, &buf, 1);
+	if (ret < 0)
+		return ret;
+
+	if ((buf & mask) == (set && mask))
+		return 0;
+
+	buf = (buf & ~mask) | (set & mask);
+	ret = dm_i2c_read(dev, reg, &buf, 1);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int rv3029_eeprom_exit(struct udevice *dev)
+{
+	/* Re-enable eeprom refresh */
+	return rv3029_update_bits(dev, RV3029_ONOFF_CTRL,
+				  RV3029_ONOFF_CTRL_EERE,
+				  RV3029_ONOFF_CTRL_EERE);
+}
+
+static int rv3029_eeprom_enter(struct udevice *dev)
+{
+	int ret;
+	u8 sr;
+
+	/* Check whether we are in the allowed voltage range. */
+	ret = rv3029_get_sr(dev, &sr);
+	if (ret < 0)
+		return ret;
+	if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) {
+		/* We clear the bits and retry once just in case
+		 * we had a brown out in early startup.
+		 */
+		sr &= ~RV3029_STATUS_VLOW1;
+		sr &= ~RV3029_STATUS_VLOW2;
+		ret = rv3029_set_sr(dev, sr);
+		if (ret < 0)
+			return ret;
+		udelay(10000);
+		ret = rv3029_get_sr(dev, &sr);
+		if (ret < 0)
+			return ret;
+		if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) {
+			dev_err(dev, "Supply voltage is too low to safely access the EEPROM.\n");
+			return -ENODEV;
+		}
+	}
+
+	/* Disable eeprom refresh. */
+	ret = rv3029_update_bits(dev,
+				 RV3029_ONOFF_CTRL, RV3029_ONOFF_CTRL_EERE, 0);
+	if (ret < 0)
+		return ret;
+
+	/* Wait for any previous eeprom accesses to finish. */
+	ret = rv3029_eeprom_busywait(dev);
+	if (ret < 0)
+		rv3029_eeprom_exit(dev);
+
+	return ret;
+}
+
+static int rv3029_eeprom_read(struct udevice *dev, u8 reg,
+			      u8 buf[], size_t len)
+{
+	int ret, err;
+
+	err = rv3029_eeprom_enter(dev);
+	if (err < 0)
+		return err;
+
+	ret = dm_i2c_read(dev, reg, buf, len);
+
+	err = rv3029_eeprom_exit(dev);
+	if (err < 0)
+		return err;
+
+	return ret;
+}
+
+static int rv3029_eeprom_write(struct udevice *dev, u8 reg,
+			       u8 const buf[], size_t len)
+{
+	int ret;
+	size_t i;
+	u8 tmp;
+
+	ret = rv3029_eeprom_enter(dev);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < len; i++, reg++) {
+		ret = dm_i2c_read(dev, reg, &tmp, 1);
+		if (ret < 0)
+			break;
+		if (tmp != buf[i]) {
+			ret = dm_i2c_write(dev, reg, &buf[i], 1);
+			if (ret < 0)
+				break;
+		}
+		ret = rv3029_eeprom_busywait(dev);
+		if (ret < 0)
+			break;
+	}
+
+	ret = rv3029_eeprom_exit(dev);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int rv3029_eeprom_update_bits(struct udevice *dev,
+				     u8 reg, u8 mask, u8 set)
+{
+	u8 buf;
+	int ret;
+
+	ret = rv3029_eeprom_read(dev, reg, &buf, 1);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * If the EEPROM already reads the correct bitpattern, we don't need
+	 * to update it.
+	 */
+	if ((buf & mask) == (set & mask))
+		return 0;
+
+	buf = (buf & ~mask) | (set & mask);
+	ret = rv3029_eeprom_write(dev, reg, &buf, 1);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void rv3029_trickle_config(struct udevice *dev)
+{
+	static const struct rv3029_trickle_tab_elem {
+		u32 r;		/* resistance in ohms */
+		u8 conf;	/* trickle config bits */
+	} rv3029_trickle_tab[] = {
+		{
+			.r	= 1076,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
+				  RV3029_TRICKLE_20K | RV3029_TRICKLE_80K,
+		}, {
+			.r	= 1091,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
+				  RV3029_TRICKLE_20K,
+		}, {
+			.r	= 1137,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
+				  RV3029_TRICKLE_80K,
+		}, {
+			.r	= 1154,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_5K,
+		}, {
+			.r	= 1371,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_20K |
+				  RV3029_TRICKLE_80K,
+		}, {
+			.r	= 1395,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_20K,
+		}, {
+			.r	= 1472,
+			.conf	= RV3029_TRICKLE_1K | RV3029_TRICKLE_80K,
+		}, {
+			.r	= 1500,
+			.conf	= RV3029_TRICKLE_1K,
+		}, {
+			.r	= 3810,
+			.conf	= RV3029_TRICKLE_5K | RV3029_TRICKLE_20K |
+				  RV3029_TRICKLE_80K,
+		}, {
+			.r	= 4000,
+			.conf	= RV3029_TRICKLE_5K | RV3029_TRICKLE_20K,
+		}, {
+			.r	= 4706,
+			.conf	= RV3029_TRICKLE_5K | RV3029_TRICKLE_80K,
+		}, {
+			.r	= 5000,
+			.conf	= RV3029_TRICKLE_5K,
+		}, {
+			.r	= 16000,
+			.conf	= RV3029_TRICKLE_20K | RV3029_TRICKLE_80K,
+		}, {
+			.r	= 20000,
+			.conf	= RV3029_TRICKLE_20K,
+		}, {
+			.r	= 80000,
+			.conf	= RV3029_TRICKLE_80K,
+		},
+	};
+	int err;
+	u32 ohms;
+	u8 trickle_set_bits = 0;
+
+	/* Configure the trickle charger. */
+	err = dev_read_u32(dev, "trickle-resistor-ohms", &ohms);
+
+	if (!err) {
+		/* Find trickle-charger config */
+		for (int i = 0; i < ARRAY_SIZE(rv3029_trickle_tab); i++)
+			if (rv3029_trickle_tab[i].r >= ohms) {
+				dev_dbg(dev, "trickle charger at %d ohms\n",
+					rv3029_trickle_tab[i].r);
+				trickle_set_bits = rv3029_trickle_tab[i].conf;
+				break;
+			}
+	}
+
+	dev_dbg(dev, "trickle charger config 0x%x\n", trickle_set_bits);
+	err = rv3029_eeprom_update_bits(dev, RV3029_CONTROL_E2P_EECTRL,
+					RV3029_TRICKLE_MASK,
+					trickle_set_bits);
+	if (err < 0)
+		dev_dbg(dev, "failed to update trickle charger\n");
+}
+#else
+static inline void rv3029_trickle_config(struct udevice *dev)
+{
+}
+#endif
+
+static int rv3029_probe(struct udevice *dev)
+{
+	i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS |
+				DM_I2C_CHIP_WR_ADDRESS);
+
+	rv3029_trickle_config(dev);
+	return 0;
+}
+
+static const struct rtc_ops rv3029_rtc_ops = {
+	.get = rv3029_rtc_get,
+	.set = rv3029_rtc_set,
+	.read8 = rv3029_rtc_read8,
+	.write8 = rv3029_rtc_write8,
+	.reset = rv3029_rtc_reset,
+};
+
+static const struct udevice_id rv3029_rtc_ids[] = {
+	{ .compatible = "mc,rv3029" },
+	{ .compatible = "mc,rv3029c2" },
+	{ }
+};
+
+U_BOOT_DRIVER(rtc_rv3029) = {
+	.name	= "rtc-rv3029",
+	.id	= UCLASS_RTC,
+	.probe	= rv3029_probe,
+	.of_match = rv3029_rtc_ids,
+	.ops	= &rv3029_rtc_ops,
+};