Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/drivers/char/s3c2410-rtc.c
Parent Directory | Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 13531 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 13531 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* drivers/char/s3c2410_rtc.c |
2 | * |
3 | * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk> |
4 | * http://www.simtec.co.uk/products/SWLINUX/ |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. |
9 | * |
10 | * S3C2410 Internal RTC Driver |
11 | * |
12 | * Changelog: |
13 | * 08-Nov-2004 BJD Initial creation |
14 | * 12-Nov-2004 BJD Added periodic IRQ and PM code |
15 | * 22-Nov-2004 BJD Sign-test on alarm code to check for <0 |
16 | * 10-Mar-2005 LCVR Changed S3C2410_VA_RTC to S3C24XX_VA_RTC |
17 | */ |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/fs.h> |
21 | #include <linux/string.h> |
22 | #include <linux/init.h> |
23 | #include <linux/device.h> |
24 | #include <linux/interrupt.h> |
25 | #include <linux/rtc.h> |
26 | #include <linux/bcd.h> |
27 | |
28 | #include <asm/hardware.h> |
29 | #include <asm/uaccess.h> |
30 | #include <asm/io.h> |
31 | #include <asm/irq.h> |
32 | #include <asm/rtc.h> |
33 | |
34 | #include <asm/mach/time.h> |
35 | |
36 | #include <asm/hardware/clock.h> |
37 | #include <asm/arch/regs-rtc.h> |
38 | |
39 | /* need this for the RTC_AF definitions */ |
40 | #include <linux/mc146818rtc.h> |
41 | |
42 | #undef S3C24XX_VA_RTC |
43 | #define S3C24XX_VA_RTC s3c2410_rtc_base |
44 | |
45 | static struct resource *s3c2410_rtc_mem; |
46 | |
47 | static void __iomem *s3c2410_rtc_base; |
48 | static int s3c2410_rtc_alarmno = NO_IRQ; |
49 | static int s3c2410_rtc_tickno = NO_IRQ; |
50 | static int s3c2410_rtc_freq = 1; |
51 | |
52 | static DEFINE_SPINLOCK(s3c2410_rtc_pie_lock); |
53 | |
54 | /* IRQ Handlers */ |
55 | |
56 | static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r) |
57 | { |
58 | rtc_update(1, RTC_AF | RTC_IRQF); |
59 | return IRQ_HANDLED; |
60 | } |
61 | |
62 | static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r) |
63 | { |
64 | rtc_update(1, RTC_PF | RTC_IRQF); |
65 | return IRQ_HANDLED; |
66 | } |
67 | |
68 | /* Update control registers */ |
69 | static void s3c2410_rtc_setaie(int to) |
70 | { |
71 | unsigned int tmp; |
72 | |
73 | pr_debug("%s: aie=%d\n", __FUNCTION__, to); |
74 | |
75 | tmp = readb(S3C2410_RTCALM); |
76 | |
77 | if (to) |
78 | tmp |= S3C2410_RTCALM_ALMEN; |
79 | else |
80 | tmp &= ~S3C2410_RTCALM_ALMEN; |
81 | |
82 | |
83 | writeb(tmp, S3C2410_RTCALM); |
84 | } |
85 | |
86 | static void s3c2410_rtc_setpie(int to) |
87 | { |
88 | unsigned int tmp; |
89 | |
90 | pr_debug("%s: pie=%d\n", __FUNCTION__, to); |
91 | |
92 | spin_lock_irq(&s3c2410_rtc_pie_lock); |
93 | tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; |
94 | |
95 | if (to) |
96 | tmp |= S3C2410_TICNT_ENABLE; |
97 | |
98 | writeb(tmp, S3C2410_TICNT); |
99 | spin_unlock_irq(&s3c2410_rtc_pie_lock); |
100 | } |
101 | |
102 | static void s3c2410_rtc_setfreq(int freq) |
103 | { |
104 | unsigned int tmp; |
105 | |
106 | spin_lock_irq(&s3c2410_rtc_pie_lock); |
107 | tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE; |
108 | |
109 | s3c2410_rtc_freq = freq; |
110 | |
111 | tmp |= (128 / freq)-1; |
112 | |
113 | writeb(tmp, S3C2410_TICNT); |
114 | spin_unlock_irq(&s3c2410_rtc_pie_lock); |
115 | } |
116 | |
117 | /* Time read/write */ |
118 | |
119 | static int s3c2410_rtc_gettime(struct rtc_time *rtc_tm) |
120 | { |
121 | unsigned int have_retried = 0; |
122 | |
123 | retry_get_time: |
124 | rtc_tm->tm_min = readb(S3C2410_RTCMIN); |
125 | rtc_tm->tm_hour = readb(S3C2410_RTCHOUR); |
126 | rtc_tm->tm_mday = readb(S3C2410_RTCDATE); |
127 | rtc_tm->tm_mon = readb(S3C2410_RTCMON); |
128 | rtc_tm->tm_year = readb(S3C2410_RTCYEAR); |
129 | rtc_tm->tm_sec = readb(S3C2410_RTCSEC); |
130 | |
131 | /* the only way to work out wether the system was mid-update |
132 | * when we read it is to check the second counter, and if it |
133 | * is zero, then we re-try the entire read |
134 | */ |
135 | |
136 | if (rtc_tm->tm_sec == 0 && !have_retried) { |
137 | have_retried = 1; |
138 | goto retry_get_time; |
139 | } |
140 | |
141 | pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", |
142 | rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, |
143 | rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); |
144 | |
145 | BCD_TO_BIN(rtc_tm->tm_sec); |
146 | BCD_TO_BIN(rtc_tm->tm_min); |
147 | BCD_TO_BIN(rtc_tm->tm_hour); |
148 | BCD_TO_BIN(rtc_tm->tm_mday); |
149 | BCD_TO_BIN(rtc_tm->tm_mon); |
150 | BCD_TO_BIN(rtc_tm->tm_year); |
151 | |
152 | rtc_tm->tm_year += 100; |
153 | rtc_tm->tm_mon -= 1; |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | |
159 | static int s3c2410_rtc_settime(struct rtc_time *tm) |
160 | { |
161 | /* the rtc gets round the y2k problem by just not supporting it */ |
162 | |
163 | if (tm->tm_year < 100) |
164 | return -EINVAL; |
165 | |
166 | writeb(BIN2BCD(tm->tm_sec), S3C2410_RTCSEC); |
167 | writeb(BIN2BCD(tm->tm_min), S3C2410_RTCMIN); |
168 | writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR); |
169 | writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE); |
170 | writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON); |
171 | writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR); |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | static int s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm) |
177 | { |
178 | struct rtc_time *alm_tm = &alrm->time; |
179 | unsigned int alm_en; |
180 | |
181 | alm_tm->tm_sec = readb(S3C2410_ALMSEC); |
182 | alm_tm->tm_min = readb(S3C2410_ALMMIN); |
183 | alm_tm->tm_hour = readb(S3C2410_ALMHOUR); |
184 | alm_tm->tm_mon = readb(S3C2410_ALMMON); |
185 | alm_tm->tm_mday = readb(S3C2410_ALMDATE); |
186 | alm_tm->tm_year = readb(S3C2410_ALMYEAR); |
187 | |
188 | alm_en = readb(S3C2410_RTCALM); |
189 | |
190 | pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", |
191 | alm_en, |
192 | alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, |
193 | alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); |
194 | |
195 | |
196 | /* decode the alarm enable field */ |
197 | |
198 | if (alm_en & S3C2410_RTCALM_SECEN) { |
199 | BCD_TO_BIN(alm_tm->tm_sec); |
200 | } else { |
201 | alm_tm->tm_sec = 0xff; |
202 | } |
203 | |
204 | if (alm_en & S3C2410_RTCALM_MINEN) { |
205 | BCD_TO_BIN(alm_tm->tm_min); |
206 | } else { |
207 | alm_tm->tm_min = 0xff; |
208 | } |
209 | |
210 | if (alm_en & S3C2410_RTCALM_HOUREN) { |
211 | BCD_TO_BIN(alm_tm->tm_hour); |
212 | } else { |
213 | alm_tm->tm_hour = 0xff; |
214 | } |
215 | |
216 | if (alm_en & S3C2410_RTCALM_DAYEN) { |
217 | BCD_TO_BIN(alm_tm->tm_mday); |
218 | } else { |
219 | alm_tm->tm_mday = 0xff; |
220 | } |
221 | |
222 | if (alm_en & S3C2410_RTCALM_MONEN) { |
223 | BCD_TO_BIN(alm_tm->tm_mon); |
224 | alm_tm->tm_mon -= 1; |
225 | } else { |
226 | alm_tm->tm_mon = 0xff; |
227 | } |
228 | |
229 | if (alm_en & S3C2410_RTCALM_YEAREN) { |
230 | BCD_TO_BIN(alm_tm->tm_year); |
231 | } else { |
232 | alm_tm->tm_year = 0xffff; |
233 | } |
234 | |
235 | /* todo - set alrm->enabled ? */ |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm) |
241 | { |
242 | struct rtc_time *tm = &alrm->time; |
243 | unsigned int alrm_en; |
244 | |
245 | pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", |
246 | alrm->enabled, |
247 | tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, |
248 | tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); |
249 | |
250 | if (alrm->enabled || 1) { |
251 | alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; |
252 | writeb(0x00, S3C2410_RTCALM); |
253 | |
254 | if (tm->tm_sec < 60 && tm->tm_sec >= 0) { |
255 | alrm_en |= S3C2410_RTCALM_SECEN; |
256 | writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC); |
257 | } |
258 | |
259 | if (tm->tm_min < 60 && tm->tm_min >= 0) { |
260 | alrm_en |= S3C2410_RTCALM_MINEN; |
261 | writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN); |
262 | } |
263 | |
264 | if (tm->tm_hour < 24 && tm->tm_hour >= 0) { |
265 | alrm_en |= S3C2410_RTCALM_HOUREN; |
266 | writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR); |
267 | } |
268 | |
269 | pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); |
270 | |
271 | writeb(alrm_en, S3C2410_RTCALM); |
272 | enable_irq_wake(s3c2410_rtc_alarmno); |
273 | } else { |
274 | alrm_en = readb(S3C2410_RTCALM); |
275 | alrm_en &= ~S3C2410_RTCALM_ALMEN; |
276 | writeb(alrm_en, S3C2410_RTCALM); |
277 | disable_irq_wake(s3c2410_rtc_alarmno); |
278 | } |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg) |
284 | { |
285 | switch (cmd) { |
286 | case RTC_AIE_OFF: |
287 | case RTC_AIE_ON: |
288 | s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0); |
289 | return 0; |
290 | |
291 | case RTC_PIE_OFF: |
292 | case RTC_PIE_ON: |
293 | s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0); |
294 | return 0; |
295 | |
296 | case RTC_IRQP_READ: |
297 | return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg); |
298 | |
299 | case RTC_IRQP_SET: |
300 | if (arg < 1 || arg > 64) |
301 | return -EINVAL; |
302 | |
303 | if (!capable(CAP_SYS_RESOURCE)) |
304 | return -EACCES; |
305 | |
306 | /* check for power of 2 */ |
307 | |
308 | if ((arg & (arg-1)) != 0) |
309 | return -EINVAL; |
310 | |
311 | pr_debug("s3c2410_rtc: setting frequency %ld\n", arg); |
312 | |
313 | s3c2410_rtc_setfreq(arg); |
314 | return 0; |
315 | |
316 | case RTC_UIE_ON: |
317 | case RTC_UIE_OFF: |
318 | return -EINVAL; |
319 | } |
320 | |
321 | return -EINVAL; |
322 | } |
323 | |
324 | static int s3c2410_rtc_proc(char *buf) |
325 | { |
326 | unsigned int rtcalm = readb(S3C2410_RTCALM); |
327 | unsigned int ticnt = readb (S3C2410_TICNT); |
328 | char *p = buf; |
329 | |
330 | p += sprintf(p, "alarm_IRQ\t: %s\n", |
331 | (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" ); |
332 | p += sprintf(p, "periodic_IRQ\t: %s\n", |
333 | (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); |
334 | p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq); |
335 | |
336 | return p - buf; |
337 | } |
338 | |
339 | static int s3c2410_rtc_open(void) |
340 | { |
341 | int ret; |
342 | |
343 | ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq, |
344 | SA_INTERRUPT, "s3c2410-rtc alarm", NULL); |
345 | |
346 | if (ret) |
347 | printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno); |
348 | |
349 | ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq, |
350 | SA_INTERRUPT, "s3c2410-rtc tick", NULL); |
351 | |
352 | if (ret) { |
353 | printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno); |
354 | goto tick_err; |
355 | } |
356 | |
357 | return ret; |
358 | |
359 | tick_err: |
360 | free_irq(s3c2410_rtc_alarmno, NULL); |
361 | return ret; |
362 | } |
363 | |
364 | static void s3c2410_rtc_release(void) |
365 | { |
366 | /* do not clear AIE here, it may be needed for wake */ |
367 | |
368 | s3c2410_rtc_setpie(0); |
369 | free_irq(s3c2410_rtc_alarmno, NULL); |
370 | free_irq(s3c2410_rtc_tickno, NULL); |
371 | } |
372 | |
373 | static struct rtc_ops s3c2410_rtcops = { |
374 | .owner = THIS_MODULE, |
375 | .open = s3c2410_rtc_open, |
376 | .release = s3c2410_rtc_release, |
377 | .ioctl = s3c2410_rtc_ioctl, |
378 | .read_time = s3c2410_rtc_gettime, |
379 | .set_time = s3c2410_rtc_settime, |
380 | .read_alarm = s3c2410_rtc_getalarm, |
381 | .set_alarm = s3c2410_rtc_setalarm, |
382 | .proc = s3c2410_rtc_proc, |
383 | }; |
384 | |
385 | static void s3c2410_rtc_enable(struct device *dev, int en) |
386 | { |
387 | unsigned int tmp; |
388 | |
389 | if (s3c2410_rtc_base == NULL) |
390 | return; |
391 | |
392 | if (!en) { |
393 | tmp = readb(S3C2410_RTCCON); |
394 | writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON); |
395 | |
396 | tmp = readb(S3C2410_TICNT); |
397 | writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT); |
398 | } else { |
399 | /* re-enable the device, and check it is ok */ |
400 | |
401 | if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ |
402 | dev_info(dev, "rtc disabled, re-enabling\n"); |
403 | |
404 | tmp = readb(S3C2410_RTCCON); |
405 | writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON); |
406 | } |
407 | |
408 | if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ |
409 | dev_info(dev, "removing S3C2410_RTCCON_CNTSEL\n"); |
410 | |
411 | tmp = readb(S3C2410_RTCCON); |
412 | writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON); |
413 | } |
414 | |
415 | if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ |
416 | dev_info(dev, "removing S3C2410_RTCCON_CLKRST\n"); |
417 | |
418 | tmp = readb(S3C2410_RTCCON); |
419 | writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON); |
420 | } |
421 | } |
422 | } |
423 | |
424 | static int s3c2410_rtc_remove(struct device *dev) |
425 | { |
426 | unregister_rtc(&s3c2410_rtcops); |
427 | |
428 | s3c2410_rtc_setpie(0); |
429 | s3c2410_rtc_setaie(0); |
430 | |
431 | if (s3c2410_rtc_mem != NULL) { |
432 | pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n"); |
433 | iounmap(s3c2410_rtc_base); |
434 | release_resource(s3c2410_rtc_mem); |
435 | kfree(s3c2410_rtc_mem); |
436 | } |
437 | |
438 | return 0; |
439 | } |
440 | |
441 | static int s3c2410_rtc_probe(struct device *dev) |
442 | { |
443 | struct platform_device *pdev = to_platform_device(dev); |
444 | struct resource *res; |
445 | int ret; |
446 | |
447 | pr_debug("%s: probe=%p, device=%p\n", __FUNCTION__, pdev, dev); |
448 | |
449 | /* find the IRQs */ |
450 | |
451 | s3c2410_rtc_tickno = platform_get_irq(pdev, 1); |
452 | if (s3c2410_rtc_tickno <= 0) { |
453 | dev_err(dev, "no irq for rtc tick\n"); |
454 | return -ENOENT; |
455 | } |
456 | |
457 | s3c2410_rtc_alarmno = platform_get_irq(pdev, 0); |
458 | if (s3c2410_rtc_alarmno <= 0) { |
459 | dev_err(dev, "no irq for alarm\n"); |
460 | return -ENOENT; |
461 | } |
462 | |
463 | pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", |
464 | s3c2410_rtc_tickno, s3c2410_rtc_alarmno); |
465 | |
466 | /* get the memory region */ |
467 | |
468 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
469 | if (res == NULL) { |
470 | dev_err(dev, "failed to get memory region resource\n"); |
471 | return -ENOENT; |
472 | } |
473 | |
474 | s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1, |
475 | pdev->name); |
476 | |
477 | if (s3c2410_rtc_mem == NULL) { |
478 | dev_err(dev, "failed to reserve memory region\n"); |
479 | ret = -ENOENT; |
480 | goto exit_err; |
481 | } |
482 | |
483 | s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1); |
484 | if (s3c2410_rtc_base == NULL) { |
485 | dev_err(dev, "failed ioremap()\n"); |
486 | ret = -EINVAL; |
487 | goto exit_err; |
488 | } |
489 | |
490 | s3c2410_rtc_mem = res; |
491 | pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base); |
492 | |
493 | pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON)); |
494 | |
495 | /* check to see if everything is setup correctly */ |
496 | |
497 | s3c2410_rtc_enable(dev, 1); |
498 | |
499 | pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON)); |
500 | |
501 | s3c2410_rtc_setfreq(s3c2410_rtc_freq); |
502 | |
503 | /* register RTC and exit */ |
504 | |
505 | register_rtc(&s3c2410_rtcops); |
506 | return 0; |
507 | |
508 | exit_err: |
509 | dev_err(dev, "error %d during initialisation\n", ret); |
510 | |
511 | return ret; |
512 | } |
513 | |
514 | #ifdef CONFIG_PM |
515 | |
516 | /* S3C2410 RTC Power management control */ |
517 | |
518 | static struct timespec s3c2410_rtc_delta; |
519 | |
520 | static int ticnt_save; |
521 | |
522 | static int s3c2410_rtc_suspend(struct device *dev, pm_message_t state, u32 level) |
523 | { |
524 | struct rtc_time tm; |
525 | struct timespec time; |
526 | |
527 | time.tv_nsec = 0; |
528 | |
529 | if (level == SUSPEND_POWER_DOWN) { |
530 | /* save TICNT for anyone using periodic interrupts */ |
531 | |
532 | ticnt_save = readb(S3C2410_TICNT); |
533 | |
534 | /* calculate time delta for suspend */ |
535 | |
536 | s3c2410_rtc_gettime(&tm); |
537 | rtc_tm_to_time(&tm, &time.tv_sec); |
538 | save_time_delta(&s3c2410_rtc_delta, &time); |
539 | s3c2410_rtc_enable(dev, 0); |
540 | } |
541 | |
542 | return 0; |
543 | } |
544 | |
545 | static int s3c2410_rtc_resume(struct device *dev, u32 level) |
546 | { |
547 | struct rtc_time tm; |
548 | struct timespec time; |
549 | |
550 | time.tv_nsec = 0; |
551 | |
552 | s3c2410_rtc_enable(dev, 1); |
553 | s3c2410_rtc_gettime(&tm); |
554 | rtc_tm_to_time(&tm, &time.tv_sec); |
555 | restore_time_delta(&s3c2410_rtc_delta, &time); |
556 | |
557 | writeb(ticnt_save, S3C2410_TICNT); |
558 | return 0; |
559 | } |
560 | #else |
561 | #define s3c2410_rtc_suspend NULL |
562 | #define s3c2410_rtc_resume NULL |
563 | #endif |
564 | |
565 | static struct device_driver s3c2410_rtcdrv = { |
566 | .name = "s3c2410-rtc", |
567 | .bus = &platform_bus_type, |
568 | .probe = s3c2410_rtc_probe, |
569 | .remove = s3c2410_rtc_remove, |
570 | .suspend = s3c2410_rtc_suspend, |
571 | .resume = s3c2410_rtc_resume, |
572 | }; |
573 | |
574 | static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n"; |
575 | |
576 | static int __init s3c2410_rtc_init(void) |
577 | { |
578 | printk(banner); |
579 | return driver_register(&s3c2410_rtcdrv); |
580 | } |
581 | |
582 | static void __exit s3c2410_rtc_exit(void) |
583 | { |
584 | driver_unregister(&s3c2410_rtcdrv); |
585 | } |
586 | |
587 | module_init(s3c2410_rtc_init); |
588 | module_exit(s3c2410_rtc_exit); |
589 | |
590 | MODULE_DESCRIPTION("S3C24XX RTC Driver"); |
591 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); |
592 | MODULE_LICENSE("GPL"); |