Patchwork [V1] MIPS: Add RTC support for loongson1B

login
register
mail settings
Submitter zhzhl555@gmail.com
Date Nov. 25, 2011, 2:52 a.m.
Message ID <1322189527-4761-1-git-send-email-zhzhl555@gmail.com>
Download mbox | patch
Permalink /patch/127636/
State New
Headers show

Comments

zhzhl555@gmail.com - Nov. 25, 2011, 2:52 a.m.
From: zhao zhang <zhzhl555@gmail.com>

V1: replace __raw_writel/__raw_readl with writel/readl.
thanks for Linus Wallei's advice.
This patch adds RTC support(TOY counter0) for loongson1B.

Signed-off-by: zhao zhang <zhzhl555@gmail.com>
---
 drivers/rtc/Kconfig    |   10 ++
 drivers/rtc/Makefile   |    1 +
 drivers/rtc/rtc-ls1x.c |  213 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 224 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-ls1x.c
Wolfram Sang - Nov. 25, 2011, 9:13 a.m.
On Fri, Nov 25, 2011 at 10:52:07AM +0800, zhzhl555@gmail.com wrote:
> From: zhao zhang <zhzhl555@gmail.com>
> 
> V1: replace __raw_writel/__raw_readl with writel/readl.
> thanks for Linus Wallei's advice.
> This patch adds RTC support(TOY counter0) for loongson1B.
> 
> Signed-off-by: zhao zhang <zhzhl555@gmail.com>
> ---

Grepping says that rtc-au1xxx.c also has registers with the same name.
Has it been checked if those drivers can be merged/reused?

Regards,

   Wolfram
zhzhl555@gmail.com - Nov. 25, 2011, 9:49 a.m.
I have checked carefully,
1:the au1xx used one register but loongon1B used two 32bit registers
(write0/write1 and read0/read1) to represent time.
2:the data organization are also different.
so, it's hard to reuse.




在 2011年11月25日 下午5:13,Wolfram Sang <w.sang@pengutronix.de>写道:

> On Fri, Nov 25, 2011 at 10:52:07AM +0800, zhzhl555@gmail.com wrote:
> > From: zhao zhang <zhzhl555@gmail.com>
> >
> > V1: replace __raw_writel/__raw_readl with writel/readl.
> > thanks for Linus Wallei's advice.
> > This patch adds RTC support(TOY counter0) for loongson1B.
> >
> > Signed-off-by: zhao zhang <zhzhl555@gmail.com>
> > ---
>
> Grepping says that rtc-au1xxx.c also has registers with the same name.
> Has it been checked if those drivers can be merged/reused?
>
> Regards,
>
>   Wolfram
>
> --
> Pengutronix e.K.                           | Wolfram Sang                |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
>
> iEYEARECAAYFAk7PXCcACgkQD27XaX1/VRuVRwCbBHtgb9dJhBSGGcHtZgpdFCWM
> QJgAoJ4JXCrKCFqcNCcfInANSQ8pyvQc
> =8K9q
> -----END PGP SIGNATURE-----
>
>
Wolfram Sang - Nov. 25, 2011, 9:51 a.m.
On Fri, Nov 25, 2011 at 05:49:04PM +0800, zhao zhang wrote:
>    I have checked carefully,
>    1:the au1xx used one register but loongon1B used two 32bit registers
>    (write0/write1 and read0/read1) to represent time.
>    2:the data organization are also different. 
>    so, it's hard to reuse.

Fair enough, thanks for checking.
Wolfram Sang - Nov. 27, 2011, 9:18 a.m.
On Fri, Nov 25, 2011 at 10:52:07AM +0800, zhzhl555@gmail.com wrote:

> +	writel(t, SYS_TOYWRITE1);
> +	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS)
> +		usleep_range(1000, 3000);
> +	__asm__ volatile ("sync");

Timeout?

