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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [char/] [genrtc.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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/errno.h>
47
#include <linux/miscdevice.h>
48
#include <linux/fcntl.h>
49
 
50
#include <linux/rtc.h>
51
#include <linux/init.h>
52
#include <linux/poll.h>
53
#include <linux/proc_fs.h>
54
#include <linux/workqueue.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
static struct work_struct genrtc_task;
86
static struct timer_list timer_task;
87
 
88
static unsigned int oldsecs;
89
static int lostint;
90
static unsigned long tt_exp;
91
 
92
static void gen_rtc_timer(unsigned long data);
93
 
94
static volatile int stask_active;              /* schedule_work */
95
static volatile int ttask_active;              /* timer_task */
96
static int stop_rtc_timers;                    /* don't requeue tasks */
97
static DEFINE_SPINLOCK(gen_rtc_lock);
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(struct work_struct *work)
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_work(&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_work(&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 __user *buf,
174
                        size_t count, loff_t *ppos)
175
{
176
        unsigned long data;
177
        ssize_t retval;
178
 
179
        if (count != sizeof (unsigned int) && count != sizeof (unsigned long))
180
                return -EINVAL;
181
 
182
        if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data)
183
                return -EAGAIN;
184
 
185
        retval = wait_event_interruptible(gen_rtc_wait,
186
                        (data = xchg(&gen_rtc_irq_data, 0)));
187
        if (retval)
188
                goto out;
189
 
190
        /* first test allows optimizer to nuke this case for 32-bit machines */
191
        if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) {
192
                unsigned int uidata = data;
193
                retval = put_user(uidata, (unsigned int __user *)buf) ?:
194
                        sizeof(unsigned int);
195
        }
196
        else {
197
                retval = put_user(data, (unsigned long __user *)buf) ?:
198
                        sizeof(unsigned long);
199
        }
200
out:
201
        return retval;
202
}
203
 
204
static unsigned int gen_rtc_poll(struct file *file,
205
                                 struct poll_table_struct *wait)
206
{
207
        poll_wait(file, &gen_rtc_wait, wait);
208
        if (gen_rtc_irq_data != 0)
209
                return POLLIN | POLLRDNORM;
210
        return 0;
211
}
212
 
213
#endif
214
 
215
/*
216
 * Used to disable/enable interrupts, only RTC_UIE supported
217
 * We also clear out any old irq data after an ioctl() that
218
 * meddles with the interrupt enable/disable bits.
219
 */
220
 
221
static inline void gen_clear_rtc_irq_bit(unsigned char bit)
222
{
223
#ifdef CONFIG_GEN_RTC_X
224
        stop_rtc_timers = 1;
225
        if (ttask_active){
226
                del_timer_sync(&timer_task);
227
                ttask_active = 0;
228
        }
229
        while (stask_active)
230
                schedule();
231
 
232
        spin_lock(&gen_rtc_lock);
233
        irq_active = 0;
234
        spin_unlock(&gen_rtc_lock);
235
#endif
236
}
237
 
238
static inline int gen_set_rtc_irq_bit(unsigned char bit)
239
{
240
#ifdef CONFIG_GEN_RTC_X
241
        spin_lock(&gen_rtc_lock);
242
        if ( !irq_active ) {
243
                irq_active = 1;
244
                stop_rtc_timers = 0;
245
                lostint = 0;
246
                INIT_WORK(&genrtc_task, genrtc_troutine);
247
                oldsecs = get_rtc_ss();
248
                init_timer(&timer_task);
249
 
250
                stask_active = 1;
251
                if (schedule_work(&genrtc_task) == 0){
252
                        stask_active = 0;
253
                }
254
        }
255
        spin_unlock(&gen_rtc_lock);
256
        gen_rtc_irq_data = 0;
257
        return 0;
258
#else
259
        return -EINVAL;
260
#endif
261
}
262
 
263
static int gen_rtc_ioctl(struct inode *inode, struct file *file,
264
                         unsigned int cmd, unsigned long arg)
