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