diff options
Diffstat (limited to 'drivers/rtc/rtc-s3c.c')
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 238 |
1 files changed, 181 insertions, 57 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index e0d7b9991505..cf953ecbfca9 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c | |||
@@ -1,5 +1,8 @@ | |||
1 | /* drivers/rtc/rtc-s3c.c | 1 | /* drivers/rtc/rtc-s3c.c |
2 | * | 2 | * |
3 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
3 | * Copyright (c) 2004,2006 Simtec Electronics | 6 | * Copyright (c) 2004,2006 Simtec Electronics |
4 | * Ben Dooks, <ben@simtec.co.uk> | 7 | * Ben Dooks, <ben@simtec.co.uk> |
5 | * http://armlinux.simtec.co.uk/ | 8 | * http://armlinux.simtec.co.uk/ |
@@ -21,6 +24,7 @@ | |||
21 | #include <linux/bcd.h> | 24 | #include <linux/bcd.h> |
22 | #include <linux/clk.h> | 25 | #include <linux/clk.h> |
23 | #include <linux/log2.h> | 26 | #include <linux/log2.h> |
27 | #include <linux/slab.h> | ||
24 | 28 | ||
25 | #include <mach/hardware.h> | 29 | #include <mach/hardware.h> |
26 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
@@ -28,14 +32,21 @@ | |||
28 | #include <asm/irq.h> | 32 | #include <asm/irq.h> |
29 | #include <plat/regs-rtc.h> | 33 | #include <plat/regs-rtc.h> |
30 | 34 | ||
35 | enum s3c_cpu_type { | ||
36 | TYPE_S3C2410, | ||
37 | TYPE_S3C64XX, | ||
38 | }; | ||
39 | |||
31 | /* I have yet to find an S3C implementation with more than one | 40 | /* I have yet to find an S3C implementation with more than one |
32 | * of these rtc blocks in */ | 41 | * of these rtc blocks in */ |
33 | 42 | ||
34 | static struct resource *s3c_rtc_mem; | 43 | static struct resource *s3c_rtc_mem; |
35 | 44 | ||
45 | static struct clk *rtc_clk; | ||
36 | static void __iomem *s3c_rtc_base; | 46 | static void __iomem *s3c_rtc_base; |
37 | static int s3c_rtc_alarmno = NO_IRQ; | 47 | static int s3c_rtc_alarmno = NO_IRQ; |
38 | static int s3c_rtc_tickno = NO_IRQ; | 48 | static int s3c_rtc_tickno = NO_IRQ; |
49 | static enum s3c_cpu_type s3c_rtc_cpu_type; | ||
39 | 50 | ||
40 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); | 51 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); |
41 | 52 | ||
@@ -46,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) | |||
46 | struct rtc_device *rdev = id; | 57 | struct rtc_device *rdev = id; |
47 | 58 | ||
48 | rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); | 59 | rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); |
60 | |||
61 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
62 | writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); | ||
63 | |||
49 | return IRQ_HANDLED; | 64 | return IRQ_HANDLED; |
50 | } | 65 | } |
51 | 66 | ||
@@ -54,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id) | |||
54 | struct rtc_device *rdev = id; | 69 | struct rtc_device *rdev = id; |
55 | 70 | ||
56 | rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); | 71 | rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); |
72 | |||
73 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
74 | writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); | ||
75 | |||
57 | return IRQ_HANDLED; | 76 | return IRQ_HANDLED; |
58 | } | 77 | } |
59 | 78 | ||
@@ -79,12 +98,25 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) | |||
79 | pr_debug("%s: pie=%d\n", __func__, enabled); | 98 | pr_debug("%s: pie=%d\n", __func__, enabled); |
80 | 99 | ||
81 | spin_lock_irq(&s3c_rtc_pie_lock); | 100 | spin_lock_irq(&s3c_rtc_pie_lock); |
82 | tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; | ||
83 | 101 | ||
84 | if (enabled) | 102 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { |
85 | tmp |= S3C2410_TICNT_ENABLE; | 103 | tmp = readw(s3c_rtc_base + S3C2410_RTCCON); |
104 | tmp &= ~S3C64XX_RTCCON_TICEN; | ||
105 | |||
106 | if (enabled) | ||
107 | tmp |= S3C64XX_RTCCON_TICEN; | ||
108 | |||
109 | writew(tmp, s3c_rtc_base + S3C2410_RTCCON); | ||
110 | } else { | ||
111 | tmp = readb(s3c_rtc_base + S3C2410_TICNT); | ||
112 | tmp &= ~S3C2410_TICNT_ENABLE; | ||
113 | |||
114 | if (enabled) | ||
115 | tmp |= S3C2410_TICNT_ENABLE; | ||
116 | |||
117 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | ||
118 | } | ||
86 | 119 | ||
87 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | ||
88 | spin_unlock_irq(&s3c_rtc_pie_lock); | 120 | spin_unlock_irq(&s3c_rtc_pie_lock); |
89 | 121 | ||
90 | return 0; | 122 | return 0; |
@@ -92,17 +124,23 @@ static int s3c_rtc_setpie(struct device *dev, int enabled) | |||
92 | 124 | ||
93 | static int s3c_rtc_setfreq(struct device *dev, int freq) | 125 | static int s3c_rtc_setfreq(struct device *dev, int freq) |
94 | { | 126 | { |
95 | unsigned int tmp; | 127 | struct platform_device *pdev = to_platform_device(dev); |
128 | struct rtc_device *rtc_dev = platform_get_drvdata(pdev); | ||
129 | unsigned int tmp = 0; | ||
96 | 130 | ||
97 | if (!is_power_of_2(freq)) | 131 | if (!is_power_of_2(freq)) |
98 | return -EINVAL; | 132 | return -EINVAL; |
99 | 133 | ||
100 | spin_lock_irq(&s3c_rtc_pie_lock); | 134 | spin_lock_irq(&s3c_rtc_pie_lock); |
101 | 135 | ||
102 | tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; | 136 | if (s3c_rtc_cpu_type == TYPE_S3C2410) { |
103 | tmp |= (128 / freq)-1; | 137 | tmp = readb(s3c_rtc_base + S3C2410_TICNT); |
138 | tmp &= S3C2410_TICNT_ENABLE; | ||
139 | } | ||
140 | |||
141 | tmp |= (rtc_dev->max_user_freq / freq)-1; | ||
104 | 142 | ||
105 | writeb(tmp, s3c_rtc_base + S3C2410_TICNT); | 143 | writel(tmp, s3c_rtc_base + S3C2410_TICNT); |
106 | spin_unlock_irq(&s3c_rtc_pie_lock); | 144 | spin_unlock_irq(&s3c_rtc_pie_lock); |
107 | 145 | ||
108 | return 0; | 146 | return 0; |
@@ -133,8 +171,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | |||
133 | goto retry_get_time; | 171 | goto retry_get_time; |
134 | } | 172 | } |
135 | 173 | ||
136 | pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", | 174 | pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", |
137 | rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, | 175 | 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, |
138 | rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); | 176 | rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); |
139 | 177 | ||
140 | rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); | 178 | rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); |
@@ -147,7 +185,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | |||
147 | rtc_tm->tm_year += 100; | 185 | rtc_tm->tm_year += 100; |
148 | rtc_tm->tm_mon -= 1; | 186 | rtc_tm->tm_mon -= 1; |
149 | 187 | ||
150 | return 0; | 188 | return rtc_valid_tm(rtc_tm); |
151 | } | 189 | } |
152 | 190 | ||
153 | static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) | 191 | static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) |
@@ -155,8 +193,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) | |||
155 | void __iomem *base = s3c_rtc_base; | 193 | void __iomem *base = s3c_rtc_base; |
156 | int year = tm->tm_year - 100; | 194 | int year = tm->tm_year - 100; |
157 | 195 | ||
158 | pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n", | 196 | pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n", |
159 | tm->tm_year, tm->tm_mon, tm->tm_mday, | 197 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, |
160 | tm->tm_hour, tm->tm_min, tm->tm_sec); | 198 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
161 | 199 | ||
162 | /* we get around y2k by simply not supporting it */ | 200 | /* we get around y2k by simply not supporting it */ |
@@ -193,9 +231,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
193 | 231 | ||
194 | alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; | 232 | alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; |
195 | 233 | ||
196 | pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", | 234 | pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", |
197 | alm_en, | 235 | alm_en, |
198 | alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, | 236 | 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, |
199 | alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); | 237 | alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); |
200 | 238 | ||
201 | 239 | ||
@@ -204,34 +242,34 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
204 | if (alm_en & S3C2410_RTCALM_SECEN) | 242 | if (alm_en & S3C2410_RTCALM_SECEN) |
205 | alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); | 243 | alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); |
206 | else | 244 | else |
207 | alm_tm->tm_sec = 0xff; | 245 | alm_tm->tm_sec = -1; |
208 | 246 | ||
209 | if (alm_en & S3C2410_RTCALM_MINEN) | 247 | if (alm_en & S3C2410_RTCALM_MINEN) |
210 | alm_tm->tm_min = bcd2bin(alm_tm->tm_min); | 248 | alm_tm->tm_min = bcd2bin(alm_tm->tm_min); |
211 | else | 249 | else |
212 | alm_tm->tm_min = 0xff; | 250 | alm_tm->tm_min = -1; |
213 | 251 | ||
214 | if (alm_en & S3C2410_RTCALM_HOUREN) | 252 | if (alm_en & S3C2410_RTCALM_HOUREN) |
215 | alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); | 253 | alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); |
216 | else | 254 | else |
217 | alm_tm->tm_hour = 0xff; | 255 | alm_tm->tm_hour = -1; |
218 | 256 | ||
219 | if (alm_en & S3C2410_RTCALM_DAYEN) | 257 | if (alm_en & S3C2410_RTCALM_DAYEN) |
220 | alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); | 258 | alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); |
221 | else | 259 | else |
222 | alm_tm->tm_mday = 0xff; | 260 | alm_tm->tm_mday = -1; |
223 | 261 | ||
224 | if (alm_en & S3C2410_RTCALM_MONEN) { | 262 | if (alm_en & S3C2410_RTCALM_MONEN) { |
225 | alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); | 263 | alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); |
226 | alm_tm->tm_mon -= 1; | 264 | alm_tm->tm_mon -= 1; |
227 | } else { | 265 | } else { |
228 | alm_tm->tm_mon = 0xff; | 266 | alm_tm->tm_mon = -1; |
229 | } | 267 | } |
230 | 268 | ||
231 | if (alm_en & S3C2410_RTCALM_YEAREN) | 269 | if (alm_en & S3C2410_RTCALM_YEAREN) |
232 | alm_tm->tm_year = bcd2bin(alm_tm->tm_year); | 270 | alm_tm->tm_year = bcd2bin(alm_tm->tm_year); |
233 | else | 271 | else |
234 | alm_tm->tm_year = 0xffff; | 272 | alm_tm->tm_year = -1; |
235 | 273 | ||
236 | return 0; | 274 | return 0; |
237 | } | 275 | } |
@@ -242,10 +280,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
242 | void __iomem *base = s3c_rtc_base; | 280 | void __iomem *base = s3c_rtc_base; |
243 | unsigned int alrm_en; | 281 | unsigned int alrm_en; |
244 | 282 | ||
245 | pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", | 283 | pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", |
246 | alrm->enabled, | 284 | alrm->enabled, |
247 | tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, | 285 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, |
248 | tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); | 286 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
249 | 287 | ||
250 | 288 | ||
251 | alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; | 289 | alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; |
@@ -272,20 +310,22 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
272 | 310 | ||
273 | s3c_rtc_setaie(alrm->enabled); | 311 | s3c_rtc_setaie(alrm->enabled); |
274 | 312 | ||
275 | if (alrm->enabled) | ||
276 | enable_irq_wake(s3c_rtc_alarmno); | ||
277 | else | ||
278 | disable_irq_wake(s3c_rtc_alarmno); | ||
279 | |||
280 | return 0; | 313 | return 0; |
281 | } | 314 | } |
282 | 315 | ||
283 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) | 316 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) |
284 | { | 317 | { |
285 | unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT); | 318 | unsigned int ticnt; |
286 | 319 | ||
287 | seq_printf(seq, "periodic_IRQ\t: %s\n", | 320 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { |
288 | (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); | 321 | ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); |
322 | ticnt &= S3C64XX_RTCCON_TICEN; | ||
323 | } else { | ||
324 | ticnt = readb(s3c_rtc_base + S3C2410_TICNT); | ||
325 | ticnt &= S3C2410_TICNT_ENABLE; | ||
326 | } | ||
327 | |||
328 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
289 | return 0; | 329 | return 0; |
290 | } | 330 | } |
291 | 331 | ||
@@ -339,7 +379,8 @@ static const struct rtc_class_ops s3c_rtcops = { | |||
339 | .set_alarm = s3c_rtc_setalarm, | 379 | .set_alarm = s3c_rtc_setalarm, |
340 | .irq_set_freq = s3c_rtc_setfreq, | 380 | .irq_set_freq = s3c_rtc_setfreq, |
341 | .irq_set_state = s3c_rtc_setpie, | 381 | .irq_set_state = s3c_rtc_setpie, |
342 | .proc = s3c_rtc_proc, | 382 | .proc = s3c_rtc_proc, |
383 | .alarm_irq_enable = s3c_rtc_setaie, | ||
343 | }; | 384 | }; |
344 | 385 | ||
345 | static void s3c_rtc_enable(struct platform_device *pdev, int en) | 386 | static void s3c_rtc_enable(struct platform_device *pdev, int en) |
@@ -351,33 +392,42 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) | |||
351 | return; | 392 | return; |
352 | 393 | ||
353 | if (!en) { | 394 | if (!en) { |
354 | tmp = readb(base + S3C2410_RTCCON); | 395 | tmp = readw(base + S3C2410_RTCCON); |
355 | writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); | 396 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) |
356 | 397 | tmp &= ~S3C64XX_RTCCON_TICEN; | |
357 | tmp = readb(base + S3C2410_TICNT); | 398 | tmp &= ~S3C2410_RTCCON_RTCEN; |
358 | writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); | 399 | writew(tmp, base + S3C2410_RTCCON); |
400 | |||
401 | if (s3c_rtc_cpu_type == TYPE_S3C2410) { | ||
402 | tmp = readb(base + S3C2410_TICNT); | ||
403 | tmp &= ~S3C2410_TICNT_ENABLE; | ||
404 | writeb(tmp, base + S3C2410_TICNT); | ||
405 | } | ||
359 | } else { | 406 | } else { |
360 | /* re-enable the device, and check it is ok */ | 407 | /* re-enable the device, and check it is ok */ |
361 | 408 | ||
362 | if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ | 409 | if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { |
363 | dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); | 410 | dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); |
364 | 411 | ||
365 | tmp = readb(base + S3C2410_RTCCON); | 412 | tmp = readw(base + S3C2410_RTCCON); |
366 | writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON); | 413 | writew(tmp | S3C2410_RTCCON_RTCEN, |
414 | base + S3C2410_RTCCON); | ||
367 | } | 415 | } |
368 | 416 | ||
369 | if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ | 417 | if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { |
370 | dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); | 418 | dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); |
371 | 419 | ||
372 | tmp = readb(base + S3C2410_RTCCON); | 420 | tmp = readw(base + S3C2410_RTCCON); |
373 | writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON); | 421 | writew(tmp & ~S3C2410_RTCCON_CNTSEL, |
422 | base + S3C2410_RTCCON); | ||
374 | } | 423 | } |
375 | 424 | ||
376 | if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ | 425 | if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { |
377 | dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); | 426 | dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); |
378 | 427 | ||
379 | tmp = readb(base + S3C2410_RTCCON); | 428 | tmp = readw(base + S3C2410_RTCCON); |
380 | writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON); | 429 | writew(tmp & ~S3C2410_RTCCON_CLKRST, |
430 | base + S3C2410_RTCCON); | ||
381 | } | 431 | } |
382 | } | 432 | } |
383 | } | 433 | } |
@@ -392,6 +442,10 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) | |||
392 | s3c_rtc_setpie(&dev->dev, 0); | 442 | s3c_rtc_setpie(&dev->dev, 0); |
393 | s3c_rtc_setaie(0); | 443 | s3c_rtc_setaie(0); |
394 | 444 | ||
445 | clk_disable(rtc_clk); | ||
446 | clk_put(rtc_clk); | ||
447 | rtc_clk = NULL; | ||
448 | |||
395 | iounmap(s3c_rtc_base); | 449 | iounmap(s3c_rtc_base); |
396 | release_resource(s3c_rtc_mem); | 450 | release_resource(s3c_rtc_mem); |
397 | kfree(s3c_rtc_mem); | 451 | kfree(s3c_rtc_mem); |
@@ -402,6 +456,7 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev) | |||
402 | static int __devinit s3c_rtc_probe(struct platform_device *pdev) | 456 | static int __devinit s3c_rtc_probe(struct platform_device *pdev) |
403 | { | 457 | { |
404 | struct rtc_device *rtc; | 458 | struct rtc_device *rtc; |
459 | struct rtc_time rtc_tm; | ||
405 | struct resource *res; | 460 | struct resource *res; |
406 | int ret; | 461 | int ret; |
407 | 462 | ||
@@ -449,14 +504,22 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
449 | goto err_nomap; | 504 | goto err_nomap; |
450 | } | 505 | } |
451 | 506 | ||
507 | rtc_clk = clk_get(&pdev->dev, "rtc"); | ||
508 | if (IS_ERR(rtc_clk)) { | ||
509 | dev_err(&pdev->dev, "failed to find rtc clock source\n"); | ||
510 | ret = PTR_ERR(rtc_clk); | ||
511 | rtc_clk = NULL; | ||
512 | goto err_clk; | ||
513 | } | ||
514 | |||
515 | clk_enable(rtc_clk); | ||
516 | |||
452 | /* check to see if everything is setup correctly */ | 517 | /* check to see if everything is setup correctly */ |
453 | 518 | ||
454 | s3c_rtc_enable(pdev, 1); | 519 | s3c_rtc_enable(pdev, 1); |
455 | 520 | ||
456 | pr_debug("s3c2410_rtc: RTCCON=%02x\n", | 521 | pr_debug("s3c2410_rtc: RTCCON=%02x\n", |
457 | readb(s3c_rtc_base + S3C2410_RTCCON)); | 522 | readw(s3c_rtc_base + S3C2410_RTCCON)); |
458 | |||
459 | s3c_rtc_setfreq(&pdev->dev, 1); | ||
460 | 523 | ||
461 | device_init_wakeup(&pdev->dev, 1); | 524 | device_init_wakeup(&pdev->dev, 1); |
462 | 525 | ||
@@ -471,13 +534,42 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
471 | goto err_nortc; | 534 | goto err_nortc; |
472 | } | 535 | } |
473 | 536 | ||
474 | rtc->max_user_freq = 128; | 537 | s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; |
538 | |||
539 | /* Check RTC Time */ | ||
540 | |||
541 | s3c_rtc_gettime(NULL, &rtc_tm); | ||
542 | |||
543 | if (rtc_valid_tm(&rtc_tm)) { | ||
544 | rtc_tm.tm_year = 100; | ||
545 | rtc_tm.tm_mon = 0; | ||
546 | rtc_tm.tm_mday = 1; | ||
547 | rtc_tm.tm_hour = 0; | ||
548 | rtc_tm.tm_min = 0; | ||
549 | rtc_tm.tm_sec = 0; | ||
550 | |||
551 | s3c_rtc_settime(NULL, &rtc_tm); | ||
552 | |||
553 | dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); | ||
554 | } | ||
555 | |||
556 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
557 | rtc->max_user_freq = 32768; | ||
558 | else | ||
559 | rtc->max_user_freq = 128; | ||
475 | 560 | ||
476 | platform_set_drvdata(pdev, rtc); | 561 | platform_set_drvdata(pdev, rtc); |
562 | |||
563 | s3c_rtc_setfreq(&pdev->dev, 1); | ||
564 | |||
477 | return 0; | 565 | return 0; |
478 | 566 | ||
479 | err_nortc: | 567 | err_nortc: |
480 | s3c_rtc_enable(pdev, 0); | 568 | s3c_rtc_enable(pdev, 0); |
569 | clk_disable(rtc_clk); | ||
570 | clk_put(rtc_clk); | ||
571 | |||
572 | err_clk: | ||
481 | iounmap(s3c_rtc_base); | 573 | iounmap(s3c_rtc_base); |
482 | 574 | ||
483 | err_nomap: | 575 | err_nomap: |
@@ -491,20 +583,38 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev) | |||
491 | 583 | ||
492 | /* RTC Power management control */ | 584 | /* RTC Power management control */ |
493 | 585 | ||
494 | static int ticnt_save; | 586 | static int ticnt_save, ticnt_en_save; |
495 | 587 | ||
496 | static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 588 | static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) |
497 | { | 589 | { |
498 | /* save TICNT for anyone using periodic interrupts */ | 590 | /* save TICNT for anyone using periodic interrupts */ |
499 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); | 591 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); |
592 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | ||
593 | ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); | ||
594 | ticnt_en_save &= S3C64XX_RTCCON_TICEN; | ||
595 | } | ||
500 | s3c_rtc_enable(pdev, 0); | 596 | s3c_rtc_enable(pdev, 0); |
597 | |||
598 | if (device_may_wakeup(&pdev->dev)) | ||
599 | enable_irq_wake(s3c_rtc_alarmno); | ||
600 | |||
501 | return 0; | 601 | return 0; |
502 | } | 602 | } |
503 | 603 | ||
504 | static int s3c_rtc_resume(struct platform_device *pdev) | 604 | static int s3c_rtc_resume(struct platform_device *pdev) |
505 | { | 605 | { |
606 | unsigned int tmp; | ||
607 | |||
506 | s3c_rtc_enable(pdev, 1); | 608 | s3c_rtc_enable(pdev, 1); |
507 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); | 609 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); |
610 | if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { | ||
611 | tmp = readw(s3c_rtc_base + S3C2410_RTCCON); | ||
612 | writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); | ||
613 | } | ||
614 | |||
615 | if (device_may_wakeup(&pdev->dev)) | ||
616 | disable_irq_wake(s3c_rtc_alarmno); | ||
617 | |||
508 | return 0; | 618 | return 0; |
509 | } | 619 | } |
510 | #else | 620 | #else |
@@ -512,13 +622,27 @@ static int s3c_rtc_resume(struct platform_device *pdev) | |||
512 | #define s3c_rtc_resume NULL | 622 | #define s3c_rtc_resume NULL |
513 | #endif | 623 | #endif |
514 | 624 | ||
515 | static struct platform_driver s3c2410_rtc_driver = { | 625 | static struct platform_device_id s3c_rtc_driver_ids[] = { |
626 | { | ||
627 | .name = "s3c2410-rtc", | ||
628 | .driver_data = TYPE_S3C2410, | ||
629 | }, { | ||
630 | .name = "s3c64xx-rtc", | ||
631 | .driver_data = TYPE_S3C64XX, | ||
632 | }, | ||
633 | { } | ||
634 | }; | ||
635 | |||
636 | MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); | ||
637 | |||
638 | static struct platform_driver s3c_rtc_driver = { | ||
516 | .probe = s3c_rtc_probe, | 639 | .probe = s3c_rtc_probe, |
517 | .remove = __devexit_p(s3c_rtc_remove), | 640 | .remove = __devexit_p(s3c_rtc_remove), |
518 | .suspend = s3c_rtc_suspend, | 641 | .suspend = s3c_rtc_suspend, |
519 | .resume = s3c_rtc_resume, | 642 | .resume = s3c_rtc_resume, |
643 | .id_table = s3c_rtc_driver_ids, | ||
520 | .driver = { | 644 | .driver = { |
521 | .name = "s3c2410-rtc", | 645 | .name = "s3c-rtc", |
522 | .owner = THIS_MODULE, | 646 | .owner = THIS_MODULE, |
523 | }, | 647 | }, |
524 | }; | 648 | }; |
@@ -528,12 +652,12 @@ static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics | |||
528 | static int __init s3c_rtc_init(void) | 652 | static int __init s3c_rtc_init(void) |
529 | { | 653 | { |
530 | printk(banner); | 654 | printk(banner); |
531 | return platform_driver_register(&s3c2410_rtc_driver); | 655 | return platform_driver_register(&s3c_rtc_driver); |
532 | } | 656 | } |
533 | 657 | ||
534 | static void __exit s3c_rtc_exit(void) | 658 | static void __exit s3c_rtc_exit(void) |
535 | { | 659 | { |
536 | platform_driver_unregister(&s3c2410_rtc_driver); | 660 | platform_driver_unregister(&s3c_rtc_driver); |
537 | } | 661 | } |
538 | 662 | ||
539 | module_init(s3c_rtc_init); | 663 | module_init(s3c_rtc_init); |