diff mbox

rtc: Fix m48t35 century bit handling

Message ID 200901162014.26421.mfuchs@ma-fu.de
State Rejected, archived
Headers show

Commit Message

Matthias Fuchs Jan. 16, 2009, 7:14 p.m. UTC
Hi,

I think the handling of the century bit in m48t35 RTCs is incorrect.
At least I got problems with recents years.

I must admit that I do not really understand magic lines like this:

	yrs -= 1970;
	if (yrs > 255)    /* They are unsigned */
		return -EINVAL;

	if (yrs > 169)
		return -EINVAL;

I updated the driver to handle the RTC's century bit correctly.
This was totally missing before. With my following patch the RTC
seems to be fine and interpretation of the registers meets the
chip's manual. Also the bootloader U-Boot handles the RTC in the
same way (which of course could also be wrong :-).

Signed-off-by: Matthias Fuchs <mfuchs@ma-fu.de>
---
 drivers/rtc/rtc-m48t35.c |   37 +++++++++++++------------------------
 1 files changed, 13 insertions(+), 24 deletions(-)

Comments

Alessandro Zummo Jan. 18, 2009, 2:16 p.m. UTC | #1
On Fri, 16 Jan 2009 20:14:25 +0100
Matthias Fuchs <mfuchs@ma-fu.de> wrote:

> 
> I think the handling of the century bit in m48t35 RTCs is incorrect.
> At least I got problems with recents years.
> 
> I must admit that I do not really understand magic lines like this:
> 
> 	yrs -= 1970;
> 	if (yrs > 255)    /* They are unsigned */
> 		return -EINVAL;
> 
> 	if (yrs > 169)
> 		return -EINVAL;
> 
> I updated the driver to handle the RTC's century bit correctly.
> This was totally missing before. With my following patch the RTC
> seems to be fine and interpretation of the registers meets the
> chip's manual. Also the bootloader U-Boot handles the RTC in the
> same way (which of course could also be wrong :-).

 Thomas, can you give it a look?



