rtc: pcf2127: use OFS flag to detect unreliable date and warn the user
authorAndrea Scian <andrea.scian@dave.eu>
Tue, 16 Jun 2015 09:39:47 +0000 (11:39 +0200)
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>
Sat, 5 Sep 2015 11:19:06 +0000 (13:19 +0200)
The PCF2127 datasheet states that it's wrong to say that the date in
unreliable if BLF (battery low flag) is set but instead, OSF (seconds
register) should be used to check if oscillator, for any reason, stopped.
Battery may be low (usually below 2V5 threshold) but the date may be anyway
correct (typically date is unreliable when input voltage is below 1V2).

Signed-off-by: Andrea Scian <andrea.scian@dave.eu>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
drivers/rtc/rtc-pcf2127.c

index 350c5c7cb67802ffc342dc5df724fdcf6fce5cb1..baf45c9ca65eefe77e4e8d815a0d510a9432b928 100644 (file)
 #define PCF2127_REG_MO          (0x08)
 #define PCF2127_REG_YR          (0x09)
 
+#define PCF2127_OSF             BIT(7)  /* Oscillator Fail flag */
+
 static struct i2c_driver pcf2127_driver;
 
 struct pcf2127 {
        struct rtc_device *rtc;
        int voltage_low; /* indicates if a low_voltage was detected */
+       int oscillator_failed; /* OSF was detected and date is unreliable */
 };
 
 /*
@@ -59,7 +62,18 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
        if (buf[PCF2127_REG_CTRL3] & 0x04) {
                pcf2127->voltage_low = 1;
                dev_info(&client->dev,
-                       "low voltage detected, date/time is not reliable.\n");
+                       "low voltage detected, check/replace RTC battery.\n");
+       }
+
+       if (buf[PCF2127_REG_SC] & PCF2127_OSF) {
+               /*
+                * no need clear the flag here,
+                * it will be cleared once the new date is saved
+                */
+               pcf2127->oscillator_failed = 1;
+               dev_warn(&client->dev,
+                        "oscillator stop detected, date/time is not reliable\n");
+               return -EINVAL;
        }
 
        dev_dbg(&client->dev,
@@ -93,6 +107,7 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
 
 static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 {
+       struct pcf2127 *pcf2127 = i2c_get_clientdata(client);
        unsigned char buf[8];
        int i = 0, err;
 
@@ -106,7 +121,7 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
        buf[i++] = PCF2127_REG_SC;
 
        /* hours, minutes and seconds */
-       buf[i++] = bin2bcd(tm->tm_sec);
+       buf[i++] = bin2bcd(tm->tm_sec); /* this will also clear OSF flag */
        buf[i++] = bin2bcd(tm->tm_min);
        buf[i++] = bin2bcd(tm->tm_hour);
        buf[i++] = bin2bcd(tm->tm_mday);
@@ -126,6 +141,9 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
                return -EIO;
        }
 
+       /* clear OSF flag in client data */
+       pcf2127->oscillator_failed = 0;
+
        return 0;
 }
 
@@ -138,7 +156,9 @@ static int pcf2127_rtc_ioctl(struct device *dev,
        switch (cmd) {
        case RTC_VL_READ:
                if (pcf2127->voltage_low)
-                       dev_info(dev, "low voltage detected, date/time is not reliable.\n");
+                       dev_info(dev, "low voltage detected, check/replace battery\n");
+               if (pcf2127->oscillator_failed)
+                       dev_info(dev, "oscillator stop detected, date/time is not reliable\n");
 
                if (copy_to_user((void __user *)arg, &pcf2127->voltage_low,
                                        sizeof(int)))