| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Driver for NEC VR4100 series  Real Time Clock unit. | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2003-2005  Yoichi Yuasa <yuasa@hh.iij4u.or.jp> | 
|  | 5 | * | 
|  | 6 | *  This program is free software; you can redistribute it and/or modify | 
|  | 7 | *  it under the terms of the GNU General Public License as published by | 
|  | 8 | *  the Free Software Foundation; either version 2 of the License, or | 
|  | 9 | *  (at your option) any later version. | 
|  | 10 | * | 
|  | 11 | *  This program is distributed in the hope that it will be useful, | 
|  | 12 | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 14 | *  GNU General Public License for more details. | 
|  | 15 | * | 
|  | 16 | *  You should have received a copy of the GNU General Public License | 
|  | 17 | *  along with this program; if not, write to the Free Software | 
|  | 18 | *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | 19 | */ | 
|  | 20 | #include <linux/device.h> | 
|  | 21 | #include <linux/fs.h> | 
|  | 22 | #include <linux/init.h> | 
|  | 23 | #include <linux/ioport.h> | 
|  | 24 | #include <linux/irq.h> | 
|  | 25 | #include <linux/mc146818rtc.h> | 
|  | 26 | #include <linux/miscdevice.h> | 
|  | 27 | #include <linux/module.h> | 
|  | 28 | #include <linux/poll.h> | 
|  | 29 | #include <linux/rtc.h> | 
|  | 30 | #include <linux/spinlock.h> | 
|  | 31 | #include <linux/types.h> | 
|  | 32 | #include <linux/wait.h> | 
|  | 33 |  | 
|  | 34 | #include <asm/div64.h> | 
|  | 35 | #include <asm/io.h> | 
|  | 36 | #include <asm/time.h> | 
|  | 37 | #include <asm/uaccess.h> | 
|  | 38 | #include <asm/vr41xx/vr41xx.h> | 
|  | 39 |  | 
|  | 40 | MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>"); | 
|  | 41 | MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); | 
|  | 42 | MODULE_LICENSE("GPL"); | 
|  | 43 |  | 
|  | 44 | #define RTC1_TYPE1_START	0x0b0000c0UL | 
|  | 45 | #define RTC1_TYPE1_END		0x0b0000dfUL | 
|  | 46 | #define RTC2_TYPE1_START	0x0b0001c0UL | 
|  | 47 | #define RTC2_TYPE1_END		0x0b0001dfUL | 
|  | 48 |  | 
|  | 49 | #define RTC1_TYPE2_START	0x0f000100UL | 
|  | 50 | #define RTC1_TYPE2_END		0x0f00011fUL | 
|  | 51 | #define RTC2_TYPE2_START	0x0f000120UL | 
|  | 52 | #define RTC2_TYPE2_END		0x0f00013fUL | 
|  | 53 |  | 
|  | 54 | #define RTC1_SIZE		0x20 | 
|  | 55 | #define RTC2_SIZE		0x20 | 
|  | 56 |  | 
|  | 57 | /* RTC 1 registers */ | 
|  | 58 | #define ETIMELREG		0x00 | 
|  | 59 | #define ETIMEMREG		0x02 | 
|  | 60 | #define ETIMEHREG		0x04 | 
|  | 61 | /* RFU */ | 
|  | 62 | #define ECMPLREG		0x08 | 
|  | 63 | #define ECMPMREG		0x0a | 
|  | 64 | #define ECMPHREG		0x0c | 
|  | 65 | /* RFU */ | 
|  | 66 | #define RTCL1LREG		0x10 | 
|  | 67 | #define RTCL1HREG		0x12 | 
|  | 68 | #define RTCL1CNTLREG		0x14 | 
|  | 69 | #define RTCL1CNTHREG		0x16 | 
|  | 70 | #define RTCL2LREG		0x18 | 
|  | 71 | #define RTCL2HREG		0x1a | 
|  | 72 | #define RTCL2CNTLREG		0x1c | 
|  | 73 | #define RTCL2CNTHREG		0x1e | 
|  | 74 |  | 
|  | 75 | /* RTC 2 registers */ | 
|  | 76 | #define TCLKLREG		0x00 | 
|  | 77 | #define TCLKHREG		0x02 | 
|  | 78 | #define TCLKCNTLREG		0x04 | 
|  | 79 | #define TCLKCNTHREG		0x06 | 
|  | 80 | /* RFU */ | 
|  | 81 | #define RTCINTREG		0x1e | 
|  | 82 | #define TCLOCK_INT		0x08 | 
|  | 83 | #define RTCLONG2_INT		0x04 | 
|  | 84 | #define RTCLONG1_INT		0x02 | 
|  | 85 | #define ELAPSEDTIME_INT	0x01 | 
|  | 86 |  | 
|  | 87 | #define RTC_FREQUENCY		32768 | 
|  | 88 | #define MAX_PERIODIC_RATE	6553 | 
|  | 89 | #define MAX_USER_PERIODIC_RATE	64 | 
|  | 90 |  | 
|  | 91 | static void __iomem *rtc1_base; | 
|  | 92 | static void __iomem *rtc2_base; | 
|  | 93 |  | 
|  | 94 | #define rtc1_read(offset)		readw(rtc1_base + (offset)) | 
|  | 95 | #define rtc1_write(offset, value)	writew((value), rtc1_base + (offset)) | 
|  | 96 |  | 
|  | 97 | #define rtc2_read(offset)		readw(rtc2_base + (offset)) | 
|  | 98 | #define rtc2_write(offset, value)	writew((value), rtc2_base + (offset)) | 
|  | 99 |  | 
|  | 100 | static unsigned long epoch = 1970;	/* Jan 1 1970 00:00:00 */ | 
|  | 101 |  | 
|  | 102 | static spinlock_t rtc_task_lock; | 
|  | 103 | static wait_queue_head_t rtc_wait; | 
|  | 104 | static unsigned long rtc_irq_data; | 
|  | 105 | static struct fasync_struct *rtc_async_queue; | 
|  | 106 | static rtc_task_t *rtc_callback; | 
|  | 107 | static char rtc_name[] = "RTC"; | 
|  | 108 | static unsigned long periodic_frequency; | 
|  | 109 | static unsigned long periodic_count; | 
|  | 110 |  | 
|  | 111 | typedef enum { | 
|  | 112 | RTC_RELEASE, | 
|  | 113 | RTC_OPEN, | 
|  | 114 | } rtc_status_t; | 
|  | 115 |  | 
|  | 116 | static rtc_status_t rtc_status; | 
|  | 117 |  | 
|  | 118 | typedef enum { | 
|  | 119 | FUNCTION_RTC_IOCTL, | 
|  | 120 | FUNCTION_RTC_CONTROL, | 
|  | 121 | } rtc_callfrom_t; | 
|  | 122 |  | 
|  | 123 | struct resource rtc_resource[2] = { | 
|  | 124 | {	.name	= rtc_name, | 
|  | 125 | .flags	= IORESOURCE_MEM,	}, | 
|  | 126 | {	.name	= rtc_name, | 
|  | 127 | .flags	= IORESOURCE_MEM,	}, | 
|  | 128 | }; | 
|  | 129 |  | 
|  | 130 | #define RTC_NUM_RESOURCES	sizeof(rtc_resource) / sizeof(struct resource) | 
|  | 131 |  | 
|  | 132 | static inline unsigned long read_elapsed_second(void) | 
|  | 133 | { | 
|  | 134 | unsigned long first_low, first_mid, first_high; | 
|  | 135 | unsigned long second_low, second_mid, second_high; | 
|  | 136 |  | 
|  | 137 | do { | 
|  | 138 | first_low = rtc1_read(ETIMELREG); | 
|  | 139 | first_mid = rtc1_read(ETIMEMREG); | 
|  | 140 | first_high = rtc1_read(ETIMEHREG); | 
|  | 141 | second_low = rtc1_read(ETIMELREG); | 
|  | 142 | second_mid = rtc1_read(ETIMEMREG); | 
|  | 143 | second_high = rtc1_read(ETIMEHREG); | 
|  | 144 | } while (first_low != second_low || first_mid != second_mid || | 
|  | 145 | first_high != second_high); | 
|  | 146 |  | 
|  | 147 | return (first_high << 17) | (first_mid << 1) | (first_low >> 15); | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | static inline void write_elapsed_second(unsigned long sec) | 
|  | 151 | { | 
|  | 152 | spin_lock_irq(&rtc_lock); | 
|  | 153 |  | 
|  | 154 | rtc1_write(ETIMELREG, (uint16_t)(sec << 15)); | 
|  | 155 | rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1)); | 
|  | 156 | rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17)); | 
|  | 157 |  | 
|  | 158 | spin_unlock_irq(&rtc_lock); | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | static void set_alarm(struct rtc_time *time) | 
|  | 162 | { | 
|  | 163 | unsigned long alarm_sec; | 
|  | 164 |  | 
|  | 165 | alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, | 
|  | 166 | time->tm_hour, time->tm_min, time->tm_sec); | 
|  | 167 |  | 
|  | 168 | spin_lock_irq(&rtc_lock); | 
|  | 169 |  | 
|  | 170 | rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15)); | 
|  | 171 | rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1)); | 
|  | 172 | rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); | 
|  | 173 |  | 
|  | 174 | spin_unlock_irq(&rtc_lock); | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | static void read_alarm(struct rtc_time *time) | 
|  | 178 | { | 
|  | 179 | unsigned long low, mid, high; | 
|  | 180 |  | 
|  | 181 | spin_lock_irq(&rtc_lock); | 
|  | 182 |  | 
|  | 183 | low = rtc1_read(ECMPLREG); | 
|  | 184 | mid = rtc1_read(ECMPMREG); | 
|  | 185 | high = rtc1_read(ECMPHREG); | 
|  | 186 |  | 
|  | 187 | spin_unlock_irq(&rtc_lock); | 
|  | 188 |  | 
|  | 189 | to_tm((high << 17) | (mid << 1) | (low >> 15), time); | 
|  | 190 | time->tm_year -= 1900; | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | static void read_time(struct rtc_time *time) | 
|  | 194 | { | 
|  | 195 | unsigned long epoch_sec, elapsed_sec; | 
|  | 196 |  | 
|  | 197 | epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); | 
|  | 198 | elapsed_sec = read_elapsed_second(); | 
|  | 199 |  | 
|  | 200 | to_tm(epoch_sec + elapsed_sec, time); | 
|  | 201 | time->tm_year -= 1900; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | static void set_time(struct rtc_time *time) | 
|  | 205 | { | 
|  | 206 | unsigned long epoch_sec, current_sec; | 
|  | 207 |  | 
|  | 208 | epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); | 
|  | 209 | current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, | 
|  | 210 | time->tm_hour, time->tm_min, time->tm_sec); | 
|  | 211 |  | 
|  | 212 | write_elapsed_second(current_sec - epoch_sec); | 
|  | 213 | } | 
|  | 214 |  | 
|  | 215 | static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 
|  | 216 | { | 
|  | 217 | DECLARE_WAITQUEUE(wait, current); | 
|  | 218 | unsigned long irq_data; | 
|  | 219 | int retval = 0; | 
|  | 220 |  | 
|  | 221 | if (count != sizeof(unsigned int) && count != sizeof(unsigned long)) | 
|  | 222 | return -EINVAL; | 
|  | 223 |  | 
|  | 224 | add_wait_queue(&rtc_wait, &wait); | 
|  | 225 |  | 
|  | 226 | do { | 
|  | 227 | __set_current_state(TASK_INTERRUPTIBLE); | 
|  | 228 |  | 
|  | 229 | spin_lock_irq(&rtc_lock); | 
|  | 230 | irq_data = rtc_irq_data; | 
|  | 231 | rtc_irq_data = 0; | 
|  | 232 | spin_unlock_irq(&rtc_lock); | 
|  | 233 |  | 
|  | 234 | if (irq_data != 0) | 
|  | 235 | break; | 
|  | 236 |  | 
|  | 237 | if (file->f_flags & O_NONBLOCK) { | 
|  | 238 | retval = -EAGAIN; | 
|  | 239 | break; | 
|  | 240 | } | 
|  | 241 |  | 
|  | 242 | if (signal_pending(current)) { | 
|  | 243 | retval = -ERESTARTSYS; | 
|  | 244 | break; | 
|  | 245 | } | 
|  | 246 | } while (1); | 
|  | 247 |  | 
|  | 248 | if (retval == 0) { | 
|  | 249 | if (count == sizeof(unsigned int)) { | 
|  | 250 | retval = put_user(irq_data, (unsigned int __user *)buf); | 
|  | 251 | if (retval == 0) | 
|  | 252 | retval = sizeof(unsigned int); | 
|  | 253 | } else { | 
|  | 254 | retval = put_user(irq_data, (unsigned long __user *)buf); | 
|  | 255 | if (retval == 0) | 
|  | 256 | retval = sizeof(unsigned long); | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | __set_current_state(TASK_RUNNING); | 
|  | 262 | remove_wait_queue(&rtc_wait, &wait); | 
|  | 263 |  | 
|  | 264 | return retval; | 
|  | 265 | } | 
|  | 266 |  | 
|  | 267 | static unsigned int rtc_poll(struct file *file, struct poll_table_struct *table) | 
|  | 268 | { | 
|  | 269 | poll_wait(file, &rtc_wait, table); | 
|  | 270 |  | 
|  | 271 | if (rtc_irq_data != 0) | 
|  | 272 | return POLLIN | POLLRDNORM; | 
|  | 273 |  | 
|  | 274 | return 0; | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from) | 
|  | 278 | { | 
|  | 279 | struct rtc_time time; | 
|  | 280 | unsigned long count; | 
|  | 281 |  | 
|  | 282 | switch (cmd) { | 
|  | 283 | case RTC_AIE_ON: | 
|  | 284 | enable_irq(ELAPSEDTIME_IRQ); | 
|  | 285 | break; | 
|  | 286 | case RTC_AIE_OFF: | 
|  | 287 | disable_irq(ELAPSEDTIME_IRQ); | 
|  | 288 | break; | 
|  | 289 | case RTC_PIE_ON: | 
|  | 290 | enable_irq(RTCLONG1_IRQ); | 
|  | 291 | break; | 
|  | 292 | case RTC_PIE_OFF: | 
|  | 293 | disable_irq(RTCLONG1_IRQ); | 
|  | 294 | break; | 
|  | 295 | case RTC_ALM_SET: | 
|  | 296 | if (copy_from_user(&time, (struct rtc_time __user *)arg, | 
|  | 297 | sizeof(struct rtc_time))) | 
|  | 298 | return -EFAULT; | 
|  | 299 |  | 
|  | 300 | set_alarm(&time); | 
|  | 301 | break; | 
|  | 302 | case RTC_ALM_READ: | 
|  | 303 | memset(&time, 0, sizeof(struct rtc_time)); | 
|  | 304 | read_alarm(&time); | 
|  | 305 | break; | 
|  | 306 | case RTC_RD_TIME: | 
|  | 307 | memset(&time, 0, sizeof(struct rtc_time)); | 
|  | 308 | read_time(&time); | 
|  | 309 | if (copy_to_user((void __user *)arg, &time, sizeof(struct rtc_time))) | 
|  | 310 | return -EFAULT; | 
|  | 311 | break; | 
|  | 312 | case RTC_SET_TIME: | 
|  | 313 | if (capable(CAP_SYS_TIME) == 0) | 
|  | 314 | return -EACCES; | 
|  | 315 |  | 
|  | 316 | if (copy_from_user(&time, (struct rtc_time __user *)arg, | 
|  | 317 | sizeof(struct rtc_time))) | 
|  | 318 | return -EFAULT; | 
|  | 319 |  | 
|  | 320 | set_time(&time); | 
|  | 321 | break; | 
|  | 322 | case RTC_IRQP_READ: | 
|  | 323 | return put_user(periodic_frequency, (unsigned long __user *)arg); | 
|  | 324 | break; | 
|  | 325 | case RTC_IRQP_SET: | 
|  | 326 | if (arg > MAX_PERIODIC_RATE) | 
|  | 327 | return -EINVAL; | 
|  | 328 |  | 
|  | 329 | if (from == FUNCTION_RTC_IOCTL && arg > MAX_USER_PERIODIC_RATE && | 
|  | 330 | capable(CAP_SYS_RESOURCE) == 0) | 
|  | 331 | return -EACCES; | 
|  | 332 |  | 
|  | 333 | periodic_frequency = arg; | 
|  | 334 |  | 
|  | 335 | count = RTC_FREQUENCY; | 
|  | 336 | do_div(count, arg); | 
|  | 337 |  | 
|  | 338 | periodic_count = count; | 
|  | 339 |  | 
|  | 340 | spin_lock_irq(&rtc_lock); | 
|  | 341 |  | 
|  | 342 | rtc1_write(RTCL1LREG, count); | 
|  | 343 | rtc1_write(RTCL1HREG, count >> 16); | 
|  | 344 |  | 
|  | 345 | spin_unlock_irq(&rtc_lock); | 
|  | 346 | break; | 
|  | 347 | case RTC_EPOCH_READ: | 
|  | 348 | return put_user(epoch, (unsigned long __user *)arg); | 
|  | 349 | case RTC_EPOCH_SET: | 
|  | 350 | /* Doesn't support before 1900 */ | 
|  | 351 | if (arg < 1900) | 
|  | 352 | return -EINVAL; | 
|  | 353 |  | 
|  | 354 | if (capable(CAP_SYS_TIME) == 0) | 
|  | 355 | return -EACCES; | 
|  | 356 |  | 
|  | 357 | epoch = arg; | 
|  | 358 | break; | 
|  | 359 | default: | 
|  | 360 | return -EINVAL; | 
|  | 361 | } | 
|  | 362 |  | 
|  | 363 | return 0; | 
|  | 364 | } | 
|  | 365 |  | 
|  | 366 | static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | 
|  | 367 | unsigned long arg) | 
|  | 368 | { | 
|  | 369 | return rtc_do_ioctl(cmd, arg, FUNCTION_RTC_IOCTL); | 
|  | 370 | } | 
|  | 371 |  | 
|  | 372 | static int rtc_open(struct inode *inode, struct file *file) | 
|  | 373 | { | 
|  | 374 | spin_lock_irq(&rtc_lock); | 
|  | 375 |  | 
|  | 376 | if (rtc_status == RTC_OPEN) { | 
|  | 377 | spin_unlock_irq(&rtc_lock); | 
|  | 378 | return -EBUSY; | 
|  | 379 | } | 
|  | 380 |  | 
|  | 381 | rtc_status = RTC_OPEN; | 
|  | 382 | rtc_irq_data = 0; | 
|  | 383 |  | 
|  | 384 | spin_unlock_irq(&rtc_lock); | 
|  | 385 |  | 
|  | 386 | return 0; | 
|  | 387 | } | 
|  | 388 |  | 
|  | 389 | static int rtc_release(struct inode *inode, struct file *file) | 
|  | 390 | { | 
|  | 391 | if (file->f_flags & FASYNC) | 
|  | 392 | (void)fasync_helper(-1, file, 0, &rtc_async_queue); | 
|  | 393 |  | 
|  | 394 | spin_lock_irq(&rtc_lock); | 
|  | 395 |  | 
|  | 396 | rtc1_write(ECMPLREG, 0); | 
|  | 397 | rtc1_write(ECMPMREG, 0); | 
|  | 398 | rtc1_write(ECMPHREG, 0); | 
|  | 399 | rtc1_write(RTCL1LREG, 0); | 
|  | 400 | rtc1_write(RTCL1HREG, 0); | 
|  | 401 |  | 
|  | 402 | rtc_status = RTC_RELEASE; | 
|  | 403 |  | 
|  | 404 | spin_unlock_irq(&rtc_lock); | 
|  | 405 |  | 
|  | 406 | disable_irq(ELAPSEDTIME_IRQ); | 
|  | 407 | disable_irq(RTCLONG1_IRQ); | 
|  | 408 |  | 
|  | 409 | return 0; | 
|  | 410 | } | 
|  | 411 |  | 
|  | 412 | static int rtc_fasync(int fd, struct file *file, int on) | 
|  | 413 | { | 
|  | 414 | return fasync_helper(fd, file, on, &rtc_async_queue); | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | static struct file_operations rtc_fops = { | 
|  | 418 | .owner		= THIS_MODULE, | 
|  | 419 | .llseek		= no_llseek, | 
|  | 420 | .read		= rtc_read, | 
|  | 421 | .poll		= rtc_poll, | 
|  | 422 | .ioctl		= rtc_ioctl, | 
|  | 423 | .open		= rtc_open, | 
|  | 424 | .release	= rtc_release, | 
|  | 425 | .fasync		= rtc_fasync, | 
|  | 426 | }; | 
|  | 427 |  | 
|  | 428 | static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 
|  | 429 | { | 
|  | 430 | spin_lock(&rtc_lock); | 
|  | 431 | rtc2_write(RTCINTREG, ELAPSEDTIME_INT); | 
|  | 432 |  | 
|  | 433 | rtc_irq_data += 0x100; | 
|  | 434 | rtc_irq_data &= ~0xff; | 
|  | 435 | rtc_irq_data |= RTC_AF; | 
|  | 436 | spin_unlock(&rtc_lock); | 
|  | 437 |  | 
|  | 438 | spin_lock(&rtc_lock); | 
|  | 439 | if (rtc_callback) | 
|  | 440 | rtc_callback->func(rtc_callback->private_data); | 
|  | 441 | spin_unlock(&rtc_lock); | 
|  | 442 |  | 
|  | 443 | wake_up_interruptible(&rtc_wait); | 
|  | 444 |  | 
|  | 445 | kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); | 
|  | 446 |  | 
|  | 447 | return IRQ_HANDLED; | 
|  | 448 | } | 
|  | 449 |  | 
|  | 450 | static irqreturn_t rtclong1_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 
|  | 451 | { | 
|  | 452 | unsigned long count = periodic_count; | 
|  | 453 |  | 
|  | 454 | spin_lock(&rtc_lock); | 
|  | 455 | rtc2_write(RTCINTREG, RTCLONG1_INT); | 
|  | 456 |  | 
|  | 457 | rtc1_write(RTCL1LREG, count); | 
|  | 458 | rtc1_write(RTCL1HREG, count >> 16); | 
|  | 459 |  | 
|  | 460 | rtc_irq_data += 0x100; | 
|  | 461 | rtc_irq_data &= ~0xff; | 
|  | 462 | rtc_irq_data |= RTC_PF; | 
|  | 463 | spin_unlock(&rtc_lock); | 
|  | 464 |  | 
|  | 465 | spin_lock(&rtc_task_lock); | 
|  | 466 | if (rtc_callback) | 
|  | 467 | rtc_callback->func(rtc_callback->private_data); | 
|  | 468 | spin_unlock(&rtc_task_lock); | 
|  | 469 |  | 
|  | 470 | wake_up_interruptible(&rtc_wait); | 
|  | 471 |  | 
|  | 472 | kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); | 
|  | 473 |  | 
|  | 474 | return IRQ_HANDLED; | 
|  | 475 | } | 
|  | 476 |  | 
|  | 477 | int rtc_register(rtc_task_t *task) | 
|  | 478 | { | 
|  | 479 | if (task == NULL || task->func == NULL) | 
|  | 480 | return -EINVAL; | 
|  | 481 |  | 
|  | 482 | spin_lock_irq(&rtc_lock); | 
|  | 483 | if (rtc_status == RTC_OPEN) { | 
|  | 484 | spin_unlock_irq(&rtc_lock); | 
|  | 485 | return -EBUSY; | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 | spin_lock(&rtc_task_lock); | 
|  | 489 | if (rtc_callback != NULL) { | 
|  | 490 | spin_unlock(&rtc_task_lock); | 
|  | 491 | spin_unlock_irq(&rtc_task_lock); | 
|  | 492 | return -EBUSY; | 
|  | 493 | } | 
|  | 494 |  | 
|  | 495 | rtc_callback = task; | 
|  | 496 | spin_unlock(&rtc_task_lock); | 
|  | 497 |  | 
|  | 498 | rtc_status = RTC_OPEN; | 
|  | 499 |  | 
|  | 500 | spin_unlock_irq(&rtc_lock); | 
|  | 501 |  | 
|  | 502 | return 0; | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | EXPORT_SYMBOL_GPL(rtc_register); | 
|  | 506 |  | 
|  | 507 | int rtc_unregister(rtc_task_t *task) | 
|  | 508 | { | 
|  | 509 | spin_lock_irq(&rtc_task_lock); | 
|  | 510 | if (task == NULL || rtc_callback != task) { | 
|  | 511 | spin_unlock_irq(&rtc_task_lock); | 
|  | 512 | return -ENXIO; | 
|  | 513 | } | 
|  | 514 |  | 
|  | 515 | spin_lock(&rtc_lock); | 
|  | 516 |  | 
|  | 517 | rtc1_write(ECMPLREG, 0); | 
|  | 518 | rtc1_write(ECMPMREG, 0); | 
|  | 519 | rtc1_write(ECMPHREG, 0); | 
|  | 520 | rtc1_write(RTCL1LREG, 0); | 
|  | 521 | rtc1_write(RTCL1HREG, 0); | 
|  | 522 |  | 
|  | 523 | rtc_status = RTC_RELEASE; | 
|  | 524 |  | 
|  | 525 | spin_unlock(&rtc_lock); | 
|  | 526 |  | 
|  | 527 | rtc_callback = NULL; | 
|  | 528 |  | 
|  | 529 | spin_unlock_irq(&rtc_task_lock); | 
|  | 530 |  | 
|  | 531 | disable_irq(ELAPSEDTIME_IRQ); | 
|  | 532 | disable_irq(RTCLONG1_IRQ); | 
|  | 533 |  | 
|  | 534 | return 0; | 
|  | 535 | } | 
|  | 536 |  | 
|  | 537 | EXPORT_SYMBOL_GPL(rtc_unregister); | 
|  | 538 |  | 
|  | 539 | int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) | 
|  | 540 | { | 
|  | 541 | int retval = 0; | 
|  | 542 |  | 
|  | 543 | spin_lock_irq(&rtc_task_lock); | 
|  | 544 |  | 
|  | 545 | if (rtc_callback != task) | 
|  | 546 | retval = -ENXIO; | 
|  | 547 | else | 
|  | 548 | rtc_do_ioctl(cmd, arg, FUNCTION_RTC_CONTROL); | 
|  | 549 |  | 
|  | 550 | spin_unlock_irq(&rtc_task_lock); | 
|  | 551 |  | 
|  | 552 | return retval; | 
|  | 553 | } | 
|  | 554 |  | 
|  | 555 | EXPORT_SYMBOL_GPL(rtc_control); | 
|  | 556 |  | 
|  | 557 | static struct miscdevice rtc_miscdevice = { | 
|  | 558 | .minor	= RTC_MINOR, | 
|  | 559 | .name	= rtc_name, | 
|  | 560 | .fops	= &rtc_fops, | 
|  | 561 | }; | 
|  | 562 |  | 
|  | 563 | static int rtc_probe(struct device *dev) | 
|  | 564 | { | 
|  | 565 | struct platform_device *pdev; | 
|  | 566 | unsigned int irq; | 
|  | 567 | int retval; | 
|  | 568 |  | 
|  | 569 | pdev = to_platform_device(dev); | 
|  | 570 | if (pdev->num_resources != 2) | 
|  | 571 | return -EBUSY; | 
|  | 572 |  | 
|  | 573 | rtc1_base = ioremap(pdev->resource[0].start, RTC1_SIZE); | 
|  | 574 | if (rtc1_base == NULL) | 
|  | 575 | return -EBUSY; | 
|  | 576 |  | 
|  | 577 | rtc2_base = ioremap(pdev->resource[1].start, RTC2_SIZE); | 
|  | 578 | if (rtc2_base == NULL) { | 
|  | 579 | iounmap(rtc1_base); | 
|  | 580 | rtc1_base = NULL; | 
|  | 581 | return -EBUSY; | 
|  | 582 | } | 
|  | 583 |  | 
|  | 584 | retval = misc_register(&rtc_miscdevice); | 
|  | 585 | if (retval < 0) { | 
|  | 586 | iounmap(rtc1_base); | 
|  | 587 | iounmap(rtc2_base); | 
|  | 588 | rtc1_base = NULL; | 
|  | 589 | rtc2_base = NULL; | 
|  | 590 | return retval; | 
|  | 591 | } | 
|  | 592 |  | 
|  | 593 | spin_lock_irq(&rtc_lock); | 
|  | 594 |  | 
|  | 595 | rtc1_write(ECMPLREG, 0); | 
|  | 596 | rtc1_write(ECMPMREG, 0); | 
|  | 597 | rtc1_write(ECMPHREG, 0); | 
|  | 598 | rtc1_write(RTCL1LREG, 0); | 
|  | 599 | rtc1_write(RTCL1HREG, 0); | 
|  | 600 |  | 
|  | 601 | rtc_status = RTC_RELEASE; | 
|  | 602 | rtc_irq_data = 0; | 
|  | 603 |  | 
|  | 604 | spin_unlock_irq(&rtc_lock); | 
|  | 605 |  | 
|  | 606 | init_waitqueue_head(&rtc_wait); | 
|  | 607 |  | 
|  | 608 | irq = ELAPSEDTIME_IRQ; | 
|  | 609 | retval = request_irq(irq, elapsedtime_interrupt, SA_INTERRUPT, | 
|  | 610 | "elapsed_time", NULL); | 
|  | 611 | if (retval == 0) { | 
|  | 612 | irq = RTCLONG1_IRQ; | 
|  | 613 | retval = request_irq(irq, rtclong1_interrupt, SA_INTERRUPT, | 
|  | 614 | "rtclong1", NULL); | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | if (retval < 0) { | 
|  | 618 | printk(KERN_ERR "rtc: IRQ%d is busy\n", irq); | 
|  | 619 | if (irq == RTCLONG1_IRQ) | 
|  | 620 | free_irq(ELAPSEDTIME_IRQ, NULL); | 
|  | 621 | iounmap(rtc1_base); | 
|  | 622 | iounmap(rtc2_base); | 
|  | 623 | rtc1_base = NULL; | 
|  | 624 | rtc2_base = NULL; | 
|  | 625 | return retval; | 
|  | 626 | } | 
|  | 627 |  | 
|  | 628 | disable_irq(ELAPSEDTIME_IRQ); | 
|  | 629 | disable_irq(RTCLONG1_IRQ); | 
|  | 630 |  | 
|  | 631 | spin_lock_init(&rtc_task_lock); | 
|  | 632 |  | 
|  | 633 | printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n"); | 
|  | 634 |  | 
|  | 635 | return 0; | 
|  | 636 | } | 
|  | 637 |  | 
|  | 638 | static int rtc_remove(struct device *dev) | 
|  | 639 | { | 
|  | 640 | int retval; | 
|  | 641 |  | 
|  | 642 | retval = misc_deregister(&rtc_miscdevice); | 
|  | 643 | if (retval < 0) | 
|  | 644 | return retval; | 
|  | 645 |  | 
|  | 646 | free_irq(ELAPSEDTIME_IRQ, NULL); | 
|  | 647 | free_irq(RTCLONG1_IRQ, NULL); | 
|  | 648 | if (rtc1_base != NULL) | 
|  | 649 | iounmap(rtc1_base); | 
|  | 650 | if (rtc2_base != NULL) | 
|  | 651 | iounmap(rtc2_base); | 
|  | 652 |  | 
|  | 653 | return 0; | 
|  | 654 | } | 
|  | 655 |  | 
|  | 656 | static struct platform_device *rtc_platform_device; | 
|  | 657 |  | 
|  | 658 | static struct device_driver rtc_device_driver = { | 
|  | 659 | .name		= rtc_name, | 
|  | 660 | .bus		= &platform_bus_type, | 
|  | 661 | .probe		= rtc_probe, | 
|  | 662 | .remove		= rtc_remove, | 
|  | 663 | }; | 
|  | 664 |  | 
|  | 665 | static int __devinit vr41xx_rtc_init(void) | 
|  | 666 | { | 
|  | 667 | int retval; | 
|  | 668 |  | 
|  | 669 | switch (current_cpu_data.cputype) { | 
|  | 670 | case CPU_VR4111: | 
|  | 671 | case CPU_VR4121: | 
|  | 672 | rtc_resource[0].start = RTC1_TYPE1_START; | 
|  | 673 | rtc_resource[0].end = RTC1_TYPE1_END; | 
|  | 674 | rtc_resource[1].start = RTC2_TYPE1_START; | 
|  | 675 | rtc_resource[1].end = RTC2_TYPE1_END; | 
|  | 676 | break; | 
|  | 677 | case CPU_VR4122: | 
|  | 678 | case CPU_VR4131: | 
|  | 679 | case CPU_VR4133: | 
|  | 680 | rtc_resource[0].start = RTC1_TYPE2_START; | 
|  | 681 | rtc_resource[0].end = RTC1_TYPE2_END; | 
|  | 682 | rtc_resource[1].start = RTC2_TYPE2_START; | 
|  | 683 | rtc_resource[1].end = RTC2_TYPE2_END; | 
|  | 684 | break; | 
|  | 685 | default: | 
|  | 686 | return -ENODEV; | 
|  | 687 | break; | 
|  | 688 | } | 
|  | 689 |  | 
|  | 690 | rtc_platform_device = platform_device_register_simple("RTC", -1, rtc_resource, RTC_NUM_RESOURCES); | 
|  | 691 | if (IS_ERR(rtc_platform_device)) | 
|  | 692 | return PTR_ERR(rtc_platform_device); | 
|  | 693 |  | 
|  | 694 | retval = driver_register(&rtc_device_driver); | 
|  | 695 | if (retval < 0) | 
|  | 696 | platform_device_unregister(rtc_platform_device); | 
|  | 697 |  | 
|  | 698 | return retval; | 
|  | 699 | } | 
|  | 700 |  | 
|  | 701 | static void __devexit vr41xx_rtc_exit(void) | 
|  | 702 | { | 
|  | 703 | driver_unregister(&rtc_device_driver); | 
|  | 704 |  | 
|  | 705 | platform_device_unregister(rtc_platform_device); | 
|  | 706 | } | 
|  | 707 |  | 
|  | 708 | module_init(vr41xx_rtc_init); | 
|  | 709 | module_exit(vr41xx_rtc_exit); |