> +
> +static int __devinit ls1x_rtc_probe(struct platform_device *pdev)
> +{
> +	struct rtc_device *rtcdev;
> +	unsigned long v;
> +	int ret;
> +
> +	v = readl(SYS_COUNTER_CNTRL);
> +	if (!(v & RTC_CNTR_OK)) {
> +		dev_err(&pdev->dev, "rtc counters not working\n");
> +		ret = -ENODEV;
> +		goto err;
> +	}
> +	ret = -ETIMEDOUT;

Why not putting this line to the corresponding dev_err-block?

> +	/*set to 1 HZ if needed*/

Minor: Spaces around comment-markers, here and in other places

/* Comment */

> +	if (readl(SYS_TOYTRIM) != 32767) {
> +		v = 0x100000;
> +		while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
> +			usleep_range(1000, 3000);

Timeout?

> +
> +		if (!v) {
> +			dev_err(&pdev->dev, "time out\n");
> +			goto err;
> +		}
> +		writel(32767, SYS_TOYTRIM);
> +		__asm__ volatile("sync");
> +	}
> +	/*this loop coundn't be endless*/
> +	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
> +		usleep_range(1000, 3000);

Timeout again. First, the comment does not help. Why are you sure the loop
could not be endless? And: Does it really need to be a usleep_range() instead
of a simple msleep()? And to make sure: There is no interrupt signalling the
status changed?

> +
> +	rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev,
> +					&ls1x_rtc_ops , THIS_MODULE);
> +	if (IS_ERR(rtcdev)) {
> +		ret = PTR_ERR(rtcdev);
> +		goto err;
> +	}
> +
> +	platform_set_drvdata(pdev, rtcdev);
> +	return 0;
> +err:
> +	return ret;
> +}
> +

...

> +static int __init ls1x_rtc_init(void)
> +{
> +	return platform_driver_probe(&ls1x_rtc_driver, ls1x_rtc_probe);
> +}
> +
> +static void __exit ls1x_rtc_exit(void)
> +{
> +	platform_driver_unregister(&ls1x_rtc_driver);
> +}
> +
> +
> +module_init(ls1x_rtc_init);
> +module_exit(ls1x_rtc_exit);

Please use the new module_platform_driver()-macro.

Thanks,

   Wolfram
zhzhl555@gmail.com - Nov. 28, 2011, 5:06 a.m.
1:  Here is a  polling checking for TOY write status bit. if hardware done,
the bit will be cleared. so if hardware has problem, the while loop will be
infinite, it can never break out.  Does i really need to add timeout
checking
code although have checked in probe code.

2:  Just set the default value, the real return value will be set in the
following
code. Line 154, err information will be put into dev_err-block.

3: The minor, accept.

4: Because in probe, we can not assume the hardware is OK,
so add a counter( v = 0x100000) to avoid infinite loop.

5: a): Since i have checked the RTC timing was OK, and  toytrim write
status was
OK again, so i can sure, the next writing will be OK.
    b): Just following the  Documentation/timers/timers-howto.txt.
    c): i can make sure.

6: agree.


在 2011年11月27日 下午5:18,Wolfram Sang <w.sang@pengutronix.de>写道:

> On Fri, Nov 25, 2011 at 10:52:07AM +0800, zhzhl555@gmail.com wrote:
>
> > +     writel(t, SYS_TOYWRITE1);
> > +     while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS)
> > +             usleep_range(1000, 3000);
> > +     __asm__ volatile ("sync");
>
> Timeout?
>
> > +
> > +static int __devinit ls1x_rtc_probe(struct platform_device *pdev)
> > +{
> > +     struct rtc_device *rtcdev;
> > +     unsigned long v;
> > +     int ret;
> > +
> > +     v = readl(SYS_COUNTER_CNTRL);
> > +     if (!(v & RTC_CNTR_OK)) {
> > +             dev_err(&pdev->dev, "rtc counters not working\n");
> > +             ret = -ENODEV;
> > +             goto err;
> > +     }
> > +     ret = -ETIMEDOUT;
>
> Why not putting this line to the corresponding dev_err-block?
>
> > +     /*set to 1 HZ if needed*/
>
> Minor: Spaces around comment-markers, here and in other places
>
> /* Comment */
>
> > +     if (readl(SYS_TOYTRIM) != 32767) {
> > +             v = 0x100000;
> > +             while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
> > +                     usleep_range(1000, 3000);
>
> Timeout?
>
> > +
> > +             if (!v) {
> > +                     dev_err(&pdev->dev, "time out\n");
> > +                     goto err;
> > +             }
> > +             writel(32767, SYS_TOYTRIM);
> > +             __asm__ volatile("sync");
> > +     }
> > +     /*this loop coundn't be endless*/
> > +     while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
> > +             usleep_range(1000, 3000);
>
> Timeout again. First, the comment does not help. Why are you sure the loop
> could not be endless? And: Does it really need to be a usleep_range()
> instead
> of a simple msleep()? And to make sure: There is no interrupt signalling
> the
> status changed?
>
> > +
> > +     rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev,
> > +                                     &ls1x_rtc_ops , THIS_MODULE);
> > +     if (IS_ERR(rtcdev)) {
> > +             ret = PTR_ERR(rtcdev);
> > +             goto err;
> > +     }
> > +
> > +     platform_set_drvdata(pdev, rtcdev);
> > +     return 0;
> > +err:
> > +     return ret;
> > +}
> > +
>
> ...
>
> > +static int __init ls1x_rtc_init(void)
> > +{
> > +     return platform_driver_probe(&ls1x_rtc_driver, ls1x_rtc_probe);
> > +}
> > +
> > +static void __exit ls1x_rtc_exit(void)
> > +{
> > +     platform_driver_unregister(&ls1x_rtc_driver);
> > +}
> > +
> > +
> > +module_init(ls1x_rtc_init);
> > +module_exit(ls1x_rtc_exit);
>
> Please use the new module_platform_driver()-macro.
>
> Thanks,
>
>   Wolfram
>
> --
> Pengutronix e.K.                           | Wolfram Sang                |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
>