> Signed-off-by: Matthias Fuchs <mfuchs@ma-fu.de>
> ---
>  drivers/rtc/rtc-m48t35.c |   37 +++++++++++++------------------------
>  1 files changed, 13 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
> index 0b21975..05ca9f5 100644
> --- a/drivers/rtc/rtc-m48t35.c
> +++ b/drivers/rtc/rtc-m48t35.c
> @@ -20,7 +20,7 @@
>  #include <linux/bcd.h>
>  #include <linux/io.h>
>  
> -#define DRV_VERSION		"1.0"
> +#define DRV_VERSION		"1.1"
>  
>  struct m48t35_rtc {
>  	u8	pad[0x7ff8];    /* starts at 0x7ff8 */
> @@ -37,6 +37,9 @@ struct m48t35_rtc {
>  #define M48T35_RTC_SET		0x80
>  #define M48T35_RTC_READ		0x40
>  
> +#define M48T35_RTC_CEB		0x20
> +#define M48T35_RTC_CB		0x10
> +
>  struct m48t35_priv {
>  	struct rtc_device *rtc;
>  	struct m48t35_rtc __iomem *reg;
> @@ -48,7 +51,7 @@ struct m48t35_priv {
>  static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  {
>  	struct m48t35_priv *priv = dev_get_drvdata(dev);
> -	u8 control;
> +	u8 control, day;
>  
>  	/*
>  	 * Only the values that we read from the RTC are set. We leave
> @@ -62,6 +65,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  	tm->tm_sec = readb(&priv->reg->sec);
>  	tm->tm_min = readb(&priv->reg->min);
>  	tm->tm_hour = readb(&priv->reg->hour);
> +	day = readb(&priv->reg->day);
>  	tm->tm_mday = readb(&priv->reg->date);
>  	tm->tm_mon = readb(&priv->reg->month);
>  	tm->tm_year = readb(&priv->reg->year);
> @@ -75,13 +79,8 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  	tm->tm_mon = bcd2bin(tm->tm_mon);
>  	tm->tm_year = bcd2bin(tm->tm_year);
>  
> -	/*
> -	 * Account for differences between how the RTC uses the values
> -	 * and how they are defined in a struct rtc_time;
> -	 */
> -	tm->tm_year += 70;
> -	if (tm->tm_year <= 69)
> -		tm->tm_year += 100;
> +	tm->tm_year += (day & M48T35_RTC_CB) ? 100 : 0;
> +	tm->tm_wday = (day & 0x07) - 1;
>  
>  	tm->tm_mon--;
>  	return rtc_valid_tm(tm);
> @@ -90,7 +89,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
>  {
>  	struct m48t35_priv *priv = dev_get_drvdata(dev);
> -	unsigned char mon, day, hrs, min, sec;
> +	unsigned char mon, day, wday, hrs, min, sec;
>  	unsigned int yrs;
>  	u8 control;
>  
> @@ -100,26 +99,15 @@ static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
>  	hrs = tm->tm_hour;
>  	min = tm->tm_min;
>  	sec = tm->tm_sec;
> -
> -	if (yrs < 1970)
> -		return -EINVAL;
> -
> -	yrs -= 1970;
> -	if (yrs > 255)    /* They are unsigned */
> -		return -EINVAL;
> -
> -	if (yrs > 169)
> -		return -EINVAL;
> -
> -	if (yrs >= 100)
> -		yrs -= 100;
> +	wday = (tm->tm_wday + 1) |
> +		M48T35_RTC_CEB | ((yrs >= 2000) ? M48T35_RTC_CB : 0);
>  
>  	sec = bin2bcd(sec);
>  	min = bin2bcd(min);
>  	hrs = bin2bcd(hrs);
>  	day = bin2bcd(day);
>  	mon = bin2bcd(mon);
> -	yrs = bin2bcd(yrs);
> +	yrs = bin2bcd(yrs % 100);
>  
>  	spin_lock_irq(&priv->lock);
>  	control = readb(&priv->reg->control);
> @@ -127,6 +115,7 @@ static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
>  	writeb(yrs, &priv->reg->year);
>  	writeb(mon, &priv->reg->month);
>  	writeb(day, &priv->reg->date);
> +	writeb(wday, &priv->reg->day);
>  	writeb(hrs, &priv->reg->hour);
>  	writeb(min, &priv->reg->min);
>  	writeb(sec, &priv->reg->sec);
> -- 
> 1.5.6.3
> 
> 
> >
Alessandro Zummo April 2, 2009, 1:15 p.m. UTC | #2
On Fri, 16 Jan 2009 20:14:25 +0100
Matthias Fuchs <mfuchs@ma-fu.de> wrote:

> 
> I think the handling of the century bit in m48t35 RTCs is incorrect.
> At least I got problems with recents years.
> 
> I must admit that I do not really understand magic lines like this:
> 
> 	yrs -= 1970;
> 	if (yrs > 255)    /* They are unsigned */
> 		return -EINVAL;
> 
> 	if (yrs > 169)
> 		return -EINVAL;
> 
> I updated the driver to handle the RTC's century bit correctly.
> This was totally missing before. With my following patch the RTC
> seems to be fine and interpretation of the registers meets the
> chip's manual. Also the bootloader U-Boot handles the RTC in the
> same way (which of course could also be wrong :-).

 Thomas, can you give it a look?



> Signed-off-by: Matthias Fuchs <mfuchs@ma-fu.de>
> ---
>  drivers/rtc/rtc-m48t35.c |   37 +++++++++++++------------------------
>  1 files changed, 13 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
> index 0b21975..05ca9f5 100644
> --- a/drivers/rtc/rtc-m48t35.c
> +++ b/drivers/rtc/rtc-m48t35.c
> @@ -20,7 +20,7 @@
>  #include <linux/bcd.h>
>  #include <linux/io.h>
>  
> -#define DRV_VERSION		"1.0"
> +#define DRV_VERSION		"1.1"
>  
>  struct m48t35_rtc {
>  	u8	pad[0x7ff8];    /* starts at 0x7ff8 */
> @@ -37,6 +37,9 @@ struct m48t35_rtc {
>  #define M48T35_RTC_SET		0x80
>  #define M48T35_RTC_READ		0x40
>  
> +#define M48T35_RTC_CEB		0x20
> +#define M48T35_RTC_CB		0x10
> +
>  struct m48t35_priv {
>  	struct rtc_device *rtc;
>  	struct m48t35_rtc __iomem *reg;
> @@ -48,7 +51,7 @@ struct m48t35_priv {
>  static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  {
>  	struct m48t35_priv *priv = dev_get_drvdata(dev);
> -	u8 control;
> +	u8 control, day;
>  
>  	/*
>  	 * Only the values that we read from the RTC are set. We leave
> @@ -62,6 +65,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  	tm->tm_sec = readb(&priv->reg->sec);
>  	tm->tm_min = readb(&priv->reg->min);
>  	tm->tm_hour = readb(&priv->reg->hour);
> +	day = readb(&priv->reg->day);
>  	tm->tm_mday = readb(&priv->reg->date);
>  	tm->tm_mon = readb(&priv->reg->month);
>  	tm->tm_year = readb(&priv->reg->year);
> @@ -75,13 +79,8 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  	tm->tm_mon = bcd2bin(tm->tm_mon);
>  	tm->tm_year = bcd2bin(tm->tm_year);
>  
> -	/*
> -	 * Account for differences between how the RTC uses the values
> -	 * and how they are defined in a struct rtc_time;
> -	 */
> -	tm->tm_year += 70;
> -	if (tm->tm_year <= 69)
> -		tm->tm_year += 100;
> +	tm->tm_year += (day & M48T35_RTC_CB) ? 100 : 0;
> +	tm->tm_wday = (day & 0x07) - 1;
>  
>  	tm->tm_mon--;
>  	return rtc_valid_tm(tm);
> @@ -90,7 +89,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
>  static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
>  {
>  	struct m48t35_priv *priv = dev_get_drvdata(dev);
> -	unsigned char mon, day, hrs, min, sec;
> +	unsigned char mon, day, wday, hrs, min, sec;
>  	unsigned int yrs;
>  	u8 control;
>  
> @@ -100,26 +99,15 @@ static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
>  	hrs = tm->tm_hour;
>  	min = tm->tm_min;
>  	sec = tm->tm_sec;
> -
> -	if (yrs < 1970)
> -		return -EINVAL;
> -
> -	yrs -= 1970;
> -	if (yrs > 255)    /* They are unsigned */
> -		return -EINVAL;
> -
> -	if (yrs > 169)
> -		return -EINVAL;
> -
> -	if (yrs >= 100)
> -		yrs -= 100;
> +	wday = (tm->tm_wday + 1) |
> +		M48T35_RTC_CEB | ((yrs >= 2000) ? M48T35_RTC_CB : 0);
>  
>  	sec = bin2bcd(sec);
>  	min = bin2bcd(min);
>  	hrs = bin2bcd(hrs);
>  	day = bin2bcd(day);
>  	mon = bin2bcd(mon);
> -	yrs = bin2bcd(yrs);
> +	yrs = bin2bcd(yrs % 100);
>  
>  	spin_lock_irq(&priv->lock);
>  	control = readb(&priv->reg->control);
> @@ -127,6 +115,7 @@ static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
>  	writeb(yrs, &priv->reg->year);
>  	writeb(mon, &priv->reg->month);
>  	writeb(day, &priv->reg->date);
> +	writeb(wday, &priv->reg->day);
>  	writeb(hrs, &priv->reg->hour);
>  	writeb(min, &priv->reg->min);
>  	writeb(sec, &priv->reg->sec);
> -- 
> 1.5.6.3
> 
> 
> >
Thomas Bogendoerfer April 21, 2009, 9:27 p.m. UTC | #3
On Thu, Apr 02, 2009 at 03:15:35PM +0200, Alessandro Zummo wrote:
> On Fri, 16 Jan 2009 20:14:25 +0100
> Matthias Fuchs <mfuchs@ma-fu.de> wrote:
> 
> > 
> > I think the handling of the century bit in m48t35 RTCs is incorrect.
> > At least I got problems with recents years.
> > 
> > I must admit that I do not really understand magic lines like this:
> > 
> > 	yrs -= 1970;
> > 	if (yrs > 255)    /* They are unsigned */
> > 		return -EINVAL;
> > 
> > 	if (yrs > 169)
> > 		return -EINVAL;
> > 
> > I updated the driver to handle the RTC's century bit correctly.
> > This was totally missing before. With my following patch the RTC
> > seems to be fine and interpretation of the registers meets the
> > chip's manual. Also the bootloader U-Boot handles the RTC in the
> > same way (which of course could also be wrong :-).
> 
>  Thomas, can you give it a look?

this breaks rtc handling on my Origin 200 completely. Looks like
we need an abstraction for the special SGI year treatment and other
users of the rtc. Which platform uses this rtc ?

Thomas.
diff mbox

Patch

diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 0b21975..05ca9f5 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -20,7 +20,7 @@ 
 #include <linux/bcd.h>
 #include <linux/io.h>
 
-#define DRV_VERSION		"1.0"
+#define DRV_VERSION		"1.1"
 
 struct m48t35_rtc {
 	u8	pad[0x7ff8];    /* starts at 0x7ff8 */
@@ -37,6 +37,9 @@  struct m48t35_rtc {
 #define M48T35_RTC_SET		0x80
 #define M48T35_RTC_READ		0x40
 
+#define M48T35_RTC_CEB		0x20
+#define M48T35_RTC_CB		0x10
+
 struct m48t35_priv {
 	struct rtc_device *rtc;
 	struct m48t35_rtc __iomem *reg;
@@ -48,7 +51,7 @@  struct m48t35_priv {
 static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
 {
 	struct m48t35_priv *priv = dev_get_drvdata(dev);
-	u8 control;
+	u8 control, day;
 
 	/*
 	 * Only the values that we read from the RTC are set. We leave
@@ -62,6 +65,7 @@  static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
 	tm->tm_sec = readb(&priv->reg->sec);
 	tm->tm_min = readb(&priv->reg->min);
 	tm->tm_hour = readb(&priv->reg->hour);
+	day = readb(&priv->reg->day);
 	tm->tm_mday = readb(&priv->reg->date);
 	tm->tm_mon = readb(&priv->reg->month);
 	tm->tm_year = readb(&priv->reg->year);
@@ -75,13 +79,8 @@  static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
 	tm->tm_mon = bcd2bin(tm->tm_mon);
 	tm->tm_year = bcd2bin(tm->tm_year);
 
-	/*
-	 * Account for differences between how the RTC uses the values
-	 * and how they are defined in a struct rtc_time;
-	 */
-	tm->tm_year += 70;
-	if (tm->tm_year <= 69)
-		tm->tm_year += 100;
+	tm->tm_year += (day & M48T35_RTC_CB) ? 100 : 0;
+	tm->tm_wday = (day & 0x07) - 1;
 
 	tm->tm_mon--;
 	return rtc_valid_tm(tm);
@@ -90,7 +89,7 @@  static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
 static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
 {
 	struct m48t35_priv *priv = dev_get_drvdata(dev);
-	unsigned char mon, day, hrs, min, sec;
+	unsigned char mon, day, wday, hrs, min, sec;
 	unsigned int yrs;
 	u8 control;
 
@@ -100,26 +99,15 @@  static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
 	hrs = tm->tm_hour;
 	min = tm->tm_min;
 	sec = tm->tm_sec;
-
-	if (yrs < 1970)
-		return -EINVAL;
-
-	yrs -= 1970;
-	if (yrs > 255)    /* They are unsigned */
-		return -EINVAL;
-
-	if (yrs > 169)
-		return -EINVAL;
-
-	if (yrs >= 100)
-		yrs -= 100;
+	wday = (tm->tm_wday + 1) |
+		M48T35_RTC_CEB | ((yrs >= 2000) ? M48T35_RTC_CB : 0);
 
 	sec = bin2bcd(sec);
 	min = bin2bcd(min);
 	hrs = bin2bcd(hrs);
 	day = bin2bcd(day);
 	mon = bin2bcd(mon);
-	yrs = bin2bcd(yrs);
+	yrs = bin2bcd(yrs % 100);
 
 	spin_lock_irq(&priv->lock);
 	control = readb(&priv->reg->control);
@@ -127,6 +115,7 @@  static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
 	writeb(yrs, &priv->reg->year);
 	writeb(mon, &priv->reg->month);
 	writeb(day, &priv->reg->date);
+	writeb(wday, &priv->reg->day);
 	writeb(hrs, &priv->reg->hour);
 	writeb(min, &priv->reg->min);
 	writeb(sec, &priv->reg->sec);