Patchwork rtc: add support for the M48T201 chip to rtc-m48t59.c

login
register
mail settings
Submitter Andrew Klossner
Date July 10, 2009, 6:57 p.m.
Message ID <200907101857.n6AIvP7J001896@pogo.cesa.opbu.xerox.com>
Download mbox | patch
Permalink /patch/29686/
State Under Review
Headers show

Comments

Andrew Klossner - July 10, 2009, 6:57 p.m.
rtc-m48t59: add support for the M48T201 chip.
    
Also:
Fix a bug in m48t59_rtc_probe failure return that would iounmap an
  address that hadn't been ioremapped.
Fix a Y2K bug for the M48T59: 31-Dec-1999 would step to 1-Jan-1900.
Fix diagnostic outputs to show the date correctly.
Add a FIXME comment describing a likely bug in alarm-setting code.

Signed-off-by: Andrew Klossner <andrew@cesa.opbu.xerox.com>

---

--~--~---------~--~----~------------~-------~--~----~
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.
-~----------~----~----~----~------~----~------~--~---
Alessandro Zummo - Nov. 18, 2009, 11:51 a.m.
On Fri, 10 Jul 2009 11:57:25 -0700
Andrew Klossner <andrew@cesa.opbu.xerox.com> wrote:

 Mark, any thoughts on this?

