kogiidena | 94c0fa5 | 2006-09-27 14:53:35 +0900 | [diff] [blame^] | 1 | /* |
| 2 | * arch/sh/boards/landisk/rtc.c -- RTC support |
| 3 | * |
| 4 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> |
| 5 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka |
| 6 | */ |
| 7 | /* |
| 8 | * modifed by kogiidena |
| 9 | * 2005.09.16 |
| 10 | */ |
| 11 | |
| 12 | #include <linux/config.h> |
| 13 | #include <linux/init.h> |
| 14 | #include <linux/kernel.h> |
| 15 | #include <linux/sched.h> |
| 16 | #include <linux/time.h> |
| 17 | #include <linux/delay.h> |
| 18 | #include <linux/spinlock.h> |
| 19 | |
| 20 | #ifndef BCD_TO_BIN |
| 21 | #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) |
| 22 | #endif |
| 23 | |
| 24 | #ifndef BIN_TO_BCD |
| 25 | #define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) |
| 26 | #endif |
| 27 | |
| 28 | extern void (*rtc_get_time) (struct timespec *); |
| 29 | extern int (*rtc_set_time) (const time_t); |
| 30 | extern spinlock_t rtc_lock; |
| 31 | |
| 32 | extern void |
| 33 | rs5c313_set_cmos_time(unsigned int BCD_yr, unsigned int BCD_mon, |
| 34 | unsigned int BCD_day, unsigned int BCD_hr, |
| 35 | unsigned int BCD_min, unsigned int BCD_sec); |
| 36 | |
| 37 | extern unsigned long |
| 38 | rs5c313_get_cmos_time(unsigned int *BCD_yr, unsigned int *BCD_mon, |
| 39 | unsigned int *BCD_day, unsigned int *BCD_hr, |
| 40 | unsigned int *BCD_min, unsigned int *BCD_sec); |
| 41 | |
| 42 | void landisk_rtc_gettimeofday(struct timespec *tv) |
| 43 | { |
| 44 | unsigned int BCD_yr, BCD_mon, BCD_day, BCD_hr, BCD_min, BCD_sec; |
| 45 | unsigned long flags; |
| 46 | |
| 47 | spin_lock_irqsave(&rtc_lock, flags); |
| 48 | tv->tv_sec = rs5c313_get_cmos_time |
| 49 | (&BCD_yr, &BCD_mon, &BCD_day, &BCD_hr, &BCD_min, &BCD_sec); |
| 50 | tv->tv_nsec = 0; |
| 51 | spin_unlock_irqrestore(&rtc_lock, flags); |
| 52 | } |
| 53 | |
| 54 | int landisk_rtc_settimeofday(const time_t secs) |
| 55 | { |
| 56 | int retval = 0; |
| 57 | int real_seconds, real_minutes, cmos_minutes; |
| 58 | unsigned long flags; |
| 59 | unsigned long nowtime = secs; |
| 60 | unsigned int BCD_yr, BCD_mon, BCD_day, BCD_hr, BCD_min, BCD_sec; |
| 61 | |
| 62 | spin_lock_irqsave(&rtc_lock, flags); |
| 63 | |
| 64 | rs5c313_get_cmos_time |
| 65 | (&BCD_yr, &BCD_mon, &BCD_day, &BCD_hr, &BCD_min, &BCD_sec); |
| 66 | cmos_minutes = BCD_min; |
| 67 | BCD_TO_BIN(cmos_minutes); |
| 68 | |
| 69 | /* |
| 70 | * since we're only adjusting minutes and seconds, |
| 71 | * don't interfere with hour overflow. This avoids |
| 72 | * messing with unknown time zones but requires your |
| 73 | * RTC not to be off by more than 15 minutes |
| 74 | */ |
| 75 | real_seconds = nowtime % 60; |
| 76 | real_minutes = nowtime / 60; |
| 77 | if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) |
| 78 | real_minutes += 30; /* correct for half hour time zone */ |
| 79 | real_minutes %= 60; |
| 80 | |
| 81 | if (abs(real_minutes - cmos_minutes) < 30) { |
| 82 | BIN_TO_BCD(real_seconds); |
| 83 | BIN_TO_BCD(real_minutes); |
| 84 | rs5c313_set_cmos_time(BCD_yr, BCD_mon, BCD_day, BCD_hr, |
| 85 | real_minutes, real_seconds); |
| 86 | } else { |
| 87 | printk(KERN_WARNING |
| 88 | "set_rtc_time: can't update from %d to %d\n", |
| 89 | cmos_minutes, real_minutes); |
| 90 | retval = -1; |
| 91 | } |
| 92 | |
| 93 | spin_unlock_irqrestore(&rtc_lock, flags); |
| 94 | return retval; |
| 95 | } |
| 96 | |
| 97 | |
| 98 | void landisk_time_init(void) |
| 99 | { |
| 100 | rtc_get_time = landisk_rtc_gettimeofday; |
| 101 | rtc_set_time = landisk_rtc_settimeofday; |
| 102 | } |