[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