[PATCH 2/2] rtc: ds1307: add initial support for EPSON RX8025

Alexander Feilke <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <[email protected]>
From: Alexander Feilke <[email protected]>

Add support for the EPSON RX8025 RTC. The date/time registers of this
chip are compatible with the DS1307.

Inspired by Linux 6.18 driver code.

Signed-off-by: Alexander Feilke <[email protected]>
---
 drivers/rtc/ds1307.c | 137 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 3 deletions(-)

diff --git a/drivers/rtc/ds1307.c b/drivers/rtc/ds1307.c
index db9bf43214c..eb53a531492 100644
--- a/drivers/rtc/ds1307.c
+++ b/drivers/rtc/ds1307.c
@@ -16,6 +16,7 @@
 #include <config.h>
 #include <command.h>
 #include <dm.h>
+#include <linux/delay.h>
 #include <log.h>
 #include <rtc.h>
 #include <i2c.h>
@@ -27,6 +28,7 @@ enum ds_type {
 	ds_1340,
 	m41t11,
 	mcp794xx,
+	rx_8130,
 };
 
 struct rtc_ds1370_data {
@@ -73,6 +75,28 @@ struct rtc_ds1370_data {
 #define MCP7941X_BIT_ST		0x80
 #define MCP7941X_BIT_VBATEN	0x08
 
+/* RX8130-specific bits */
+#define RX8130_REG_ALARM_MIN		0x17
+#define RX8130_REG_ALARM_HOUR		0x18
+#define RX8130_REG_ALARM_WEEK_OR_DAY	0x19
+#define RX8130_REG_EXTENSION		0x1c
+#define RX8130_REG_EXTENSION_TE		BIT(4)
+#define RX8130_REG_EXTENSION_USEL	BIT(5)
+#define RX8130_REG_EXTENSION_FSEL0	BIT(6)
+#define RX8130_REG_EXTENSION_FESL1	BIT(7)
+#define RX8130_REG_FLAG			0x1d
+#define RX8130_REG_FLAG_VLF		BIT(1)
+#define RX8130_REG_FLAG_AF		BIT(3)
+#define RX8130_REG_CONTROL0		0x1e
+#define RX8130_REG_CONTROL0_AIE		BIT(3)
+#define RX8130_REG_CONTROL0_TIE		BIT(4)
+#define RX8130_REG_CONTROL0_UIE		BIT(5)
+#define RX8130_REG_CONTROL0_STOP	BIT(6)
+#define RX8130_REG_CONTROL0_TEST	BIT(7)
+#define RX8130_REG_CONTROL1		0x1f
+#define RX8130_REG_CONTROL1_INIEN	BIT(4)
+#define RX8130_REG_CONTROL1_CHGEN	BIT(5)
+
 static int ds1307_rtc_set(struct udevice *dev, const struct rtc_time *tm)
 {
 	int ret;
@@ -88,7 +112,11 @@ static int ds1307_rtc_set(struct udevice *dev, const struct rtc_time *tm)
 
 	buf[RTC_YR_REG_ADDR] = bin2bcd(tm->tm_year % 100);
 	buf[RTC_MON_REG_ADDR] = bin2bcd(tm->tm_mon);
-	buf[RTC_DAY_REG_ADDR] = bin2bcd(tm->tm_wday + 1);
+	/* rx8130 is bit position, not BCD */
+	if (data->type == rx_8130)
+		buf[RTC_DAY_REG_ADDR] = 1 << tm->tm_wday;
+	else
+		buf[RTC_DAY_REG_ADDR] = bin2bcd(tm->tm_wday + 1);
 	buf[RTC_DATE_REG_ADDR] = bin2bcd(tm->tm_mday);
 	buf[RTC_HR_REG_ADDR] = bin2bcd(tm->tm_hour);
 	buf[RTC_MIN_REG_ADDR] = bin2bcd(tm->tm_min);
@@ -97,6 +125,10 @@ static int ds1307_rtc_set(struct udevice *dev, const struct rtc_time *tm)
 	if (data->type == mcp794xx) {
 		buf[RTC_DAY_REG_ADDR] |= MCP7941X_BIT_VBATEN;
 		buf[RTC_SEC_REG_ADDR] |= MCP7941X_BIT_ST;
+	} else if (data->type == rx_8130) {
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_CONTROL0, 0, RX8130_REG_CONTROL0_STOP);
+		if (ret < 0)
+			return ret;
 	}
 
 	ret = dm_i2c_write(dev, data->offset, buf, sizeof(buf));
@@ -105,7 +137,18 @@ static int ds1307_rtc_set(struct udevice *dev, const struct rtc_time *tm)
 
 	if (data->type == ds_1337) {
 		/* Ensure oscillator is enabled */
-		dm_i2c_reg_write(dev, plat->offset + DS1337_CTL_REG_ADDR, 0);
+		dm_i2c_reg_write(dev, data->offset + DS1337_CTL_REG_ADDR, 0);
+	} else if (data->type == rx_8130) {
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_CONTROL0, RX8130_REG_CONTROL0_STOP, 0);
+		if (ret < 0)
+			return ret;
+
+		/* clear Voltage Loss Flag as data is available now */
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_FLAG, RX8130_REG_FLAG_VLF, 0);
+		if (ret < 0) {
+			printf("RTC: write VLF error %d\n", ret);
+			return ret;
+		}
 	}
 
 	return 0;
@@ -131,6 +174,17 @@ static int ds1307_rtc_get(struct udevice *dev, struct rtc_time *tm)
 			/* clear the OSF flag */
 			dm_i2c_reg_write(dev, data->offset + reg, status & ~RTC_STAT_BIT_OSF);
 		}