> 
> rtc-m48t59: add support for the M48T201 chip.
>     
> Also:
> Fix a bug in m48t59_rtc_probe failure return that would iounmap an
>   address that hadn't been ioremapped.
> Fix a Y2K bug for the M48T59: 31-Dec-1999 would step to 1-Jan-1900.
> Fix diagnostic outputs to show the date correctly.
> Add a FIXME comment describing a likely bug in alarm-setting code.
> 
> Signed-off-by: Andrew Klossner <andrew@cesa.opbu.xerox.com>
> 
> ---
> diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
> index 33921a6..c06e88f 100644
> --- a/drivers/rtc/rtc-m48t59.c
> +++ b/drivers/rtc/rtc-m48t59.c
> @@ -82,8 +82,10 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
>  	tm->tm_mday	= bcd2bin(M48T59_READ(M48T59_MDAY));
>  
>  	val = M48T59_READ(M48T59_WDAY);
> -	if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
> -	    (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
> +	if (pdata->type == M48T59RTC_TYPE_M48T201)
> +		tm->tm_year += (bcd2bin(M48T59_READ(M48T59_CENTURY))-19) * 100;
> +	else if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
> +		 (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
>  		dev_dbg(dev, "Century bit is enabled\n");
>  		tm->tm_year += 100;	/* one century */
>  	}
> @@ -101,8 +103,8 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
>  	M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
>  	spin_unlock_irqrestore(&m48t59->lock, flags);
>  
> -	dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
> -		tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
> +	dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d:%02d:%02d\n",
> +		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
>  		tm->tm_hour, tm->tm_min, tm->tm_sec);
>  	return 0;
>  }
> @@ -121,8 +123,8 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  	year -= 68;
>  #endif
>  
> -	dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n",
> -		year + 1900, tm->tm_mon, tm->tm_mday,
> +	dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d:%02d:%02d\n",
> +		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
>  		tm->tm_hour, tm->tm_min, tm->tm_sec);
>  
>  	if (year < 0)
> @@ -139,11 +141,17 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  	/* tm_mon is 0-11 */
>  	M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
>  	M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR);
> -
> -	if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100))
> -		val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
> +	if (pdata->type == M48T59RTC_TYPE_M48T59) {
> +		/* The Century Enable Bit must be set regardless of whether
> +		 * the Century Bit is set, otherwise the clock would advance
> +		 * from 31-Dec-1999 to 1-Jan-1900.
> +		 */
> +		val = M48T59_WDAY_CEB | (year >= 100 ? M48T59_WDAY_CB : 0);
> +	}
>  	val |= (bin2bcd(tm->tm_wday) & 0x07);
>  	M48T59_WRITE(val, M48T59_WDAY);
> +	if (pdata->type == M48T59RTC_TYPE_M48T201)
> +		M48T59_WRITE(bin2bcd(year / 100 + 19), M48T59_CENTURY);
>  
>  	/* Clear the WRITE bit */
>  	M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
> @@ -177,10 +185,15 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	tm->tm_year += 68;
>  #endif
>  	/* tm_mon is 0-11 */
> -	tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
> +	if (pdata->type == M48T59RTC_TYPE_M48T201)
> +		tm->tm_mon = bcd2bin(M48T59_READ(M48T59_INTR) & 0x1f) - 1;
> +	else
> +		tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
>  
>  	val = M48T59_READ(M48T59_WDAY);
> -	if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB))
> +	if (pdata->type == M48T59RTC_TYPE_M48T201)
> +		tm->tm_year += (bcd2bin(M48T59_READ(M48T59_CENTURY))-19) * 100;
> +	else if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB))
>  		tm->tm_year += 100;	/* one century */
>  
>  	tm->tm_mday = bcd2bin(M48T59_READ(M48T59_ALARM_DATE));
> @@ -192,8 +205,8 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
>  	spin_unlock_irqrestore(&m48t59->lock, flags);
>  
> -	dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
> -		tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
> +	dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d:%02d:%02d\n",
> +		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
>  		tm->tm_hour, tm->tm_min, tm->tm_sec);
>  	return 0;
>  }
> @@ -228,6 +241,10 @@ static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	 */
>  	mday = tm->tm_mday;
>  	mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
> +	/*
> +	 * FIXME: this is wrong when we're setting an alarm to go off after
> +	 * midnight tonight.  The alarm will never happen.
> +	 */
>  	if (mday == 0xff)
>  		mday = M48T59_READ(M48T59_MDAY);
>  
> @@ -244,6 +261,10 @@ static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	/* Issue the WRITE command */
>  	M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
>  
> +	if (pdata->type == M48T59RTC_TYPE_M48T201)
> +		M48T59_WRITE((M48T59_READ(M48T59_INTR) & 0xe0) |
> +			     (bin2bcd(tm->tm_mon + 1) & 0x1f),
> +			     M48T59_INTR);
>  	M48T59_WRITE(mday, M48T59_ALARM_DATE);
>  	M48T59_WRITE(hour, M48T59_ALARM_HOUR);
>  	M48T59_WRITE(min, M48T59_ALARM_MIN);
> @@ -253,8 +274,8 @@ static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
>  	spin_unlock_irqrestore(&m48t59->lock, flags);
>  
> -	dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
> -		year + 1900, tm->tm_mon, tm->tm_mday,
> +	dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d:%02d:%02d\n",
> +		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
>  		tm->tm_hour, tm->tm_min, tm->tm_sec);
>  	return 0;
>  }
> @@ -272,12 +293,16 @@ static int m48t59_rtc_ioctl(struct device *dev, unsigned int cmd,
>  	int ret = 0;
>  
>  	spin_lock_irqsave(&m48t59->lock, flags);
> +	/*
> +	 * Preserve the other fields in M48T59_INTR in case it's an M48T201
> +	 * and they contain the alarm month.
> +	 */
>  	switch (cmd) {
>  	case RTC_AIE_OFF:	/* alarm interrupt off */
> -		M48T59_WRITE(0x00, M48T59_INTR);
> +		M48T59_CLEAR_BITS(M48T59_INTR_AFE, M48T59_INTR);
>  		break;
>  	case RTC_AIE_ON:	/* alarm interrupt on */
> -		M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR);
> +		M48T59_SET_BITS(M48T59_INTR_AFE, M48T59_INTR);
>  		break;
>  	default:
>  		ret = -ENOIOCTLCMD;
> @@ -475,6 +500,11 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
>  		ops = &m48t02_rtc_ops;
>  		pdata->offset = 0x1ff0;
>  		break;
> +	case M48T59RTC_TYPE_M48T201:
> +		name = "m48t201";
> +		ops = &m48t59_rtc_ops;
> +		pdata->offset = 0x7fff0;
> +		break;
>  	default:
>  		dev_err(&pdev->dev, "Unknown RTC type\n");
>  		ret = -ENODEV;
> @@ -502,7 +532,7 @@ out:
>  		rtc_device_unregister(m48t59->rtc);
>  	if (m48t59->irq != NO_IRQ)
>  		free_irq(m48t59->irq, &pdev->dev);
> -	if (m48t59->ioaddr)
> +	if (m48t59->ioaddr && !pdata->ioaddr)
>  		iounmap(m48t59->ioaddr);
>  	if (m48t59)
>  		kfree(m48t59);
> diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h
> index 6fc9614..8e2df6f 100644
> --- a/include/linux/rtc/m48t59.h
> +++ b/include/linux/rtc/m48t59.h
> @@ -38,7 +38,7 @@
>  #define M48T59_ALARM_HOUR	0x4
>  #define M48T59_ALARM_MIN	0x3
>  #define M48T59_ALARM_SEC	0x2
> -#define M48T59_UNUSED		0x1
> +#define M48T59_CENTURY		0x1	/* Century (M48T201 only) */
>  #define M48T59_FLAGS		0x0
>  #define M48T59_FLAGS_WDT		0x80	/* watchdog timer expired */
>  #define M48T59_FLAGS_AF			0x40	/* alarm */
> @@ -47,6 +47,7 @@
>  #define M48T59RTC_TYPE_M48T59	0 /* to keep compatibility */
>  #define M48T59RTC_TYPE_M48T02	1
>  #define M48T59RTC_TYPE_M48T08	2
> +#define M48T59RTC_TYPE_M48T201	3
>  
>  struct m48t59_plat_data {
>  	/* The method to access M48T59 registers */
> 
> --~--~---------~--~----~------------~-------~--~----~
> 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.
> -~----------~----~----~----~------~----~------~--~---
>

Patch

diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index 33921a6..c06e88f 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -82,8 +82,10 @@  static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
 	tm->tm_mday	= bcd2bin(M48T59_READ(M48T59_MDAY));
 
 	val = M48T59_READ(M48T59_WDAY);
-	if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
-	    (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
+	if (pdata->type == M48T59RTC_TYPE_M48T201)
+		tm->tm_year += (bcd2bin(M48T59_READ(M48T59_CENTURY))-19) * 100;
+	else if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
+		 (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
 		dev_dbg(dev, "Century bit is enabled\n");
 		tm->tm_year += 100;	/* one century */
 	}
@@ -101,8 +103,8 @@  static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
 	M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
 	spin_unlock_irqrestore(&m48t59->lock, flags);
 
-	dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
-		tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
+	dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d:%02d:%02d\n",
+		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
 		tm->tm_hour, tm->tm_min, tm->tm_sec);
 	return 0;
 }
@@ -121,8 +123,8 @@  static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	year -= 68;
 #endif
 
-	dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n",
-		year + 1900, tm->tm_mon, tm->tm_mday,
+	dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d:%02d:%02d\n",
+		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
 		tm->tm_hour, tm->tm_min, tm->tm_sec);
 
 	if (year < 0)
