fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / char / rtc.c
index fddefbb..664f36c 100644 (file)
  *     1.09a   Pete Zaitcev: Sun SPARC
  *     1.09b   Jeff Garzik: Modularize, init cleanup
  *     1.09c   Jeff Garzik: SMP cleanup
- *     1.10    Paul Barton-Davis: add support for async I/O
+ *     1.10    Paul Barton-Davis: add support for async I/O
  *     1.10a   Andrea Arcangeli: Alpha updates
  *     1.10b   Andrew Morton: SMP lock fix
  *     1.10c   Cesar Barros: SMP locking fixes and cleanup
  *     1.10d   Paul Gortmaker: delete paranoia check in rtc_exit
  *     1.10e   Maciej W. Rozycki: Handle DECstation's year weirdness.
- *      1.11    Takashi Iwai: Kernel access functions
+ *     1.11    Takashi Iwai: Kernel access functions
  *                           rtc_register/rtc_unregister/rtc_control
  *      1.11a   Daniele Bellucci: Audit create_proc_read_entry in rtc_init
  *     1.12    Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer
  *             CONFIG_HPET_EMULATE_RTC
- *
+ *     1.12a   Maciej W. Rozycki: Handle memory-mapped chips properly.
+ *     1.12ac  Alan Cox: Allow read access to the day of week register
  */
 
-#define RTC_VERSION            "1.12"
-
-#define RTC_IO_EXTENT  0x8
+#define RTC_VERSION            "1.12ac"
 
 /*
  *     Note that *all* calls to CMOS_READ and CMOS_WRITE are done with
@@ -61,7 +60,6 @@
  *     this driver.)
  */
 
-#include <linux/config.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/spinlock.h>
 #include <linux/sysctl.h>
 #include <linux/wait.h>
 #include <linux/bcd.h>
+#include <linux/delay.h>
 
 #include <asm/current.h>
 #include <asm/uaccess.h>
@@ -97,7 +97,11 @@ static unsigned long rtc_port;
 static int rtc_irq = PCI_IRQ_NONE;
 #endif
 
-#if RTC_IRQ
+#ifdef CONFIG_HPET_RTC_IRQ
+#undef RTC_IRQ
+#endif
+
+#ifdef RTC_IRQ
 static int rtc_has_irq = 1;
 #endif
 
@@ -109,9 +113,14 @@ static int rtc_has_irq = 1;
 #define hpet_set_rtc_irq_bit(arg)              0
 #define hpet_rtc_timer_init()                  do { } while (0)
 #define hpet_rtc_dropped_irq()                         0
-static inline irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) {return 0;}
+#ifdef RTC_IRQ
+static irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
+{
+       return 0;
+}
+#endif
 #else
-extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
 #endif
 
 /*
@@ -125,32 +134,45 @@ static struct fasync_struct *rtc_async_queue;
 
 static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
 static struct timer_list rtc_irq_timer;
 #endif
 
-static ssize_t rtc_read(struct file *file, char *buf,
+static ssize_t rtc_read(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos);
 
 static int rtc_ioctl(struct inode *inode, struct file *file,
                     unsigned int cmd, unsigned long arg);
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
 static unsigned int rtc_poll(struct file *file, poll_table *wait);
 #endif
 
 static void get_rtc_alm_time (struct rtc_time *alm_tm);
-#if RTC_IRQ
+#ifdef RTC_IRQ
 static void rtc_dropped_irq(unsigned long data);
 
-static void set_rtc_irq_bit(unsigned char bit);
-static void mask_rtc_irq_bit(unsigned char bit);
-#endif
+static void set_rtc_irq_bit_locked(unsigned char bit);
+static void mask_rtc_irq_bit_locked(unsigned char bit);
 
-static inline unsigned char rtc_is_updating(void);
+static inline void set_rtc_irq_bit(unsigned char bit)
+{
+       spin_lock_irq(&rtc_lock);
+       set_rtc_irq_bit_locked(bit);
+       spin_unlock_irq(&rtc_lock);
+}
 
-static int rtc_read_proc(char *page, char **start, off_t off,
-                         int count, int *eof, void *data);
+static void mask_rtc_irq_bit(unsigned char bit)
+{
+       spin_lock_irq(&rtc_lock);
+       mask_rtc_irq_bit_locked(bit);
+       spin_unlock_irq(&rtc_lock);
+}
+#endif
+
+#ifdef CONFIG_PROC_FS
+static int rtc_proc_open(struct inode *inode, struct file *file);
+#endif
 
 /*
  *     Bits in rtc_status. (6 bits of room for future expansion)
@@ -171,11 +193,11 @@ static unsigned long rtc_freq = 0;        /* Current periodic IRQ rate    */
 static unsigned long rtc_irq_data = 0; /* our output to the world      */
 static unsigned long rtc_max_user_freq = 64; /* > this, need CAP_SYS_RESOURCE */
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
 /*
  * rtc_task_lock nests inside rtc_lock.
  */
