The hardware has neither a century register nor a century warp around bit.

However it does have a single byte of non volatile RAM.

Use this to provide full century and wrap around support by storing:
* The current century
* The current half century (lower=00-49, upper=50-99)

If the byte is not set the 21st century is assumed.

Signed-off-by: Martin Fuzzey <mfuz...@parkeon.com>
---
 drivers/rtc/rtc-pcf85263.c |  105 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 103 insertions(+), 2 deletions(-)

diff --git a/drivers/rtc/rtc-pcf85263.c b/drivers/rtc/rtc-pcf85263.c
index 997742d..7b55d92 100644
--- a/drivers/rtc/rtc-pcf85263.c
+++ b/drivers/rtc/rtc-pcf85263.c
@@ -83,6 +83,10 @@
 
 #define PCF85263_HR_PM         BIT(5)
 
+/* Our data stored in the RAM byte */
+#define PCF85263_STATE_CENTURY_MASK            0x7f
+#define PCF85263_STATE_UPPER_HALF_CENTURY      BIT(7)
+
 enum pcf85263_irqpin {
        PCF85263_IRQPIN_NONE,
        PCF85263_IRQPIN_INTA,
@@ -101,6 +105,8 @@ struct pcf85263 {
        struct regmap *regmap;
        enum pcf85263_irqpin irq_pin;
        int irq;
+       u8 century;             /* 1 = 1900 2 = 2000, ... */
+       bool century_half;      /* false = 0-49, true=50-99 */
        bool mode_12h;
 };
 
@@ -136,6 +142,85 @@ static int pcf85263_bin24h_to_bcd12h(int hr24)
        return bin2bcd(hr12) | pm ? 0 : PCF85263_HR_PM;
 }
 
+static inline bool pcf85263_century_half(int year)
+{
+       return (year % 100) >= 50;
+}
+
+/*
+ * Since the hardware only has a year range of 00 to 99 we use
+ * the ram byte to store the century. 1=1900, 2=2000, 3=2100
+ * A value of zero is assumed to be 2000
+ *
+ * Set the ram byte when we set the clock which lets us use any
+ * century supported by linux (tm_year=0 => 1900)
+ *
+ * Unfortunately the hardware has no wrap around flag so fix it
+ * by also storing a flag indicating if the year is in the
+ * upper or lower half of the century.
+ */
+static int pcf85263_update_ram_byte(struct pcf85263 *pcf85263)
+{
+       u8 val = pcf85263->century & PCF85263_STATE_CENTURY_MASK;
+
+       if (pcf85263->century_half)
+               val |= PCF85263_STATE_UPPER_HALF_CENTURY;
+
+       return regmap_write(pcf85263->regmap, PCF85263_REG_RAM_BYTE, val);
+}
+
+static int pcf85263_read_ram_byte(struct pcf85263 *pcf85263)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_read(pcf85263->regmap, PCF85263_REG_RAM_BYTE, &regval);
+       if (ret)
+               return ret;
+
+       pcf85263->century = regval & PCF85263_STATE_CENTURY_MASK;
+       pcf85263->century_half = !!(regval & PCF85263_STATE_UPPER_HALF_CENTURY);
+
+       if (!pcf85263->century) { /* Not valid => not initialised yet */
+               int year;
+
+               ret = regmap_read(pcf85263->regmap,
+                                 PCF85263_REG_RTC_YR, &regval);
+               if (ret)
+                       return ret;
+
+               pcf85263->century = 2;
+               year = bcd2bin(regval) + 1900 + (pcf85263->century - 1) * 100;
+               pcf85263->century_half = pcf85263_century_half(year);
+
+               dev_warn(pcf85263->dev, "No century in NVRAM - assume %d\n",
+                        year);
+       }
+
+       return 0;
+}
+
+/*
+ * Detect year overflow by comparing the half (upper, lower) of
+ * the current year with the half the last time we read it
+ */
+static int pcf85263_update_century(struct pcf85263 *pcf85263, int year)
+{
+       bool cur_century_half;
+
+       cur_century_half = pcf85263_century_half(year);
+
+       if (cur_century_half == pcf85263->century_half)
+               return 0;
+
+       if (!cur_century_half) /* Year has wrapped around */
+               pcf85263->century++;
+
+       pcf85263->century_half = cur_century_half;
+
+       return pcf85263_update_ram_byte(pcf85263);
+}
+
 static int pcf85263_read_time(struct device *dev, struct rtc_time *tm)
 {
        struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
@@ -169,7 +254,11 @@ static int pcf85263_read_time(struct device *dev, struct 
rtc_time *tm)
        tm->tm_mon  = bcd2bin(regs[PCF85263_REG_RTC_MO - first]) - 1;
        tm->tm_year = bcd2bin(regs[PCF85263_REG_RTC_YR - first]);
 
-       tm->tm_year += 100;  /* Assume 21st century */
+       ret = pcf85263_update_century(pcf85263, tm->tm_year);
+       if (ret)
+               return ret;
+
+       tm->tm_year += (pcf85263->century - 1) * 100;
 
        return 0;
 }
@@ -214,7 +303,14 @@ static int pcf85263_set_time(struct device *dev, struct 
rtc_time *tm)
        }
 
        /* Start it again */
-       return regmap_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, 0);
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, 0);
+       if (ret)
+               return ret;
+
+       pcf85263->century = (tm->tm_year / 100) + 1;
+       pcf85263->century_half = pcf85263_century_half(tm->tm_year);
+
+       return pcf85263_update_ram_byte(pcf85263);
 }
 
 static int pcf85263_enable_alarm(struct pcf85263 *pcf85263, bool enable)
@@ -422,6 +518,11 @@ static int pcf85263_init_hw(struct pcf85263 *pcf85263)
                return ret;
        }
 
+       /* Get our persistent state from the ram byte */
+       ret = pcf85263_read_ram_byte(pcf85263);
+       if (ret < 0)
+               return ret;
+
        /* Determine 12/24H mode */
        ret = regmap_read(pcf85263->regmap, PCF85263_REG_OSC, &regval);
        if (ret)

-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups 
"rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to rtc-linux+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to