rtc: ds1343: rework interrupt handling
authorAlexandre Belloni <alexandre.belloni@bootlin.com>
Sat, 19 Oct 2019 20:49:40 +0000 (22:49 +0200)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Sat, 19 Oct 2019 20:56:09 +0000 (22:56 +0200)
Rework the interrupt handling to avoid caching the values as the core is
already doing that. The core also always ensures the rtc_time passed for
the alarm is fully populated.

The only trick is in read_alarm where status needs to be read before the
alarm registers to ensure the potential irq is not cleared.

Link: https://lore.kernel.org/r/20191019204941.6203-8-alexandre.belloni@bootlin.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/rtc/rtc-ds1343.c

index fc6d77f..131f5fd 100644 (file)
@@ -78,12 +78,7 @@ struct ds1343_priv {
        struct spi_device *spi;
        struct rtc_device *rtc;
        struct regmap *map;
-       unsigned int irqen;
        int irq;
-       int alarm_sec;
-       int alarm_min;
-       int alarm_hour;
-       int alarm_mday;
 };
 
 static ssize_t ds1343_show_glitchfilter(struct device *dev,
@@ -239,93 +234,66 @@ static int ds1343_set_time(struct device *dev, struct rtc_time *dt)
                                 buf, sizeof(buf));
 }
 
-static int ds1343_update_alarm(struct device *dev)
+static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
        struct ds1343_priv *priv = dev_get_drvdata(dev);
-       unsigned int control, stat;
        unsigned char buf[4];
-       int res = 0;
+       unsigned int val;
+       int res;
 
-       res = regmap_read(priv->map, DS1343_CONTROL_REG, &control);
-       if (res)
-               return res;
+       if (priv->irq <= 0)
+               return -EINVAL;
 
-       res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
+       res = regmap_read(priv->map, DS1343_STATUS_REG, &val);
        if (res)
                return res;
 
-       control &= ~(DS1343_A0IE);
-       stat &= ~(DS1343_IRQF0);
+       alarm->pending = !!(val & DS1343_IRQF0);
 
-       res = regmap_write(priv->map, DS1343_CONTROL_REG, control);
+       res = regmap_read(priv->map, DS1343_CONTROL_REG, &val);
        if (res)
                return res;
+       alarm->enabled = !!(val & DS1343_A0IE);
 
-       res = regmap_write(priv->map, DS1343_STATUS_REG, stat);
+       res = regmap_bulk_read(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
        if (res)
                return res;
 
-       buf[0] = priv->alarm_sec < 0 || (priv->irqen & RTC_UF) ?
-               0x80 : bin2bcd(priv->alarm_sec) & 0x7F;
-       buf[1] = priv->alarm_min < 0 || (priv->irqen & RTC_UF) ?
-               0x80 : bin2bcd(priv->alarm_min) & 0x7F;
-       buf[2] = priv->alarm_hour < 0 || (priv->irqen & RTC_UF) ?
-               0x80 : bin2bcd(priv->alarm_hour) & 0x3F;
-       buf[3] = priv->alarm_mday < 0 || (priv->irqen & RTC_UF) ?
-               0x80 : bin2bcd(priv->alarm_mday) & 0x7F;
-
-       res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
-       if (res)
-               return res;
+       alarm->time.tm_sec = bcd2bin(buf[0]) & 0x7f;
+       alarm->time.tm_min = bcd2bin(buf[1]) & 0x7f;
+       alarm->time.tm_hour = bcd2bin(buf[2]) & 0x3f;
+       alarm->time.tm_mday = bcd2bin(buf[3]) & 0x3f;
 
-       if (priv->irqen) {
-               control |= DS1343_A0IE;
-               res = regmap_write(priv->map, DS1343_CONTROL_REG, control);
-       }
-
-       return res;
+       return 0;
 }
 
-static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
        struct ds1343_priv *priv = dev_get_drvdata(dev);
-       int res;
-       unsigned int stat;
+       unsigned char buf[4];
+       int res = 0;
 
        if (priv->irq <= 0)
                return -EINVAL;
 
-       res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
+       res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, DS1343_A0IE, 0);
        if (res)
                return res;
 
-       alarm->enabled = !!(priv->irqen & RTC_AF);
-       alarm->pending = !!(stat & DS1343_IRQF0);
+       buf[0] = bin2bcd(alarm->time.tm_sec);
+       buf[1] = bin2bcd(alarm->time.tm_min);
+       buf[2] = bin2bcd(alarm->time.tm_hour);
+       buf[3] = bin2bcd(alarm->time.tm_mday);
 
-       alarm->time.tm_sec = priv->alarm_sec < 0 ? 0 : priv->alarm_sec;
-       alarm->time.tm_min = priv->alarm_min < 0 ? 0 : priv->alarm_min;
-       alarm->time.tm_hour = priv->alarm_hour < 0 ? 0 : priv->alarm_hour;
-       alarm->time.tm_mday = priv->alarm_mday < 0 ? 0 : priv->alarm_mday;
-
-       return 0;
-}
-
-static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
-       struct ds1343_priv *priv = dev_get_drvdata(dev);
-
-       if (priv->irq <= 0)
-               return -EINVAL;
-
-       priv->alarm_sec = alarm->time.tm_sec;
-       priv->alarm_min = alarm->time.tm_min;
-       priv->alarm_hour = alarm->time.tm_hour;
-       priv->alarm_mday = alarm->time.tm_mday;
+       res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
+       if (res)
+               return res;
 
        if (alarm->enabled)
-               priv->irqen |= RTC_AF;
+               res = regmap_update_bits(priv->map, DS1343_CONTROL_REG,
+                                        DS1343_A0IE, DS1343_A0IE);
 
-       return ds1343_update_alarm(dev);
+       return res;
 }
 
 static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled)
@@ -335,18 +303,14 @@ static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled)
        if (priv->irq <= 0)
                return -EINVAL;
 
-       if (enabled)
-               priv->irqen |= RTC_AF;
-       else
-               priv->irqen &= ~RTC_AF;
-
-       return ds1343_update_alarm(dev);
+       return regmap_update_bits(priv->map, DS1343_CONTROL_REG,
+                                 DS1343_A0IE, enabled ? DS1343_A0IE : 0);
 }
 
 static irqreturn_t ds1343_thread(int irq, void *dev_id)
 {
        struct ds1343_priv *priv = dev_id;
-       unsigned int stat, control;
+       unsigned int stat;
        int res = 0;
 
        rtc_lock(priv->rtc);
@@ -359,14 +323,10 @@ static irqreturn_t ds1343_thread(int irq, void *dev_id)
                stat &= ~DS1343_IRQF0;
                regmap_write(priv->map, DS1343_STATUS_REG, stat);
 
-               res = regmap_read(priv->map, DS1343_CONTROL_REG, &control);
-               if (res)
-                       goto out;
-
-               control &= ~DS1343_A0IE;
-               regmap_write(priv->map, DS1343_CONTROL_REG, control);
-
                rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF);
+
+               regmap_update_bits(priv->map, DS1343_CONTROL_REG,
+                                  DS1343_A0IE, 0);
        }
 
 out:
@@ -480,8 +440,6 @@ static int ds1343_remove(struct spi_device *spi)
        struct ds1343_priv *priv = spi_get_drvdata(spi);
 
        if (spi->irq) {
-               priv->irqen &= ~RTC_AF;
-
                dev_pm_clear_wake_irq(&spi->dev);
                device_init_wakeup(&spi->dev, false);
                devm_free_irq(&spi->dev, spi->irq, priv);