diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/vr41xx_rtc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/vr41xx_rtc.c')
-rw-r--r-- | drivers/char/vr41xx_rtc.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/drivers/char/vr41xx_rtc.c b/drivers/char/vr41xx_rtc.c new file mode 100644 index 000000000000..a6dbe4da030c --- /dev/null +++ b/drivers/char/vr41xx_rtc.c | |||
@@ -0,0 +1,709 @@ | |||
1 | /* | ||
2 | * Driver for NEC VR4100 series Real Time Clock unit. | ||
3 | * | ||
4 | * Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> | ||
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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/ioport.h> | ||
24 | #include <linux/irq.h> | ||
25 | #include <linux/mc146818rtc.h> | ||
26 | #include <linux/miscdevice.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/poll.h> | ||
29 | #include <linux/rtc.h> | ||
30 | #include <linux/spinlock.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <linux/wait.h> | ||
33 | |||
34 | #include <asm/div64.h> | ||
35 | #include <asm/io.h> | ||
36 | #include <asm/time.h> | ||
37 | #include <asm/uaccess.h> | ||
38 | #include <asm/vr41xx/vr41xx.h> | ||
39 | |||
40 | MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>"); | ||
41 | MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | #define RTC1_TYPE1_START 0x0b0000c0UL | ||
45 | #define RTC1_TYPE1_END 0x0b0000dfUL | ||
46 | #define RTC2_TYPE1_START 0x0b0001c0UL | ||
47 | #define RTC2_TYPE1_END 0x0b0001dfUL | ||
48 | |||
49 | #define RTC1_TYPE2_START 0x0f000100UL | ||
50 | #define RTC1_TYPE2_END 0x0f00011fUL | ||
51 | #define RTC2_TYPE2_START 0x0f000120UL | ||
52 | #define RTC2_TYPE2_END 0x0f00013fUL | ||
53 | |||
54 | #define RTC1_SIZE 0x20 | ||
55 | #define RTC2_SIZE 0x20 | ||
56 | |||
57 | /* RTC 1 registers */ | ||
58 | #define ETIMELREG 0x00 | ||
59 | #define ETIMEMREG 0x02 | ||
60 | #define ETIMEHREG 0x04 | ||
61 | /* RFU */ | ||
62 | #define ECMPLREG 0x08 | ||
63 | #define ECMPMREG 0x0a | ||
64 | #define ECMPHREG 0x0c | ||
65 | /* RFU */ | ||
66 | #define RTCL1LREG 0x10 | ||
67 | #define RTCL1HREG 0x12 | ||
68 | #define RTCL1CNTLREG 0x14 | ||
69 | #define RTCL1CNTHREG 0x16 | ||
70 | #define RTCL2LREG 0x18 | ||
71 | #define RTCL2HREG 0x1a | ||
72 | #define RTCL2CNTLREG 0x1c | ||
73 | #define RTCL2CNTHREG 0x1e | ||
74 | |||
75 | /* RTC 2 registers */ | ||
76 | #define TCLKLREG 0x00 | ||
77 | #define TCLKHREG 0x02 | ||
78 | #define TCLKCNTLREG 0x04 | ||
79 | #define TCLKCNTHREG 0x06 | ||
80 | /* RFU */ | ||
81 | #define RTCINTREG 0x1e | ||
82 | #define TCLOCK_INT 0x08 | ||
83 | #define RTCLONG2_INT 0x04 | ||
84 | #define RTCLONG1_INT 0x02 | ||
85 | #define ELAPSEDTIME_INT 0x01 | ||
86 | |||
87 | #define RTC_FREQUENCY 32768 | ||
88 | #define MAX_PERIODIC_RATE 6553 | ||
89 | #define MAX_USER_PERIODIC_RATE 64 | ||
90 | |||
91 | static void __iomem *rtc1_base; | ||
92 | static void __iomem *rtc2_base; | ||
93 | |||
94 | #define rtc1_read(offset) readw(rtc1_base + (offset)) | ||
95 | #define rtc1_write(offset, value) writew((value), rtc1_base + (offset)) | ||
96 | |||
97 | #define rtc2_read(offset) readw(rtc2_base + (offset)) | ||
98 | #define rtc2_write(offset, value) writew((value), rtc2_base + (offset)) | ||
99 | |||
100 | static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ | ||
101 | |||
102 | static spinlock_t rtc_task_lock; | ||
103 | static wait_queue_head_t rtc_wait; | ||
104 | static unsigned long rtc_irq_data; | ||
105 | static struct fasync_struct *rtc_async_queue; | ||
106 | static rtc_task_t *rtc_callback; | ||
107 | static char rtc_name[] = "RTC"; | ||
108 | static unsigned long periodic_frequency; | ||
109 | static unsigned long periodic_count; | ||
110 | |||
111 | typedef enum { | ||
112 | RTC_RELEASE, | ||
113 | RTC_OPEN, | ||
114 | } rtc_status_t; | ||
115 | |||
116 | static rtc_status_t rtc_status; | ||
117 | |||
118 | typedef enum { | ||
119 | FUNCTION_RTC_IOCTL, | ||
120 | FUNCTION_RTC_CONTROL, | ||
121 | } rtc_callfrom_t; | ||
122 | |||
123 | struct resource rtc_resource[2] = { | ||
124 | { .name = rtc_name, | ||
125 | .flags = IORESOURCE_MEM, }, | ||
126 | { .name = rtc_name, | ||
127 | .flags = IORESOURCE_MEM, }, | ||
128 | }; | ||
129 | |||
130 | #define RTC_NUM_RESOURCES sizeof(rtc_resource) / sizeof(struct resource) | ||
131 | |||
132 | static inline unsigned long read_elapsed_second(void) | ||
133 | { | ||
134 | unsigned long first_low, first_mid, first_high; | ||
135 | unsigned long second_low, second_mid, second_high; | ||
136 | |||
137 | do { | ||
138 | first_low = rtc1_read(ETIMELREG); | ||
139 | first_mid = rtc1_read(ETIMEMREG); | ||
140 | first_high = rtc1_read(ETIMEHREG); | ||
141 | second_low = rtc1_read(ETIMELREG); | ||
142 | second_mid = rtc1_read(ETIMEMREG); | ||
143 | second_high = rtc1_read(ETIMEHREG); | ||
144 | } while (first_low != second_low || first_mid != second_mid || | ||
145 | first_high != second_high); | ||
146 | |||
147 | return (first_high << 17) | (first_mid << 1) | (first_low >> 15); | ||
148 | } | ||
149 | |||
150 | static inline void write_elapsed_second(unsigned long sec) | ||
151 | { | ||
152 | spin_lock_irq(&rtc_lock); | ||
153 | |||
154 | rtc1_write(ETIMELREG, (uint16_t)(sec << 15)); | ||
155 | rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1)); | ||
156 | rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17)); | ||
157 | |||
158 | spin_unlock_irq(&rtc_lock); | ||
159 | } | ||
160 | |||
161 | static void set_alarm(struct rtc_time *time) | ||
162 | { | ||
163 | unsigned long alarm_sec; | ||
164 | |||
165 | alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, | ||
166 | time->tm_hour, time->tm_min, time->tm_sec); | ||
167 | |||
168 | spin_lock_irq(&rtc_lock); | ||
169 | |||
170 | rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15)); | ||
171 | rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1)); | ||
172 | rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); | ||
173 | |||
174 | spin_unlock_irq(&rtc_lock); | ||
175 | } | ||
176 | |||
177 | static void read_alarm(struct rtc_time *time) | ||
178 | { | ||
179 | unsigned long low, mid, high; | ||
180 | |||
181 | spin_lock_irq(&rtc_lock); | ||
182 | |||
183 | low = rtc1_read(ECMPLREG); | ||
184 | mid = rtc1_read(ECMPMREG); | ||
185 | high = rtc1_read(ECMPHREG); | ||
186 | |||
187 | spin_unlock_irq(&rtc_lock); | ||
188 | |||
189 | to_tm((high << 17) | (mid << 1) | (low >> 15), time); | ||
190 | time->tm_year -= 1900; | ||
191 | } | ||
192 | |||
193 | static void read_time(struct rtc_time *time) | ||
194 | { | ||
195 | unsigned long epoch_sec, elapsed_sec; | ||
196 | |||
197 | epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); | ||
198 | elapsed_sec = read_elapsed_second(); | ||
199 | |||
200 | to_tm(epoch_sec + elapsed_sec, time); | ||
201 | time->tm_year -= 1900; | ||
202 | } | ||
203 | |||
204 | static void set_time(struct rtc_time *time) | ||
205 | { | ||
206 | unsigned long epoch_sec, current_sec; | ||
207 | |||
208 | epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); | ||
209 | current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, | ||
210 | time->tm_hour, time->tm_min, time->tm_sec); | ||
211 | |||
212 | write_elapsed_second(current_sec - epoch_sec); | ||
213 | } | ||
214 | |||
215 | static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
216 | { | ||
217 | DECLARE_WAITQUEUE(wait, current); | ||
218 | unsigned long irq_data; | ||
219 | int retval = 0; | ||
220 | |||
221 | if (count != sizeof(unsigned int) && count != sizeof(unsigned long)) | ||
222 | return -EINVAL; | ||
223 | |||
224 | add_wait_queue(&rtc_wait, &wait); | ||
225 | |||
226 | do { | ||
227 | __set_current_state(TASK_INTERRUPTIBLE); | ||
228 | |||
229 | spin_lock_irq(&rtc_lock); | ||
230 | irq_data = rtc_irq_data; | ||
231 | rtc_irq_data = 0; | ||
232 | spin_unlock_irq(&rtc_lock); | ||
233 | |||
234 | if (irq_data != 0) | ||
235 | break; | ||
236 | |||
237 | if (file->f_flags & O_NONBLOCK) { | ||
238 | retval = -EAGAIN; | ||
239 | break; | ||
240 | } | ||
241 | |||
242 | if (signal_pending(current)) { | ||
243 | retval = -ERESTARTSYS; | ||
244 | break; | ||
245 | } | ||
246 | } while (1); | ||
247 | |||
248 | if (retval == 0) { | ||
249 | if (count == sizeof(unsigned int)) { | ||
250 | retval = put_user(irq_data, (unsigned int __user *)buf); | ||
251 | if (retval == 0) | ||
252 | retval = sizeof(unsigned int); | ||
253 | } else { | ||
254 | retval = put_user(irq_data, (unsigned long __user *)buf); | ||
255 | if (retval == 0) | ||
256 | retval = sizeof(unsigned long); | ||
257 | } | ||
258 | |||
259 | } | ||
260 | |||
261 | __set_current_state(TASK_RUNNING); | ||
262 | remove_wait_queue(&rtc_wait, &wait); | ||
263 | |||
264 | return retval; | ||
265 | } | ||
266 | |||
267 | static unsigned int rtc_poll(struct file *file, struct poll_table_struct *table) | ||
268 | { | ||
269 | poll_wait(file, &rtc_wait, table); | ||
270 | |||
271 | if (rtc_irq_data != 0) | ||
272 | return POLLIN | POLLRDNORM; | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from) | ||
278 | { | ||
279 | struct rtc_time time; | ||
280 | unsigned long count; | ||
281 | |||
282 | switch (cmd) { | ||
283 | case RTC_AIE_ON: | ||
284 | enable_irq(ELAPSEDTIME_IRQ); | ||
285 | break; | ||
286 | case RTC_AIE_OFF: | ||
287 | disable_irq(ELAPSEDTIME_IRQ); | ||
288 | break; | ||
289 | case RTC_PIE_ON: | ||
290 | enable_irq(RTCLONG1_IRQ); | ||
291 | break; | ||
292 | case RTC_PIE_OFF: | ||
293 | disable_irq(RTCLONG1_IRQ); | ||
294 | break; | ||
295 | case RTC_ALM_SET: | ||
296 | if (copy_from_user(&time, (struct rtc_time __user *)arg, | ||
297 | sizeof(struct rtc_time))) | ||
298 | return -EFAULT; | ||
299 | |||
300 | set_alarm(&time); | ||
301 | break; | ||
302 | case RTC_ALM_READ: | ||
303 | memset(&time, 0, sizeof(struct rtc_time)); | ||
304 | read_alarm(&time); | ||
305 | break; | ||
306 | case RTC_RD_TIME: | ||
307 | memset(&time, 0, sizeof(struct rtc_time)); | ||
308 | read_time(&time); | ||
309 | if (copy_to_user((void __user *)arg, &time, sizeof(struct rtc_time))) | ||
310 | return -EFAULT; | ||
311 | break; | ||
312 | case RTC_SET_TIME: | ||
313 | if (capable(CAP_SYS_TIME) == 0) | ||
314 | return -EACCES; | ||
315 | |||
316 | if (copy_from_user(&time, (struct rtc_time __user *)arg, | ||
317 | sizeof(struct rtc_time))) | ||
318 | return -EFAULT; | ||
319 | |||
320 | set_time(&time); | ||
321 | break; | ||
322 | case RTC_IRQP_READ: | ||
323 | return put_user(periodic_frequency, (unsigned long __user *)arg); | ||
324 | break; | ||
325 | case RTC_IRQP_SET: | ||
326 | if (arg > MAX_PERIODIC_RATE) | ||
327 | return -EINVAL; | ||
328 | |||
329 | if (from == FUNCTION_RTC_IOCTL && arg > MAX_USER_PERIODIC_RATE && | ||
330 | capable(CAP_SYS_RESOURCE) == 0) | ||
331 | return -EACCES; | ||
332 | |||
333 | periodic_frequency = arg; | ||
334 | |||
335 | count = RTC_FREQUENCY; | ||
336 | do_div(count, arg); | ||
337 | |||
338 | periodic_count = count; | ||
339 | |||
340 | spin_lock_irq(&rtc_lock); | ||
341 | |||
342 | rtc1_write(RTCL1LREG, count); | ||
343 | rtc1_write(RTCL1HREG, count >> 16); | ||
344 | |||
345 | spin_unlock_irq(&rtc_lock); | ||
346 | break; | ||
347 | case RTC_EPOCH_READ: | ||
348 | return put_user(epoch, (unsigned long __user *)arg); | ||
349 | case RTC_EPOCH_SET: | ||
350 | /* Doesn't support before 1900 */ | ||
351 | if (arg < 1900) | ||
352 | return -EINVAL; | ||
353 | |||
354 | if (capable(CAP_SYS_TIME) == 0) | ||
355 | return -EACCES; | ||
356 | |||
357 | epoch = arg; | ||
358 | break; | ||
359 | default: | ||
360 | return -EINVAL; | ||
361 | } | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
367 | unsigned long arg) | ||
368 | { | ||
369 | return rtc_do_ioctl(cmd, arg, FUNCTION_RTC_IOCTL); | ||
370 | } | ||
371 | |||
372 | static int rtc_open(struct inode *inode, struct file *file) | ||
373 | { | ||
374 | spin_lock_irq(&rtc_lock); | ||
375 | |||
376 | if (rtc_status == RTC_OPEN) { | ||
377 | spin_unlock_irq(&rtc_lock); | ||
378 | return -EBUSY; | ||
379 | } | ||
380 | |||
381 | rtc_status = RTC_OPEN; | ||
382 | rtc_irq_data = 0; | ||
383 | |||
384 | spin_unlock_irq(&rtc_lock); | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int rtc_release(struct inode *inode, struct file *file) | ||
390 | { | ||
391 | if (file->f_flags & FASYNC) | ||
392 | (void)fasync_helper(-1, file, 0, &rtc_async_queue); | ||
393 | |||
394 | spin_lock_irq(&rtc_lock); | ||
395 | |||
396 | rtc1_write(ECMPLREG, 0); | ||
397 | rtc1_write(ECMPMREG, 0); | ||
398 | rtc1_write(ECMPHREG, 0); | ||
399 | rtc1_write(RTCL1LREG, 0); | ||
400 | rtc1_write(RTCL1HREG, 0); | ||
401 | |||
402 | rtc_status = RTC_RELEASE; | ||
403 | |||
404 | spin_unlock_irq(&rtc_lock); | ||
405 | |||
406 | disable_irq(ELAPSEDTIME_IRQ); | ||
407 | disable_irq(RTCLONG1_IRQ); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int rtc_fasync(int fd, struct file *file, int on) | ||
413 | { | ||
414 | return fasync_helper(fd, file, on, &rtc_async_queue); | ||
415 | } | ||
416 | |||
417 | static struct file_operations rtc_fops = { | ||
418 | .owner = THIS_MODULE, | ||
419 | .llseek = no_llseek, | ||
420 | .read = rtc_read, | ||
421 | .poll = rtc_poll, | ||
422 | .ioctl = rtc_ioctl, | ||
423 | .open = rtc_open, | ||
424 | .release = rtc_release, | ||
425 | .fasync = rtc_fasync, | ||
426 | }; | ||
427 | |||
428 | static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
429 | { | ||
430 | spin_lock(&rtc_lock); | ||
431 | rtc2_write(RTCINTREG, ELAPSEDTIME_INT); | ||
432 | |||
433 | rtc_irq_data += 0x100; | ||
434 | rtc_irq_data &= ~0xff; | ||
435 | rtc_irq_data |= RTC_AF; | ||
436 | spin_unlock(&rtc_lock); | ||
437 | |||
438 | spin_lock(&rtc_lock); | ||
439 | if (rtc_callback) | ||
440 | rtc_callback->func(rtc_callback->private_data); | ||
441 | spin_unlock(&rtc_lock); | ||
442 | |||
443 | wake_up_interruptible(&rtc_wait); | ||
444 | |||
445 | kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); | ||
446 | |||
447 | return IRQ_HANDLED; | ||
448 | } | ||
449 | |||
450 | static irqreturn_t rtclong1_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
451 | { | ||
452 | unsigned long count = periodic_count; | ||
453 | |||
454 | spin_lock(&rtc_lock); | ||
455 | rtc2_write(RTCINTREG, RTCLONG1_INT); | ||
456 | |||
457 | rtc1_write(RTCL1LREG, count); | ||
458 | rtc1_write(RTCL1HREG, count >> 16); | ||
459 | |||
460 | rtc_irq_data += 0x100; | ||
461 | rtc_irq_data &= ~0xff; | ||
462 | rtc_irq_data |= RTC_PF; | ||
463 | spin_unlock(&rtc_lock); | ||
464 | |||
465 | spin_lock(&rtc_task_lock); | ||
466 | if (rtc_callback) | ||
467 | rtc_callback->func(rtc_callback->private_data); | ||
468 | spin_unlock(&rtc_task_lock); | ||
469 | |||
470 | wake_up_interruptible(&rtc_wait); | ||
471 | |||
472 | kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); | ||
473 | |||
474 | return IRQ_HANDLED; | ||
475 | } | ||
476 | |||
477 | int rtc_register(rtc_task_t *task) | ||
478 | { | ||
479 | if (task == NULL || task->func == NULL) | ||
480 | return -EINVAL; | ||
481 | |||
482 | spin_lock_irq(&rtc_lock); | ||
483 | if (rtc_status == RTC_OPEN) { | ||
484 | spin_unlock_irq(&rtc_lock); | ||
485 | return -EBUSY; | ||
486 | } | ||
487 | |||
488 | spin_lock(&rtc_task_lock); | ||
489 | if (rtc_callback != NULL) { | ||
490 | spin_unlock(&rtc_task_lock); | ||
491 | spin_unlock_irq(&rtc_task_lock); | ||
492 | return -EBUSY; | ||
493 | } | ||
494 | |||
495 | rtc_callback = task; | ||
496 | spin_unlock(&rtc_task_lock); | ||
497 | |||
498 | rtc_status = RTC_OPEN; | ||
499 | |||
500 | spin_unlock_irq(&rtc_lock); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | EXPORT_SYMBOL_GPL(rtc_register); | ||
506 | |||
507 | int rtc_unregister(rtc_task_t *task) | ||
508 | { | ||
509 | spin_lock_irq(&rtc_task_lock); | ||
510 | if (task == NULL || rtc_callback != task) { | ||
511 | spin_unlock_irq(&rtc_task_lock); | ||
512 | return -ENXIO; | ||
513 | } | ||
514 | |||
515 | spin_lock(&rtc_lock); | ||
516 | |||
517 | rtc1_write(ECMPLREG, 0); | ||
518 | rtc1_write(ECMPMREG, 0); | ||
519 | rtc1_write(ECMPHREG, 0); | ||
520 | rtc1_write(RTCL1LREG, 0); | ||
521 | rtc1_write(RTCL1HREG, 0); | ||
522 | |||
523 | rtc_status = RTC_RELEASE; | ||
524 | |||
525 | spin_unlock(&rtc_lock); | ||
526 | |||
527 | rtc_callback = NULL; | ||
528 | |||
529 | spin_unlock_irq(&rtc_task_lock); | ||
530 | |||
531 | disable_irq(ELAPSEDTIME_IRQ); | ||
532 | disable_irq(RTCLONG1_IRQ); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | EXPORT_SYMBOL_GPL(rtc_unregister); | ||
538 | |||
539 | int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) | ||
540 | { | ||
541 | int retval = 0; | ||
542 | |||
543 | spin_lock_irq(&rtc_task_lock); | ||
544 | |||
545 | if (rtc_callback != task) | ||
546 | retval = -ENXIO; | ||
547 | else | ||
548 | rtc_do_ioctl(cmd, arg, FUNCTION_RTC_CONTROL); | ||
549 | |||
550 | spin_unlock_irq(&rtc_task_lock); | ||
551 | |||
552 | return retval; | ||
553 | } | ||
554 | |||
555 | EXPORT_SYMBOL_GPL(rtc_control); | ||
556 | |||
557 | static struct miscdevice rtc_miscdevice = { | ||
558 | .minor = RTC_MINOR, | ||
559 | .name = rtc_name, | ||
560 | .fops = &rtc_fops, | ||
561 | }; | ||
562 | |||
563 | static int rtc_probe(struct device *dev) | ||
564 | { | ||
565 | struct platform_device *pdev; | ||
566 | unsigned int irq; | ||
567 | int retval; | ||
568 | |||
569 | pdev = to_platform_device(dev); | ||
570 | if (pdev->num_resources != 2) | ||
571 | return -EBUSY; | ||
572 | |||
573 | rtc1_base = ioremap(pdev->resource[0].start, RTC1_SIZE); | ||
574 | if (rtc1_base == NULL) | ||
575 | return -EBUSY; | ||
576 | |||
577 | rtc2_base = ioremap(pdev->resource[1].start, RTC2_SIZE); | ||
578 | if (rtc2_base == NULL) { | ||
579 | iounmap(rtc1_base); | ||
580 | rtc1_base = NULL; | ||
581 | return -EBUSY; | ||
582 | } | ||
583 | |||
584 | retval = misc_register(&rtc_miscdevice); | ||
585 | if (retval < 0) { | ||
586 | iounmap(rtc1_base); | ||
587 | iounmap(rtc2_base); | ||
588 | rtc1_base = NULL; | ||
589 | rtc2_base = NULL; | ||
590 | return retval; | ||
591 | } | ||
592 | |||
593 | spin_lock_irq(&rtc_lock); | ||
594 | |||
595 | rtc1_write(ECMPLREG, 0); | ||
596 | rtc1_write(ECMPMREG, 0); | ||
597 | rtc1_write(ECMPHREG, 0); | ||
598 | rtc1_write(RTCL1LREG, 0); | ||
599 | rtc1_write(RTCL1HREG, 0); | ||
600 | |||
601 | rtc_status = RTC_RELEASE; | ||
602 | rtc_irq_data = 0; | ||
603 | |||
604 | spin_unlock_irq(&rtc_lock); | ||
605 | |||
606 | init_waitqueue_head(&rtc_wait); | ||
607 | |||
608 | irq = ELAPSEDTIME_IRQ; | ||
609 | retval = request_irq(irq, elapsedtime_interrupt, SA_INTERRUPT, | ||
610 | "elapsed_time", NULL); | ||
611 | if (retval == 0) { | ||
612 | irq = RTCLONG1_IRQ; | ||
613 | retval = request_irq(irq, rtclong1_interrupt, SA_INTERRUPT, | ||
614 | "rtclong1", NULL); | ||
615 | } | ||
616 | |||
617 | if (retval < 0) { | ||
618 | printk(KERN_ERR "rtc: IRQ%d is busy\n", irq); | ||
619 | if (irq == RTCLONG1_IRQ) | ||
620 | free_irq(ELAPSEDTIME_IRQ, NULL); | ||
621 | iounmap(rtc1_base); | ||
622 | iounmap(rtc2_base); | ||
623 | rtc1_base = NULL; | ||
624 | rtc2_base = NULL; | ||
625 | return retval; | ||
626 | } | ||
627 | |||
628 | disable_irq(ELAPSEDTIME_IRQ); | ||
629 | disable_irq(RTCLONG1_IRQ); | ||
630 | |||
631 | spin_lock_init(&rtc_task_lock); | ||
632 | |||
633 | printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n"); | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | static int rtc_remove(struct device *dev) | ||
639 | { | ||
640 | int retval; | ||
641 | |||
642 | retval = misc_deregister(&rtc_miscdevice); | ||
643 | if (retval < 0) | ||
644 | return retval; | ||
645 | |||
646 | free_irq(ELAPSEDTIME_IRQ, NULL); | ||
647 | free_irq(RTCLONG1_IRQ, NULL); | ||
648 | if (rtc1_base != NULL) | ||
649 | iounmap(rtc1_base); | ||
650 | if (rtc2_base != NULL) | ||
651 | iounmap(rtc2_base); | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | static struct platform_device *rtc_platform_device; | ||
657 | |||
658 | static struct device_driver rtc_device_driver = { | ||
659 | .name = rtc_name, | ||
660 | .bus = &platform_bus_type, | ||
661 | .probe = rtc_probe, | ||
662 | .remove = rtc_remove, | ||
663 | }; | ||
664 | |||
665 | static int __devinit vr41xx_rtc_init(void) | ||
666 | { | ||
667 | int retval; | ||
668 | |||
669 | switch (current_cpu_data.cputype) { | ||
670 | case CPU_VR4111: | ||
671 | case CPU_VR4121: | ||
672 | rtc_resource[0].start = RTC1_TYPE1_START; | ||
673 | rtc_resource[0].end = RTC1_TYPE1_END; | ||
674 | rtc_resource[1].start = RTC2_TYPE1_START; | ||
675 | rtc_resource[1].end = RTC2_TYPE1_END; | ||
676 | break; | ||
677 | case CPU_VR4122: | ||
678 | case CPU_VR4131: | ||
679 | case CPU_VR4133: | ||
680 | rtc_resource[0].start = RTC1_TYPE2_START; | ||
681 | rtc_resource[0].end = RTC1_TYPE2_END; | ||
682 | rtc_resource[1].start = RTC2_TYPE2_START; | ||
683 | rtc_resource[1].end = RTC2_TYPE2_END; | ||
684 | break; | ||
685 | default: | ||
686 | return -ENODEV; | ||
687 | break; | ||
688 | } | ||
689 | |||
690 | rtc_platform_device = platform_device_register_simple("RTC", -1, rtc_resource, RTC_NUM_RESOURCES); | ||
691 | if (IS_ERR(rtc_platform_device)) | ||
692 | return PTR_ERR(rtc_platform_device); | ||
693 | |||
694 | retval = driver_register(&rtc_device_driver); | ||
695 | if (retval < 0) | ||
696 | platform_device_unregister(rtc_platform_device); | ||
697 | |||
698 | return retval; | ||
699 | } | ||
700 | |||
701 | static void __devexit vr41xx_rtc_exit(void) | ||
702 | { | ||
703 | driver_unregister(&rtc_device_driver); | ||
704 | |||
705 | platform_device_unregister(rtc_platform_device); | ||
706 | } | ||
707 | |||
708 | module_init(vr41xx_rtc_init); | ||
709 | module_exit(vr41xx_rtc_exit); | ||