drm/amd/amdgpu:flush ttm delayed work before cancel_sync
[linux-2.6-microblaze.git] / drivers / rtc / rtc-pcf2127.c
index d13c20a..56c58b0 100644 (file)
@@ -10,7 +10,7 @@
  *
  * based on the other drivers in this same directory.
  *
- * Datasheet: http://cache.nxp.com/documents/data_sheet/PCF2127.pdf
+ * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
  */
 
 #include <linux/i2c.h>
 #define PCF2127_WD_VAL_MAX             255
 #define PCF2127_WD_VAL_DEFAULT         60
 
+/* Mask for currently enabled interrupts */
+#define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1)
+#define PCF2127_CTRL2_IRQ_MASK ( \
+               PCF2127_BIT_CTRL2_AF | \
+               PCF2127_BIT_CTRL2_WDTF | \
+               PCF2127_BIT_CTRL2_TSF2)
+
 struct pcf2127 {
        struct rtc_device *rtc;
        struct watchdog_device wdd;
        struct regmap *regmap;
+       time64_t ts;
+       bool ts_valid;
+       bool irq_enabled;
 };
 
 /*
@@ -434,23 +444,96 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        return pcf2127_rtc_alarm_irq_enable(dev, alrm->enabled);
 }
 
+/*
+ * This function reads ctrl2 register, caller is responsible for calling
+ * pcf2127_wdt_active_ping()
+ */
+static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
+{
+       struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
+       struct rtc_time tm;
+       int ret;
+       unsigned char data[25];
+
+       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
+                              sizeof(data));
+       if (ret) {
+               dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
+               return ret;
+       }
+
+       dev_dbg(dev,
+               "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
+               __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
+               data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
+               data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
+               data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
+               data[PCF2127_REG_TS_YR]);
+
+       tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
+       tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
+       tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
+       tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
+       /* TS_MO register (month) value range: 1-12 */
+       tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
+       tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
+       if (tm.tm_year < 70)
+               tm.tm_year += 100; /* assume we are in 1970...2069 */
+
+       ret = rtc_valid_tm(&tm);
+       if (ret) {
+               dev_err(dev, "Invalid timestamp. ret=%d\n", ret);
+               return ret;
+       }
+
+       *ts = rtc_tm_to_time64(&tm);
+       return 0;
+};
+
+static void pcf2127_rtc_ts_snapshot(struct device *dev)
+{
+       struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
+       int ret;
+
+       /* Let userspace read the first timestamp */
+       if (pcf2127->ts_valid)
+               return;
+
+       ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts);
+       if (!ret)
+               pcf2127->ts_valid = true;
+}
+
 static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
 {
        struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
-       unsigned int ctrl2 = 0;
+       unsigned int ctrl1, ctrl2;
        int ret = 0;
 
+       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
+       if (ret)
+               return IRQ_NONE;
+
        ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
        if (ret)
                return IRQ_NONE;
 
-       if (!(ctrl2 & PCF2127_BIT_CTRL2_AF))
+       if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
                return IRQ_NONE;
 
-       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
-                    ctrl2 & ~(PCF2127_BIT_CTRL2_AF | PCF2127_BIT_CTRL2_WDTF));
+       if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
+               pcf2127_rtc_ts_snapshot(dev);
+
+       if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
+               regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
+                       ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
+
+       if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
+               regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
+                       ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
 
-       rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF);
+       if (ctrl2 & PCF2127_BIT_CTRL2_AF)
+               rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF);
 
        pcf2127_wdt_active_ping(&pcf2127->wdd);
 
@@ -475,23 +558,27 @@ static ssize_t timestamp0_store(struct device *dev,
        struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
        int ret;
 
-       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
-                                PCF2127_BIT_CTRL1_TSF1, 0);
-       if (ret) {
-               dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
-               return ret;
-       }
+       if (pcf2127->irq_enabled) {
+               pcf2127->ts_valid = false;
+       } else {
+               ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
+                       PCF2127_BIT_CTRL1_TSF1, 0);
+               if (ret) {
+                       dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
+                       return ret;
+               }
 
-       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
-                                PCF2127_BIT_CTRL2_TSF2, 0);
-       if (ret) {
-               dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
-               return ret;
-       }
+               ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
+                       PCF2127_BIT_CTRL2_TSF2, 0);
+               if (ret) {
+                       dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
+                       return ret;
+               }
 
-       ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
-       if (ret)
-               return ret;
+               ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
+               if (ret)
+                       return ret;
+       }
 
        return count;
 };
@@ -500,50 +587,36 @@ static ssize_t timestamp0_show(struct device *dev,
                               struct device_attribute *attr, char *buf)
 {
        struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
-       struct rtc_time tm;
+       unsigned int ctrl1, ctrl2;
        int ret;
-       unsigned char data[25];
-
-       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
-                              sizeof(data));
-       if (ret) {
-               dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
-               return ret;
-       }
-
-       dev_dbg(dev,
-               "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, "
-               "ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
-               __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
-               data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
-               data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
-               data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
-               data[PCF2127_REG_TS_YR]);
+       time64_t ts;
+
+       if (pcf2127->irq_enabled) {
+               if (!pcf2127->ts_valid)
+                       return 0;
+               ts = pcf2127->ts;
+       } else {
+               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
+               if (ret)
+                       return 0;
 
-       ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
-       if (ret)
-               return ret;
+               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
+               if (ret)
+                       return 0;
 
-       if (!(data[PCF2127_REG_CTRL1] & PCF2127_BIT_CTRL1_TSF1) &&
-           !(data[PCF2127_REG_CTRL2] & PCF2127_BIT_CTRL2_TSF2))
-               return 0;
+               if (!(ctrl1 & PCF2127_BIT_CTRL1_TSF1) &&
+                   !(ctrl2 & PCF2127_BIT_CTRL2_TSF2))
+                       return 0;
 
-       tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
-       tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
-       tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
-       tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
-       /* TS_MO register (month) value range: 1-12 */
-       tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
-       tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
-       if (tm.tm_year < 70)
-               tm.tm_year += 100; /* assume we are in 1970...2069 */
-
-       ret = rtc_valid_tm(&tm);
-       if (ret)
-               return ret;
+               ret = pcf2127_rtc_ts_read(dev->parent, &ts);
+               if (ret)
+                       return 0;
 
-       return sprintf(buf, "%llu\n",
-                      (unsigned long long)rtc_tm_to_time64(&tm));
+               ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
+               if (ret)
+                       return ret;
+       }
+       return sprintf(buf, "%llu\n", (unsigned long long)ts);
 };
 
 static DEVICE_ATTR_RW(timestamp0);
@@ -594,6 +667,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
                        dev_err(dev, "failed to request alarm irq\n");
                        return ret;
                }
+               pcf2127->irq_enabled = true;
        }
 
        if (alarm_irq > 0 || device_property_read_bool(dev, "wakeup-source")) {