@@ -139,11 +141,17 @@  static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	/* tm_mon is 0-11 */
 	M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
 	M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR);
-
-	if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100))
-		val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
+	if (pdata->type == M48T59RTC_TYPE_M48T59) {
+		/* The Century Enable Bit must be set regardless of whether
+		 * the Century Bit is set, otherwise the clock would advance
+		 * from 31-Dec-1999 to 1-Jan-1900.
+		 */
+		val = M48T59_WDAY_CEB | (year >= 100 ? M48T59_WDAY_CB : 0);
+	}
 	val |= (bin2bcd(tm->tm_wday) & 0x07);
 	M48T59_WRITE(val, M48T59_WDAY);
+	if (pdata->type == M48T59RTC_TYPE_M48T201)
+		M48T59_WRITE(bin2bcd(year / 100 + 19), M48T59_CENTURY);
 
 	/* Clear the WRITE bit */
 	M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
@@ -177,10 +185,15 @@  static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	tm->tm_year += 68;
 #endif
 	/* tm_mon is 0-11 */
-	tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
+	if (pdata->type == M48T59RTC_TYPE_M48T201)
+		tm->tm_mon = bcd2bin(M48T59_READ(M48T59_INTR) & 0x1f) - 1;
+	else
+		tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
 
 	val = M48T59_READ(M48T59_WDAY);
-	if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB))
+	if (pdata->type == M48T59RTC_TYPE_M48T201)
+		tm->tm_year += (bcd2bin(M48T59_READ(M48T59_CENTURY))-19) * 100;
+	else if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB))
 		tm->tm_year += 100;	/* one century */
 
 	tm->tm_mday = bcd2bin(M48T59_READ(M48T59_ALARM_DATE));
@@ -192,8 +205,8 @@  static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
 	spin_unlock_irqrestore(&m48t59->lock, flags);
 
