]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
rtc: m41t80: kickstart ocillator upon failure
authorA. Niyas Ahamed Mydeen <nmydeen@mvista.com>
Wed, 2 Apr 2025 12:05:46 +0000 (17:35 +0530)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Sun, 25 May 2025 21:56:13 +0000 (23:56 +0200)
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>
drivers/rtc/rtc-m41t80.c

index 1f58ae8b151e945558acea4fd86dd115759a7458..7074d086f1c8b2f9d9cb028c405d41c492eba644 100644 (file)
@@ -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;