linux-old/drivers/char/mips_rtc.c
<<
>>
Prefs
   1/*
   2 *      A simple generic Real Time Clock interface for Linux/MIPS
   3 *
   4 *      Copyright (C) 1996 Paul Gortmaker
   5 *
   6 *      Copyright (C) 2001 Monta Vista Software
   7 *      Author Jun Sun, jsun@mvista.com or jsun@junsun.net
   8 *
   9 *      This is a simple generic RTC driver for MIPS boards configured 
  10 *      with CONFIG_NEW_TIME_C.  For now, it makes use of the two
  11 *      abstract kernel RTC functions introduced in include/asm-mips/time.h:
  12 *
  13 *              extern unsigned long (*rtc_get_time)(void);
  14 *              extern int (*rtc_set_time)(unsigned long);
  15 *
  16 *      It uses the same protocol as the original drivers/char/rtc.c driver,
  17 *      but only implements two ioctls: RTC_RD_TIME and RTC_SET_TIME.
  18 *
  19 *      TODO :
  20 *
  21 *      1. we can extend the null rtc ops defined in arch/mips/time.c to
  22 *         at least record the elapsed time (by recording/checking jiffies)
  23 *         This way RTC_RD_TIME and RTC_SET_TIME will make more sense.
  24 *         (Maybe not.  A machine without a real RTC is broken anymore.
  25 *         Just a thought.)
  26 *
  27 *      2. If we make use of timer bh, we can emulate many RTC functions
  28 *         such as RTC alarm interrupt, periodic interrupts, etc.
  29 *
  30 *      3. It is possible to extend the kernel RTC abstractions to more
  31 *         than two functions, so that perhaps we can implement more
  32 *         full-featured RTC driver and also have a better abstraction
  33 *         to support more RTC hardware than the original RTC driver.
  34 *         It needs to be done very carefully.
  35 *
  36 * Change Log :
  37 *      v1.0    - [jsun] initial version
  38 */
  39
  40#define RTC_VERSION             "1.0"
  41
  42#include <linux/config.h>
  43#include <linux/module.h>
  44#include <linux/kernel.h>
  45#include <linux/types.h>
  46#include <linux/miscdevice.h>
  47#include <linux/fcntl.h>
  48#include <linux/init.h>
  49#include <linux/poll.h>
  50#include <linux/proc_fs.h>
  51#include <linux/spinlock.h>
  52
  53#include <asm/io.h>
  54#include <asm/uaccess.h>
  55#include <asm/system.h>
  56
  57/*
  58 * Check machine
  59 */
  60#if !defined(CONFIG_MIPS) || !defined(CONFIG_NEW_TIME_C)
  61#error "This driver is for MIPS machines with CONFIG_NEW_TIME_C defined"
  62#endif
  63
  64#include <asm/time.h>
  65
  66static unsigned long rtc_status = 0;    /* bitmapped status byte.       */
  67
  68static int rtc_read_proc(char *page, char **start, off_t off,
  69                         int count, int *eof, void *data);
  70
  71
  72#define RTC_IS_OPEN             0x01    /* means /dev/rtc is in use     */
  73
  74static spinlock_t rtc_lock;
  75
  76static int
  77rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  78          unsigned long arg)
  79{
  80        struct rtc_time rtc_tm;
  81        ulong curr_time;
  82
  83        switch (cmd) {
  84        case RTC_RD_TIME:       /* Read the time/date from RTC  */
  85                memset(&rtc_tm, 0, sizeof(struct rtc_time));
  86                curr_time = rtc_get_time();
  87                to_tm(curr_time, &rtc_tm);
  88                rtc_tm.tm_year -= 1900;
  89                return copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm)) ? 
  90                        -EFAULT : 0;
  91        case RTC_SET_TIME:      /* Set the RTC */
  92                if (!capable(CAP_SYS_TIME))
  93                        return -EACCES;
  94
  95                if (copy_from_user(&rtc_tm, 
  96                                   (struct rtc_time *) arg,
  97                                   sizeof(struct rtc_time))) 
  98                        return -EFAULT;
  99
 100                curr_time = mktime(rtc_tm.tm_year + 1900,
 101                                   rtc_tm.tm_mon + 1, /* tm_mon starts from 0 */
 102                                   rtc_tm.tm_mday,
 103                                   rtc_tm.tm_hour,
 104                                   rtc_tm.tm_min, 
 105                                   rtc_tm.tm_sec);
 106                return rtc_set_time(curr_time);
 107        default:
 108                return -EINVAL;
 109        }
 110}
 111
 112/* We use rtc_lock to protect against concurrent opens. So the BKL is not
 113 * needed here. Or anywhere else in this driver. */
 114static int rtc_open(struct inode *inode, struct file *file)
 115{
 116        spin_lock_irq(&rtc_lock);
 117
 118        if (rtc_status & RTC_IS_OPEN) {
 119                spin_unlock_irq(&rtc_lock);
 120                return -EBUSY;
 121        }
 122
 123        rtc_status |= RTC_IS_OPEN;
 124
 125        spin_unlock_irq(&rtc_lock);
 126        return 0;
 127}
 128
 129static int rtc_release(struct inode *inode, struct file *file)
 130{
 131        spin_lock_irq(&rtc_lock);
 132        rtc_status &= ~RTC_IS_OPEN;
 133        spin_unlock_irq(&rtc_lock);
 134        return 0;
 135}
 136
 137/*
 138 *      The various file operations we support.
 139 */
 140
 141static struct file_operations rtc_fops = {
 142        owner:THIS_MODULE,
 143        llseek:no_llseek,
 144        ioctl:rtc_ioctl,
 145        open:rtc_open,
 146        release:rtc_release,
 147};
 148
 149static struct miscdevice rtc_dev = {
 150        RTC_MINOR,
 151        "rtc",
 152        &rtc_fops
 153};
 154
 155static int __init rtc_init(void)
 156{
 157
 158        misc_register(&rtc_dev);
 159        create_proc_read_entry("driver/rtc", 0, 0, rtc_read_proc, NULL);
 160
 161        printk(KERN_INFO "Generic MIPS RTC Driver v" RTC_VERSION "\n");
 162
 163        return 0;
 164}
 165
 166static void __exit rtc_exit(void)
 167{
 168        remove_proc_entry("driver/rtc", NULL);
 169        misc_deregister(&rtc_dev);
 170
 171}
 172
 173module_init(rtc_init);
 174module_exit(rtc_exit);
 175EXPORT_NO_SYMBOLS;
 176
 177/*
 178 *      Info exported via "/proc/driver/rtc".
 179 */
 180
 181static int rtc_proc_output(char *buf)
 182{
 183        char *p;
 184        struct rtc_time tm;
 185        unsigned long curr_time;
 186
 187        curr_time = rtc_get_time();
 188        to_tm(curr_time, &tm);
 189
 190        p = buf;
 191
 192        /*
 193         * There is no way to tell if the luser has the RTC set for local
 194         * time or for Universal Standard Time (GMT). Probably local though.
 195         */
 196        p += sprintf(p,
 197                     "rtc_time\t: %02d:%02d:%02d\n"
 198                     "rtc_date\t: %04d-%02d-%02d\n"
 199                     "rtc_epoch\t: %04lu\n",
 200                     tm.tm_hour, tm.tm_min, tm.tm_sec,
 201                     tm.tm_year, tm.tm_mon + 1, tm.tm_mday, 0L);
 202
 203        return p - buf;
 204}
 205
 206static int rtc_read_proc(char *page, char **start, off_t off,
 207                         int count, int *eof, void *data)
 208{
 209        int len = rtc_proc_output(page);
 210        if (len <= off + count)
 211                *eof = 1;
 212        *start = page + off;
 213        len -= off;
 214        if (len > count)
 215                len = count;
 216        if (len < 0)
 217                len = 0;
 218        return len;
 219}
 220
 221MODULE_AUTHOR("Jun Sun");
 222MODULE_LICENSE("GPL");
 223
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.