-	dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
-		tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
+	dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d:%02d:%02d\n",
+		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
 		tm->tm_hour, tm->tm_min, tm->tm_sec);
 	return 0;
 }
@@ -228,6 +241,10 @@  static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	 */
 	mday = tm->tm_mday;
 	mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
+	/*
+	 * FIXME: this is wrong when we're setting an alarm to go off after
+	 * midnight tonight.  The alarm will never happen.
+	 */
 	if (mday == 0xff)
 		mday = M48T59_READ(M48T59_MDAY);
 
@@ -244,6 +261,10 @@  static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	/* Issue the WRITE command */
 	M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
 
+	if (pdata->type == M48T59RTC_TYPE_M48T201)
+		M48T59_WRITE((M48T59_READ(M48T59_INTR) & 0xe0) |
+			     (bin2bcd(tm->tm_mon + 1) & 0x1f),
+			     M48T59_INTR);
 	M48T59_WRITE(mday, M48T59_ALARM_DATE);
 	M48T59_WRITE(hour, M48T59_ALARM_HOUR);
 	M48T59_WRITE(min, M48T59_ALARM_MIN);
@@ -253,8 +274,8 @@  static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
 	spin_unlock_irqrestore(&m48t59->lock, flags);
 
-	dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
-		year + 1900, tm->tm_mon, tm->tm_mday,
+	dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d:%02d:%02d\n",
+		tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
 		tm->tm_hour, tm->tm_min, tm->tm_sec);
 	return 0;
 }
@@ -272,12 +293,16 @@  static int m48t59_rtc_ioctl(struct device *dev, unsigned int cmd,
 	int ret = 0;
 
 	spin_lock_irqsave(&m48t59->lock, flags);
+	/*
+	 * Preserve the other fields in M48T59_INTR in case it's an M48T201
+	 * and they contain the alarm month.
+	 */
 	switch (cmd) {
 	case RTC_AIE_OFF:	/* alarm interrupt off */
-		M48T59_WRITE(0x00, M48T59_INTR);
+		M48T59_CLEAR_BITS(M48T59_INTR_AFE, M48T59_INTR);
 		break;
 	case RTC_AIE_ON:	/* alarm interrupt on */
-		M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR);
+		M48T59_SET_BITS(M48T59_INTR_AFE, M48T59_INTR);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -475,6 +500,11 @@  static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
 		ops = &m48t02_rtc_ops;
 		pdata->offset = 0x1ff0;
 		break;
+	case M48T59RTC_TYPE_M48T201:
+		name = "m48t201";
+		ops = &m48t59_rtc_ops;
+		pdata->offset = 0x7fff0;
+		break;
 	default:
 		dev_err(&pdev->dev, "Unknown RTC type\n");
 		ret = -ENODEV;
@@ -502,7 +532,7 @@  out:
 		rtc_device_unregister(m48t59->rtc);
 	if (m48t59->irq != NO_IRQ)
 		free_irq(m48t59->irq, &pdev->dev);
-	if (m48t59->ioaddr)
+	if (m48t59->ioaddr && !pdata->ioaddr)
 		iounmap(m48t59->ioaddr);
 	if (m48t59)
 		kfree(m48t59);
diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h
index 6fc9614..8e2df6f 100644
--- a/include/linux/rtc/m48t59.h
+++ b/include/linux/rtc/m48t59.h
@@ -38,7 +38,7 @@ 
 #define M48T59_ALARM_HOUR	0x4
 #define M48T59_ALARM_MIN	0x3
 #define M48T59_ALARM_SEC	0x2
-#define M48T59_UNUSED		0x1
+#define M48T59_CENTURY		0x1	/* Century (M48T201 only) */
 #define M48T59_FLAGS		0x0
 #define M48T59_FLAGS_WDT		0x80	/* watchdog timer expired */
 #define M48T59_FLAGS_AF			0x40	/* alarm */
@@ -47,6 +47,7 @@ 
 #define M48T59RTC_TYPE_M48T59	0 /* to keep compatibility */
 #define M48T59RTC_TYPE_M48T02	1
 #define M48T59RTC_TYPE_M48T08	2
+#define M48T59RTC_TYPE_M48T201	3
 
 struct m48t59_plat_data {
 	/* The method to access M48T59 registers */