265
{
266
        struct rtc_time wtime;
267
        struct rtc_pll_info pll;
268
        void __user *argp = (void __user *)arg;
269
 
270
        switch (cmd) {
271
 
272
        case RTC_PLL_GET:
273
            if (get_rtc_pll(&pll))
274
                    return -EINVAL;
275
            else
276
                    return copy_to_user(argp, &pll, sizeof pll) ? -EFAULT : 0;
277
 
278
        case RTC_PLL_SET:
279
                if (!capable(CAP_SYS_TIME))
280
                        return -EACCES;
281
                if (copy_from_user(&pll, argp, sizeof(pll)))
282
                        return -EFAULT;
283
            return set_rtc_pll(&pll);
284
 
285
        case RTC_UIE_OFF:       /* disable ints from RTC updates.       */
286
                gen_clear_rtc_irq_bit(RTC_UIE);
287
                return 0;
288
 
289
        case RTC_UIE_ON:        /* enable ints for RTC updates. */
290
                return gen_set_rtc_irq_bit(RTC_UIE);
291
 
292
        case RTC_RD_TIME:       /* Read the time/date from RTC  */
293
                /* this doesn't get week-day, who cares */
294
                memset(&wtime, 0, sizeof(wtime));
295
                get_rtc_time(&wtime);
296
 
297
                return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0;
298
 
299
        case RTC_SET_TIME:      /* Set the RTC */
300
            {
301
                int year;
302
                unsigned char leap_yr;
303
 
304
                if (!capable(CAP_SYS_TIME))
305
                        return -EACCES;
306
 
307
                if (copy_from_user(&wtime, argp, sizeof(wtime)))
308
                        return -EFAULT;
309
 
310
                year = wtime.tm_year + 1900;
311
                leap_yr = ((!(year % 4) && (year % 100)) ||
312
                           !(year % 400));
313
 
314
                if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
315
                        return -EINVAL;
316
 
317
                if (wtime.tm_mday < 0 || wtime.tm_mday >
318
                    (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
319
                        return -EINVAL;
320
 
321
                if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
322
                    wtime.tm_min < 0 || wtime.tm_min >= 60 ||
323
                    wtime.tm_sec < 0 || wtime.tm_sec >= 60)
324
                        return -EINVAL;
325
 
326
                return set_rtc_time(&wtime);
327
            }
328
        }
329
 
330
        return -EINVAL;
331
}
332
 
333
/*
334
 *      We enforce only one user at a time here with the open/close.
335
 *      Also clear the previous interrupt data on an open, and clean
336
 *      up things on a close.
337
 */
338
 
339
static int gen_rtc_open(struct inode *inode, struct file *file)
340
{
341
        if (gen_rtc_status & RTC_IS_OPEN)
342
                return -EBUSY;
343
 
344
        gen_rtc_status |= RTC_IS_OPEN;
345
        gen_rtc_irq_data = 0;
346
        irq_active = 0;
347
 
348
        return 0;
349
}
350
 
351
static int gen_rtc_release(struct inode *inode, struct file *file)
352
{
353
        /*
354
         * Turn off all interrupts once the device is no longer
355
         * in use and clear the data.
356
         */
357
 
358
        gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
359
 
360
        gen_rtc_status &= ~RTC_IS_OPEN;
361
        return 0;
362
}
363
 
364
 
365
#ifdef CONFIG_PROC_FS
366
 
367
/*
368
 *      Info exported via "/proc/driver/rtc".
369
 */
370
 
371
static int gen_rtc_proc_output(char *buf)
372
{
373
        char *p;
374
        struct rtc_time tm;
375
        unsigned int flags;
376
        struct rtc_pll_info pll;
377
 
378
        p = buf;
379
 
380
        flags = get_rtc_time(&tm);
381
 
382
        p += sprintf(p,
383
                     "rtc_time\t: %02d:%02d:%02d\n"
384
                     "rtc_date\t: %04d-%02d-%02d\n"
385
                     "rtc_epoch\t: %04u\n",
386
                     tm.tm_hour, tm.tm_min, tm.tm_sec,
387
                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
388
 
389
        tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
390
 
391
        p += sprintf(p, "alarm\t\t: ");
392
        if (tm.tm_hour <= 24)
393
                p += sprintf(p, "%02d:", tm.tm_hour);
394
        else
395
                p += sprintf(p, "**:");
396
 
397
        if (tm.tm_min <= 59)
398
                p += sprintf(p, "%02d:", tm.tm_min);
399
        else
400
                p += sprintf(p, "**:");
401
 
402
        if (tm.tm_sec <= 59)
403
                p += sprintf(p, "%02d\n", tm.tm_sec);
404
        else
405
                p += sprintf(p, "**\n");
406
 
407
        p += sprintf(p,
408
                     "DST_enable\t: %s\n"
409
                     "BCD\t\t: %s\n"
410
                     "24hr\t\t: %s\n"
411
                     "square_wave\t: %s\n"
412
                     "alarm_IRQ\t: %s\n"
413
                     "update_IRQ\t: %s\n"
414
                     "periodic_IRQ\t: %s\n"
415
                     "periodic_freq\t: %ld\n"
416
                     "batt_status\t: %s\n",
417
                     (flags & RTC_DST_EN) ? "yes" : "no",
418
                     (flags & RTC_DM_BINARY) ? "no" : "yes",
419
                     (flags & RTC_24H) ? "yes" : "no",
420
                     (flags & RTC_SQWE) ? "yes" : "no",
421
                     (flags & RTC_AIE) ? "yes" : "no",
422
                     irq_active ? "yes" : "no",
423
                     (flags & RTC_PIE) ? "yes" : "no",
424
                     0L /* freq */,
425
                     (flags & RTC_BATT_BAD) ? "bad" : "okay");
426
        if (!get_rtc_pll(&pll))
427
            p += sprintf(p,
428
                         "PLL adjustment\t: %d\n"
429
                         "PLL max +ve adjustment\t: %d\n"
430
                         "PLL max -ve adjustment\t: %d\n"
431
                         "PLL +ve adjustment factor\t: %d\n"
432
                         "PLL -ve adjustment factor\t: %d\n"
433
                         "PLL frequency\t: %ld\n",
434
                         pll.pll_value,
435
                         pll.pll_max,
436
                         pll.pll_min,
437
                         pll.pll_posmult,
438
                         pll.pll_negmult,
439
                         pll.pll_clock);
440
        return p - buf;
441
}
442
 
443
static int gen_rtc_read_proc(char *page, char **start, off_t off,
444
                             int count, int *eof, void *data)
445
{
446
        int len = gen_rtc_proc_output (page);
447
        if (len <= off+count) *eof = 1;
448
        *start = page + off;
449
        len -= off;
450
        if (len>count) len = count;
451
        if (len<0) len = 0;
452
        return len;
453
}
454
 
455
static int __init gen_rtc_proc_init(void)
456
{
457
        struct proc_dir_entry *r;
458
 
459
        r = create_proc_read_entry("driver/rtc", 0, NULL, gen_rtc_read_proc, NULL);
460
        if (!r)
461
                return -ENOMEM;
462
        return 0;
463
}
464
#else
465
static inline int gen_rtc_proc_init(void) { return 0; }
466
#endif /* CONFIG_PROC_FS */
467
 
468
 
469
/*
470
 *      The various file operations we support.
471
 */
472
 
473
static const struct file_operations gen_rtc_fops = {
474
        .owner          = THIS_MODULE,
475
#ifdef CONFIG_GEN_RTC_X
476
        .read           = gen_rtc_read,
477
        .poll           = gen_rtc_poll,
478
#endif
479
        .ioctl          = gen_rtc_ioctl,
480
        .open           = gen_rtc_open,
481
        .release        = gen_rtc_release,
482
};
483
 
484
static struct miscdevice rtc_gen_dev =
485
{
486
        .minor          = RTC_MINOR,
487
        .name           = "rtc",
488
        .fops           = &gen_rtc_fops,
489
};
490
 
491
static int __init rtc_generic_init(void)
492
{
493
        int retval;
494
 
495
        printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION);
496
 
497
        retval = misc_register(&rtc_gen_dev);
498
        if (retval < 0)
499
                return retval;
500
 
501
        retval = gen_rtc_proc_init();
502
        if (retval) {
503
                misc_deregister(&rtc_gen_dev);
504
                return retval;
505
        }
506
 
507
        return 0;
508
}
509
 
510
static void __exit rtc_generic_exit(void)
511
{
512
        remove_proc_entry ("driver/rtc", NULL);
513
        misc_deregister(&rtc_gen_dev);
514
}
515
 
516
 
517
module_init(rtc_generic_init);
518
module_exit(rtc_generic_exit);
519
 
520
MODULE_AUTHOR("Richard Zidlicky");
521
MODULE_LICENSE("GPL");
522
MODULE_ALIAS_MISCDEV(RTC_MINOR);

powered by: WebSVN 2.1.0

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