| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *	Real Time Clock interface for Linux | 
|  | 3 | * | 
|  | 4 | *	Copyright (C) 1996 Paul Gortmaker | 
|  | 5 | * | 
|  | 6 | *	This driver allows use of the real time clock (built into | 
|  | 7 | *	nearly all computers) from user space. It exports the /dev/rtc | 
|  | 8 | *	interface supporting various ioctl() and also the | 
|  | 9 | *	/proc/driver/rtc pseudo-file for status information. | 
|  | 10 | * | 
|  | 11 | *	The ioctls can be used to set the interrupt behaviour and | 
|  | 12 | *	generation rate from the RTC via IRQ 8. Then the /dev/rtc | 
|  | 13 | *	interface can be used to make use of these timer interrupts, | 
|  | 14 | *	be they interval or alarm based. | 
|  | 15 | * | 
|  | 16 | *	The /dev/rtc interface will block on reads until an interrupt | 
|  | 17 | *	has been received. If a RTC interrupt has already happened, | 
|  | 18 | *	it will output an unsigned long and then block. The output value | 
|  | 19 | *	contains the interrupt status in the low byte and the number of | 
|  | 20 | *	interrupts since the last read in the remaining high bytes. The | 
|  | 21 | *	/dev/rtc interface can also be used with the select(2) call. | 
|  | 22 | * | 
|  | 23 | *	This program is free software; you can redistribute it and/or | 
|  | 24 | *	modify it under the terms of the GNU General Public License | 
|  | 25 | *	as published by the Free Software Foundation; either version | 
|  | 26 | *	2 of the License, or (at your option) any later version. | 
|  | 27 | * | 
|  | 28 | *	Based on other minimal char device drivers, like Alan's | 
|  | 29 | *	watchdog, Ted's random, etc. etc. | 
|  | 30 | * | 
|  | 31 | *	1.07	Paul Gortmaker. | 
|  | 32 | *	1.08	Miquel van Smoorenburg: disallow certain things on the | 
|  | 33 | *		DEC Alpha as the CMOS clock is also used for other things. | 
|  | 34 | *	1.09	Nikita Schmidt: epoch support and some Alpha cleanup. | 
|  | 35 | *	1.09a	Pete Zaitcev: Sun SPARC | 
|  | 36 | *	1.09b	Jeff Garzik: Modularize, init cleanup | 
|  | 37 | *	1.09c	Jeff Garzik: SMP cleanup | 
| Ralf Baechle | 12a0a70 | 2006-10-09 23:20:47 +0100 | [diff] [blame] | 38 | *	1.10	Paul Barton-Davis: add support for async I/O | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | *	1.10a	Andrea Arcangeli: Alpha updates | 
|  | 40 | *	1.10b	Andrew Morton: SMP lock fix | 
|  | 41 | *	1.10c	Cesar Barros: SMP locking fixes and cleanup | 
|  | 42 | *	1.10d	Paul Gortmaker: delete paranoia check in rtc_exit | 
|  | 43 | *	1.10e	Maciej W. Rozycki: Handle DECstation's year weirdness. | 
| Ralf Baechle | 12a0a70 | 2006-10-09 23:20:47 +0100 | [diff] [blame] | 44 | *	1.11	Takashi Iwai: Kernel access functions | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | *			      rtc_register/rtc_unregister/rtc_control | 
|  | 46 | *      1.11a   Daniele Bellucci: Audit create_proc_read_entry in rtc_init | 
|  | 47 | *	1.12	Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer | 
|  | 48 | *		CONFIG_HPET_EMULATE_RTC | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 49 | *	1.12a	Maciej W. Rozycki: Handle memory-mapped chips properly. | 
| Alan Cox | b759958 | 2006-01-11 12:17:32 -0800 | [diff] [blame] | 50 | *	1.12ac	Alan Cox: Allow read access to the day of week register | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | */ | 
|  | 52 |  | 
| Alan Cox | b759958 | 2006-01-11 12:17:32 -0800 | [diff] [blame] | 53 | #define RTC_VERSION		"1.12ac" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | /* | 
|  | 56 | *	Note that *all* calls to CMOS_READ and CMOS_WRITE are done with | 
|  | 57 | *	interrupts disabled. Due to the index-port/data-port (0x70/0x71) | 
|  | 58 | *	design of the RTC, we don't want two different things trying to | 
|  | 59 | *	get to it at once. (e.g. the periodic 11 min sync from time.c vs. | 
|  | 60 | *	this driver.) | 
|  | 61 | */ | 
|  | 62 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | #include <linux/interrupt.h> | 
|  | 64 | #include <linux/module.h> | 
|  | 65 | #include <linux/kernel.h> | 
|  | 66 | #include <linux/types.h> | 
|  | 67 | #include <linux/miscdevice.h> | 
|  | 68 | #include <linux/ioport.h> | 
|  | 69 | #include <linux/fcntl.h> | 
|  | 70 | #include <linux/mc146818rtc.h> | 
|  | 71 | #include <linux/init.h> | 
|  | 72 | #include <linux/poll.h> | 
|  | 73 | #include <linux/proc_fs.h> | 
|  | 74 | #include <linux/seq_file.h> | 
|  | 75 | #include <linux/spinlock.h> | 
|  | 76 | #include <linux/sysctl.h> | 
|  | 77 | #include <linux/wait.h> | 
|  | 78 | #include <linux/bcd.h> | 
| Luca Falavigna | 47f176f | 2005-06-28 20:44:42 -0700 | [diff] [blame] | 79 | #include <linux/delay.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 |  | 
|  | 81 | #include <asm/current.h> | 
|  | 82 | #include <asm/uaccess.h> | 
|  | 83 | #include <asm/system.h> | 
|  | 84 |  | 
| Chris Wright | 55f93af | 2007-07-21 17:10:09 +0200 | [diff] [blame] | 85 | #ifdef CONFIG_X86 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 86 | #include <asm/hpet.h> | 
|  | 87 | #endif | 
|  | 88 |  | 
| David S. Miller | cdee99d | 2007-07-19 13:59:58 -0700 | [diff] [blame] | 89 | #ifdef CONFIG_SPARC32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | #include <linux/pci.h> | 
|  | 91 | #include <asm/ebus.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 |  | 
|  | 93 | static unsigned long rtc_port; | 
|  | 94 | static int rtc_irq = PCI_IRQ_NONE; | 
|  | 95 | #endif | 
|  | 96 |  | 
|  | 97 | #ifdef	CONFIG_HPET_RTC_IRQ | 
|  | 98 | #undef	RTC_IRQ | 
|  | 99 | #endif | 
|  | 100 |  | 
|  | 101 | #ifdef RTC_IRQ | 
|  | 102 | static int rtc_has_irq = 1; | 
|  | 103 | #endif | 
|  | 104 |  | 
|  | 105 | #ifndef CONFIG_HPET_EMULATE_RTC | 
|  | 106 | #define is_hpet_enabled()			0 | 
|  | 107 | #define hpet_set_alarm_time(hrs, min, sec) 	0 | 
|  | 108 | #define hpet_set_periodic_freq(arg) 		0 | 
|  | 109 | #define hpet_mask_rtc_irq_bit(arg) 		0 | 
|  | 110 | #define hpet_set_rtc_irq_bit(arg) 		0 | 
|  | 111 | #define hpet_rtc_timer_init() 			do { } while (0) | 
|  | 112 | #define hpet_rtc_dropped_irq() 			0 | 
| Andrew Morton | 533ffc2 | 2006-12-22 01:06:36 -0800 | [diff] [blame] | 113 | #ifdef RTC_IRQ | 
|  | 114 | static irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) | 
|  | 115 | { | 
|  | 116 | return 0; | 
|  | 117 | } | 
|  | 118 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | #else | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 120 | extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 121 | #endif | 
|  | 122 |  | 
|  | 123 | /* | 
|  | 124 | *	We sponge a minor off of the misc major. No need slurping | 
|  | 125 | *	up another valuable major dev number for this. If you add | 
|  | 126 | *	an ioctl, make sure you don't conflict with SPARC's RTC | 
|  | 127 | *	ioctls. | 
|  | 128 | */ | 
|  | 129 |  | 
|  | 130 | static struct fasync_struct *rtc_async_queue; | 
|  | 131 |  | 
|  | 132 | static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); | 
|  | 133 |  | 
|  | 134 | #ifdef RTC_IRQ | 
| Jiri Slaby | 40565f1 | 2007-02-12 00:52:31 -0800 | [diff] [blame] | 135 | static void rtc_dropped_irq(unsigned long data); | 
|  | 136 |  | 
|  | 137 | static DEFINE_TIMER(rtc_irq_timer, rtc_dropped_irq, 0, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 | #endif | 
|  | 139 |  | 
|  | 140 | static ssize_t rtc_read(struct file *file, char __user *buf, | 
|  | 141 | size_t count, loff_t *ppos); | 
|  | 142 |  | 
|  | 143 | static int rtc_ioctl(struct inode *inode, struct file *file, | 
|  | 144 | unsigned int cmd, unsigned long arg); | 
|  | 145 |  | 
|  | 146 | #ifdef RTC_IRQ | 
|  | 147 | static unsigned int rtc_poll(struct file *file, poll_table *wait); | 
|  | 148 | #endif | 
|  | 149 |  | 
|  | 150 | static void get_rtc_alm_time (struct rtc_time *alm_tm); | 
|  | 151 | #ifdef RTC_IRQ | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 152 | static void set_rtc_irq_bit_locked(unsigned char bit); | 
|  | 153 | static void mask_rtc_irq_bit_locked(unsigned char bit); | 
|  | 154 |  | 
|  | 155 | static inline void set_rtc_irq_bit(unsigned char bit) | 
|  | 156 | { | 
|  | 157 | spin_lock_irq(&rtc_lock); | 
|  | 158 | set_rtc_irq_bit_locked(bit); | 
|  | 159 | spin_unlock_irq(&rtc_lock); | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | static void mask_rtc_irq_bit(unsigned char bit) | 
|  | 163 | { | 
|  | 164 | spin_lock_irq(&rtc_lock); | 
|  | 165 | mask_rtc_irq_bit_locked(bit); | 
|  | 166 | spin_unlock_irq(&rtc_lock); | 
|  | 167 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | #endif | 
|  | 169 |  | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 170 | #ifdef CONFIG_PROC_FS | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 171 | static int rtc_proc_open(struct inode *inode, struct file *file); | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 172 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 |  | 
|  | 174 | /* | 
|  | 175 | *	Bits in rtc_status. (6 bits of room for future expansion) | 
|  | 176 | */ | 
|  | 177 |  | 
|  | 178 | #define RTC_IS_OPEN		0x01	/* means /dev/rtc is in use	*/ | 
|  | 179 | #define RTC_TIMER_ON		0x02	/* missed irq timer active	*/ | 
|  | 180 |  | 
|  | 181 | /* | 
|  | 182 | * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is | 
|  | 183 | * protected by the big kernel lock. However, ioctl can still disable the timer | 
|  | 184 | * in rtc_status and then with del_timer after the interrupt has read | 
|  | 185 | * rtc_status but before mod_timer is called, which would then reenable the | 
|  | 186 | * timer (but you would need to have an awful timing before you'd trip on it) | 
|  | 187 | */ | 
|  | 188 | static unsigned long rtc_status = 0;	/* bitmapped status byte.	*/ | 
|  | 189 | static unsigned long rtc_freq = 0;	/* Current periodic IRQ rate	*/ | 
|  | 190 | static unsigned long rtc_irq_data = 0;	/* our output to the world	*/ | 
|  | 191 | static unsigned long rtc_max_user_freq = 64; /* > this, need CAP_SYS_RESOURCE */ | 
|  | 192 |  | 
|  | 193 | #ifdef RTC_IRQ | 
|  | 194 | /* | 
|  | 195 | * rtc_task_lock nests inside rtc_lock. | 
|  | 196 | */ | 
|  | 197 | static DEFINE_SPINLOCK(rtc_task_lock); | 
|  | 198 | static rtc_task_t *rtc_callback = NULL; | 
|  | 199 | #endif | 
|  | 200 |  | 
|  | 201 | /* | 
|  | 202 | *	If this driver ever becomes modularised, it will be really nice | 
|  | 203 | *	to make the epoch retain its value across module reload... | 
|  | 204 | */ | 
|  | 205 |  | 
|  | 206 | static unsigned long epoch = 1900;	/* year corresponding to 0x00	*/ | 
|  | 207 |  | 
|  | 208 | static const unsigned char days_in_mo[] = | 
|  | 209 | {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | 
|  | 210 |  | 
|  | 211 | /* | 
|  | 212 | * Returns true if a clock update is in progress | 
|  | 213 | */ | 
|  | 214 | static inline unsigned char rtc_is_updating(void) | 
|  | 215 | { | 
| Peter Zijlstra | 0b16f21 | 2006-09-25 16:24:23 -0700 | [diff] [blame] | 216 | unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 217 | unsigned char uip; | 
|  | 218 |  | 
| Peter Zijlstra | 0b16f21 | 2006-09-25 16:24:23 -0700 | [diff] [blame] | 219 | spin_lock_irqsave(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); | 
| Peter Zijlstra | 0b16f21 | 2006-09-25 16:24:23 -0700 | [diff] [blame] | 221 | spin_unlock_irqrestore(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 | return uip; | 
|  | 223 | } | 
|  | 224 |  | 
|  | 225 | #ifdef RTC_IRQ | 
|  | 226 | /* | 
| Thomas Gleixner | 0f2ed4c | 2006-07-01 19:29:33 -0700 | [diff] [blame] | 227 | *	A very tiny interrupt handler. It runs with IRQF_DISABLED set, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | *	but there is possibility of conflicting with the set_rtc_mmss() | 
|  | 229 | *	call (the rtc irq and the timer irq can easily run at the same | 
|  | 230 | *	time in two different CPUs). So we need to serialize | 
|  | 231 | *	accesses to the chip with the rtc_lock spinlock that each | 
|  | 232 | *	architecture should implement in the timer code. | 
|  | 233 | *	(See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.) | 
|  | 234 | */ | 
|  | 235 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 236 | irqreturn_t rtc_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | { | 
|  | 238 | /* | 
|  | 239 | *	Can be an alarm interrupt, update complete interrupt, | 
|  | 240 | *	or a periodic interrupt. We store the status in the | 
|  | 241 | *	low byte and the number of interrupts received since | 
|  | 242 | *	the last read in the remainder of rtc_irq_data. | 
|  | 243 | */ | 
|  | 244 |  | 
|  | 245 | spin_lock (&rtc_lock); | 
|  | 246 | rtc_irq_data += 0x100; | 
|  | 247 | rtc_irq_data &= ~0xff; | 
|  | 248 | if (is_hpet_enabled()) { | 
|  | 249 | /* | 
|  | 250 | * In this case it is HPET RTC interrupt handler | 
|  | 251 | * calling us, with the interrupt information | 
|  | 252 | * passed as arg1, instead of irq. | 
|  | 253 | */ | 
|  | 254 | rtc_irq_data |= (unsigned long)irq & 0xF0; | 
|  | 255 | } else { | 
|  | 256 | rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | if (rtc_status & RTC_TIMER_ON) | 
|  | 260 | mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); | 
|  | 261 |  | 
|  | 262 | spin_unlock (&rtc_lock); | 
|  | 263 |  | 
|  | 264 | /* Now do the rest of the actions */ | 
|  | 265 | spin_lock(&rtc_task_lock); | 
|  | 266 | if (rtc_callback) | 
|  | 267 | rtc_callback->func(rtc_callback->private_data); | 
|  | 268 | spin_unlock(&rtc_task_lock); | 
|  | 269 | wake_up_interruptible(&rtc_wait); | 
|  | 270 |  | 
|  | 271 | kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); | 
|  | 272 |  | 
|  | 273 | return IRQ_HANDLED; | 
|  | 274 | } | 
|  | 275 | #endif | 
|  | 276 |  | 
|  | 277 | /* | 
|  | 278 | * sysctl-tuning infrastructure. | 
|  | 279 | */ | 
|  | 280 | static ctl_table rtc_table[] = { | 
|  | 281 | { | 
| Eric W. Biederman | 7735362 | 2007-02-14 00:33:52 -0800 | [diff] [blame] | 282 | .ctl_name	= CTL_UNNUMBERED, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 | .procname	= "max-user-freq", | 
|  | 284 | .data		= &rtc_max_user_freq, | 
|  | 285 | .maxlen		= sizeof(int), | 
|  | 286 | .mode		= 0644, | 
|  | 287 | .proc_handler	= &proc_dointvec, | 
|  | 288 | }, | 
|  | 289 | { .ctl_name = 0 } | 
|  | 290 | }; | 
|  | 291 |  | 
|  | 292 | static ctl_table rtc_root[] = { | 
|  | 293 | { | 
| Eric W. Biederman | 7735362 | 2007-02-14 00:33:52 -0800 | [diff] [blame] | 294 | .ctl_name	= CTL_UNNUMBERED, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 | .procname	= "rtc", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | .mode		= 0555, | 
|  | 297 | .child		= rtc_table, | 
|  | 298 | }, | 
|  | 299 | { .ctl_name = 0 } | 
|  | 300 | }; | 
|  | 301 |  | 
|  | 302 | static ctl_table dev_root[] = { | 
|  | 303 | { | 
|  | 304 | .ctl_name	= CTL_DEV, | 
|  | 305 | .procname	= "dev", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 306 | .mode		= 0555, | 
|  | 307 | .child		= rtc_root, | 
|  | 308 | }, | 
|  | 309 | { .ctl_name = 0 } | 
|  | 310 | }; | 
|  | 311 |  | 
|  | 312 | static struct ctl_table_header *sysctl_header; | 
|  | 313 |  | 
|  | 314 | static int __init init_sysctl(void) | 
|  | 315 | { | 
| Eric W. Biederman | 0b4d414 | 2007-02-14 00:34:09 -0800 | [diff] [blame] | 316 | sysctl_header = register_sysctl_table(dev_root); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | return 0; | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 | static void __exit cleanup_sysctl(void) | 
|  | 321 | { | 
|  | 322 | unregister_sysctl_table(sysctl_header); | 
|  | 323 | } | 
|  | 324 |  | 
|  | 325 | /* | 
|  | 326 | *	Now all the various file operations that we export. | 
|  | 327 | */ | 
|  | 328 |  | 
|  | 329 | static ssize_t rtc_read(struct file *file, char __user *buf, | 
|  | 330 | size_t count, loff_t *ppos) | 
|  | 331 | { | 
|  | 332 | #ifndef RTC_IRQ | 
|  | 333 | return -EIO; | 
|  | 334 | #else | 
|  | 335 | DECLARE_WAITQUEUE(wait, current); | 
|  | 336 | unsigned long data; | 
|  | 337 | ssize_t retval; | 
|  | 338 |  | 
|  | 339 | if (rtc_has_irq == 0) | 
|  | 340 | return -EIO; | 
|  | 341 |  | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 342 | /* | 
|  | 343 | * Historically this function used to assume that sizeof(unsigned long) | 
|  | 344 | * is the same in userspace and kernelspace.  This lead to problems | 
|  | 345 | * for configurations with multiple ABIs such a the MIPS o32 and 64 | 
|  | 346 | * ABIs supported on the same kernel.  So now we support read of both | 
|  | 347 | * 4 and 8 bytes and assume that's the sizeof(unsigned long) in the | 
|  | 348 | * userspace ABI. | 
|  | 349 | */ | 
|  | 350 | if (count != sizeof(unsigned int) && count !=  sizeof(unsigned long)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 351 | return -EINVAL; | 
|  | 352 |  | 
|  | 353 | add_wait_queue(&rtc_wait, &wait); | 
|  | 354 |  | 
|  | 355 | do { | 
|  | 356 | /* First make it right. Then make it fast. Putting this whole | 
|  | 357 | * block within the parentheses of a while would be too | 
|  | 358 | * confusing. And no, xchg() is not the answer. */ | 
|  | 359 |  | 
|  | 360 | __set_current_state(TASK_INTERRUPTIBLE); | 
|  | 361 |  | 
|  | 362 | spin_lock_irq (&rtc_lock); | 
|  | 363 | data = rtc_irq_data; | 
|  | 364 | rtc_irq_data = 0; | 
|  | 365 | spin_unlock_irq (&rtc_lock); | 
|  | 366 |  | 
|  | 367 | if (data != 0) | 
|  | 368 | break; | 
|  | 369 |  | 
|  | 370 | if (file->f_flags & O_NONBLOCK) { | 
|  | 371 | retval = -EAGAIN; | 
|  | 372 | goto out; | 
|  | 373 | } | 
|  | 374 | if (signal_pending(current)) { | 
|  | 375 | retval = -ERESTARTSYS; | 
|  | 376 | goto out; | 
|  | 377 | } | 
|  | 378 | schedule(); | 
|  | 379 | } while (1); | 
|  | 380 |  | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 381 | if (count == sizeof(unsigned int)) | 
|  | 382 | retval = put_user(data, (unsigned int __user *)buf) ?: sizeof(int); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 383 | else | 
|  | 384 | retval = put_user(data, (unsigned long __user *)buf) ?: sizeof(long); | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 385 | if (!retval) | 
|  | 386 | retval = count; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 387 | out: | 
| Milind Arun Choudhary | cc0a8fb | 2007-05-08 00:30:52 -0700 | [diff] [blame] | 388 | __set_current_state(TASK_RUNNING); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 389 | remove_wait_queue(&rtc_wait, &wait); | 
|  | 390 |  | 
|  | 391 | return retval; | 
|  | 392 | #endif | 
|  | 393 | } | 
|  | 394 |  | 
|  | 395 | static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) | 
|  | 396 | { | 
|  | 397 | struct rtc_time wtime; | 
|  | 398 |  | 
|  | 399 | #ifdef RTC_IRQ | 
|  | 400 | if (rtc_has_irq == 0) { | 
|  | 401 | switch (cmd) { | 
|  | 402 | case RTC_AIE_OFF: | 
|  | 403 | case RTC_AIE_ON: | 
|  | 404 | case RTC_PIE_OFF: | 
|  | 405 | case RTC_PIE_ON: | 
|  | 406 | case RTC_UIE_OFF: | 
|  | 407 | case RTC_UIE_ON: | 
|  | 408 | case RTC_IRQP_READ: | 
|  | 409 | case RTC_IRQP_SET: | 
|  | 410 | return -EINVAL; | 
|  | 411 | }; | 
|  | 412 | } | 
|  | 413 | #endif | 
|  | 414 |  | 
|  | 415 | switch (cmd) { | 
|  | 416 | #ifdef RTC_IRQ | 
|  | 417 | case RTC_AIE_OFF:	/* Mask alarm int. enab. bit	*/ | 
|  | 418 | { | 
|  | 419 | mask_rtc_irq_bit(RTC_AIE); | 
|  | 420 | return 0; | 
|  | 421 | } | 
|  | 422 | case RTC_AIE_ON:	/* Allow alarm interrupts.	*/ | 
|  | 423 | { | 
|  | 424 | set_rtc_irq_bit(RTC_AIE); | 
|  | 425 | return 0; | 
|  | 426 | } | 
|  | 427 | case RTC_PIE_OFF:	/* Mask periodic int. enab. bit	*/ | 
|  | 428 | { | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 429 | unsigned long flags; /* can be called from isr via rtc_control() */ | 
|  | 430 | spin_lock_irqsave (&rtc_lock, flags); | 
|  | 431 | mask_rtc_irq_bit_locked(RTC_PIE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 432 | if (rtc_status & RTC_TIMER_ON) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 433 | rtc_status &= ~RTC_TIMER_ON; | 
|  | 434 | del_timer(&rtc_irq_timer); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 435 | } | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 436 | spin_unlock_irqrestore (&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 | return 0; | 
|  | 438 | } | 
|  | 439 | case RTC_PIE_ON:	/* Allow periodic ints		*/ | 
|  | 440 | { | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 441 | unsigned long flags; /* can be called from isr via rtc_control() */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 442 | /* | 
|  | 443 | * We don't really want Joe User enabling more | 
|  | 444 | * than 64Hz of interrupts on a multi-user machine. | 
|  | 445 | */ | 
|  | 446 | if (!kernel && (rtc_freq > rtc_max_user_freq) && | 
|  | 447 | (!capable(CAP_SYS_RESOURCE))) | 
|  | 448 | return -EACCES; | 
|  | 449 |  | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 450 | spin_lock_irqsave (&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 451 | if (!(rtc_status & RTC_TIMER_ON)) { | 
| Jiri Slaby | 40565f1 | 2007-02-12 00:52:31 -0800 | [diff] [blame] | 452 | mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + | 
|  | 453 | 2*HZ/100); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 454 | rtc_status |= RTC_TIMER_ON; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 455 | } | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 456 | set_rtc_irq_bit_locked(RTC_PIE); | 
|  | 457 | spin_unlock_irqrestore (&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 458 | return 0; | 
|  | 459 | } | 
|  | 460 | case RTC_UIE_OFF:	/* Mask ints from RTC updates.	*/ | 
|  | 461 | { | 
|  | 462 | mask_rtc_irq_bit(RTC_UIE); | 
|  | 463 | return 0; | 
|  | 464 | } | 
|  | 465 | case RTC_UIE_ON:	/* Allow ints for RTC updates.	*/ | 
|  | 466 | { | 
|  | 467 | set_rtc_irq_bit(RTC_UIE); | 
|  | 468 | return 0; | 
|  | 469 | } | 
|  | 470 | #endif | 
|  | 471 | case RTC_ALM_READ:	/* Read the present alarm time */ | 
|  | 472 | { | 
|  | 473 | /* | 
|  | 474 | * This returns a struct rtc_time. Reading >= 0xc0 | 
|  | 475 | * means "don't care" or "match all". Only the tm_hour, | 
|  | 476 | * tm_min, and tm_sec values are filled in. | 
|  | 477 | */ | 
|  | 478 | memset(&wtime, 0, sizeof(struct rtc_time)); | 
|  | 479 | get_rtc_alm_time(&wtime); | 
|  | 480 | break; | 
|  | 481 | } | 
|  | 482 | case RTC_ALM_SET:	/* Store a time into the alarm */ | 
|  | 483 | { | 
|  | 484 | /* | 
|  | 485 | * This expects a struct rtc_time. Writing 0xff means | 
|  | 486 | * "don't care" or "match all". Only the tm_hour, | 
|  | 487 | * tm_min and tm_sec are used. | 
|  | 488 | */ | 
|  | 489 | unsigned char hrs, min, sec; | 
|  | 490 | struct rtc_time alm_tm; | 
|  | 491 |  | 
|  | 492 | if (copy_from_user(&alm_tm, (struct rtc_time __user *)arg, | 
|  | 493 | sizeof(struct rtc_time))) | 
|  | 494 | return -EFAULT; | 
|  | 495 |  | 
|  | 496 | hrs = alm_tm.tm_hour; | 
|  | 497 | min = alm_tm.tm_min; | 
|  | 498 | sec = alm_tm.tm_sec; | 
|  | 499 |  | 
|  | 500 | spin_lock_irq(&rtc_lock); | 
|  | 501 | if (hpet_set_alarm_time(hrs, min, sec)) { | 
|  | 502 | /* | 
|  | 503 | * Fallthru and set alarm time in CMOS too, | 
|  | 504 | * so that we will get proper value in RTC_ALM_READ | 
|  | 505 | */ | 
|  | 506 | } | 
|  | 507 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || | 
|  | 508 | RTC_ALWAYS_BCD) | 
|  | 509 | { | 
|  | 510 | if (sec < 60) BIN_TO_BCD(sec); | 
|  | 511 | else sec = 0xff; | 
|  | 512 |  | 
|  | 513 | if (min < 60) BIN_TO_BCD(min); | 
|  | 514 | else min = 0xff; | 
|  | 515 |  | 
|  | 516 | if (hrs < 24) BIN_TO_BCD(hrs); | 
|  | 517 | else hrs = 0xff; | 
|  | 518 | } | 
|  | 519 | CMOS_WRITE(hrs, RTC_HOURS_ALARM); | 
|  | 520 | CMOS_WRITE(min, RTC_MINUTES_ALARM); | 
|  | 521 | CMOS_WRITE(sec, RTC_SECONDS_ALARM); | 
|  | 522 | spin_unlock_irq(&rtc_lock); | 
|  | 523 |  | 
|  | 524 | return 0; | 
|  | 525 | } | 
|  | 526 | case RTC_RD_TIME:	/* Read the time/date from RTC	*/ | 
|  | 527 | { | 
|  | 528 | memset(&wtime, 0, sizeof(struct rtc_time)); | 
|  | 529 | rtc_get_rtc_time(&wtime); | 
|  | 530 | break; | 
|  | 531 | } | 
|  | 532 | case RTC_SET_TIME:	/* Set the RTC */ | 
|  | 533 | { | 
|  | 534 | struct rtc_time rtc_tm; | 
|  | 535 | unsigned char mon, day, hrs, min, sec, leap_yr; | 
|  | 536 | unsigned char save_control, save_freq_select; | 
|  | 537 | unsigned int yrs; | 
|  | 538 | #ifdef CONFIG_MACH_DECSTATION | 
|  | 539 | unsigned int real_yrs; | 
|  | 540 | #endif | 
|  | 541 |  | 
|  | 542 | if (!capable(CAP_SYS_TIME)) | 
|  | 543 | return -EACCES; | 
|  | 544 |  | 
|  | 545 | if (copy_from_user(&rtc_tm, (struct rtc_time __user *)arg, | 
|  | 546 | sizeof(struct rtc_time))) | 
|  | 547 | return -EFAULT; | 
|  | 548 |  | 
|  | 549 | yrs = rtc_tm.tm_year + 1900; | 
|  | 550 | mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */ | 
|  | 551 | day = rtc_tm.tm_mday; | 
|  | 552 | hrs = rtc_tm.tm_hour; | 
|  | 553 | min = rtc_tm.tm_min; | 
|  | 554 | sec = rtc_tm.tm_sec; | 
|  | 555 |  | 
|  | 556 | if (yrs < 1970) | 
|  | 557 | return -EINVAL; | 
|  | 558 |  | 
|  | 559 | leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); | 
|  | 560 |  | 
|  | 561 | if ((mon > 12) || (day == 0)) | 
|  | 562 | return -EINVAL; | 
|  | 563 |  | 
|  | 564 | if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) | 
|  | 565 | return -EINVAL; | 
|  | 566 |  | 
|  | 567 | if ((hrs >= 24) || (min >= 60) || (sec >= 60)) | 
|  | 568 | return -EINVAL; | 
|  | 569 |  | 
|  | 570 | if ((yrs -= epoch) > 255)    /* They are unsigned */ | 
|  | 571 | return -EINVAL; | 
|  | 572 |  | 
|  | 573 | spin_lock_irq(&rtc_lock); | 
|  | 574 | #ifdef CONFIG_MACH_DECSTATION | 
|  | 575 | real_yrs = yrs; | 
|  | 576 | yrs = 72; | 
|  | 577 |  | 
|  | 578 | /* | 
|  | 579 | * We want to keep the year set to 73 until March | 
|  | 580 | * for non-leap years, so that Feb, 29th is handled | 
|  | 581 | * correctly. | 
|  | 582 | */ | 
|  | 583 | if (!leap_yr && mon < 3) { | 
|  | 584 | real_yrs--; | 
|  | 585 | yrs = 73; | 
|  | 586 | } | 
|  | 587 | #endif | 
|  | 588 | /* These limits and adjustments are independent of | 
|  | 589 | * whether the chip is in binary mode or not. | 
|  | 590 | */ | 
|  | 591 | if (yrs > 169) { | 
|  | 592 | spin_unlock_irq(&rtc_lock); | 
|  | 593 | return -EINVAL; | 
|  | 594 | } | 
|  | 595 | if (yrs >= 100) | 
|  | 596 | yrs -= 100; | 
|  | 597 |  | 
|  | 598 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) | 
|  | 599 | || RTC_ALWAYS_BCD) { | 
|  | 600 | BIN_TO_BCD(sec); | 
|  | 601 | BIN_TO_BCD(min); | 
|  | 602 | BIN_TO_BCD(hrs); | 
|  | 603 | BIN_TO_BCD(day); | 
|  | 604 | BIN_TO_BCD(mon); | 
|  | 605 | BIN_TO_BCD(yrs); | 
|  | 606 | } | 
|  | 607 |  | 
|  | 608 | save_control = CMOS_READ(RTC_CONTROL); | 
|  | 609 | CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); | 
|  | 610 | save_freq_select = CMOS_READ(RTC_FREQ_SELECT); | 
|  | 611 | CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); | 
|  | 612 |  | 
|  | 613 | #ifdef CONFIG_MACH_DECSTATION | 
|  | 614 | CMOS_WRITE(real_yrs, RTC_DEC_YEAR); | 
|  | 615 | #endif | 
|  | 616 | CMOS_WRITE(yrs, RTC_YEAR); | 
|  | 617 | CMOS_WRITE(mon, RTC_MONTH); | 
|  | 618 | CMOS_WRITE(day, RTC_DAY_OF_MONTH); | 
|  | 619 | CMOS_WRITE(hrs, RTC_HOURS); | 
|  | 620 | CMOS_WRITE(min, RTC_MINUTES); | 
|  | 621 | CMOS_WRITE(sec, RTC_SECONDS); | 
|  | 622 |  | 
|  | 623 | CMOS_WRITE(save_control, RTC_CONTROL); | 
|  | 624 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); | 
|  | 625 |  | 
|  | 626 | spin_unlock_irq(&rtc_lock); | 
|  | 627 | return 0; | 
|  | 628 | } | 
|  | 629 | #ifdef RTC_IRQ | 
|  | 630 | case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/ | 
|  | 631 | { | 
|  | 632 | return put_user(rtc_freq, (unsigned long __user *)arg); | 
|  | 633 | } | 
|  | 634 | case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/ | 
|  | 635 | { | 
|  | 636 | int tmp = 0; | 
|  | 637 | unsigned char val; | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 638 | unsigned long flags; /* can be called from isr via rtc_control() */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 639 |  | 
|  | 640 | /* | 
|  | 641 | * The max we can do is 8192Hz. | 
|  | 642 | */ | 
|  | 643 | if ((arg < 2) || (arg > 8192)) | 
|  | 644 | return -EINVAL; | 
|  | 645 | /* | 
|  | 646 | * We don't really want Joe User generating more | 
|  | 647 | * than 64Hz of interrupts on a multi-user machine. | 
|  | 648 | */ | 
|  | 649 | if (!kernel && (arg > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE))) | 
|  | 650 | return -EACCES; | 
|  | 651 |  | 
|  | 652 | while (arg > (1<<tmp)) | 
|  | 653 | tmp++; | 
|  | 654 |  | 
|  | 655 | /* | 
|  | 656 | * Check that the input was really a power of 2. | 
|  | 657 | */ | 
|  | 658 | if (arg != (1<<tmp)) | 
|  | 659 | return -EINVAL; | 
|  | 660 |  | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 661 | spin_lock_irqsave(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 662 | if (hpet_set_periodic_freq(arg)) { | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 663 | spin_unlock_irqrestore(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 664 | return 0; | 
|  | 665 | } | 
|  | 666 | rtc_freq = arg; | 
|  | 667 |  | 
|  | 668 | val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0; | 
|  | 669 | val |= (16 - tmp); | 
|  | 670 | CMOS_WRITE(val, RTC_FREQ_SELECT); | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 671 | spin_unlock_irqrestore(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 672 | return 0; | 
|  | 673 | } | 
|  | 674 | #endif | 
|  | 675 | case RTC_EPOCH_READ:	/* Read the epoch.	*/ | 
|  | 676 | { | 
|  | 677 | return put_user (epoch, (unsigned long __user *)arg); | 
|  | 678 | } | 
|  | 679 | case RTC_EPOCH_SET:	/* Set the epoch.	*/ | 
|  | 680 | { | 
|  | 681 | /* | 
|  | 682 | * There were no RTC clocks before 1900. | 
|  | 683 | */ | 
|  | 684 | if (arg < 1900) | 
|  | 685 | return -EINVAL; | 
|  | 686 |  | 
|  | 687 | if (!capable(CAP_SYS_TIME)) | 
|  | 688 | return -EACCES; | 
|  | 689 |  | 
|  | 690 | epoch = arg; | 
|  | 691 | return 0; | 
|  | 692 | } | 
|  | 693 | default: | 
|  | 694 | return -ENOTTY; | 
|  | 695 | } | 
|  | 696 | return copy_to_user((void __user *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; | 
|  | 697 | } | 
|  | 698 |  | 
|  | 699 | static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | 
|  | 700 | unsigned long arg) | 
|  | 701 | { | 
|  | 702 | return rtc_do_ioctl(cmd, arg, 0); | 
|  | 703 | } | 
|  | 704 |  | 
|  | 705 | /* | 
|  | 706 | *	We enforce only one user at a time here with the open/close. | 
|  | 707 | *	Also clear the previous interrupt data on an open, and clean | 
|  | 708 | *	up things on a close. | 
|  | 709 | */ | 
|  | 710 |  | 
|  | 711 | /* We use rtc_lock to protect against concurrent opens. So the BKL is not | 
|  | 712 | * needed here. Or anywhere else in this driver. */ | 
|  | 713 | static int rtc_open(struct inode *inode, struct file *file) | 
|  | 714 | { | 
|  | 715 | spin_lock_irq (&rtc_lock); | 
|  | 716 |  | 
|  | 717 | if(rtc_status & RTC_IS_OPEN) | 
|  | 718 | goto out_busy; | 
|  | 719 |  | 
|  | 720 | rtc_status |= RTC_IS_OPEN; | 
|  | 721 |  | 
|  | 722 | rtc_irq_data = 0; | 
|  | 723 | spin_unlock_irq (&rtc_lock); | 
|  | 724 | return 0; | 
|  | 725 |  | 
|  | 726 | out_busy: | 
|  | 727 | spin_unlock_irq (&rtc_lock); | 
|  | 728 | return -EBUSY; | 
|  | 729 | } | 
|  | 730 |  | 
|  | 731 | static int rtc_fasync (int fd, struct file *filp, int on) | 
|  | 732 |  | 
|  | 733 | { | 
|  | 734 | return fasync_helper (fd, filp, on, &rtc_async_queue); | 
|  | 735 | } | 
|  | 736 |  | 
|  | 737 | static int rtc_release(struct inode *inode, struct file *file) | 
|  | 738 | { | 
|  | 739 | #ifdef RTC_IRQ | 
|  | 740 | unsigned char tmp; | 
|  | 741 |  | 
|  | 742 | if (rtc_has_irq == 0) | 
|  | 743 | goto no_irq; | 
|  | 744 |  | 
|  | 745 | /* | 
|  | 746 | * Turn off all interrupts once the device is no longer | 
|  | 747 | * in use, and clear the data. | 
|  | 748 | */ | 
|  | 749 |  | 
|  | 750 | spin_lock_irq(&rtc_lock); | 
|  | 751 | if (!hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE)) { | 
|  | 752 | tmp = CMOS_READ(RTC_CONTROL); | 
|  | 753 | tmp &=  ~RTC_PIE; | 
|  | 754 | tmp &=  ~RTC_AIE; | 
|  | 755 | tmp &=  ~RTC_UIE; | 
|  | 756 | CMOS_WRITE(tmp, RTC_CONTROL); | 
|  | 757 | CMOS_READ(RTC_INTR_FLAGS); | 
|  | 758 | } | 
|  | 759 | if (rtc_status & RTC_TIMER_ON) { | 
|  | 760 | rtc_status &= ~RTC_TIMER_ON; | 
|  | 761 | del_timer(&rtc_irq_timer); | 
|  | 762 | } | 
|  | 763 | spin_unlock_irq(&rtc_lock); | 
|  | 764 |  | 
|  | 765 | if (file->f_flags & FASYNC) { | 
|  | 766 | rtc_fasync (-1, file, 0); | 
|  | 767 | } | 
|  | 768 | no_irq: | 
|  | 769 | #endif | 
|  | 770 |  | 
|  | 771 | spin_lock_irq (&rtc_lock); | 
|  | 772 | rtc_irq_data = 0; | 
|  | 773 | rtc_status &= ~RTC_IS_OPEN; | 
|  | 774 | spin_unlock_irq (&rtc_lock); | 
|  | 775 | return 0; | 
|  | 776 | } | 
|  | 777 |  | 
|  | 778 | #ifdef RTC_IRQ | 
|  | 779 | /* Called without the kernel lock - fine */ | 
|  | 780 | static unsigned int rtc_poll(struct file *file, poll_table *wait) | 
|  | 781 | { | 
|  | 782 | unsigned long l; | 
|  | 783 |  | 
|  | 784 | if (rtc_has_irq == 0) | 
|  | 785 | return 0; | 
|  | 786 |  | 
|  | 787 | poll_wait(file, &rtc_wait, wait); | 
|  | 788 |  | 
|  | 789 | spin_lock_irq (&rtc_lock); | 
|  | 790 | l = rtc_irq_data; | 
|  | 791 | spin_unlock_irq (&rtc_lock); | 
|  | 792 |  | 
|  | 793 | if (l != 0) | 
|  | 794 | return POLLIN | POLLRDNORM; | 
|  | 795 | return 0; | 
|  | 796 | } | 
|  | 797 | #endif | 
|  | 798 |  | 
|  | 799 | /* | 
|  | 800 | * exported stuffs | 
|  | 801 | */ | 
|  | 802 |  | 
|  | 803 | EXPORT_SYMBOL(rtc_register); | 
|  | 804 | EXPORT_SYMBOL(rtc_unregister); | 
|  | 805 | EXPORT_SYMBOL(rtc_control); | 
|  | 806 |  | 
|  | 807 | int rtc_register(rtc_task_t *task) | 
|  | 808 | { | 
|  | 809 | #ifndef RTC_IRQ | 
|  | 810 | return -EIO; | 
|  | 811 | #else | 
|  | 812 | if (task == NULL || task->func == NULL) | 
|  | 813 | return -EINVAL; | 
|  | 814 | spin_lock_irq(&rtc_lock); | 
|  | 815 | if (rtc_status & RTC_IS_OPEN) { | 
|  | 816 | spin_unlock_irq(&rtc_lock); | 
|  | 817 | return -EBUSY; | 
|  | 818 | } | 
|  | 819 | spin_lock(&rtc_task_lock); | 
|  | 820 | if (rtc_callback) { | 
|  | 821 | spin_unlock(&rtc_task_lock); | 
|  | 822 | spin_unlock_irq(&rtc_lock); | 
|  | 823 | return -EBUSY; | 
|  | 824 | } | 
|  | 825 | rtc_status |= RTC_IS_OPEN; | 
|  | 826 | rtc_callback = task; | 
|  | 827 | spin_unlock(&rtc_task_lock); | 
|  | 828 | spin_unlock_irq(&rtc_lock); | 
|  | 829 | return 0; | 
|  | 830 | #endif | 
|  | 831 | } | 
|  | 832 |  | 
|  | 833 | int rtc_unregister(rtc_task_t *task) | 
|  | 834 | { | 
|  | 835 | #ifndef RTC_IRQ | 
|  | 836 | return -EIO; | 
|  | 837 | #else | 
|  | 838 | unsigned char tmp; | 
|  | 839 |  | 
|  | 840 | spin_lock_irq(&rtc_lock); | 
|  | 841 | spin_lock(&rtc_task_lock); | 
|  | 842 | if (rtc_callback != task) { | 
|  | 843 | spin_unlock(&rtc_task_lock); | 
|  | 844 | spin_unlock_irq(&rtc_lock); | 
|  | 845 | return -ENXIO; | 
|  | 846 | } | 
|  | 847 | rtc_callback = NULL; | 
|  | 848 |  | 
|  | 849 | /* disable controls */ | 
|  | 850 | if (!hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE)) { | 
|  | 851 | tmp = CMOS_READ(RTC_CONTROL); | 
|  | 852 | tmp &= ~RTC_PIE; | 
|  | 853 | tmp &= ~RTC_AIE; | 
|  | 854 | tmp &= ~RTC_UIE; | 
|  | 855 | CMOS_WRITE(tmp, RTC_CONTROL); | 
|  | 856 | CMOS_READ(RTC_INTR_FLAGS); | 
|  | 857 | } | 
|  | 858 | if (rtc_status & RTC_TIMER_ON) { | 
|  | 859 | rtc_status &= ~RTC_TIMER_ON; | 
|  | 860 | del_timer(&rtc_irq_timer); | 
|  | 861 | } | 
|  | 862 | rtc_status &= ~RTC_IS_OPEN; | 
|  | 863 | spin_unlock(&rtc_task_lock); | 
|  | 864 | spin_unlock_irq(&rtc_lock); | 
|  | 865 | return 0; | 
|  | 866 | #endif | 
|  | 867 | } | 
|  | 868 |  | 
|  | 869 | int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) | 
|  | 870 | { | 
|  | 871 | #ifndef RTC_IRQ | 
|  | 872 | return -EIO; | 
|  | 873 | #else | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 874 | unsigned long flags; | 
|  | 875 | if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET) | 
|  | 876 | return -EINVAL; | 
|  | 877 | spin_lock_irqsave(&rtc_task_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 878 | if (rtc_callback != task) { | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 879 | spin_unlock_irqrestore(&rtc_task_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 880 | return -ENXIO; | 
|  | 881 | } | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 882 | spin_unlock_irqrestore(&rtc_task_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 883 | return rtc_do_ioctl(cmd, arg, 1); | 
|  | 884 | #endif | 
|  | 885 | } | 
|  | 886 |  | 
|  | 887 |  | 
|  | 888 | /* | 
|  | 889 | *	The various file operations we support. | 
|  | 890 | */ | 
|  | 891 |  | 
| Arjan van de Ven | 62322d2 | 2006-07-03 00:24:21 -0700 | [diff] [blame] | 892 | static const struct file_operations rtc_fops = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 893 | .owner		= THIS_MODULE, | 
|  | 894 | .llseek		= no_llseek, | 
|  | 895 | .read		= rtc_read, | 
|  | 896 | #ifdef RTC_IRQ | 
|  | 897 | .poll		= rtc_poll, | 
|  | 898 | #endif | 
|  | 899 | .ioctl		= rtc_ioctl, | 
|  | 900 | .open		= rtc_open, | 
|  | 901 | .release	= rtc_release, | 
|  | 902 | .fasync		= rtc_fasync, | 
|  | 903 | }; | 
|  | 904 |  | 
|  | 905 | static struct miscdevice rtc_dev = { | 
|  | 906 | .minor		= RTC_MINOR, | 
|  | 907 | .name		= "rtc", | 
|  | 908 | .fops		= &rtc_fops, | 
|  | 909 | }; | 
|  | 910 |  | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 911 | #ifdef CONFIG_PROC_FS | 
| Arjan van de Ven | 62322d2 | 2006-07-03 00:24:21 -0700 | [diff] [blame] | 912 | static const struct file_operations rtc_proc_fops = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 913 | .owner = THIS_MODULE, | 
|  | 914 | .open = rtc_proc_open, | 
|  | 915 | .read  = seq_read, | 
|  | 916 | .llseek = seq_lseek, | 
|  | 917 | .release = single_release, | 
|  | 918 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 919 | #endif | 
|  | 920 |  | 
|  | 921 | static int __init rtc_init(void) | 
|  | 922 | { | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 923 | #ifdef CONFIG_PROC_FS | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 924 | struct proc_dir_entry *ent; | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 925 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 926 | #if defined(__alpha__) || defined(__mips__) | 
|  | 927 | unsigned int year, ctrl; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 928 | char *guess = NULL; | 
|  | 929 | #endif | 
| David S. Miller | cdee99d | 2007-07-19 13:59:58 -0700 | [diff] [blame] | 930 | #ifdef CONFIG_SPARC32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 931 | struct linux_ebus *ebus; | 
|  | 932 | struct linux_ebus_device *edev; | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 933 | #else | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 934 | void *r; | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 935 | #ifdef RTC_IRQ | 
|  | 936 | irq_handler_t rtc_int_handler_ptr; | 
|  | 937 | #endif | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 938 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 939 |  | 
| David S. Miller | cdee99d | 2007-07-19 13:59:58 -0700 | [diff] [blame] | 940 | #ifdef CONFIG_SPARC32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 941 | for_each_ebus(ebus) { | 
|  | 942 | for_each_ebusdev(edev, ebus) { | 
| David S. Miller | 690c8fd | 2006-06-22 19:12:03 -0700 | [diff] [blame] | 943 | if(strcmp(edev->prom_node->name, "rtc") == 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 944 | rtc_port = edev->resource[0].start; | 
|  | 945 | rtc_irq = edev->irqs[0]; | 
|  | 946 | goto found; | 
|  | 947 | } | 
|  | 948 | } | 
|  | 949 | } | 
| Jan Beulich | f3e92d3 | 2006-12-13 00:35:04 -0800 | [diff] [blame] | 950 | rtc_has_irq = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 951 | printk(KERN_ERR "rtc_init: no PC rtc found\n"); | 
|  | 952 | return -EIO; | 
|  | 953 |  | 
|  | 954 | found: | 
|  | 955 | if (rtc_irq == PCI_IRQ_NONE) { | 
|  | 956 | rtc_has_irq = 0; | 
|  | 957 | goto no_irq; | 
|  | 958 | } | 
|  | 959 |  | 
|  | 960 | /* | 
|  | 961 | * XXX Interrupt pin #7 in Espresso is shared between RTC and | 
| David S. Miller | 53d0fc2 | 2005-09-05 23:33:05 -0700 | [diff] [blame] | 962 | * PCI Slot 2 INTA# (and some INTx# in Slot 1). | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 963 | */ | 
| Thomas Gleixner | 0f2ed4c | 2006-07-01 19:29:33 -0700 | [diff] [blame] | 964 | if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc", (void *)&rtc_port)) { | 
| Jan Beulich | f3e92d3 | 2006-12-13 00:35:04 -0800 | [diff] [blame] | 965 | rtc_has_irq = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 966 | printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq); | 
|  | 967 | return -EIO; | 
|  | 968 | } | 
|  | 969 | no_irq: | 
|  | 970 | #else | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 971 | if (RTC_IOMAPPED) | 
|  | 972 | r = request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc"); | 
|  | 973 | else | 
|  | 974 | r = request_mem_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc"); | 
|  | 975 | if (!r) { | 
| Jan Beulich | f3e92d3 | 2006-12-13 00:35:04 -0800 | [diff] [blame] | 976 | #ifdef RTC_IRQ | 
|  | 977 | rtc_has_irq = 0; | 
|  | 978 | #endif | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 979 | printk(KERN_ERR "rtc: I/O resource %lx is not free.\n", | 
|  | 980 | (long)(RTC_PORT(0))); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 981 | return -EIO; | 
|  | 982 | } | 
|  | 983 |  | 
|  | 984 | #ifdef RTC_IRQ | 
|  | 985 | if (is_hpet_enabled()) { | 
|  | 986 | rtc_int_handler_ptr = hpet_rtc_interrupt; | 
|  | 987 | } else { | 
|  | 988 | rtc_int_handler_ptr = rtc_interrupt; | 
|  | 989 | } | 
|  | 990 |  | 
| Thomas Gleixner | 0f2ed4c | 2006-07-01 19:29:33 -0700 | [diff] [blame] | 991 | if(request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED, "rtc", NULL)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 992 | /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ | 
| Jan Beulich | f3e92d3 | 2006-12-13 00:35:04 -0800 | [diff] [blame] | 993 | rtc_has_irq = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 994 | printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 995 | if (RTC_IOMAPPED) | 
|  | 996 | release_region(RTC_PORT(0), RTC_IO_EXTENT); | 
|  | 997 | else | 
|  | 998 | release_mem_region(RTC_PORT(0), RTC_IO_EXTENT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 999 | return -EIO; | 
|  | 1000 | } | 
|  | 1001 | hpet_rtc_timer_init(); | 
|  | 1002 |  | 
|  | 1003 | #endif | 
|  | 1004 |  | 
| David S. Miller | cdee99d | 2007-07-19 13:59:58 -0700 | [diff] [blame] | 1005 | #endif /* CONFIG_SPARC32 vs. others */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1006 |  | 
|  | 1007 | if (misc_register(&rtc_dev)) { | 
|  | 1008 | #ifdef RTC_IRQ | 
|  | 1009 | free_irq(RTC_IRQ, NULL); | 
| Jan Beulich | f3e92d3 | 2006-12-13 00:35:04 -0800 | [diff] [blame] | 1010 | rtc_has_irq = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1011 | #endif | 
|  | 1012 | release_region(RTC_PORT(0), RTC_IO_EXTENT); | 
|  | 1013 | return -ENODEV; | 
|  | 1014 | } | 
|  | 1015 |  | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 1016 | #ifdef CONFIG_PROC_FS | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1017 | ent = create_proc_entry("driver/rtc", 0, NULL); | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 1018 | if (ent) | 
|  | 1019 | ent->proc_fops = &rtc_proc_fops; | 
|  | 1020 | else | 
|  | 1021 | printk(KERN_WARNING "rtc: Failed to register with procfs.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1022 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1023 |  | 
|  | 1024 | #if defined(__alpha__) || defined(__mips__) | 
|  | 1025 | rtc_freq = HZ; | 
|  | 1026 |  | 
|  | 1027 | /* Each operating system on an Alpha uses its own epoch. | 
|  | 1028 | Let's try to guess which one we are using now. */ | 
|  | 1029 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1030 | if (rtc_is_updating() != 0) | 
| Luca Falavigna | 47f176f | 2005-06-28 20:44:42 -0700 | [diff] [blame] | 1031 | msleep(20); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1032 |  | 
|  | 1033 | spin_lock_irq(&rtc_lock); | 
|  | 1034 | year = CMOS_READ(RTC_YEAR); | 
|  | 1035 | ctrl = CMOS_READ(RTC_CONTROL); | 
|  | 1036 | spin_unlock_irq(&rtc_lock); | 
|  | 1037 |  | 
|  | 1038 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) | 
|  | 1039 | BCD_TO_BIN(year);       /* This should never happen... */ | 
|  | 1040 |  | 
|  | 1041 | if (year < 20) { | 
|  | 1042 | epoch = 2000; | 
|  | 1043 | guess = "SRM (post-2000)"; | 
|  | 1044 | } else if (year >= 20 && year < 48) { | 
|  | 1045 | epoch = 1980; | 
|  | 1046 | guess = "ARC console"; | 
|  | 1047 | } else if (year >= 48 && year < 72) { | 
|  | 1048 | epoch = 1952; | 
|  | 1049 | guess = "Digital UNIX"; | 
|  | 1050 | #if defined(__mips__) | 
|  | 1051 | } else if (year >= 72 && year < 74) { | 
|  | 1052 | epoch = 2000; | 
|  | 1053 | guess = "Digital DECstation"; | 
|  | 1054 | #else | 
|  | 1055 | } else if (year >= 70) { | 
|  | 1056 | epoch = 1900; | 
|  | 1057 | guess = "Standard PC (1900)"; | 
|  | 1058 | #endif | 
|  | 1059 | } | 
|  | 1060 | if (guess) | 
|  | 1061 | printk(KERN_INFO "rtc: %s epoch (%lu) detected\n", guess, epoch); | 
|  | 1062 | #endif | 
|  | 1063 | #ifdef RTC_IRQ | 
|  | 1064 | if (rtc_has_irq == 0) | 
|  | 1065 | goto no_irq2; | 
|  | 1066 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1067 | spin_lock_irq(&rtc_lock); | 
|  | 1068 | rtc_freq = 1024; | 
|  | 1069 | if (!hpet_set_periodic_freq(rtc_freq)) { | 
|  | 1070 | /* Initialize periodic freq. to CMOS reset default, which is 1024Hz */ | 
|  | 1071 | CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT); | 
|  | 1072 | } | 
|  | 1073 | spin_unlock_irq(&rtc_lock); | 
|  | 1074 | no_irq2: | 
|  | 1075 | #endif | 
|  | 1076 |  | 
|  | 1077 | (void) init_sysctl(); | 
|  | 1078 |  | 
|  | 1079 | printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n"); | 
|  | 1080 |  | 
|  | 1081 | return 0; | 
|  | 1082 | } | 
|  | 1083 |  | 
|  | 1084 | static void __exit rtc_exit (void) | 
|  | 1085 | { | 
|  | 1086 | cleanup_sysctl(); | 
|  | 1087 | remove_proc_entry ("driver/rtc", NULL); | 
|  | 1088 | misc_deregister(&rtc_dev); | 
|  | 1089 |  | 
| David S. Miller | cdee99d | 2007-07-19 13:59:58 -0700 | [diff] [blame] | 1090 | #ifdef CONFIG_SPARC32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1091 | if (rtc_has_irq) | 
|  | 1092 | free_irq (rtc_irq, &rtc_port); | 
|  | 1093 | #else | 
| Maciej W. Rozycki | 38e0e8c | 2006-07-10 04:45:30 -0700 | [diff] [blame] | 1094 | if (RTC_IOMAPPED) | 
|  | 1095 | release_region(RTC_PORT(0), RTC_IO_EXTENT); | 
|  | 1096 | else | 
|  | 1097 | release_mem_region(RTC_PORT(0), RTC_IO_EXTENT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1098 | #ifdef RTC_IRQ | 
|  | 1099 | if (rtc_has_irq) | 
|  | 1100 | free_irq (RTC_IRQ, NULL); | 
|  | 1101 | #endif | 
| David S. Miller | cdee99d | 2007-07-19 13:59:58 -0700 | [diff] [blame] | 1102 | #endif /* CONFIG_SPARC32 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1103 | } | 
|  | 1104 |  | 
|  | 1105 | module_init(rtc_init); | 
|  | 1106 | module_exit(rtc_exit); | 
|  | 1107 |  | 
|  | 1108 | #ifdef RTC_IRQ | 
|  | 1109 | /* | 
|  | 1110 | * 	At IRQ rates >= 4096Hz, an interrupt may get lost altogether. | 
|  | 1111 | *	(usually during an IDE disk interrupt, with IRQ unmasking off) | 
|  | 1112 | *	Since the interrupt handler doesn't get called, the IRQ status | 
|  | 1113 | *	byte doesn't get read, and the RTC stops generating interrupts. | 
|  | 1114 | *	A timer is set, and will call this function if/when that happens. | 
|  | 1115 | *	To get it out of this stalled state, we just read the status. | 
|  | 1116 | *	At least a jiffy of interrupts (rtc_freq/HZ) will have been lost. | 
|  | 1117 | *	(You *really* shouldn't be trying to use a non-realtime system | 
|  | 1118 | *	for something that requires a steady > 1KHz signal anyways.) | 
|  | 1119 | */ | 
|  | 1120 |  | 
|  | 1121 | static void rtc_dropped_irq(unsigned long data) | 
|  | 1122 | { | 
|  | 1123 | unsigned long freq; | 
|  | 1124 |  | 
|  | 1125 | spin_lock_irq (&rtc_lock); | 
|  | 1126 |  | 
|  | 1127 | if (hpet_rtc_dropped_irq()) { | 
|  | 1128 | spin_unlock_irq(&rtc_lock); | 
|  | 1129 | return; | 
|  | 1130 | } | 
|  | 1131 |  | 
|  | 1132 | /* Just in case someone disabled the timer from behind our back... */ | 
|  | 1133 | if (rtc_status & RTC_TIMER_ON) | 
|  | 1134 | mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); | 
|  | 1135 |  | 
|  | 1136 | rtc_irq_data += ((rtc_freq/HZ)<<8); | 
|  | 1137 | rtc_irq_data &= ~0xff; | 
|  | 1138 | rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);	/* restart */ | 
|  | 1139 |  | 
|  | 1140 | freq = rtc_freq; | 
|  | 1141 |  | 
|  | 1142 | spin_unlock_irq(&rtc_lock); | 
|  | 1143 |  | 
| Ben Collins | b2ff457 | 2007-07-15 23:40:11 -0700 | [diff] [blame] | 1144 | if (printk_ratelimit()) | 
|  | 1145 | printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", freq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1146 |  | 
|  | 1147 | /* Now we have new data */ | 
|  | 1148 | wake_up_interruptible(&rtc_wait); | 
|  | 1149 |  | 
|  | 1150 | kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); | 
|  | 1151 | } | 
|  | 1152 | #endif | 
|  | 1153 |  | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 1154 | #ifdef CONFIG_PROC_FS | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1155 | /* | 
|  | 1156 | *	Info exported via "/proc/driver/rtc". | 
|  | 1157 | */ | 
|  | 1158 |  | 
|  | 1159 | static int rtc_proc_show(struct seq_file *seq, void *v) | 
|  | 1160 | { | 
|  | 1161 | #define YN(bit) ((ctrl & bit) ? "yes" : "no") | 
|  | 1162 | #define NY(bit) ((ctrl & bit) ? "no" : "yes") | 
|  | 1163 | struct rtc_time tm; | 
|  | 1164 | unsigned char batt, ctrl; | 
|  | 1165 | unsigned long freq; | 
|  | 1166 |  | 
|  | 1167 | spin_lock_irq(&rtc_lock); | 
|  | 1168 | batt = CMOS_READ(RTC_VALID) & RTC_VRT; | 
|  | 1169 | ctrl = CMOS_READ(RTC_CONTROL); | 
|  | 1170 | freq = rtc_freq; | 
|  | 1171 | spin_unlock_irq(&rtc_lock); | 
|  | 1172 |  | 
|  | 1173 |  | 
|  | 1174 | rtc_get_rtc_time(&tm); | 
|  | 1175 |  | 
|  | 1176 | /* | 
|  | 1177 | * There is no way to tell if the luser has the RTC set for local | 
|  | 1178 | * time or for Universal Standard Time (GMT). Probably local though. | 
|  | 1179 | */ | 
|  | 1180 | seq_printf(seq, | 
|  | 1181 | "rtc_time\t: %02d:%02d:%02d\n" | 
|  | 1182 | "rtc_date\t: %04d-%02d-%02d\n" | 
|  | 1183 | "rtc_epoch\t: %04lu\n", | 
|  | 1184 | tm.tm_hour, tm.tm_min, tm.tm_sec, | 
|  | 1185 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); | 
|  | 1186 |  | 
|  | 1187 | get_rtc_alm_time(&tm); | 
|  | 1188 |  | 
|  | 1189 | /* | 
|  | 1190 | * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will | 
|  | 1191 | * match any value for that particular field. Values that are | 
|  | 1192 | * greater than a valid time, but less than 0xc0 shouldn't appear. | 
|  | 1193 | */ | 
|  | 1194 | seq_puts(seq, "alarm\t\t: "); | 
|  | 1195 | if (tm.tm_hour <= 24) | 
|  | 1196 | seq_printf(seq, "%02d:", tm.tm_hour); | 
|  | 1197 | else | 
|  | 1198 | seq_puts(seq, "**:"); | 
|  | 1199 |  | 
|  | 1200 | if (tm.tm_min <= 59) | 
|  | 1201 | seq_printf(seq, "%02d:", tm.tm_min); | 
|  | 1202 | else | 
|  | 1203 | seq_puts(seq, "**:"); | 
|  | 1204 |  | 
|  | 1205 | if (tm.tm_sec <= 59) | 
|  | 1206 | seq_printf(seq, "%02d\n", tm.tm_sec); | 
|  | 1207 | else | 
|  | 1208 | seq_puts(seq, "**\n"); | 
|  | 1209 |  | 
|  | 1210 | seq_printf(seq, | 
|  | 1211 | "DST_enable\t: %s\n" | 
|  | 1212 | "BCD\t\t: %s\n" | 
|  | 1213 | "24hr\t\t: %s\n" | 
|  | 1214 | "square_wave\t: %s\n" | 
|  | 1215 | "alarm_IRQ\t: %s\n" | 
|  | 1216 | "update_IRQ\t: %s\n" | 
|  | 1217 | "periodic_IRQ\t: %s\n" | 
|  | 1218 | "periodic_freq\t: %ld\n" | 
|  | 1219 | "batt_status\t: %s\n", | 
|  | 1220 | YN(RTC_DST_EN), | 
|  | 1221 | NY(RTC_DM_BINARY), | 
|  | 1222 | YN(RTC_24H), | 
|  | 1223 | YN(RTC_SQWE), | 
|  | 1224 | YN(RTC_AIE), | 
|  | 1225 | YN(RTC_UIE), | 
|  | 1226 | YN(RTC_PIE), | 
|  | 1227 | freq, | 
|  | 1228 | batt ? "okay" : "dead"); | 
|  | 1229 |  | 
|  | 1230 | return  0; | 
|  | 1231 | #undef YN | 
|  | 1232 | #undef NY | 
|  | 1233 | } | 
|  | 1234 |  | 
|  | 1235 | static int rtc_proc_open(struct inode *inode, struct file *file) | 
|  | 1236 | { | 
|  | 1237 | return single_open(file, rtc_proc_show, NULL); | 
|  | 1238 | } | 
| Jan Beulich | 9cef779 | 2006-12-13 00:35:05 -0800 | [diff] [blame] | 1239 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1240 |  | 
|  | 1241 | void rtc_get_rtc_time(struct rtc_time *rtc_tm) | 
|  | 1242 | { | 
| Ingo Molnar | 0f74964 | 2006-07-12 09:03:10 -0700 | [diff] [blame] | 1243 | unsigned long uip_watchdog = jiffies, flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1244 | unsigned char ctrl; | 
|  | 1245 | #ifdef CONFIG_MACH_DECSTATION | 
|  | 1246 | unsigned int real_year; | 
|  | 1247 | #endif | 
|  | 1248 |  | 
|  | 1249 | /* | 
|  | 1250 | * read RTC once any update in progress is done. The update | 
| Luca Falavigna | 47f176f | 2005-06-28 20:44:42 -0700 | [diff] [blame] | 1251 | * can take just over 2ms. We wait 20ms. There is no need to | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1252 | * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. | 
|  | 1253 | * If you need to know *exactly* when a second has started, enable | 
|  | 1254 | * periodic update complete interrupts, (via ioctl) and then | 
|  | 1255 | * immediately read /dev/rtc which will block until you get the IRQ. | 
|  | 1256 | * Once the read clears, read the RTC time (again via ioctl). Easy. | 
|  | 1257 | */ | 
|  | 1258 |  | 
| Steven Rostedt | 358333a | 2006-09-29 01:59:04 -0700 | [diff] [blame] | 1259 | while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) | 
| Petr Vandrovec | 403fe5a | 2005-08-05 15:50:07 +0200 | [diff] [blame] | 1260 | cpu_relax(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1261 |  | 
|  | 1262 | /* | 
|  | 1263 | * Only the values that we read from the RTC are set. We leave | 
| Alan Cox | b759958 | 2006-01-11 12:17:32 -0800 | [diff] [blame] | 1264 | * tm_wday, tm_yday and tm_isdst untouched. Note that while the | 
|  | 1265 | * RTC has RTC_DAY_OF_WEEK, we should usually ignore it, as it is | 
|  | 1266 | * only updated by the RTC when initially set to a non-zero value. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1267 | */ | 
| Ingo Molnar | 0f74964 | 2006-07-12 09:03:10 -0700 | [diff] [blame] | 1268 | spin_lock_irqsave(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1269 | rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); | 
|  | 1270 | rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); | 
|  | 1271 | rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); | 
|  | 1272 | rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); | 
|  | 1273 | rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); | 
|  | 1274 | rtc_tm->tm_year = CMOS_READ(RTC_YEAR); | 
| Alan Cox | b759958 | 2006-01-11 12:17:32 -0800 | [diff] [blame] | 1275 | /* Only set from 2.6.16 onwards */ | 
|  | 1276 | rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK); | 
|  | 1277 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1278 | #ifdef CONFIG_MACH_DECSTATION | 
|  | 1279 | real_year = CMOS_READ(RTC_DEC_YEAR); | 
|  | 1280 | #endif | 
|  | 1281 | ctrl = CMOS_READ(RTC_CONTROL); | 
| Ingo Molnar | 0f74964 | 2006-07-12 09:03:10 -0700 | [diff] [blame] | 1282 | spin_unlock_irqrestore(&rtc_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1283 |  | 
|  | 1284 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) | 
|  | 1285 | { | 
|  | 1286 | BCD_TO_BIN(rtc_tm->tm_sec); | 
|  | 1287 | BCD_TO_BIN(rtc_tm->tm_min); | 
|  | 1288 | BCD_TO_BIN(rtc_tm->tm_hour); | 
|  | 1289 | BCD_TO_BIN(rtc_tm->tm_mday); | 
|  | 1290 | BCD_TO_BIN(rtc_tm->tm_mon); | 
|  | 1291 | BCD_TO_BIN(rtc_tm->tm_year); | 
| Alan Cox | b759958 | 2006-01-11 12:17:32 -0800 | [diff] [blame] | 1292 | BCD_TO_BIN(rtc_tm->tm_wday); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1293 | } | 
|  | 1294 |  | 
|  | 1295 | #ifdef CONFIG_MACH_DECSTATION | 
|  | 1296 | rtc_tm->tm_year += real_year - 72; | 
|  | 1297 | #endif | 
|  | 1298 |  | 
|  | 1299 | /* | 
|  | 1300 | * Account for differences between how the RTC uses the values | 
|  | 1301 | * and how they are defined in a struct rtc_time; | 
|  | 1302 | */ | 
|  | 1303 | if ((rtc_tm->tm_year += (epoch - 1900)) <= 69) | 
|  | 1304 | rtc_tm->tm_year += 100; | 
|  | 1305 |  | 
|  | 1306 | rtc_tm->tm_mon--; | 
|  | 1307 | } | 
|  | 1308 |  | 
|  | 1309 | static void get_rtc_alm_time(struct rtc_time *alm_tm) | 
|  | 1310 | { | 
|  | 1311 | unsigned char ctrl; | 
|  | 1312 |  | 
|  | 1313 | /* | 
|  | 1314 | * Only the values that we read from the RTC are set. That | 
|  | 1315 | * means only tm_hour, tm_min, and tm_sec. | 
|  | 1316 | */ | 
|  | 1317 | spin_lock_irq(&rtc_lock); | 
|  | 1318 | alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM); | 
|  | 1319 | alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM); | 
|  | 1320 | alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM); | 
|  | 1321 | ctrl = CMOS_READ(RTC_CONTROL); | 
|  | 1322 | spin_unlock_irq(&rtc_lock); | 
|  | 1323 |  | 
|  | 1324 | if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) | 
|  | 1325 | { | 
|  | 1326 | BCD_TO_BIN(alm_tm->tm_sec); | 
|  | 1327 | BCD_TO_BIN(alm_tm->tm_min); | 
|  | 1328 | BCD_TO_BIN(alm_tm->tm_hour); | 
|  | 1329 | } | 
|  | 1330 | } | 
|  | 1331 |  | 
|  | 1332 | #ifdef RTC_IRQ | 
|  | 1333 | /* | 
|  | 1334 | * Used to disable/enable interrupts for any one of UIE, AIE, PIE. | 
|  | 1335 | * Rumour has it that if you frob the interrupt enable/disable | 
|  | 1336 | * bits in RTC_CONTROL, you should read RTC_INTR_FLAGS, to | 
|  | 1337 | * ensure you actually start getting interrupts. Probably for | 
|  | 1338 | * compatibility with older/broken chipset RTC implementations. | 
|  | 1339 | * We also clear out any old irq data after an ioctl() that | 
|  | 1340 | * meddles with the interrupt enable/disable bits. | 
|  | 1341 | */ | 
|  | 1342 |  | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 1343 | static void mask_rtc_irq_bit_locked(unsigned char bit) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1344 | { | 
|  | 1345 | unsigned char val; | 
|  | 1346 |  | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 1347 | if (hpet_mask_rtc_irq_bit(bit)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1348 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1349 | val = CMOS_READ(RTC_CONTROL); | 
|  | 1350 | val &=  ~bit; | 
|  | 1351 | CMOS_WRITE(val, RTC_CONTROL); | 
|  | 1352 | CMOS_READ(RTC_INTR_FLAGS); | 
|  | 1353 |  | 
|  | 1354 | rtc_irq_data = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1355 | } | 
|  | 1356 |  | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 1357 | static void set_rtc_irq_bit_locked(unsigned char bit) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1358 | { | 
|  | 1359 | unsigned char val; | 
|  | 1360 |  | 
| Takashi Iwai | c334876 | 2005-11-07 11:14:57 +0100 | [diff] [blame] | 1361 | if (hpet_set_rtc_irq_bit(bit)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1362 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1363 | val = CMOS_READ(RTC_CONTROL); | 
|  | 1364 | val |= bit; | 
|  | 1365 | CMOS_WRITE(val, RTC_CONTROL); | 
|  | 1366 | CMOS_READ(RTC_INTR_FLAGS); | 
|  | 1367 |  | 
|  | 1368 | rtc_irq_data = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1369 | } | 
|  | 1370 | #endif | 
|  | 1371 |  | 
|  | 1372 | MODULE_AUTHOR("Paul Gortmaker"); | 
|  | 1373 | MODULE_LICENSE("GPL"); | 
|  | 1374 | MODULE_ALIAS_MISCDEV(RTC_MINOR); |