Merge branch 'llseek' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/bkl
[linux-2.6.git] / drivers / char / genrtc.c
1 /*
2  *      Real Time Clock interface for
3  *              - q40 and other m68k machines,
4  *              - HP PARISC machines
5  *              - PowerPC machines
6  *      emulate some RTC irq capabilities in software
7  *
8  *      Copyright (C) 1999 Richard Zidlicky
9  *
10  *      based on Paul Gortmaker's rtc.c device and
11  *           Sam Creasey Generic rtc driver
12  *
13  *      This driver allows use of the real time clock (built into
14  *      nearly all computers) from user space. It exports the /dev/rtc
15  *      interface supporting various ioctl() and also the /proc/driver/rtc
16  *      pseudo-file for status information.
17  *
18  *      The ioctls can be used to set the interrupt behaviour where
19  *      supported.
20  *
21  *      The /dev/rtc interface will block on reads until an interrupt
22  *      has been received. If a RTC interrupt has already happened,
23  *      it will output an unsigned long and then block. The output value
24  *      contains the interrupt status in the low byte and the number of
25  *      interrupts since the last read in the remaining high bytes. The
26  *      /dev/rtc interface can also be used with the select(2) call.
27  *
28  *      This program is free software; you can redistribute it and/or
29  *      modify it under the terms of the GNU General Public License
30  *      as published by the Free Software Foundation; either version
31  *      2 of the License, or (at your option) any later version.
32  *
33
34  *      1.01 fix for 2.3.X                    rz@linux-m68k.org
35  *      1.02 merged with code from genrtc.c   rz@linux-m68k.org
36  *      1.03 make it more portable            zippel@linux-m68k.org
37  *      1.04 removed useless timer code       rz@linux-m68k.org
38  *      1.05 portable RTC_UIE emulation       rz@linux-m68k.org
39  *      1.06 set_rtc_time can return an error trini@kernel.crashing.org
40  *      1.07 ported to HP PARISC (hppa)       Helge Deller <deller@gmx.de>
41  */
42
43 #define RTC_VERSION     "1.07"
44
45 #include <linux/module.h>
46 #include <linux/sched.h>
47 #include <linux/errno.h>
48 #include <linux/miscdevice.h>
49 #include <linux/fcntl.h>
50
51 #include <linux/rtc.h>
52 #include <linux/init.h>
53 #include <linux/poll.h>
54 #include <linux/proc_fs.h>
55 #include <linux/mutex.h>
56 #include <linux/workqueue.h>
57
58 #include <asm/uaccess.h>
59 #include <asm/system.h>
60 #include <asm/rtc.h>
61
62 /*
63  *      We sponge a minor off of the misc major. No need slurping
64  *      up another valuable major dev number for this. If you add
65  *      an ioctl, make sure you don't conflict with SPARC's RTC
66  *      ioctls.
67  */
68
69 static DEFINE_MUTEX(gen_rtc_mutex);
70 static DECLARE_WAIT_QUEUE_HEAD(gen_rtc_wait);
71
72 /*
73  *      Bits in gen_rtc_status.
74  */
75
76 #define RTC_IS_OPEN             0x01    /* means /dev/rtc is in use     */
77
78 static unsigned char gen_rtc_status;    /* bitmapped status byte.       */
79 static unsigned long gen_rtc_irq_data;  /* our output to the world      */
80
81 /* months start at 0 now */
82 static unsigned char days_in_mo[] =
83 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
84
85 static int irq_active;
86
87 #ifdef CONFIG_GEN_RTC_X
88 static struct work_struct genrtc_task;
89 static struct timer_list timer_task;
90
91 static unsigned int oldsecs;
92 static int lostint;
93 static unsigned long tt_exp;
94
95 static void gen_rtc_timer(unsigned long data);
96
97 static volatile int stask_active;              /* schedule_work */
98 static volatile int ttask_active;              /* timer_task */
99 static int stop_rtc_timers;                    /* don't requeue tasks */
100 static DEFINE_SPINLOCK(gen_rtc_lock);
101
102 static void gen_rtc_interrupt(unsigned long arg);
103
104 /*
105  * Routine to poll RTC seconds field for change as often as possible,
106  * after first RTC_UIE use timer to reduce polling
107  */
108 static void genrtc_troutine(struct work_struct *work)
109 {
110         unsigned int tmp = get_rtc_ss();
111         
112         if (stop_rtc_timers) {
113                 stask_active = 0;
114                 return;
115         }
116
117         if (oldsecs != tmp){
118                 oldsecs = tmp;
119
120                 timer_task.function = gen_rtc_timer;
121                 timer_task.expires = jiffies + HZ - (HZ/10);
122                 tt_exp=timer_task.expires;
123                 ttask_active=1;
124                 stask_active=0;
125                 add_timer(&timer_task);
126
127                 gen_rtc_interrupt(0);
128         } else if (schedule_work(&genrtc_task) == 0)
129                 stask_active = 0;
130 }
131
132 static void gen_rtc_timer(unsigned long data)
133 {
134         lostint = get_rtc_ss() - oldsecs ;
135         if (lostint<0) 
136                 lostint = 60 - lostint;
137         if (time_after(jiffies, tt_exp))
138                 printk(KERN_INFO "genrtc: timer task delayed by %ld jiffies\n",
139                        jiffies-tt_exp);
140         ttask_active=0;
141         stask_active=1;
142         if ((schedule_work(&genrtc_task) == 0))
143                 stask_active = 0;
144 }
145
146 /* 
147  * call gen_rtc_interrupt function to signal an RTC_UIE,
148  * arg is unused.
149  * Could be invoked either from a real interrupt handler or
150  * from some routine that periodically (eg 100HZ) monitors
151  * whether RTC_SECS changed
152  */
153 static void gen_rtc_interrupt(unsigned long arg)
154 {
155         /*  We store the status in the low byte and the number of
156          *      interrupts received since the last read in the remainder
157          *      of rtc_irq_data.  */
158
159         gen_rtc_irq_data += 0x100;
160         gen_rtc_irq_data &= ~0xff;
161         gen_rtc_irq_data |= RTC_UIE;
162
163         if (lostint){
164                 printk("genrtc: system delaying clock ticks?\n");
165                 /* increment count so that userspace knows something is wrong */
166                 gen_rtc_irq_data += ((lostint-1)<<8);
167                 lostint = 0;
168         }
169
170         wake_up_interruptible(&gen_rtc_wait);
171 }
172
173 /*
174  *      Now all the various file operations that we export.
175  */
176 static ssize_t gen_rtc_read(struct file *file, char __user *buf,
177                         size_t count, loff_t *ppos)
178 {
179         unsigned long data;
180         ssize_t retval;
181
182         if (count != sizeof (unsigned int) && count != sizeof (unsigned long))
183                 return -EINVAL;
184
185         if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data)
186                 return -EAGAIN;
187
188         retval = wait_event_interruptible(gen_rtc_wait,
189                         (data = xchg(&gen_rtc_irq_data, 0)));
190         if (retval)
191                 goto out;
192
193         /* first test allows optimizer to nuke this case for 32-bit machines */
194         if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) {
195                 unsigned int uidata = data;
196                 retval = put_user(uidata, (unsigned int __user *)buf) ?:
197                         sizeof(unsigned int);
198         }
199         else {
200                 retval = put_user(data, (unsigned long __user *)buf) ?:
201                         sizeof(unsigned long);
202         }
203 out:
204         return retval;
205 }
206
207 static unsigned int gen_rtc_poll(struct file *file,
208                                  struct poll_table_struct *wait)
209 {
210         poll_wait(file, &gen_rtc_wait, wait);
211         if (gen_rtc_irq_data != 0)
212                 return POLLIN | POLLRDNORM;
213         return 0;
214 }
215
216 #endif
217
218 /*
219  * Used to disable/enable interrupts, only RTC_UIE supported
220  * We also clear out any old irq data after an ioctl() that
221  * meddles with the interrupt enable/disable bits.
222  */
223
224 static inline void gen_clear_rtc_irq_bit(unsigned char bit)
225 {
226 #ifdef CONFIG_GEN_RTC_X
227         stop_rtc_timers = 1;
228         if (ttask_active){
229                 del_timer_sync(&timer_task);
230                 ttask_active = 0;
231         }
232         while (stask_active)
233                 schedule();
234
235         spin_lock(&gen_rtc_lock);
236         irq_active = 0;
237         spin_unlock(&gen_rtc_lock);
238 #endif
239 }
240
241 static inline int gen_set_rtc_irq_bit(unsigned char bit)
242 {
243 #ifdef CONFIG_GEN_RTC_X
244         spin_lock(&gen_rtc_lock);
245         if ( !irq_active ) {
246                 irq_active = 1;
247                 stop_rtc_timers = 0;
248                 lostint = 0;
249                 INIT_WORK(&genrtc_task, genrtc_troutine);
250                 oldsecs = get_rtc_ss();
251                 init_timer(&timer_task);
252
253                 stask_active = 1;
254                 if (schedule_work(&genrtc_task) == 0){
255                         stask_active = 0;
256                 }
257         }
258         spin_unlock(&gen_rtc_lock);
259         gen_rtc_irq_data = 0;
260         return 0;
261 #else
262         return -EINVAL;
263 #endif
264 }
265
266 static int gen_rtc_ioctl(struct file *file,
267                          unsigned int cmd, unsigned long arg)
268 {
269         struct rtc_time wtime;
270         struct rtc_pll_info pll;
271         void __user *argp = (void __user *)arg;
272
273         switch (cmd) {
274
275         case RTC_PLL_GET:
276             if (get_rtc_pll(&pll))
277                     return -EINVAL;
278             else
279                     return copy_to_user(argp, &pll, sizeof pll) ? -EFAULT : 0;
280
281         case RTC_PLL_SET:
282                 if (!capable(CAP_SYS_TIME))
283                         return -EACCES;
284                 if (copy_from_user(&pll, argp, sizeof(pll)))
285                         return -EFAULT;
286             return set_rtc_pll(&pll);
287
288         case RTC_UIE_OFF:       /* disable ints from RTC updates.       */
289                 gen_clear_rtc_irq_bit(RTC_UIE);
290                 return 0;
291
292         case RTC_UIE_ON:        /* enable ints for RTC updates. */
293                 return gen_set_rtc_irq_bit(RTC_UIE);
294
295         case RTC_RD_TIME:       /* Read the time/date from RTC  */
296                 /* this doesn't get week-day, who cares */
297                 memset(&wtime, 0, sizeof(wtime));
298                 get_rtc_time(&wtime);
299
300                 return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0;
301
302         case RTC_SET_TIME:      /* Set the RTC */
303             {
304                 int year;
305                 unsigned char leap_yr;
306
307                 if (!capable(CAP_SYS_TIME))
308                         return -EACCES;
309
310                 if (copy_from_user(&wtime, argp, sizeof(wtime)))
311                         return -EFAULT;
312
313                 year = wtime.tm_year + 1900;
314                 leap_yr = ((!(year % 4) && (year % 100)) ||
315                            !(year % 400));
316
317                 if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
318                         return -EINVAL;
319
320                 if (wtime.tm_mday < 0 || wtime.tm_mday >
321                     (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
322                         return -EINVAL;
323
324                 if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
325                     wtime.tm_min < 0 || wtime.tm_min >= 60 ||
326                     wtime.tm_sec < 0 || wtime.tm_sec >= 60)
327                         return -EINVAL;
328
329                 return set_rtc_time(&wtime);
330             }
331         }
332
333         return -EINVAL;
334 }
335
336 static long gen_rtc_unlocked_ioctl(struct file *file, unsigned int cmd,
337                                    unsigned long arg)
338 {
339         int ret;
340
341         mutex_lock(&gen_rtc_mutex);
342         ret = gen_rtc_ioctl(file, cmd, arg);
343         mutex_unlock(&gen_rtc_mutex);
344
345         return ret;
346 }
347
348 /*
349  *      We enforce only one user at a time here with the open/close.
350  *      Also clear the previous interrupt data on an open, and clean
351  *      up things on a close.
352  */
353
354 static int gen_rtc_open(struct inode *inode, struct file *file)
355 {
356         mutex_lock(&gen_rtc_mutex);
357         if (gen_rtc_status & RTC_IS_OPEN) {
358                 mutex_unlock(&gen_rtc_mutex);
359                 return -EBUSY;
360         }
361
362         gen_rtc_status |= RTC_IS_OPEN;
363         gen_rtc_irq_data = 0;
364         irq_active = 0;
365         mutex_unlock(&gen_rtc_mutex);
366
367         return 0;
368 }
369
370 static int gen_rtc_release(struct inode *inode, struct file *file)
371 {
372         /*
373          * Turn off all interrupts once the device is no longer
374          * in use and clear the data.
375          */
376
377         gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
378
379         gen_rtc_status &= ~RTC_IS_OPEN;
380         return 0;
381 }
382
383
384 #ifdef CONFIG_PROC_FS
385
386 /*
387  *      Info exported via "/proc/driver/rtc".
388  */
389
390 static int gen_rtc_proc_output(char *buf)
391 {
392         char *p;
393         struct rtc_time tm;
394         unsigned int flags;
395         struct rtc_pll_info pll;
396
397         p = buf;
398
399         flags = get_rtc_time(&tm);
400
401         p += sprintf(p,
402                      "rtc_time\t: %02d:%02d:%02d\n"
403                      "rtc_date\t: %04d-%02d-%02d\n"
404                      "rtc_epoch\t: %04u\n",
405                      tm.tm_hour, tm.tm_min, tm.tm_sec,
406                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
407
408         tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
409
410         p += sprintf(p, "alarm\t\t: ");
411         if (tm.tm_hour <= 24)
412                 p += sprintf(p, "%02d:", tm.tm_hour);
413         else
414                 p += sprintf(p, "**:");
415
416         if (tm.tm_min <= 59)
417                 p += sprintf(p, "%02d:", tm.tm_min);
418         else
419                 p += sprintf(p, "**:");
420
421         if (tm.tm_sec <= 59)
422                 p += sprintf(p, "%02d\n", tm.tm_sec);
423         else
424                 p += sprintf(p, "**\n");
425
426         p += sprintf(p,
427                      "DST_enable\t: %s\n"
428                      "BCD\t\t: %s\n"
429                      "24hr\t\t: %s\n"
430                      "square_wave\t: %s\n"
431                      "alarm_IRQ\t: %s\n"
432                      "update_IRQ\t: %s\n"
433                      "periodic_IRQ\t: %s\n"
434                      "periodic_freq\t: %ld\n"
435                      "batt_status\t: %s\n",
436                      (flags & RTC_DST_EN) ? "yes" : "no",
437                      (flags & RTC_DM_BINARY) ? "no" : "yes",
438                      (flags & RTC_24H) ? "yes" : "no",
439                      (flags & RTC_SQWE) ? "yes" : "no",
440                      (flags & RTC_AIE) ? "yes" : "no",
441                      irq_active ? "yes" : "no",
442                      (flags & RTC_PIE) ? "yes" : "no",
443                      0L /* freq */,
444                      (flags & RTC_BATT_BAD) ? "bad" : "okay");
445         if (!get_rtc_pll(&pll))
446             p += sprintf(p,
447                          "PLL adjustment\t: %d\n"
448                          "PLL max +ve adjustment\t: %d\n"
449                          "PLL max -ve adjustment\t: %d\n"
450                          "PLL +ve adjustment factor\t: %d\n"
451                          "PLL -ve adjustment factor\t: %d\n"
452                          "PLL frequency\t: %ld\n",
453                          pll.pll_value,
454                          pll.pll_max,
455                          pll.pll_min,
456                          pll.pll_posmult,
457                          pll.pll_negmult,
458                          pll.pll_clock);
459         return p - buf;
460 }
461
462 static int gen_rtc_read_proc(char *page, char **start, off_t off,
463                              int count, int *eof, void *data)
464 {
465         int len = gen_rtc_proc_output (page);
466         if (len <= off+count) *eof = 1;
467         *start = page + off;
468         len -= off;
469         if (len>count) len = count;
470         if (len<0) len = 0;
471         return len;
472 }
473
474 static int __init gen_rtc_proc_init(void)
475 {
476         struct proc_dir_entry *r;
477
478         r = create_proc_read_entry("driver/rtc", 0, NULL, gen_rtc_read_proc, NULL);
479         if (!r)
480                 return -ENOMEM;
481         return 0;
482 }
483 #else
484 static inline int gen_rtc_proc_init(void) { return 0; }
485 #endif /* CONFIG_PROC_FS */
486
487
488 /*
489  *      The various file operations we support.
490  */
491
492 static const struct file_operations gen_rtc_fops = {
493         .owner          = THIS_MODULE,
494 #ifdef CONFIG_GEN_RTC_X
495         .read           = gen_rtc_read,
496         .poll           = gen_rtc_poll,
497 #endif
498         .unlocked_ioctl = gen_rtc_unlocked_ioctl,
499         .open           = gen_rtc_open,
500         .release        = gen_rtc_release,
501         .llseek         = noop_llseek,
502 };
503
504 static struct miscdevice rtc_gen_dev =
505 {
506         .minor          = RTC_MINOR,
507         .name           = "rtc",
508         .fops           = &gen_rtc_fops,
509 };
510
511 static int __init rtc_generic_init(void)
512 {
513         int retval;
514
515         printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION);
516
517         retval = misc_register(&rtc_gen_dev);
518         if (retval < 0)
519                 return retval;
520
521         retval = gen_rtc_proc_init();
522         if (retval) {
523                 misc_deregister(&rtc_gen_dev);
524                 return retval;
525         }
526
527         return 0;
528 }
529
530 static void __exit rtc_generic_exit(void)
531 {
532         remove_proc_entry ("driver/rtc", NULL);
533         misc_deregister(&rtc_gen_dev);
534 }
535
536
537 module_init(rtc_generic_init);
538 module_exit(rtc_generic_exit);
539
540 MODULE_AUTHOR("Richard Zidlicky");
541 MODULE_LICENSE("GPL");
542 MODULE_ALIAS_MISCDEV(RTC_MINOR);