+	} else if (data->type == rx_8130) {
+		ret = dm_i2c_reg_read(dev, RX8130_REG_FLAG);
+		if (ret < 0) {
+			printf("RTC: read error %d\n", ret);
+			return ret;
+		}
+
+		if (ret & RX8130_REG_FLAG_VLF) {
+			printf("RTC: oscillator failed, set time!\n");
+			return -EINVAL;
+		}
 	}
 
 	tm->tm_sec  = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F);
@@ -141,7 +195,11 @@ static int ds1307_rtc_get(struct udevice *dev, struct rtc_time *tm)
 	tm->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) +
 			      (bcd2bin(buf[RTC_YR_REG_ADDR]) >= 70 ?
 			       1900 : 2000);
-	tm->tm_wday = bcd2bin((buf[RTC_DAY_REG_ADDR] - 1) & 0x07);
+	/* rx8130 is bit position, not BCD */
+	if (data->type == rx_8130)
+		tm->tm_wday = fls(buf[RTC_DAY_REG_ADDR] & 0x07);
+	else
+		tm->tm_wday = bcd2bin((buf[RTC_DAY_REG_ADDR] - 1) & 0x07);
 	tm->tm_yday = 0;
 	tm->tm_isdst = 0;
 
@@ -152,11 +210,78 @@ static int ds1307_rtc_get(struct udevice *dev, struct rtc_time *tm)
 	return 0;
 }
 
+int ds1307_rx8130_rtc_reset(struct udevice *dev) {
+	/*
+	 * RX8130CE manual, 18.2 Software Reset
+	 */
+	const uint8_t reg_address[]  = { 0x1e, 0x1e, 0x50, 0x53, 0x66, 0x6b, 0x6b };
+	const uint8_t reg_sequence[] = { 0x00, 0x80, 0x6c, 0x01, 0x03, 0x02, 0x01 };
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(reg_sequence); ++i) {
+		ret = dm_i2c_reg_write(dev, reg_address[i], reg_sequence[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	mdelay(150);
+
+	/* Dummy read before reading FLAG register */
+	ret = dm_i2c_reg_read(dev, RX8130_REG_FLAG);
+	ret = dm_i2c_reg_read(dev, RX8130_REG_FLAG);
+	if (ret < 0)
+		return ret;
+
+	while (ret >= 0 && (ret & RX8130_REG_FLAG_VLF)) {
+		ret &= ~RX8130_REG_FLAG_VLF;
+		dm_i2c_reg_write(dev, RX8130_REG_FLAG, ret);
+		mdelay(1);
+		ret = dm_i2c_reg_read(dev, RX8130_REG_FLAG);
+	}
+	printf("rx8130 VLF cleared: %d\n", ret);
+
+	/* Clear TE bit and Disable FOUT */
+	if (ret >= 0)
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_EXTENSION, RX8130_REG_EXTENSION_TE,
+			RX8130_REG_EXTENSION_FSEL0 | RX8130_REG_EXTENSION_FESL1);
+
+	/* Clear VLF bit */
+	if (ret >= 0)
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_FLAG, RX8130_REG_FLAG_VLF, 0);
+
+	/* Clear TEST, AIE, TIE, UIE for inhibit interrupt output of suddenness */
+	if (ret >= 0)
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_CONTROL0, RX8130_REG_CONTROL0_TEST |
+			RX8130_REG_CONTROL0_AIE |  RX8130_REG_CONTROL0_TIE |
+			RX8130_REG_CONTROL0_UIE, 0);
+
+	/* Set INIEN to 1 */
+	if (ret >= 0)
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_CONTROL1, 0, RX8130_REG_CONTROL1_INIEN);
+
+	/* Set CHGEN to 0 */
+	if (ret >= 0)
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_CONTROL1, RX8130_REG_CONTROL1_CHGEN, 0);
+
+	/* Start clock */
+	if (ret >= 0)
+		ret = dm_i2c_reg_clrset(dev, RX8130_REG_CONTROL0, RX8130_REG_CONTROL0_STOP, 0);
+
+	return ret;
+}
+
 static int ds1307_rtc_reset(struct udevice *dev)
 {
 	int ret;
 	struct rtc_ds1370_data *data = (void*)dev_get_driver_data(dev);
 
+	if (data->type == rx_8130) {
+		ret = ds1307_rx8130_rtc_reset(dev);
+		if (ret < 0)
+			return ret;
+	}
+
 	/*
 	 * reset clock/oscillator in the seconds register:
 	 * on DS1307 bit 7 enables Clock Halt (CH),
@@ -222,6 +347,11 @@ static const struct rtc_ds1370_data ds_1340_data = {
 	.offset = 0,
 };
 
+static const struct rtc_ds1370_data rx_8130_data = {
+	.type   = rx_8130,
+	.offset = 0x10,
+};
+
 static const struct rtc_ds1370_data mcp794xx_data = {
 	.type   = mcp794xx,
 	.offset = 0,
@@ -237,6 +367,7 @@ static const struct udevice_id ds1307_rtc_ids[] = {
 	{ .compatible = "dallas,ds1337", .data = (ulong)&ds_1337_data },
 	{ .compatible = "dallas,ds1339", .data = (ulong)&ds_1339_data },
 	{ .compatible = "dallas,ds1340", .data = (ulong)&ds_1340_data },
+	{ .compatible = "epson,rx8130", .data = (ulong)&rx_8130_data },
 	{ .compatible = "microchip,mcp7940x", .data = (ulong)&mcp794xx_data },
 	{ .compatible = "microchip,mcp7941x", .data = (ulong)&mcp794xx_data },
 	{ .compatible = "st,m41t11", .data = (ulong)&m41t11_data },
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.