u-boot/drivers/rtc/rx8025.c
Mathew McBride 771fc0c079 rtc: rx8025: set date in a single i2c transaction
The RX8025/RX8035 does not like having it's time registers
set byte-by-byte in separate I2C transactions.

From the note at the top of the file, it appears
target-dependent workarounds have been used in the
past for this.

Resolve this by setting the time registers in a single
I2C transaction.

As part of this, also ensure the '24/12' flag in the RTC
is reset before writing the date (instead of after), otherwise
the RX8035 will clear the seconds and minutes registers.

Tested on Traverse Ten64 (NXP LS1088A) with RX8035.

Signed-off-by: Mathew McBride <matt@traverse.com.au>
2021-10-03 14:40:56 -04:00

261 lines
6.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007
* Matthias Fuchs, esd gmbh, matthias.fuchs@esd-electronics.com.
*/
/*
* Epson RX8025 RTC driver.
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <i2c.h>
#include <rtc.h>
/*---------------------------------------------------------------------*/
#undef DEBUG_RTC
#ifdef DEBUG_RTC
#define DEBUGR(fmt,args...) printf(fmt ,##args)
#else
#define DEBUGR(fmt,args...)
#endif
/*---------------------------------------------------------------------*/
enum rx_model {
model_rx_8025,
model_rx_8035,
};
/*
* RTC register addresses
*/
#define RTC_SEC_REG_ADDR 0x00
#define RTC_MIN_REG_ADDR 0x01
#define RTC_HR_REG_ADDR 0x02
#define RTC_DAY_REG_ADDR 0x03
#define RTC_DATE_REG_ADDR 0x04
#define RTC_MON_REG_ADDR 0x05
#define RTC_YR_REG_ADDR 0x06
#define RTC_OFFSET_REG_ADDR 0x07
#define RTC_CTL1_REG_ADDR 0x0e
#define RTC_CTL2_REG_ADDR 0x0f
/*
* Control register 1 bits
*/
#define RTC_CTL1_BIT_2412 0x20
/*
* Control register 2 bits
*/
#define RTC_CTL2_BIT_PON 0x10
#define RTC_CTL2_BIT_VDET 0x40
#define RTC_CTL2_BIT_XST 0x20
#define RTC_CTL2_BIT_VDSL 0x80
/*
* Note: the RX8025 I2C RTC requires register
* reads and write to consist of a single bus
* cycle. It is not allowed to write the register
* address in a first cycle that is terminated by
* a STOP condition. The chips needs a 'restart'
* sequence (start sequence without a prior stop).
*/
#define rtc_read(reg) buf[(reg) & 0xf]
static int rtc_write(struct udevice *dev, uchar reg, uchar val);
static int rx8025_is_osc_stopped(enum rx_model model, int ctrl2)
{
int xstp = ctrl2 & RTC_CTL2_BIT_XST;
/* XSTP bit has different polarity on RX-8025 vs RX-8035.
* RX-8025: 0 == oscillator stopped
* RX-8035: 1 == oscillator stopped
*/
if (model == model_rx_8025)
xstp = !xstp;
return xstp;
}
/*
* Get the current time from the RTC
*/
static int rx8025_rtc_get(struct udevice *dev, struct rtc_time *tmp)
{
int rel = 0;
uchar sec, min, hour, mday, wday, mon, year, ctl2;
uchar buf[16];
if (dm_i2c_read(dev, 0, buf, sizeof(buf))) {
printf("Error reading from RTC\n");
return -EIO;
}
sec = rtc_read(RTC_SEC_REG_ADDR);
min = rtc_read(RTC_MIN_REG_ADDR);
hour = rtc_read(RTC_HR_REG_ADDR);
wday = rtc_read(RTC_DAY_REG_ADDR);
mday = rtc_read(RTC_DATE_REG_ADDR);
mon = rtc_read(RTC_MON_REG_ADDR);
year = rtc_read(RTC_YR_REG_ADDR);
DEBUGR("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
/* dump status */
ctl2 = rtc_read(RTC_CTL2_REG_ADDR);
if (ctl2 & RTC_CTL2_BIT_PON) {
printf("RTC: power-on detected\n");
rel = -1;
}
if (ctl2 & RTC_CTL2_BIT_VDET) {
printf("RTC: voltage drop detected\n");
rel = -1;
}
if (rx8025_is_osc_stopped(dev->driver_data, ctl2)) {
printf("RTC: oscillator stop detected\n");
rel = -1;
}
tmp->tm_sec = bcd2bin(sec & 0x7F);
tmp->tm_min = bcd2bin(min & 0x7F);
if (rtc_read(RTC_CTL1_REG_ADDR) & RTC_CTL1_BIT_2412)
tmp->tm_hour = bcd2bin(hour & 0x3F);
else
tmp->tm_hour = bcd2bin(hour & 0x1F) % 12 +
((hour & 0x20) ? 12 : 0);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon & 0x1F);
tmp->tm_year = bcd2bin (year) + ( bcd2bin (year) >= 70 ? 1900 : 2000);
tmp->tm_wday = bcd2bin (wday & 0x07);
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
DEBUGR("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);
return rel;
}
/*
* Set the RTC
*/
static int rx8025_rtc_set(struct udevice *dev, const struct rtc_time *tmp)
{
/* To work around the read/write cycle issue mentioned
* at the top of this file, write all the time registers
* in one I2C transaction
*/
u8 write_op[8];
/* 2412 flag must be set before doing a RTC write,
* otherwise the seconds and minute register
* will be cleared when the flag is set
*/
if (rtc_write(dev, RTC_CTL1_REG_ADDR, RTC_CTL1_BIT_2412))
return -EIO;
DEBUGR("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);
if (tmp->tm_year < 1970 || tmp->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
write_op[RTC_SEC_REG_ADDR] = bin2bcd(tmp->tm_sec);
write_op[RTC_MIN_REG_ADDR] = bin2bcd(tmp->tm_min);
write_op[RTC_HR_REG_ADDR] = bin2bcd(tmp->tm_hour);
write_op[RTC_DAY_REG_ADDR] = bin2bcd(tmp->tm_wday);
write_op[RTC_DATE_REG_ADDR] = bin2bcd(tmp->tm_mday);
write_op[RTC_MON_REG_ADDR] = bin2bcd(tmp->tm_mon);
write_op[RTC_YR_REG_ADDR] = bin2bcd(tmp->tm_year % 100);
write_op[RTC_OFFSET_REG_ADDR] = 0;
return dm_i2c_write(dev, 0, &write_op[0], 8);
}
/*
* Reset the RTC
*/
static int rx8025_rtc_reset(struct udevice *dev)
{
uchar buf[16];
uchar ctl2;
if (dm_i2c_read(dev, 0, buf, sizeof(buf))) {
printf("Error reading from RTC\n");
return -EIO;
}
ctl2 = rtc_read(RTC_CTL2_REG_ADDR);
ctl2 &= ~(RTC_CTL2_BIT_PON | RTC_CTL2_BIT_VDET);
if (dev->driver_data == model_rx_8035)
ctl2 &= ~(RTC_CTL2_BIT_XST);
else
ctl2 |= RTC_CTL2_BIT_XST;
return rtc_write(dev, RTC_CTL2_REG_ADDR, ctl2);
}
/*
* Helper functions
*/
static int rtc_write(struct udevice *dev, uchar reg, uchar val)
{
uchar buf[2];
buf[0] = reg << 4;
buf[1] = val;
if (dm_i2c_write(dev, 0, buf, 2)) {
printf("Error writing to RTC\n");
return -EIO;
}
return 0;
}
static int rx8025_probe(struct udevice *dev)
{
uchar buf[16];
int ret = 0;
if (i2c_get_chip_offset_len(dev) != 1)
ret = i2c_set_chip_offset_len(dev, 1);
if (ret)
return ret;
return dm_i2c_read(dev, 0, buf, sizeof(buf));
}
static const struct rtc_ops rx8025_rtc_ops = {
.get = rx8025_rtc_get,
.set = rx8025_rtc_set,
.reset = rx8025_rtc_reset,
};
static const struct udevice_id rx8025_rtc_ids[] = {
{ .compatible = "epson,rx8025", .data = model_rx_8025 },
{ .compatible = "epson,rx8035", .data = model_rx_8035 },
{ }
};
U_BOOT_DRIVER(rx8025_rtc) = {
.name = "rx8025_rtc",
.id = UCLASS_RTC,
.probe = rx8025_probe,
.of_match = rx8025_rtc_ids,
.ops = &rx8025_rtc_ops,
};