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