-static spinlock_t rtc_task_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(rtc_task_lock);
 static rtc_task_t *rtc_callback = NULL;
 #endif
 
@@ -189,9 +211,23 @@ static unsigned long epoch = 1900; /* year corresponding to 0x00   */
 static const unsigned char days_in_mo[] = 
 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
-#if RTC_IRQ
 /*
- *     A very tiny interrupt handler. It runs with SA_INTERRUPT set,
+ * Returns true if a clock update is in progress
+ */
+static inline unsigned char rtc_is_updating(void)
+{
+       unsigned long flags;
+       unsigned char uip;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return uip;
+}
+
+#ifdef RTC_IRQ
+/*
+ *     A very tiny interrupt handler. It runs with IRQF_DISABLED set,
  *     but there is possibility of conflicting with the set_rtc_mmss()
  *     call (the rtc irq and the timer irq can easily run at the same
  *     time in two different CPUs). So we need to serialize
@@ -200,7 +236,7 @@ static const unsigned char days_in_mo[] =
  *     (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
  */
 
-irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t rtc_interrupt(int irq, void *dev_id)
 {
        /*
         *      Can be an alarm interrupt, update complete interrupt,
@@ -295,10 +331,10 @@ static void __exit cleanup_sysctl(void)
  *     Now all the various file operations that we export.
  */
 
-static ssize_t rtc_read(struct file *file, char *buf,
+static ssize_t rtc_read(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos)
 {
-#if !RTC_IRQ
+#ifndef RTC_IRQ
        return -EIO;
 #else
        DECLARE_WAITQUEUE(wait, current);
@@ -308,7 +344,15 @@ static ssize_t rtc_read(struct file *file, char *buf,
        if (rtc_has_irq == 0)
                return -EIO;
 
-       if (count < sizeof(unsigned))
+       /*
+        * Historically this function used to assume that sizeof(unsigned long)
+        * is the same in userspace and kernelspace.  This lead to problems
+        * for configurations with multiple ABIs such a the MIPS o32 and 64
+        * ABIs supported on the same kernel.  So now we support read of both
+        * 4 and 8 bytes and assume that's the sizeof(unsigned long) in the
+        * userspace ABI.
+        */
+       if (count != sizeof(unsigned int) && count !=  sizeof(unsigned long))
                return -EINVAL;
 
        add_wait_queue(&rtc_wait, &wait);
@@ -339,10 +383,12 @@ static ssize_t rtc_read(struct file *file, char *buf,
                schedule();
        } while (1);
 
-       if (count < sizeof(unsigned long))
-               retval = put_user(data, (unsigned int *)buf) ?: sizeof(int); 
+       if (count == sizeof(unsigned int))
+               retval = put_user(data, (unsigned int __user *)buf) ?: sizeof(int);
        else
-               retval = put_user(data, (unsigned long *)buf) ?: sizeof(long);
+               retval = put_user(data, (unsigned long __user *)buf) ?: sizeof(long);
+       if (!retval)
+               retval = count;
  out:
        current->state = TASK_RUNNING;
        remove_wait_queue(&rtc_wait, &wait);
@@ -355,7 +401,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
 {
        struct rtc_time wtime; 
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
        if (rtc_has_irq == 0) {
                switch (cmd) {
                case RTC_AIE_OFF:
@@ -372,7 +418,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
 #endif
 
        switch (cmd) {
-#if RTC_IRQ
+#ifdef RTC_IRQ
        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
        {
                mask_rtc_irq_bit(RTC_AIE);
@@ -385,18 +431,19 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
        }
        case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
        {
-               mask_rtc_irq_bit(RTC_PIE);
+               unsigned long flags; /* can be called from isr via rtc_control() */
+               spin_lock_irqsave (&rtc_lock, flags);
+               mask_rtc_irq_bit_locked(RTC_PIE);
                if (rtc_status & RTC_TIMER_ON) {
-                       spin_lock_irq (&rtc_lock);
                        rtc_status &= ~RTC_TIMER_ON;
                        del_timer(&rtc_irq_timer);
-                       spin_unlock_irq (&rtc_lock);
                }
+               spin_unlock_irqrestore (&rtc_lock, flags);
                return 0;
        }
        case RTC_PIE_ON:        /* Allow periodic ints          */
        {
-
+               unsigned long flags; /* can be called from isr via rtc_control() */
                /*
                 * We don't really want Joe User enabling more
                 * than 64Hz of interrupts on a multi-user machine.
@@ -405,14 +452,14 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                        (!capable(CAP_SYS_RESOURCE)))
                        return -EACCES;
 
+               spin_lock_irqsave (&rtc_lock, flags);
                if (!(rtc_status & RTC_TIMER_ON)) {
-                       spin_lock_irq (&rtc_lock);
                        rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
                        add_timer(&rtc_irq_timer);
                        rtc_status |= RTC_TIMER_ON;
-                       spin_unlock_irq (&rtc_lock);
                }
-               set_rtc_irq_bit(RTC_PIE);
+               set_rtc_irq_bit_locked(RTC_PIE);
+               spin_unlock_irqrestore (&rtc_lock, flags);
                return 0;
        }
        case RTC_UIE_OFF:       /* Mask ints from RTC updates.  */
@@ -447,7 +494,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                unsigned char hrs, min, sec;
                struct rtc_time alm_tm;
 
-               if (copy_from_user(&alm_tm, (struct rtc_time*)arg,
+               if (copy_from_user(&alm_tm, (struct rtc_time __user *)arg,
                                   sizeof(struct rtc_time)))
                        return -EFAULT;
 
@@ -493,14 +540,14 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                unsigned char mon, day, hrs, min, sec, leap_yr;
                unsigned char save_control, save_freq_select;
                unsigned int yrs;
-#ifdef CONFIG_DECSTATION
+#ifdef CONFIG_MACH_DECSTATION
                unsigned int real_yrs;
 #endif
 
                if (!capable(CAP_SYS_TIME))
                        return -EACCES;
 
-               if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
+               if (copy_from_user(&rtc_tm, (struct rtc_time __user *)arg,
                                   sizeof(struct rtc_time)))
                        return -EFAULT;
 
@@ -529,7 +576,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                        return -EINVAL;
 
                spin_lock_irq(&rtc_lock);
-#ifdef CONFIG_DECSTATION
+#ifdef CONFIG_MACH_DECSTATION
                real_yrs = yrs;
                yrs = 72;
 
@@ -568,7 +615,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
                CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 
-#ifdef CONFIG_DECSTATION
+#ifdef CONFIG_MACH_DECSTATION
                CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
 #endif
                CMOS_WRITE(yrs, RTC_YEAR);
@@ -584,15 +631,16 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                spin_unlock_irq(&rtc_lock);
                return 0;
        }
-#if RTC_IRQ
+#ifdef RTC_IRQ
        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
        {
-               return put_user(rtc_freq, (unsigned long *)arg);
+               return put_user(rtc_freq, (unsigned long __user *)arg);
        }
        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
        {
                int tmp = 0;
                unsigned char val;
+               unsigned long flags; /* can be called from isr via rtc_control() */
 
                /* 
                 * The max we can do is 8192Hz.
@@ -615,9 +663,9 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                if (arg != (1<<tmp))
                        return -EINVAL;
 
-               spin_lock_irq(&rtc_lock);
+               spin_lock_irqsave(&rtc_lock, flags);
                if (hpet_set_periodic_freq(arg)) {
-                       spin_unlock_irq(&rtc_lock);
+                       spin_unlock_irqrestore(&rtc_lock, flags);
                        return 0;
                }
                rtc_freq = arg;
@@ -625,13 +673,13 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
                val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
                val |= (16 - tmp);
                CMOS_WRITE(val, RTC_FREQ_SELECT);
-               spin_unlock_irq(&rtc_lock);
+               spin_unlock_irqrestore(&rtc_lock, flags);
                return 0;
        }
 #endif
        case RTC_EPOCH_READ:    /* Read the epoch.      */
        {
-               return put_user (epoch, (unsigned long *)arg);
+               return put_user (epoch, (unsigned long __user *)arg);
        }
        case RTC_EPOCH_SET:     /* Set the epoch.       */
        {
@@ -650,7 +698,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
        default:
                return -ENOTTY;
        }
-       return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+       return copy_to_user((void __user *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
 }
 
 static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
@@ -693,7 +741,7 @@ static int rtc_fasync (int fd, struct file *filp, int on)
 
 static int rtc_release(struct inode *inode, struct file *file)
 {
-#if RTC_IRQ
+#ifdef RTC_IRQ
        unsigned char tmp;
 
        if (rtc_has_irq == 0)
@@ -732,7 +780,7 @@ no_irq:
        return 0;
 }
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
 /* Called without the kernel lock - fine */
 static unsigned int rtc_poll(struct file *file, poll_table *wait)
 {
@@ -763,7 +811,7 @@ EXPORT_SYMBOL(rtc_control);
 
 int rtc_register(rtc_task_t *task)
 {
-#if !RTC_IRQ
+#ifndef RTC_IRQ
        return -EIO;
 #else
        if (task == NULL || task->func == NULL)
@@ -789,7 +837,7 @@ int rtc_register(rtc_task_t *task)
 
 int rtc_unregister(rtc_task_t *task)
 {
-#if !RTC_IRQ
+#ifndef RTC_IRQ
        return -EIO;
 #else
        unsigned char tmp;
@@ -825,15 +873,18 @@ int rtc_unregister(rtc_task_t *task)
 
 int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg)
 {
-#if !RTC_IRQ
+#ifndef RTC_IRQ
        return -EIO;
 #else
-       spin_lock_irq(&rtc_task_lock);
+       unsigned long flags;
+       if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET)
+               return -EINVAL;
+       spin_lock_irqsave(&rtc_task_lock, flags);
        if (rtc_callback != task) {
-               spin_unlock_irq(&rtc_task_lock);
+               spin_unlock_irqrestore(&rtc_task_lock, flags);
                return -ENXIO;
        }
-       spin_unlock_irq(&rtc_task_lock);
+       spin_unlock_irqrestore(&rtc_task_lock, flags);
        return rtc_do_ioctl(cmd, arg, 1);
 #endif
 }
@@ -843,11 +894,11 @@ int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg)
  *     The various file operations we support.
  */
 
-static struct file_operations rtc_fops = {
+static const struct file_operations rtc_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .read           = rtc_read,
-#if RTC_IRQ
+#ifdef RTC_IRQ
        .poll           = rtc_poll,
 #endif
        .ioctl          = rtc_ioctl,
@@ -856,22 +907,29 @@ static struct file_operations rtc_fops = {
        .fasync         = rtc_fasync,
 };
 
-static struct miscdevice rtc_dev=
-{
-       RTC_MINOR,
-       "rtc",
-       &rtc_fops
+static struct miscdevice rtc_dev = {
+       .minor          = RTC_MINOR,
+       .name           = "rtc",
+       .fops           = &rtc_fops,
 };
 
-#if RTC_IRQ
-static irqreturn_t (*rtc_int_handler_ptr)(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_PROC_FS
+static const struct file_operations rtc_proc_fops = {
+       .owner = THIS_MODULE,
+       .open = rtc_proc_open,
+       .read  = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
 #endif
 
 static int __init rtc_init(void)
 {
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *ent;
+#endif
 #if defined(__alpha__) || defined(__mips__)
        unsigned int year, ctrl;
-       unsigned long uip_watchdog;
        char *guess = NULL;
 #endif
 #ifdef __sparc__
@@ -881,12 +939,17 @@ static int __init rtc_init(void)
        struct sparc_isa_bridge *isa_br;
        struct sparc_isa_device *isa_dev;
 #endif
+#else
+       void *r;
+#ifdef RTC_IRQ
+       irq_handler_t rtc_int_handler_ptr;
+#endif
 #endif
 
 #ifdef __sparc__
        for_each_ebus(ebus) {
                for_each_ebusdev(edev, ebus) {
-                       if(strcmp(edev->prom_name, "rtc") == 0) {
+                       if(strcmp(edev->prom_node->name, "rtc") == 0) {
                                rtc_port = edev->resource[0].start;
                                rtc_irq = edev->irqs[0];
                                goto found;
@@ -896,7 +959,7 @@ static int __init rtc_init(void)
 #ifdef __sparc_v9__
        for_each_isa(isa_br) {
                for_each_isadev(isa_dev, isa_br) {
-                       if (strcmp(isa_dev->prom_name, "rtc") == 0) {
+                       if (strcmp(isa_dev->prom_node->name, "rtc") == 0) {
                                rtc_port = isa_dev->resource.start;
                                rtc_irq = isa_dev->irq;
                                goto found;
@@ -904,6 +967,7 @@ static int __init rtc_init(void)
                }
        }
 #endif
+       rtc_has_irq = 0;
        printk(KERN_ERR "rtc_init: no PC rtc found\n");
        return -EIO;
 
@@ -915,35 +979,43 @@ found:
 
        /*
         * XXX Interrupt pin #7 in Espresso is shared between RTC and
-        * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here
-        * is asking for trouble with add-on boards. Change to SA_SHIRQ.
+        * PCI Slot 2 INTA# (and some INTx# in Slot 1).
         */
-       if (request_irq(rtc_irq, rtc_interrupt, SA_INTERRUPT, "rtc", (void *)&rtc_port)) {
-               /*
-                * Standard way for sparc to print irq's is to use
-                * __irq_itoa(). I think for EBus it's ok to use %d.
-                */
+       if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc", (void *)&rtc_port)) {
+               rtc_has_irq = 0;
                printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
                return -EIO;
        }
 no_irq:
 #else
-       if (!request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc")) {
-               printk(KERN_ERR "rtc: I/O port %d is not free.\n", RTC_PORT (0));
+       if (RTC_IOMAPPED)
+               r = request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
+       else
+               r = request_mem_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
+       if (!r) {
+#ifdef RTC_IRQ
+               rtc_has_irq = 0;
+#endif
+               printk(KERN_ERR "rtc: I/O resource %lx is not free.\n",
+                      (long)(RTC_PORT(0)));
                return -EIO;
        }
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
        if (is_hpet_enabled()) {
                rtc_int_handler_ptr = hpet_rtc_interrupt;
        } else {
                rtc_int_handler_ptr = rtc_interrupt;
        }
 
-       if(request_irq(RTC_IRQ, rtc_int_handler_ptr, SA_INTERRUPT, "rtc", NULL)) {
+       if(request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED, "rtc", NULL)) {
                /* Yeah right, seeing as irq 8 doesn't even hit the bus. */
+               rtc_has_irq = 0;
                printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
-               release_region(RTC_PORT(0), RTC_IO_EXTENT);
+               if (RTC_IOMAPPED)
+                       release_region(RTC_PORT(0), RTC_IO_EXTENT);
+               else
+                       release_mem_region(RTC_PORT(0), RTC_IO_EXTENT);
                return -EIO;
        }
        hpet_rtc_timer_init();
@@ -953,20 +1025,21 @@ no_irq:
 #endif /* __sparc__ vs. others */
 
        if (misc_register(&rtc_dev)) {
-#if RTC_IRQ
+#ifdef RTC_IRQ
                free_irq(RTC_IRQ, NULL);
+               rtc_has_irq = 0;
 #endif
                release_region(RTC_PORT(0), RTC_IO_EXTENT);
                return -ENODEV;
        }
-       if (create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL) == NULL) {
-#if RTC_IRQ
-               free_irq(RTC_IRQ, NULL);
+
+#ifdef CONFIG_PROC_FS
+       ent = create_proc_entry("driver/rtc", 0, NULL);
+       if (ent)
+               ent->proc_fops = &rtc_proc_fops;
+       else
+               printk(KERN_WARNING "rtc: Failed to register with procfs.\n");
 #endif
-               release_region(RTC_PORT(0), RTC_IO_EXTENT);
-               misc_deregister(&rtc_dev);
-               return -ENOMEM;
-       }
 
 #if defined(__alpha__) || defined(__mips__)
        rtc_freq = HZ;
@@ -974,12 +1047,8 @@ no_irq:
        /* Each operating system on an Alpha uses its own epoch.
           Let's try to guess which one we are using now. */
        
-       uip_watchdog = jiffies;
        if (rtc_is_updating() != 0)
-               while (jiffies - uip_watchdog < 2*HZ/100) { 
-                       barrier();
-                       cpu_relax();
-               }
+               msleep(20);
        
        spin_lock_irq(&rtc_lock);
        year = CMOS_READ(RTC_YEAR);
@@ -1011,7 +1080,7 @@ no_irq:
        if (guess)
                printk(KERN_INFO "rtc: %s epoch (%lu) detected\n", guess, epoch);
 #endif
-#if RTC_IRQ
+#ifdef RTC_IRQ
        if (rtc_has_irq == 0)
                goto no_irq2;
 
@@ -1044,8 +1113,11 @@ static void __exit rtc_exit (void)
        if (rtc_has_irq)
                free_irq (rtc_irq, &rtc_port);
 #else
-       release_region (RTC_PORT (0), RTC_IO_EXTENT);
-#if RTC_IRQ
+       if (RTC_IOMAPPED)
+               release_region(RTC_PORT(0), RTC_IO_EXTENT);
+       else
+               release_mem_region(RTC_PORT(0), RTC_IO_EXTENT);
+#ifdef RTC_IRQ
        if (rtc_has_irq)
                free_irq (RTC_IRQ, NULL);
 #endif
@@ -1055,7 +1127,7 @@ static void __exit rtc_exit (void)
 module_init(rtc_init);
 module_exit(rtc_exit);
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
 /*
  *     At IRQ rates >= 4096Hz, an interrupt may get lost altogether.
  *     (usually during an IDE disk interrupt, with IRQ unmasking off)
@@ -1100,15 +1172,15 @@ static void rtc_dropped_irq(unsigned long data)
 }
 #endif
 
+#ifdef CONFIG_PROC_FS
 /*
  *     Info exported via "/proc/driver/rtc".
  */
 
-static int rtc_proc_output (char *buf)
+static int rtc_proc_show(struct seq_file *seq, void *v)
 {
 #define YN(bit) ((ctrl & bit) ? "yes" : "no")
 #define NY(bit) ((ctrl & bit) ? "no" : "yes")
-       char *p;
        struct rtc_time tm;
        unsigned char batt, ctrl;
        unsigned long freq;
@@ -1119,7 +1191,6 @@ static int rtc_proc_output (char *buf)
        freq = rtc_freq;
        spin_unlock_irq(&rtc_lock);
 
-       p = buf;
 
        rtc_get_rtc_time(&tm);
 
@@ -1127,12 +1198,12 @@ static int rtc_proc_output (char *buf)
         * There is no way to tell if the luser has the RTC set for local
         * time or for Universal Standard Time (GMT). Probably local though.
         */
-       p += sprintf(p,
-                    "rtc_time\t: %02d:%02d:%02d\n"
-                    "rtc_date\t: %04d-%02d-%02d\n"
-                    "rtc_epoch\t: %04lu\n",
-                    tm.tm_hour, tm.tm_min, tm.tm_sec,
-                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
+       seq_printf(seq,
+                  "rtc_time\t: %02d:%02d:%02d\n"
+                  "rtc_date\t: %04d-%02d-%02d\n"
+                  "rtc_epoch\t: %04lu\n",
+                  tm.tm_hour, tm.tm_min, tm.tm_sec,
+                  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
 
        get_rtc_alm_time(&tm);
 
@@ -1141,84 +1212,64 @@ static int rtc_proc_output (char *buf)
         * match any value for that particular field. Values that are
         * greater than a valid time, but less than 0xc0 shouldn't appear.
         */
-       p += sprintf(p, "alarm\t\t: ");
+       seq_puts(seq, "alarm\t\t: ");
        if (tm.tm_hour <= 24)
-               p += sprintf(p, "%02d:", tm.tm_hour);
+               seq_printf(seq, "%02d:", tm.tm_hour);
        else
-               p += sprintf(p, "**:");
+               seq_puts(seq, "**:");
 
        if (tm.tm_min <= 59)
-               p += sprintf(p, "%02d:", tm.tm_min);
+               seq_printf(seq, "%02d:", tm.tm_min);
        else
-               p += sprintf(p, "**:");
+               seq_puts(seq, "**:");
 
        if (tm.tm_sec <= 59)
-               p += sprintf(p, "%02d\n", tm.tm_sec);
+               seq_printf(seq, "%02d\n", tm.tm_sec);
        else
-               p += sprintf(p, "**\n");
-
-       p += sprintf(p,
-                    "DST_enable\t: %s\n"
-                    "BCD\t\t: %s\n"
-                    "24hr\t\t: %s\n"
-                    "square_wave\t: %s\n"
-                    "alarm_IRQ\t: %s\n"
-                    "update_IRQ\t: %s\n"
-                    "periodic_IRQ\t: %s\n"
-                    "periodic_freq\t: %ld\n"
-                    "batt_status\t: %s\n",
-                    YN(RTC_DST_EN),
-                    NY(RTC_DM_BINARY),
-                    YN(RTC_24H),
-                    YN(RTC_SQWE),
-                    YN(RTC_AIE),
-                    YN(RTC_UIE),
-                    YN(RTC_PIE),
-                    freq,
-                    batt ? "okay" : "dead");
-
-       return  p - buf;
+               seq_puts(seq, "**\n");
+
+       seq_printf(seq,
+                  "DST_enable\t: %s\n"
+                  "BCD\t\t: %s\n"
+                  "24hr\t\t: %s\n"
+                  "square_wave\t: %s\n"
+                  "alarm_IRQ\t: %s\n"
+                  "update_IRQ\t: %s\n"
+                  "periodic_IRQ\t: %s\n"
+                  "periodic_freq\t: %ld\n"
+                  "batt_status\t: %s\n",
+                  YN(RTC_DST_EN),
+                  NY(RTC_DM_BINARY),
+                  YN(RTC_24H),
+                  YN(RTC_SQWE),
+                  YN(RTC_AIE),
+                  YN(RTC_UIE),
+                  YN(RTC_PIE),
+                  freq,
+                  batt ? "okay" : "dead");
+
+       return  0;
 #undef YN
 #undef NY
 }
 
-static int rtc_read_proc(char *page, char **start, off_t off,
-                         int count, int *eof, void *data)
-{
-        int len = rtc_proc_output (page);
-        if (len <= off+count) *eof = 1;
-        *start = page + off;
-        len -= off;
-        if (len>count) len = count;
-        if (len<0) len = 0;
-        return len;
-}
-
-/*
- * Returns true if a clock update is in progress
- */
-/* FIXME shouldn't this be above rtc_init to make it fully inlined? */
-static inline unsigned char rtc_is_updating(void)
+static int rtc_proc_open(struct inode *inode, struct file *file)
 {
-       unsigned char uip;
-
-       spin_lock_irq(&rtc_lock);
-       uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
-       spin_unlock_irq(&rtc_lock);
-       return uip;
+       return single_open(file, rtc_proc_show, NULL);
 }
+#endif
 
 void rtc_get_rtc_time(struct rtc_time *rtc_tm)
 {
-       unsigned long uip_watchdog = jiffies;
+       unsigned long uip_watchdog = jiffies, flags;
        unsigned char ctrl;
-#ifdef CONFIG_DECSTATION
+#ifdef CONFIG_MACH_DECSTATION
        unsigned int real_year;
 #endif
 
        /*
         * read RTC once any update in progress is done. The update
-        * can take just over 2ms. We wait 10 to 20ms. There is no need to
+        * can take just over 2ms. We wait 20ms. There is no need to
         * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
         * If you need to know *exactly* when a second has started, enable
         * periodic update complete interrupts, (via ioctl) and then 
@@ -1226,30 +1277,30 @@ void rtc_get_rtc_time(struct rtc_time *rtc_tm)
         * Once the read clears, read the RTC time (again via ioctl). Easy.
         */
 
-       if (rtc_is_updating() != 0)
-               while (jiffies - uip_watchdog < 2*HZ/100) {
-                       barrier();
-                       cpu_relax();
-               }
+       while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100)
+               cpu_relax();
 
        /*
         * Only the values that we read from the RTC are set. We leave
-        * tm_wday, tm_yday and tm_isdst untouched. Even though the
-        * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
-        * by the RTC when initially set to a non-zero value.
+        * tm_wday, tm_yday and tm_isdst untouched. Note that while the
+        * RTC has RTC_DAY_OF_WEEK, we should usually ignore it, as it is
+        * only updated by the RTC when initially set to a non-zero value.
         */
-       spin_lock_irq(&rtc_lock);
+       spin_lock_irqsave(&rtc_lock, flags);
        rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
        rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
        rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
        rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
        rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
        rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
-#ifdef CONFIG_DECSTATION
+       /* Only set from 2.6.16 onwards */
+       rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK);
+
+#ifdef CONFIG_MACH_DECSTATION
        real_year = CMOS_READ(RTC_DEC_YEAR);
 #endif
        ctrl = CMOS_READ(RTC_CONTROL);
-       spin_unlock_irq(&rtc_lock);
+       spin_unlock_irqrestore(&rtc_lock, flags);
 
        if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
        {
@@ -1259,9 +1310,10 @@ void rtc_get_rtc_time(struct rtc_time *rtc_tm)
                BCD_TO_BIN(rtc_tm->tm_mday);
                BCD_TO_BIN(rtc_tm->tm_mon);
                BCD_TO_BIN(rtc_tm->tm_year);
+               BCD_TO_BIN(rtc_tm->tm_wday);
        }
 
-#ifdef CONFIG_DECSTATION
+#ifdef CONFIG_MACH_DECSTATION
        rtc_tm->tm_year += real_year - 72;
 #endif
 
@@ -1298,7 +1350,7 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm)
        }
 }
 
-#if RTC_IRQ
+#ifdef RTC_IRQ
 /*
  * Used to disable/enable interrupts for any one of UIE, AIE, PIE.
  * Rumour has it that if you frob the interrupt enable/disable
@@ -1309,40 +1361,32 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm)
  * meddles with the interrupt enable/disable bits.
  */
 
-static void mask_rtc_irq_bit(unsigned char bit)
+static void mask_rtc_irq_bit_locked(unsigned char bit)
 {
        unsigned char val;
 
-       spin_lock_irq(&rtc_lock);
-       if (hpet_mask_rtc_irq_bit(bit)) {
-               spin_unlock_irq(&rtc_lock);
+       if (hpet_mask_rtc_irq_bit(bit))
                return;
-       }
        val = CMOS_READ(RTC_CONTROL);
        val &=  ~bit;
        CMOS_WRITE(val, RTC_CONTROL);
        CMOS_READ(RTC_INTR_FLAGS);
 
        rtc_irq_data = 0;
-       spin_unlock_irq(&rtc_lock);
 }
 
-static void set_rtc_irq_bit(unsigned char bit)
+static void set_rtc_irq_bit_locked(unsigned char bit)
 {
        unsigned char val;
 
-       spin_lock_irq(&rtc_lock);
-       if (hpet_set_rtc_irq_bit(bit)) {
-               spin_unlock_irq(&rtc_lock);
+       if (hpet_set_rtc_irq_bit(bit))
                return;
-       }
        val = CMOS_READ(RTC_CONTROL);
        val |= bit;
        CMOS_WRITE(val, RTC_CONTROL);
        CMOS_READ(RTC_INTR_FLAGS);
 
        rtc_irq_data = 0;
-       spin_unlock_irq(&rtc_lock);
 }
 #endif