Patch

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5a538fc..6f8c2d7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1070,4 +1070,14 @@  config RTC_DRV_PUV3
 	  This drive can also be built as a module. If so, the module
 	  will be called rtc-puv3.
 
+config RTC_DRV_LOONGSON1
+	tristate "loongson1 RTC support"
+	depends on MACH_LOONGSON1
+	help
+	  This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year
+	  counter) to be used as a RTC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-ls1x.
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e69823..48153fe 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -109,3 +109,4 @@  obj-$(CONFIG_RTC_DRV_VT8500)	+= rtc-vt8500.o
 obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
 obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_LOONGSON1)	+= rtc-ls1x.o
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c
new file mode 100644
index 0000000..c752a30
--- /dev/null
+++ b/drivers/rtc/rtc-ls1x.c
@@ -0,0 +1,213 @@ 
+/*
+ * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com>
+ *
+ * Derived from driver/rtc/rtc-au1xxx.c
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <asm/mach-loongson1/loongson1.h>
+
+#define LS1X_RTC_REG_OFFSET	(LS1X_RTC_BASE + 0x20)
+#define LS1X_RTC_REGS(x) \
+		((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x)))
+
+/*RTC programmable counters 0 and 1*/
+#define SYS_COUNTER_CNTRL		(LS1X_RTC_REGS(0x20))
+#define SYS_CNTRL_ERS			(1 << 23)
+#define SYS_CNTRL_RTS			(1 << 20)
+#define SYS_CNTRL_RM2			(1 << 19)
+#define SYS_CNTRL_RM1			(1 << 18)
+#define SYS_CNTRL_RM0			(1 << 17)
+#define SYS_CNTRL_RS			(1 << 16)
+#define SYS_CNTRL_BP			(1 << 14)
+#define SYS_CNTRL_REN			(1 << 13)
+#define SYS_CNTRL_BRT			(1 << 12)
+#define SYS_CNTRL_TEN			(1 << 11)
+#define SYS_CNTRL_BTT			(1 << 10)
+#define SYS_CNTRL_E0			(1 << 8)
+#define SYS_CNTRL_ETS			(1 << 7)
+#define SYS_CNTRL_32S			(1 << 5)
+#define SYS_CNTRL_TTS			(1 << 4)
+#define SYS_CNTRL_TM2			(1 << 3)
+#define SYS_CNTRL_TM1			(1 << 2)
+#define SYS_CNTRL_TM0			(1 << 1)
+#define SYS_CNTRL_TS			(1 << 0)
+
+/* Programmable Counter 0 Registers */
+#define SYS_TOYTRIM		(LS1X_RTC_REGS(0))
+#define SYS_TOYWRITE0		(LS1X_RTC_REGS(4))
+#define SYS_TOYWRITE1		(LS1X_RTC_REGS(8))
+#define SYS_TOYREAD0		(LS1X_RTC_REGS(0xC))
+#define SYS_TOYREAD1		(LS1X_RTC_REGS(0x10))
+#define SYS_TOYMATCH0		(LS1X_RTC_REGS(0x14))
+#define SYS_TOYMATCH1		(LS1X_RTC_REGS(0x18))
+#define SYS_TOYMATCH2		(LS1X_RTC_REGS(0x1C))
+
+/* Programmable Counter 1 Registers */
+#define SYS_RTCTRIM		(LS1X_RTC_REGS(0x40))
+#define SYS_RTCWRITE0		(LS1X_RTC_REGS(0x44))
+#define SYS_RTCREAD0		(LS1X_RTC_REGS(0x48))
+#define SYS_RTCMATCH0		(LS1X_RTC_REGS(0x4C))
+#define SYS_RTCMATCH1		(LS1X_RTC_REGS(0x50))
+#define SYS_RTCMATCH2		(LS1X_RTC_REGS(0x54))
+
+#define LS1X_SEC_OFFSET		(4)
+#define LS1X_MIN_OFFSET		(10)
+#define LS1X_HOUR_OFFSET	(16)
+#define LS1X_DAY_OFFSET		(21)
+#define LS1X_MONTH_OFFSET	(26)
+
+
+#define LS1X_SEC_MASK		(0x3f)
+#define LS1X_MIN_MASK		(0x3f)
+#define LS1X_HOUR_MASK		(0x1f)
+#define LS1X_DAY_MASK		(0x1f)
+#define LS1X_MONTH_MASK		(0x3f)
+#define LS1X_YEAR_MASK		(0xffffffff)
+
+#define ls1x_get_sec(t)		(((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK)
+#define ls1x_get_min(t)		(((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK)
+#define ls1x_get_hour(t)	(((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK)
+#define ls1x_get_day(t)		(((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK)
+#define ls1x_get_month(t)	(((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK)
+
+#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
+
+static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm)
+{
+	unsigned long v, t;
+
+	v = readl(SYS_TOYREAD0);
+	t = readl(SYS_TOYREAD1);
+
+	memset(rtm, 0, sizeof(struct rtc_time));
+	t  = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v),
+			ls1x_get_day(v), ls1x_get_hour(v),
+			ls1x_get_min(v), ls1x_get_sec(v));
+	rtc_time_to_tm(t, rtm);
+
+	return rtc_valid_tm(rtm);
+}
+
+static int ls1x_rtc_set_time(struct device *dev, struct  rtc_time *rtm)
+{
+	unsigned long v, t;
+
+	v = ((rtm->tm_mon + 1)  << LS1X_MONTH_OFFSET)
+		| (rtm->tm_mday << LS1X_DAY_OFFSET)
+		| (rtm->tm_hour << LS1X_HOUR_OFFSET)
+		| (rtm->tm_min  << LS1X_MIN_OFFSET)
+		| (rtm->tm_sec  << LS1X_SEC_OFFSET);
+
+	t = rtm->tm_year + 1900;
+	writel(v, SYS_TOYWRITE0);
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS)
+		usleep_range(1000, 3000);
+	__asm__ volatile ("sync");
+
+	writel(t, SYS_TOYWRITE1);
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS)
+		usleep_range(1000, 3000);
+	__asm__ volatile ("sync");
+
+	v = readl(SYS_COUNTER_CNTRL);
+	return 0;
+}
+
+static struct rtc_class_ops  ls1x_rtc_ops = {
+	.read_time	= ls1x_rtc_read_time,
+	.set_time	= ls1x_rtc_set_time,
+};
+
+static int __devinit ls1x_rtc_probe(struct platform_device *pdev)
+{
+	struct rtc_device *rtcdev;
+	unsigned long v;
+	int ret;
+
+	v = readl(SYS_COUNTER_CNTRL);
+	if (!(v & RTC_CNTR_OK)) {
+		dev_err(&pdev->dev, "rtc counters not working\n");
+		ret = -ENODEV;
+		goto err;
+	}
+	ret = -ETIMEDOUT;
+	/*set to 1 HZ if needed*/
+	if (readl(SYS_TOYTRIM) != 32767) {
+		v = 0x100000;
+		while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
+			usleep_range(1000, 3000);
+
+		if (!v) {
+			dev_err(&pdev->dev, "time out\n");
+			goto err;
+		}
+		writel(32767, SYS_TOYTRIM);
+		__asm__ volatile("sync");
+	}
+	/*this loop coundn't be endless*/
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
+		usleep_range(1000, 3000);
+
+	rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev,
+					&ls1x_rtc_ops , THIS_MODULE);
+	if (IS_ERR(rtcdev)) {
+		ret = PTR_ERR(rtcdev);
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, rtcdev);
+	return 0;
+err:
+	return ret;
+}
+
+static int __devexit ls1x_rtc_remove(struct platform_device *pdev)
+{
+	struct rtc_device *rtcdev = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(rtcdev);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver  ls1x_rtc_driver = {
+	.driver		= {
+		.name	= "ls1x-rtc",
+		.owner	= THIS_MODULE,
+	},
+	.remove		= __devexit_p(ls1x_rtc_remove),
+};
+
+static int __init ls1x_rtc_init(void)
+{
+	return platform_driver_probe(&ls1x_rtc_driver, ls1x_rtc_probe);
+}
+
+static void __exit ls1x_rtc_exit(void)
+{
+	platform_driver_unregister(&ls1x_rtc_driver);
+}
+
+
+module_init(ls1x_rtc_init);
+module_exit(ls1x_rtc_exit);
+
+
+MODULE_DESCRIPTION("LOONGSON1 TOY-counter-based RTC driver");
+MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>");
+MODULE_LICENSE("GPL");
+