linux/drivers/char/genrtc.c
<<
>>
Prefs
   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/smp_lock.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
  69static DECLARE_WAIT_QUEUE_HEAD(gen_rtc_wait);
  70
  71/*
  72 *      Bits in gen_rtc_status.
  73 */
  74
  75#define RTC_IS_OPEN             0x01    /* means /dev/rtc is in use     */
  76
  77static unsigned char gen_rtc_status;    /* bitmapped status byte.       */
  78static unsigned long gen_rtc_irq_data;  /* our output to the world      */
  79
  80/* months start at 0 now */
  81static unsigned char days_in_mo[] =
  82{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  83
  84static int irq_active;
  85
  86#ifdef CONFIG_GEN_RTC_X
  87static struct work_struct genrtc_task;
  88static struct timer_list timer_task;
  89
  90static unsigned int oldsecs;
  91static int lostint;
  92static unsigned long tt_exp;
  93
  94static void gen_rtc_timer(unsigned long data);
  95
  96static volatile int stask_active;              /* schedule_work */
  97static volatile int ttask_active;              /* timer_task */
  98static int stop_rtc_timers;                    /* don't requeue tasks */
  99static DEFINE_SPINLOCK(gen_rtc_lock);
 100
 101static void gen_rtc_interrupt(unsigned long arg);
 102
 103/*
 104 * Routine to poll RTC seconds field for change as often as possible,
 105 * after first RTC_UIE use timer to reduce polling
 106 */
 107static void genrtc_troutine(struct work_struct *work)
 108{
 109        unsigned int tmp = get_rtc_ss();
 110        
 111        if (stop_rtc_timers) {
 112                stask_active = 0;
 113                return;
 114        }
 115
 116        if (oldsecs != tmp){
 117                oldsecs = tmp;
 118
 119                timer_task.function = gen_rtc_timer;
 120                timer_task.expires = jiffies + HZ - (HZ/10);
 121                tt_exp=timer_task.expires;
 122                ttask_active=1;
 123                stask_active=0;
 124                add_timer(&timer_task);
 125
 126                gen_rtc_interrupt(0);
 127        } else if (schedule_work(&genrtc_task) == 0)
 128                stask_active = 0;
 129}
 130
 131static void gen_rtc_timer(unsigned long data)
 132{
 133        lostint = get_rtc_ss() - oldsecs ;
 134        if (lostint<0) 
 135                lostint = 60 - lostint;
 136        if (time_after(jiffies, tt_exp))
 137                printk(KERN_INFO "genrtc: timer task delayed by %ld jiffies\n",
 138                       jiffies-tt_exp);
 139        ttask_active=0;
 140        stask_active=1;
 141        if ((schedule_work(&genrtc_task) == 0))
 142                stask_active = 0;
 143}
 144
 145/* 
 146 * call gen_rtc_interrupt function to signal an RTC_UIE,
 147 * arg is unused.
 148 * Could be invoked either from a real interrupt handler or
 149 * from some routine that periodically (eg 100HZ) monitors
 150 * whether RTC_SECS changed
 151 */
 152static void gen_rtc_interrupt(unsigned long arg)
 153{
 154        /*  We store the status in the low byte and the number of
 155         *      interrupts received since the last read in the remainder
 156         *      of rtc_irq_data.  */
 157
 158        gen_rtc_irq_data += 0x100;
 159        gen_rtc_irq_data &= ~0xff;
 160        gen_rtc_irq_data |= RTC_UIE;
 161
 162        if (lostint){
 163                printk("genrtc: system delaying clock ticks?\n");
 164                /* increment count so that userspace knows something is wrong */
 165                gen_rtc_irq_data += ((lostint-1)<<8);
 166                lostint = 0;
 167        }
 168
 169        wake_up_interruptible(&gen_rtc_wait);
 170}
 171
 172/*
 173 *      Now all the various file operations that we export.
 174 */
 175static ssize_t gen_rtc_read(struct file *file, char __user *buf,
 176                        size_t count, loff_t *ppos)
 177{
 178        unsigned long data;
 179        ssize_t retval;
 180
 181        if (count != sizeof (unsigned int) && count != sizeof (unsigned long))
 182                return -EINVAL;
 183
 184        if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data)
 185                return -EAGAIN;
 186
 187        retval = wait_event_interruptible(gen_rtc_wait,
 188                        (data = xchg(&gen_rtc_irq_data, 0)));
 189        if (retval)
 190                goto out;
 191
 192        /* first test allows optimizer to nuke this case for 32-bit machines */
 193        if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) {
 194                unsigned int uidata = data;
 195                retval = put_user(uidata, (unsigned int __user *)buf) ?:
 196                        sizeof(unsigned int);
 197        }
 198        else {
 199                retval = put_user(data, (unsigned long __user *)buf) ?:
 200                        sizeof(unsigned long);
 201        }
 202out:
 203        return retval;
 204}
 205
 206static unsigned int gen_rtc_poll(struct file *file,
 207                                 struct poll_table_struct *wait)
 208{
 209        poll_wait(file, &gen_rtc_wait, wait);
 210        if (gen_rtc_irq_data != 0)
 211                return POLLIN | POLLRDNORM;
 212        return 0;
 213}
 214
 215#endif
 216
 217/*
 218 * Used to disable/enable interrupts, only RTC_UIE supported
 219 * We also clear out any old irq data after an ioctl() that
 220 * meddles with the interrupt enable/disable bits.
 221 */
 222
 223static inline void gen_clear_rtc_irq_bit(unsigned char bit)
 224{
 225#ifdef CONFIG_GEN_RTC_X
 226        stop_rtc_timers = 1;
 227        if (ttask_active){
 228                del_timer_sync(&timer_task);
 229                ttask_active = 0;
 230        }
 231        while (stask_active)
 232                schedule();
 233
 234        spin_lock(&gen_rtc_lock);
 235        irq_active = 0;
 236        spin_unlock(&gen_rtc_lock);
 237#endif
 238}
 239
 240static inline int gen_set_rtc_irq_bit(unsigned char bit)
 241{
 242#ifdef CONFIG_GEN_RTC_X
 243        spin_lock(&gen_rtc_lock);
 244        if ( !irq_active ) {
 245                irq_active = 1;
 246                stop_rtc_timers = 0;
 247                lostint = 0;
 248                INIT_WORK(&genrtc_task, genrtc_troutine);
 249                oldsecs = get_rtc_ss();
 250                init_timer(&timer_task);
 251
 252                stask_active = 1;
 253                if (schedule_work(&genrtc_task) == 0){
 254                        stask_active = 0;
 255                }
 256        }
 257        spin_unlock(&gen_rtc_lock);
 258        gen_rtc_irq_data = 0;
 259        return 0;
 260#else
 261        return -EINVAL;
 262#endif
 263}
 264
 265static int gen_rtc_ioctl(struct inode *inode, struct file *file,
 266                         unsigned int cmd, unsigned long arg)
 267{
 268        struct rtc_time wtime;
 269        struct rtc_pll_info pll;
 270        void __user *argp = (void __user *)arg;
 271
 272        switch (cmd) {
 273
 274        case RTC_PLL_GET:
 275            if (get_rtc_pll(&pll))
 276                    return -EINVAL;
 277            else
 278                    return copy_to_user(argp, &pll, sizeof pll) ? -EFAULT : 0;
 279
 280        case RTC_PLL_SET:
 281                if (!capable(CAP_SYS_TIME))
 282                        return -EACCES;
 283                if (copy_from_user(&pll, argp, sizeof(pll)))
 284                        return -EFAULT;
 285            return set_rtc_pll(&pll);
 286
 287        case RTC_UIE_OFF:       /* disable ints from RTC updates.       */
 288                gen_clear_rtc_irq_bit(RTC_UIE);
 289                return 0;
 290
 291        case RTC_UIE_ON:        /* enable ints for RTC updates. */
 292                return gen_set_rtc_irq_bit(RTC_UIE);
 293
 294        case RTC_RD_TIME:       /* Read the time/date from RTC  */
 295                /* this doesn't get week-day, who cares */
 296                memset(&wtime, 0, sizeof(wtime));
 297                get_rtc_time(&wtime);
 298
 299                return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0;
 300
 301        case RTC_SET_TIME:      /* Set the RTC */
 302            {
 303                int year;
 304                unsigned char leap_yr;
 305
 306                if (!capable(CAP_SYS_TIME))
 307                        return -EACCES;
 308
 309                if (copy_from_user(&wtime, argp, sizeof(wtime)))
 310                        return -EFAULT;
 311
 312                year = wtime.tm_year + 1900;
 313                leap_yr = ((!(year % 4) && (year % 100)) ||
 314                           !(year % 400));
 315
 316                if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
 317                        return -EINVAL;
 318
 319                if (wtime.tm_mday < 0 || wtime.tm_mday >
 320                    (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
 321                        return -EINVAL;
 322
 323                if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
 324                    wtime.tm_min < 0 || wtime.tm_min >= 60 ||
 325                    wtime.tm_sec < 0 || wtime.tm_sec >= 60)
 326                        return -EINVAL;
 327
 328                return set_rtc_time(&wtime);
 329            }
 330        }
 331
 332        return -EINVAL;
 333}
 334
 335/*
 336 *      We enforce only one user at a time here with the open/close.
 337 *      Also clear the previous interrupt data on an open, and clean
 338 *      up things on a close.
 339 */
 340
 341static int gen_rtc_open(struct inode *inode, struct file *file)
 342{
 343        lock_kernel();
 344        if (gen_rtc_status & RTC_IS_OPEN) {
 345                unlock_kernel();
 346                return -EBUSY;
 347        }
 348
 349        gen_rtc_status |= RTC_IS_OPEN;
 350        gen_rtc_irq_data = 0;
 351        irq_active = 0;
 352        unlock_kernel();
 353
 354        return 0;
 355}
 356
 357static int gen_rtc_release(struct inode *inode, struct file *file)
 358{
 359        /*
 360         * Turn off all interrupts once the device is no longer
 361         * in use and clear the data.
 362         */
 363
 364        gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
 365
 366        gen_rtc_status &= ~RTC_IS_OPEN;
 367        return 0;
 368}
 369
 370
 371#ifdef CONFIG_PROC_FS
 372
 373/*
 374 *      Info exported via "/proc/driver/rtc".
 375 */
 376
 377static int gen_rtc_proc_output(char *buf)
 378{
 379        char *p;
 380        struct rtc_time tm;
 381        unsigned int flags;
 382        struct rtc_pll_info pll;
 383
 384        p = buf;
 385
 386        flags = get_rtc_time(&tm);
 387
 388        p += sprintf(p,
 389                     "rtc_time\t: %02d:%02d:%02d\n"
 390                     "rtc_date\t: %04d-%02d-%02d\n"
 391                     "rtc_epoch\t: %04u\n",
 392                     tm.tm_hour, tm.tm_min, tm.tm_sec,
 393                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
 394
 395        tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
 396
 397        p += sprintf(p, "alarm\t\t: ");
 398        if (tm.tm_hour <= 24)
 399                p += sprintf(p, "%02d:", tm.tm_hour);
 400        else
 401                p += sprintf(p, "**:");
 402
 403        if (tm.tm_min <= 59)
 404                p += sprintf(p, "%02d:", tm.tm_min);
 405        else
 406                p += sprintf(p, "**:");
 407
 408        if (tm.tm_sec <= 59)
 409                p += sprintf(p, "%02d\n", tm.tm_sec);
 410        else
 411                p += sprintf(p, "**\n");
 412
 413        p += sprintf(p,
 414                     "DST_enable\t: %s\n"
 415                     "BCD\t\t: %s\n"
 416                     "24hr\t\t: %s\n"
 417                     "square_wave\t: %s\n"
 418                     "alarm_IRQ\t: %s\n"
 419                     "update_IRQ\t: %s\n"
 420                     "periodic_IRQ\t: %s\n"
 421                     "periodic_freq\t: %ld\n"
 422                     "batt_status\t: %s\n",
 423                     (flags & RTC_DST_EN) ? "yes" : "no",
 424                     (flags & RTC_DM_BINARY) ? "no" : "yes",
 425                     (flags & RTC_24H) ? "yes" : "no",
 426                     (flags & RTC_SQWE) ? "yes" : "no",
 427                     (flags & RTC_AIE) ? "yes" : "no",
 428                     irq_active ? "yes" : "no",
 429                     (flags & RTC_PIE) ? "yes" : "no",
 430                     0L /* freq */,
 431                     (flags & RTC_BATT_BAD) ? "bad" : "okay");
 432        if (!get_rtc_pll(&pll))
 433            p += sprintf(p,
 434                         "PLL adjustment\t: %d\n"
 435                         "PLL max +ve adjustment\t: %d\n"
 436                         "PLL max -ve adjustment\t: %d\n"
 437                         "PLL +ve adjustment factor\t: %d\n"
 438                         "PLL -ve adjustment factor\t: %d\n"
 439                         "PLL frequency\t: %ld\n",
 440                         pll.pll_value,
 441                         pll.pll_max,
 442                         pll.pll_min,
 443                         pll.pll_posmult,
 444                         pll.pll_negmult,
 445                         pll.pll_clock);
 446        return p - buf;
 447}
 448
 449static int gen_rtc_read_proc(char *page, char **start, off_t off,
 450                             int count, int *eof, void *data)
 451{
 452        int len = gen_rtc_proc_output (page);
 453        if (len <= off+count) *eof = 1;
 454        *start = page + off;
 455        len -= off;
 456        if (len>count) len = count;
 457        if (len<0) len = 0;
 458        return len;
 459}
 460
 461static int __init gen_rtc_proc_init(void)
 462{
 463        struct proc_dir_entry *r;
 464
 465        r = create_proc_read_entry("driver/rtc", 0, NULL, gen_rtc_read_proc, NULL);
 466        if (!r)
 467                return -ENOMEM;
 468        return 0;
 469}
 470#else
 471static inline int gen_rtc_proc_init(void) { return 0; }
 472#endif /* CONFIG_PROC_FS */
 473
 474
 475/*
 476 *      The various file operations we support.
 477 */
 478
 479static const struct file_operations gen_rtc_fops = {
 480        .owner          = THIS_MODULE,
 481#ifdef CONFIG_GEN_RTC_X
 482        .read           = gen_rtc_read,
 483        .poll           = gen_rtc_poll,
 484#endif
 485        .ioctl          = gen_rtc_ioctl,
 486        .open           = gen_rtc_open,
 487        .release        = gen_rtc_release,
 488};
 489
 490static struct miscdevice rtc_gen_dev =
 491{
 492        .minor          = RTC_MINOR,
 493        .name           = "rtc",
 494        .fops           = &gen_rtc_fops,
 495};
 496
 497static int __init rtc_generic_init(void)
 498{
 499        int retval;
 500
 501        printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION);
 502
 503        retval = misc_register(&rtc_gen_dev);
 504        if (retval < 0)
 505                return retval;
 506
 507        retval = gen_rtc_proc_init();
 508        if (retval) {
 509                misc_deregister(&rtc_gen_dev);
 510                return retval;
 511        }
 512
 513        return 0;
 514}
 515
 516static void __exit rtc_generic_exit(void)
 517{
 518        remove_proc_entry ("driver/rtc", NULL);
 519        misc_deregister(&rtc_gen_dev);
 520}
 521
 522
 523module_init(rtc_generic_init);
 524module_exit(rtc_generic_exit);
 525
 526MODULE_AUTHOR("Richard Zidlicky");
 527MODULE_LICENSE("GPL");
 528MODULE_ALIAS_MISCDEV(RTC_MINOR);
 529