OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [genrtc.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.