All of lore.kernel.org
 help / color / mirror / Atom feed
From: Martin Fuzzey <mfuzzey@parkeon.com>
To: Alessandro Zummo <a.zummo@towertech.it>,
	Rob Herring <robh+dt@kernel.org>,
	Alexandre Belloni <alexandre.belloni@free-electrons.com>
Cc: devicetree@vger.kernel.org, rtc-linux@googlegroups.com
Subject: [rtc-linux] [PATCH 3/3] rtc: pcf85263: Support multiple centuries
Date: Mon, 01 Aug 2016 17:50:36 +0200	[thread overview]
Message-ID: <20160801155036.32232.7242.stgit@localhost> (raw)
In-Reply-To: <20160801155029.32232.56063.stgit@localhost>

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 <mfuzzey@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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

WARNING: multiple messages have this Message-ID (diff)
From: Martin Fuzzey <mfuzzey-mB3Nsq4MPf1BDgjK7y7TUQ@public.gmane.org>
To: Alessandro Zummo
	<a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org>,
	Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
	Alexandre Belloni
	<alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
Subject: [PATCH 3/3] rtc: pcf85263: Support multiple centuries
Date: Mon, 01 Aug 2016 17:50:36 +0200	[thread overview]
Message-ID: <20160801155036.32232.7242.stgit@localhost> (raw)
In-Reply-To: <20160801155029.32232.56063.stgit@localhost>

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 <mfuzzey-mB3Nsq4MPf1BDgjK7y7TUQ@public.gmane.org>
---
 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+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

  parent reply	other threads:[~2016-08-01 15:50 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-01 15:50 [rtc-linux] [PATCH 0/3] rtc: Add pcf85263 Martin Fuzzey
2016-08-01 15:50 ` Martin Fuzzey
2016-08-01 15:50 ` [rtc-linux] [PATCH 1/3] dt-binding: rtc Add DT binding for NXP 85263 RTC Martin Fuzzey
2016-08-01 15:50   ` Martin Fuzzey
2016-09-21 20:43   ` [rtc-linux] " Alexandre Belloni
2016-09-21 20:43     ` Alexandre Belloni
2016-08-01 15:50 ` [rtc-linux] [PATCH 2/3] rtc: pcf85263: Basic driver Martin Fuzzey
2016-08-01 15:50   ` Martin Fuzzey
2016-09-21 21:35   ` [rtc-linux] " Alexandre Belloni
2016-09-21 21:35     ` Alexandre Belloni
2017-03-15 17:28     ` [rtc-linux] " leonardo.romor
2016-08-01 15:50 ` Martin Fuzzey [this message]
2016-08-01 15:50   ` [PATCH 3/3] rtc: pcf85263: Support multiple centuries Martin Fuzzey
2016-09-21 21:33   ` [rtc-linux] " Alexandre Belloni
2016-09-21 21:33     ` Alexandre Belloni

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160801155036.32232.7242.stgit@localhost \
    --to=mfuzzey@parkeon.com \
    --cc=a.zummo@towertech.it \
    --cc=alexandre.belloni@free-electrons.com \
    --cc=devicetree@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=rtc-linux@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.