]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/x86/kernel/rtc.c
x86: isolate the rtc code for sharing
[linux-2.6-omap-h63xx.git] / arch / x86 / kernel / rtc.c
1 /*
2  * RTC related functions
3  */
4 #include <linux/bcd.h>
5 #include <linux/mc146818rtc.h>
6
7 #include <asm/time.h>
8
9 /*
10  * In order to set the CMOS clock precisely, set_rtc_mmss has to be
11  * called 500 ms after the second nowtime has started, because when
12  * nowtime is written into the registers of the CMOS clock, it will
13  * jump to the next second precisely 500 ms later. Check the Motorola
14  * MC146818A or Dallas DS12887 data sheet for details.
15  *
16  * BUG: This routine does not handle hour overflow properly; it just
17  *      sets the minutes. Usually you'll only notice that after reboot!
18  */
19 int mach_set_rtc_mmss(unsigned long nowtime)
20 {
21         int retval = 0;
22         int real_seconds, real_minutes, cmos_minutes;
23         unsigned char save_control, save_freq_select;
24
25         save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
26         CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
27
28         save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
29         CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
30
31         cmos_minutes = CMOS_READ(RTC_MINUTES);
32         if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
33                 BCD_TO_BIN(cmos_minutes);
34
35         /*
36          * since we're only adjusting minutes and seconds,
37          * don't interfere with hour overflow. This avoids
38          * messing with unknown time zones but requires your
39          * RTC not to be off by more than 15 minutes
40          */
41         real_seconds = nowtime % 60;
42         real_minutes = nowtime / 60;
43         if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
44                 real_minutes += 30;             /* correct for half hour time zone */
45         real_minutes %= 60;
46
47         if (abs(real_minutes - cmos_minutes) < 30) {
48                 if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
49                         BIN_TO_BCD(real_seconds);
50                         BIN_TO_BCD(real_minutes);
51                 }
52                 CMOS_WRITE(real_seconds,RTC_SECONDS);
53                 CMOS_WRITE(real_minutes,RTC_MINUTES);
54         } else {
55                 printk(KERN_WARNING
56                        "set_rtc_mmss: can't update from %d to %d\n",
57                        cmos_minutes, real_minutes);
58                 retval = -1;
59         }
60
61         /* The following flags have to be released exactly in this order,
62          * otherwise the DS12887 (popular MC146818A clone with integrated
63          * battery and quartz) will not reset the oscillator and will not
64          * update precisely 500 ms later. You won't find this mentioned in
65          * the Dallas Semiconductor data sheets, but who believes data
66          * sheets anyway ...                           -- Markus Kuhn
67          */
68         CMOS_WRITE(save_control, RTC_CONTROL);
69         CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
70
71         return retval;
72 }
73
74 unsigned long mach_get_cmos_time(void)
75 {
76         unsigned int year, mon, day, hour, min, sec;
77
78         do {
79                 sec = CMOS_READ(RTC_SECONDS);
80                 min = CMOS_READ(RTC_MINUTES);
81                 hour = CMOS_READ(RTC_HOURS);
82                 day = CMOS_READ(RTC_DAY_OF_MONTH);
83                 mon = CMOS_READ(RTC_MONTH);
84                 year = CMOS_READ(RTC_YEAR);
85         } while (sec != CMOS_READ(RTC_SECONDS));
86
87         if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
88                 BCD_TO_BIN(sec);
89                 BCD_TO_BIN(min);
90                 BCD_TO_BIN(hour);
91                 BCD_TO_BIN(day);
92                 BCD_TO_BIN(mon);
93                 BCD_TO_BIN(year);
94         }
95
96         year += 1900;
97         if (year < 1970)
98                 year += 100;
99
100         return mktime(year, mon, day, hour, min, sec);
101 }
102
103 DEFINE_SPINLOCK(rtc_lock);
104 EXPORT_SYMBOL(rtc_lock);
105
106 /*
107  * This is a special lock that is owned by the CPU and holds the index
108  * register we are working with.  It is required for NMI access to the
109  * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
110  */
111 volatile unsigned long cmos_lock = 0;
112 EXPORT_SYMBOL(cmos_lock);
113
114 /* Routines for accessing the CMOS RAM/RTC. */
115 unsigned char rtc_cmos_read(unsigned char addr)
116 {
117         unsigned char val;
118
119         lock_cmos_prefix(addr);
120         outb_p(addr, RTC_PORT(0));
121         val = inb_p(RTC_PORT(1));
122         lock_cmos_suffix(addr);
123         return val;
124 }
125 EXPORT_SYMBOL(rtc_cmos_read);
126
127 void rtc_cmos_write(unsigned char val, unsigned char addr)
128 {
129         lock_cmos_prefix(addr);
130         outb_p(addr, RTC_PORT(0));
131         outb_p(val, RTC_PORT(1));
132         lock_cmos_suffix(addr);
133 }
134 EXPORT_SYMBOL(rtc_cmos_write);
135
136 static int set_rtc_mmss(unsigned long nowtime)
137 {
138         int retval;
139         unsigned long flags;
140
141         /* gets recalled with irq locally disabled */
142         /* XXX - does irqsave resolve this? -johnstul */
143         spin_lock_irqsave(&rtc_lock, flags);
144         retval = set_wallclock(nowtime);
145         spin_unlock_irqrestore(&rtc_lock, flags);
146
147         return retval;
148 }
149
150 /* not static: needed by APM */
151 unsigned long read_persistent_clock(void)
152 {
153         unsigned long retval;
154         unsigned long flags;
155
156         spin_lock_irqsave(&rtc_lock, flags);
157         retval = get_wallclock();
158         spin_unlock_irqrestore(&rtc_lock, flags);
159
160         return retval;
161 }
162
163 int update_persistent_clock(struct timespec now)
164 {
165         return set_rtc_mmss(now.tv_sec);
166 }