diff options
author | zhao zhang <zhzhl555@gmail.com> | 2012-03-23 18:02:32 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-23 19:58:39 -0400 |
commit | b4f0b880c8d7eb225b79dec663780b4dcdea7fbc (patch) | |
tree | c99698d0030ac9c70b55f2fc03fe937772afa395 | |
parent | 0abc920116303e81702a38429a1b61a896e02b37 (diff) |
MIPS: add RTC support for loongson1B
Add RTC support(TOY counter0) for loongson1B SOC
Signed-off-by: zhao zhang <zhzhl555@gmail.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Ralf Baechle <ralf@linux-mips.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ls1x.c | 210 |
3 files changed, 221 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3a125b835546..850febcbaf73 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -1070,4 +1070,14 @@ config RTC_DRV_PUV3 | |||
1070 | This drive can also be built as a module. If so, the module | 1070 | This drive can also be built as a module. If so, the module |
1071 | will be called rtc-puv3. | 1071 | will be called rtc-puv3. |
1072 | 1072 | ||
1073 | config RTC_DRV_LOONGSON1 | ||
1074 | tristate "loongson1 RTC support" | ||
1075 | depends on MACH_LOONGSON1 | ||
1076 | help | ||
1077 | This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year | ||
1078 | counter) to be used as a RTC. | ||
1079 | |||
1080 | This driver can also be built as a module. If so, the module | ||
1081 | will be called rtc-ls1x. | ||
1082 | |||
1073 | endif # RTC_CLASS | 1083 | endif # RTC_CLASS |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6e6982335c10..6c9387a3a3ad 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -53,6 +53,7 @@ obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o | |||
53 | obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o | 53 | obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o |
54 | obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o | 54 | obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o |
55 | obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o | 55 | obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o |
56 | obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o | ||
56 | obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o | 57 | obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o |
57 | obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o | 58 | obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o |
58 | obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o | 59 | obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o |
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c new file mode 100644 index 000000000000..07e81c5f8247 --- /dev/null +++ b/drivers/rtc/rtc-ls1x.c | |||
@@ -0,0 +1,210 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com> | ||
3 | * | ||
4 | * Derived from driver/rtc/rtc-au1xxx.c | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/rtc.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <asm/mach-loongson1/loongson1.h> | ||
21 | |||
22 | #define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20) | ||
23 | #define LS1X_RTC_REGS(x) \ | ||
24 | ((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x))) | ||
25 | |||
26 | /*RTC programmable counters 0 and 1*/ | ||
27 | #define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20)) | ||
28 | #define SYS_CNTRL_ERS (1 << 23) | ||
29 | #define SYS_CNTRL_RTS (1 << 20) | ||
30 | #define SYS_CNTRL_RM2 (1 << 19) | ||
31 | #define SYS_CNTRL_RM1 (1 << 18) | ||
32 | #define SYS_CNTRL_RM0 (1 << 17) | ||
33 | #define SYS_CNTRL_RS (1 << 16) | ||
34 | #define SYS_CNTRL_BP (1 << 14) | ||
35 | #define SYS_CNTRL_REN (1 << 13) | ||
36 | #define SYS_CNTRL_BRT (1 << 12) | ||
37 | #define SYS_CNTRL_TEN (1 << 11) | ||
38 | #define SYS_CNTRL_BTT (1 << 10) | ||
39 | #define SYS_CNTRL_E0 (1 << 8) | ||
40 | #define SYS_CNTRL_ETS (1 << 7) | ||
41 | #define SYS_CNTRL_32S (1 << 5) | ||
42 | #define SYS_CNTRL_TTS (1 << 4) | ||
43 | #define SYS_CNTRL_TM2 (1 << 3) | ||
44 | #define SYS_CNTRL_TM1 (1 << 2) | ||
45 | #define SYS_CNTRL_TM0 (1 << 1) | ||
46 | #define SYS_CNTRL_TS (1 << 0) | ||
47 | |||
48 | /* Programmable Counter 0 Registers */ | ||
49 | #define SYS_TOYTRIM (LS1X_RTC_REGS(0)) | ||
50 | #define SYS_TOYWRITE0 (LS1X_RTC_REGS(4)) | ||
51 | #define SYS_TOYWRITE1 (LS1X_RTC_REGS(8)) | ||
52 | #define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC)) | ||
53 | #define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10)) | ||
54 | #define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14)) | ||
55 | #define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18)) | ||
56 | #define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C)) | ||
57 | |||
58 | /* Programmable Counter 1 Registers */ | ||
59 | #define SYS_RTCTRIM (LS1X_RTC_REGS(0x40)) | ||
60 | #define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44)) | ||
61 | #define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48)) | ||
62 | #define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C)) | ||
63 | #define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50)) | ||
64 | #define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54)) | ||
65 | |||
66 | #define LS1X_SEC_OFFSET (4) | ||
67 | #define LS1X_MIN_OFFSET (10) | ||
68 | #define LS1X_HOUR_OFFSET (16) | ||
69 | #define LS1X_DAY_OFFSET (21) | ||
70 | #define LS1X_MONTH_OFFSET (26) | ||
71 | |||
72 | |||
73 | #define LS1X_SEC_MASK (0x3f) | ||
74 | #define LS1X_MIN_MASK (0x3f) | ||
75 | #define LS1X_HOUR_MASK (0x1f) | ||
76 | #define LS1X_DAY_MASK (0x1f) | ||
77 | #define LS1X_MONTH_MASK (0x3f) | ||
78 | #define LS1X_YEAR_MASK (0xffffffff) | ||
79 | |||
80 | #define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK) | ||
81 | #define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK) | ||
82 | #define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK) | ||
83 | #define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK) | ||
84 | #define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK) | ||
85 | |||
86 | #define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) | ||
87 | |||
88 | static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm) | ||
89 | { | ||
90 | unsigned long v, t; | ||
91 | |||
92 | v = readl(SYS_TOYREAD0); | ||
93 | t = readl(SYS_TOYREAD1); | ||
94 | |||
95 | memset(rtm, 0, sizeof(struct rtc_time)); | ||
96 | t = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v), | ||
97 | ls1x_get_day(v), ls1x_get_hour(v), | ||
98 | ls1x_get_min(v), ls1x_get_sec(v)); | ||
99 | rtc_time_to_tm(t, rtm); | ||
100 | |||
101 | return rtc_valid_tm(rtm); | ||
102 | } | ||
103 | |||
104 | static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm) | ||
105 | { | ||
106 | unsigned long v, t, c; | ||
107 | int ret = -ETIMEDOUT; | ||
108 | |||
109 | v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET) | ||
110 | | (rtm->tm_mday << LS1X_DAY_OFFSET) | ||
111 | | (rtm->tm_hour << LS1X_HOUR_OFFSET) | ||
112 | | (rtm->tm_min << LS1X_MIN_OFFSET) | ||
113 | | (rtm->tm_sec << LS1X_SEC_OFFSET); | ||
114 | |||
115 | writel(v, SYS_TOYWRITE0); | ||
116 | c = 0x10000; | ||
117 | /* add timeout check counter, for more safe */ | ||
118 | while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c) | ||
119 | usleep_range(1000, 3000); | ||
120 | |||
121 | if (!c) { | ||
122 | dev_err(dev, "set time timeout!\n"); | ||
123 | goto err; | ||
124 | } | ||
125 | |||
126 | t = rtm->tm_year + 1900; | ||
127 | writel(t, SYS_TOYWRITE1); | ||
128 | c = 0x10000; | ||
129 | while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c) | ||
130 | usleep_range(1000, 3000); | ||
131 | |||
132 | if (!c) { | ||
133 | dev_err(dev, "set time timeout!\n"); | ||
134 | goto err; | ||
135 | } | ||
136 | return 0; | ||
137 | err: | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | static struct rtc_class_ops ls1x_rtc_ops = { | ||
142 | .read_time = ls1x_rtc_read_time, | ||
143 | .set_time = ls1x_rtc_set_time, | ||
144 | }; | ||
145 | |||
146 | static int __devinit ls1x_rtc_probe(struct platform_device *pdev) | ||
147 | { | ||
148 | struct rtc_device *rtcdev; | ||
149 | unsigned long v; | ||
150 | int ret; | ||
151 | |||
152 | v = readl(SYS_COUNTER_CNTRL); | ||
153 | if (!(v & RTC_CNTR_OK)) { | ||
154 | dev_err(&pdev->dev, "rtc counters not working\n"); | ||
155 | ret = -ENODEV; | ||
156 | goto err; | ||
157 | } | ||
158 | ret = -ETIMEDOUT; | ||
159 | /* set to 1 HZ if needed */ | ||
160 | if (readl(SYS_TOYTRIM) != 32767) { | ||
161 | v = 0x100000; | ||
162 | while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v) | ||
163 | usleep_range(1000, 3000); | ||
164 | |||
165 | if (!v) { | ||
166 | dev_err(&pdev->dev, "time out\n"); | ||
167 | goto err; | ||
168 | } | ||
169 | writel(32767, SYS_TOYTRIM); | ||
170 | } | ||
171 | /* this loop coundn't be endless */ | ||
172 | while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) | ||
173 | usleep_range(1000, 3000); | ||
174 | |||
175 | rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev, | ||
176 | &ls1x_rtc_ops , THIS_MODULE); | ||
177 | if (IS_ERR(rtcdev)) { | ||
178 | ret = PTR_ERR(rtcdev); | ||
179 | goto err; | ||
180 | } | ||
181 | |||
182 | platform_set_drvdata(pdev, rtcdev); | ||
183 | return 0; | ||
184 | err: | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | static int __devexit ls1x_rtc_remove(struct platform_device *pdev) | ||
189 | { | ||
190 | struct rtc_device *rtcdev = platform_get_drvdata(pdev); | ||
191 | |||
192 | rtc_device_unregister(rtcdev); | ||
193 | platform_set_drvdata(pdev, NULL); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static struct platform_driver ls1x_rtc_driver = { | ||
199 | .driver = { | ||
200 | .name = "ls1x-rtc", | ||
201 | .owner = THIS_MODULE, | ||
202 | }, | ||
203 | .remove = __devexit_p(ls1x_rtc_remove), | ||
204 | .probe = ls1x_rtc_probe, | ||
205 | }; | ||
206 | |||
207 | module_platform_driver(ls1x_rtc_driver); | ||
208 | |||
209 | MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>"); | ||
210 | MODULE_LICENSE("GPL"); | ||