]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/AT386/bbclock.c
xnu-344.21.73.tar.gz
[apple/xnu.git] / osfmk / i386 / AT386 / bbclock.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * @OSF_COPYRIGHT@
27 */
28 /*
29 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
30
31 All Rights Reserved
32
33 Permission to use, copy, modify, and distribute this software and
34 its documentation for any purpose and without fee is hereby
35 granted, provided that the above copyright notice appears in all
36 copies and that both the copyright notice and this permission notice
37 appear in supporting documentation, and that the name of Intel
38 not be used in advertising or publicity pertaining to distribution
39 of the software without specific, written prior permission.
40
41 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
42 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
43 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
44 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
45 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
46 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
47 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48 */
49
50 #include <types.h>
51 #include <mach/message.h>
52 #include <kern/thread.h>
53 #include <kern/clock.h>
54 #include <kern/spl.h>
55 #include <kern/processor.h>
56 #include <kern/misc_protos.h>
57 #include <i386/pio.h>
58 #include <i386/AT386/rtc.h>
59 #include <i386/AT386/bbclock_entries.h>
60
61 /* local data */
62 static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
63
64 extern char dectohexdec(
65 int n);
66 extern int hexdectodec(
67 char c);
68 extern int yeartoday(
69 int yr);
70 extern void rtcput(
71 struct rtc_st * regs);
72 extern int rtcget(
73 struct rtc_st * regs);
74
75 #define LOCK_BBC() splclock()
76 #define UNLOCK_BBC(s) splx(s)
77
78 /*
79 * Configure battery-backed clock.
80 */
81 int
82 bbc_config(void)
83 {
84 int BbcFlag;
85 struct rtc_st rtclk;
86
87 #if NCPUS > 1 && AT386
88 mp_disable_preemption();
89 if (cpu_number() != master_cpu) {
90 mp_enable_preemption();
91 return(1);
92 }
93 #endif
94 /*
95 * Setup device.
96 */
97 outb(RTC_ADDR, RTC_A);
98 outb(RTC_DATA, RTC_DIV2 | RTC_RATE6);
99 outb(RTC_ADDR, RTC_B);
100 outb(RTC_DATA, RTC_HM);
101
102 /*
103 * Probe the device by trying to read it.
104 */
105 BbcFlag = (rtcget(&rtclk) ? 0 : 1);
106 if (BbcFlag)
107 printf("battery clock configured\n");
108 else
109 printf("WARNING: Battery Clock Failure!\n");
110 #if NCPUS > 1 && AT386
111 mp_enable_preemption();
112 #endif
113 return (BbcFlag);
114 }
115
116 /*
117 * Get the current clock time.
118 */
119 kern_return_t
120 bbc_gettime(
121 mach_timespec_t *cur_time) /* OUT */
122 {
123 struct rtc_st rtclk;
124 time_t n;
125 int sec, min, hr, dom, mon, yr;
126 int i, days = 0;
127 spl_t s;
128 thread_t thread;
129
130 #if NCPUS > 1 && AT386
131 if ((thread = current_thread()) != THREAD_NULL) {
132 thread_bind(thread, master_processor);
133 mp_disable_preemption();
134 if (current_processor() != master_processor) {
135 mp_enable_preemption();
136 thread_block(THREAD_CONTINUE_NULL);
137 } else {
138 mp_enable_preemption();
139 }
140 }
141 #endif
142 s = LOCK_BBC();
143 rtcget(&rtclk);
144 sec = hexdectodec(rtclk.rtc_sec);
145 min = hexdectodec(rtclk.rtc_min);
146 hr = hexdectodec(rtclk.rtc_hr);
147 dom = hexdectodec(rtclk.rtc_dom);
148 mon = hexdectodec(rtclk.rtc_mon);
149 yr = hexdectodec(rtclk.rtc_yr);
150 yr = (yr < 70) ? yr+100 : yr;
151 n = sec + 60 * min + 3600 * hr;
152 n += (dom - 1) * 3600 * 24;
153 if (yeartoday(yr) == 366)
154 month[1] = 29;
155 for (i = mon - 2; i >= 0; i--)
156 days += month[i];
157 month[1] = 28;
158 for (i = 70; i < yr; i++)
159 days += yeartoday(i);
160 n += days * 3600 * 24;
161 cur_time->tv_sec = n;
162 cur_time->tv_nsec = 0;
163 UNLOCK_BBC(s);
164
165 #if NCPUS > 1 && AT386
166 if (thread != THREAD_NULL)
167 thread_bind(thread, PROCESSOR_NULL);
168 #endif
169 return (KERN_SUCCESS);
170 }
171
172 /*
173 * Set the current clock time.
174 */
175 kern_return_t
176 bbc_settime(
177 mach_timespec_t *new_time)
178 {
179 struct rtc_st rtclk;
180 time_t n;
181 int diff, i, j;
182 spl_t s;
183 thread_t thread;
184
185 #if NCPUS > 1 && AT386
186 if ((thread = current_thread()) != THREAD_NULL) {
187 thread_bind(thread, master_processor);
188 mp_disable_preemption();
189 if (current_processor() != master_processor) {
190 mp_enable_preemption();
191 thread_block(THREAD_CONTINUE_NULL);
192 } else {
193 mp_enable_preemption();
194 }
195 }
196 #endif
197 s = LOCK_BBC();
198 rtcget(&rtclk);
199 diff = 0;
200 n = (new_time->tv_sec - diff) % (3600 * 24); /* hrs+mins+secs */
201 rtclk.rtc_sec = dectohexdec(n%60);
202 n /= 60;
203 rtclk.rtc_min = dectohexdec(n%60);
204 rtclk.rtc_hr = dectohexdec(n/60);
205 n = (new_time->tv_sec - diff) / (3600 * 24); /* days */
206 rtclk.rtc_dow = (n + 4) % 7; /* 1/1/70 is Thursday */
207 for (j = 1970; n >= (i = yeartoday(j)); j++)
208 n -= i;
209 rtclk.rtc_yr = dectohexdec(j % 100);
210 if (yeartoday(j) == 366)
211 month[1] = 29;
212 for (i = 0; n >= month[i]; i++)
213 n -= month[i];
214 month[1] = 28;
215 rtclk.rtc_mon = dectohexdec(++i);
216 rtclk.rtc_dom = dectohexdec(++n);
217 rtcput(&rtclk);
218 UNLOCK_BBC(s);
219
220 #if NCPUS > 1 && AT386
221 if (thread != THREAD_NULL)
222 thread_bind(current_thread(), PROCESSOR_NULL);
223 #endif
224 return (KERN_SUCCESS);
225 }
226
227 /*
228 * Get clock device attributes.
229 */
230 kern_return_t
231 bbc_getattr(
232 clock_flavor_t flavor,
233 clock_attr_t attr, /* OUT */
234 mach_msg_type_number_t *count) /* IN/OUT */
235 {
236 if (*count != 1)
237 return (KERN_FAILURE);
238 switch (flavor) {
239
240 case CLOCK_GET_TIME_RES: /* >0 res */
241 *(clock_res_t *) attr = NSEC_PER_SEC;
242 break;
243
244 case CLOCK_ALARM_CURRES: /* =0 no alarm */
245 case CLOCK_ALARM_MINRES:
246 case CLOCK_ALARM_MAXRES:
247 *(clock_res_t *) attr = 0;
248 break;
249
250 default:
251 return (KERN_INVALID_VALUE);
252 }
253 return (KERN_SUCCESS);
254 }
255
256 \f
257 /* DEVICE SPECIFIC ROUTINES */
258
259 int
260 rtcget(
261 struct rtc_st * regs)
262 {
263 outb(RTC_ADDR, RTC_D);
264 if (inb(RTC_DATA) & RTC_VRT == 0)
265 return (-1);
266 outb(RTC_ADDR, RTC_A);
267 while (inb(RTC_DATA) & RTC_UIP) /* busy wait */
268 outb(RTC_ADDR, RTC_A);
269 load_rtc((unsigned char *)regs);
270 return (0);
271 }
272
273 void
274 rtcput(
275 struct rtc_st * regs)
276 {
277 register unsigned char x;
278
279 outb(RTC_ADDR, RTC_B);
280 x = inb(RTC_DATA);
281 outb(RTC_ADDR, RTC_B);
282 outb(RTC_DATA, x | RTC_SET);
283 save_rtc((unsigned char *)regs);
284 outb(RTC_ADDR, RTC_B);
285 outb(RTC_DATA, x & ~RTC_SET);
286 }
287
288 int
289 yeartoday(
290 int year)
291 {
292 year += 1900;
293 return((year % 4) ? 365 :
294 ((year % 100) ? 366 : ((year % 400) ? 365: 366)));
295 }
296
297 int
298 hexdectodec(
299 char n)
300 {
301 return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F));
302 }
303
304 char
305 dectohexdec(
306 int n)
307 {
308 return ((char)(((n / 10) << 4) & 0xF0) | ((n % 10) & 0x0F));
309 }