mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
rtc: m41t80: kickstart ocillator upon failure
The ocillator on the m41t62 (and other chips of this type) needs a kickstart upon a failure; the RTC read routine will notice the oscillator failure and fail reads. This is added in the RTC write routine; this allows the system to know that the time in the RTC is accurate. This is following the procedure described in section 3.11 of "https://www.st.com/resource/en/datasheet/m41t62.pdf" Signed-off-by: A. Niyas Ahamed Mydeen <nmydeen@mvista.com> Reviewed-by: Corey Minyard <cminyard@mvista.com> Link: https://lore.kernel.org/r/20250402120546.336657-2-nmydeen@mvista.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
parent
818b6709e1
commit
1a7ed2fffb
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/delay.h>
|
||||
#ifdef CONFIG_RTC_DRV_M41T80_WDT
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
|
@ -204,7 +205,7 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
return flags;
|
||||
|
||||
if (flags & M41T80_FLAGS_OF) {
|
||||
dev_err(&client->dev, "Oscillator failure, data is invalid.\n");
|
||||
dev_err(&client->dev, "Oscillator failure, time may not be accurate, write time to RTC to fix it.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -227,21 +228,31 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct m41t80_data *clientdata = i2c_get_clientdata(client);
|
||||
struct rtc_time tm = *in_tm;
|
||||
unsigned char buf[8];
|
||||
int err, flags;
|
||||
time64_t time = 0;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
if (flags & M41T80_FLAGS_OF) {
|
||||
/* add 4sec of oscillator stablize time otherwise we are behind 4sec */
|
||||
time = rtc_tm_to_time64(&tm);
|
||||
rtc_time64_to_tm(time + 4, &tm);
|
||||
}
|
||||
buf[M41T80_REG_SSEC] = 0;
|
||||
buf[M41T80_REG_SEC] = bin2bcd(tm->tm_sec);
|
||||
buf[M41T80_REG_MIN] = bin2bcd(tm->tm_min);
|
||||
buf[M41T80_REG_HOUR] = bin2bcd(tm->tm_hour);
|
||||
buf[M41T80_REG_DAY] = bin2bcd(tm->tm_mday);
|
||||
buf[M41T80_REG_MON] = bin2bcd(tm->tm_mon + 1);
|
||||
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
|
||||
buf[M41T80_REG_WDAY] = tm->tm_wday;
|
||||
buf[M41T80_REG_SEC] = bin2bcd(tm.tm_sec);
|
||||
buf[M41T80_REG_MIN] = bin2bcd(tm.tm_min);
|
||||
buf[M41T80_REG_HOUR] = bin2bcd(tm.tm_hour);
|
||||
buf[M41T80_REG_DAY] = bin2bcd(tm.tm_mday);
|
||||
buf[M41T80_REG_MON] = bin2bcd(tm.tm_mon + 1);
|
||||
buf[M41T80_REG_YEAR] = bin2bcd(tm.tm_year - 100);
|
||||
buf[M41T80_REG_WDAY] = tm.tm_wday;
|
||||
|
||||
/* If the square wave output is controlled in the weekday register */
|
||||
if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
|
||||
|
|
@ -260,17 +271,34 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
dev_err(&client->dev, "Unable to write to date registers\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Clear the OF bit of Flags Register */
|
||||
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS,
|
||||
flags & ~M41T80_FLAGS_OF);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "Unable to write flags register\n");
|
||||
return err;
|
||||
if (flags & M41T80_FLAGS_OF) {
|
||||
/* OF cannot be immediately reset: oscillator has to be restarted. */
|
||||
dev_warn(&client->dev, "OF bit is still set, kickstarting clock.\n");
|
||||
err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, M41T80_SEC_ST);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "Can't set ST bit\n");
|
||||
return err;
|
||||
}
|
||||
err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, flags & ~M41T80_SEC_ST);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "Can't clear ST bit\n");
|
||||
return err;
|
||||
}
|
||||
/* oscillator must run for 4sec before we attempt to reset OF bit */
|
||||
msleep(4000);
|
||||
/* Clear the OF bit of Flags Register */
|
||||
err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags & ~M41T80_FLAGS_OF);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "Unable to write flags register\n");
|
||||
return err;
|
||||
}
|
||||
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
|
||||
if (flags < 0) {
|
||||
return flags;
|
||||
} else if (flags & M41T80_FLAGS_OF) {
|
||||
dev_err(&client->dev, "Can't clear the OF bit check battery\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user