Merge tag 'dm-4.8-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device...
[cascardo/linux.git] / drivers / rtc / rtc-s35390a.c
index f40afdd..5dab466 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/bitrev.h>
 #include <linux/bcd.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 
 #define S35390A_CMD_STATUS1    0
 #define S35390A_CMD_STATUS2    1
 #define S35390A_ALRM_BYTE_HOURS        1
 #define S35390A_ALRM_BYTE_MINS 2
 
+/* flags for STATUS1 */
 #define S35390A_FLAG_POC       0x01
 #define S35390A_FLAG_BLD       0x02
+#define S35390A_FLAG_INT2      0x04
 #define S35390A_FLAG_24H       0x40
 #define S35390A_FLAG_RESET     0x80
+
+/* flag for STATUS2 */
 #define S35390A_FLAG_TEST      0x01
 
 #define S35390A_INT2_MODE_MASK         0xF0
@@ -94,19 +99,63 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
        return 0;
 }
 
-static int s35390a_reset(struct s35390a *s35390a)
+/*
+ * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset.
+ * To keep the information if an irq is pending, pass the value read from
+ * STATUS1 to the caller.
+ */
+static int s35390a_reset(struct s35390a *s35390a, char *status1)
 {
-       char buf[1];
-
-       if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0)
-               return -EIO;
-
-       if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD)))
+       char buf;
+       int ret;
+       unsigned initcount = 0;
+
+       ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1);
+       if (ret < 0)
+               return ret;
+
+       if (*status1 & S35390A_FLAG_POC)
+               /*
+                * Do not communicate for 0.5 seconds since the power-on
+                * detection circuit is in operation.
+                */
+               msleep(500);
+       else if (!(*status1 & S35390A_FLAG_BLD))
+               /*
+                * If both POC and BLD are unset everything is fine.
+                */
                return 0;
 
-       buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H);
-       buf[0] &= 0xf0;
-       return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
+       /*
+        * At least one of POC and BLD are set, so reinitialise chip. Keeping
+        * this information in the hardware to know later that the time isn't
+        * valid is unfortunately not possible because POC and BLD are cleared
+        * on read. So the reset is best done now.
+        *
+        * The 24H bit is kept over reset, so set it already here.
+        */
+initialize:
+       *status1 = S35390A_FLAG_24H;
+       buf = S35390A_FLAG_RESET | S35390A_FLAG_24H;
+       ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
+
+       if (ret < 0)
+               return ret;
+
+       ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
+       if (ret < 0)
+               return ret;
+
+       if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) {
+               /* Try up to five times to reset the chip */
+               if (initcount < 5) {
+                       ++initcount;
+                       goto initialize;
+               } else
+                       return -EIO;
+       }
+
+       return 1;
 }
 
 static int s35390a_disable_test_mode(struct s35390a *s35390a)
@@ -217,12 +266,12 @@ static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
                alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday,
                alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday);
 
-       /* disable interrupt */
+       /* disable interrupt (which deasserts the irq line) */
        err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
        if (err < 0)
                return err;
 
-       /* clear pending interrupt, if any */
+       /* clear pending interrupt (in STATUS1 only), if any */
        err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts));
        if (err < 0)
                return err;
@@ -242,6 +291,8 @@ static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
 
        if (alm->time.tm_wday != -1)
                buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80;
+       else
+               buf[S35390A_ALRM_BYTE_WDAY] = 0;
 
        buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
                        alm->time.tm_hour) | 0x80;
@@ -269,23 +320,43 @@ static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
        if (err < 0)
                return err;
 
-       if (bitrev8(sts) != S35390A_INT2_MODE_ALARM)
-               return -EINVAL;
+       if ((bitrev8(sts) & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) {
+               /*
+                * When the alarm isn't enabled, the register to configure
+                * the alarm time isn't accessible.
+                */
+               alm->enabled = 0;
+               return 0;
+       } else {
+               alm->enabled = 1;
+       }
 
        err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
        if (err < 0)
                return err;
 
        /* This chip returns the bits of each byte in reverse order */
-       for (i = 0; i < 3; ++i) {
+       for (i = 0; i < 3; ++i)
                buf[i] = bitrev8(buf[i]);
-               buf[i] &= ~0x80;
-       }
 
-       alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]);
-       alm->time.tm_hour = s35390a_reg2hr(s35390a,
-                                               buf[S35390A_ALRM_BYTE_HOURS]);
-       alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]);
+       /*
+        * B0 of the three matching registers is an enable flag. Iff it is set
+        * the configured value is used for matching.
+        */
+       if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80)
+               alm->time.tm_wday =
+                       bcd2bin(buf[S35390A_ALRM_BYTE_WDAY] & ~0x80);
+
+       if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80)
+               alm->time.tm_hour =
+                       s35390a_reg2hr(s35390a,
+                                      buf[S35390A_ALRM_BYTE_HOURS] & ~0x80);
+
+       if (buf[S35390A_ALRM_BYTE_MINS] & 0x80)
+               alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80);
+
+       /* alarm triggers always at s=0 */
+       alm->time.tm_sec = 0;
 
        dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n",
                        __func__, alm->time.tm_min, alm->time.tm_hour,
@@ -327,11 +398,11 @@ static struct i2c_driver s35390a_driver;
 static int s35390a_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
-       int err;
+       int err, err_reset;
        unsigned int i;
        struct s35390a *s35390a;
        struct rtc_time tm;
-       char buf[1];
+       char buf, status1;
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
                err = -ENODEV;
@@ -360,29 +431,35 @@ static int s35390a_probe(struct i2c_client *client,
                }
        }
 
-       err = s35390a_reset(s35390a);
-       if (err < 0) {
+       err_reset = s35390a_reset(s35390a, &status1);
+       if (err_reset < 0) {
+               err = err_reset;
                dev_err(&client->dev, "error resetting chip\n");
                goto exit_dummy;
        }
 
-       err = s35390a_disable_test_mode(s35390a);
-       if (err < 0) {
-               dev_err(&client->dev, "error disabling test mode\n");
-               goto exit_dummy;
-       }
-
-       err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
-       if (err < 0) {
-               dev_err(&client->dev, "error checking 12/24 hour mode\n");
-               goto exit_dummy;
-       }
-       if (buf[0] & S35390A_FLAG_24H)
+       if (status1 & S35390A_FLAG_24H)
                s35390a->twentyfourhour = 1;
        else
                s35390a->twentyfourhour = 0;
 
-       if (s35390a_get_datetime(client, &tm) < 0)
+       if (status1 & S35390A_FLAG_INT2) {
+               /* disable alarm (and maybe test mode) */
+               buf = 0;
+               err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1);
+               if (err < 0) {
+                       dev_err(&client->dev, "error disabling alarm");
+                       goto exit_dummy;
+               }
+       } else {
+               err = s35390a_disable_test_mode(s35390a);
+               if (err < 0) {
+                       dev_err(&client->dev, "error disabling test mode\n");
+                       goto exit_dummy;
+               }
+       }
+
+       if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0)
                dev_warn(&client->dev, "clock needs to be set\n");
 
        device_set_wakeup_capable(&client->dev, 1);
@@ -395,6 +472,10 @@ static int s35390a_probe(struct i2c_client *client,
                err = PTR_ERR(s35390a->rtc);
                goto exit_dummy;
        }
+
+       if (status1 & S35390A_FLAG_INT2)
+               rtc_update_irq(s35390a->rtc, 1, RTC_AF);
+
        return 0;
 
 exit_dummy: