isaclock.c revision 1.3 1 /* $NetBSD: isaclock.c,v 1.3 2003/07/15 03:35:49 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1993, 1994 Charles M. Hannum.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * William Jolitz and Don Ahn.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 * @(#)clock.c 7.2 (Berkeley) 5/12/91
40 */
41 /*
42 * Mach Operating System
43 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
44 * All Rights Reserved.
45 *
46 * Permission to use, copy, modify and distribute this software and its
47 * documentation is hereby granted, provided that both the copyright
48 * notice and this permission notice appear in all copies of the
49 * software, derivative works or modified versions, and any portions
50 * thereof, and that both notices appear in supporting documentation.
51 *
52 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
53 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
54 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
55 *
56 * Carnegie Mellon requests users of this software to return to
57 *
58 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
59 * School of Computer Science
60 * Carnegie Mellon University
61 * Pittsburgh PA 15213-3890
62 *
63 * any improvements or extensions that they make and grant Carnegie Mellon
64 * the rights to redistribute these changes.
65 */
66 /*
67 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
68
69 All Rights Reserved
70
71 Permission to use, copy, modify, and distribute this software and
72 its documentation for any purpose and without fee is hereby
73 granted, provided that the above copyright notice appears in all
74 copies and that both the copyright notice and this permission notice
75 appear in supporting documentation, and that the name of Intel
76 not be used in advertising or publicity pertaining to distribution
77 of the software without specific, written prior permission.
78
79 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
80 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
81 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
82 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
83 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
84 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
85 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
86 */
87
88 /*
89 * Primitive clock interrupt routines.
90 */
91
92 #include <sys/cdefs.h>
93 __KERNEL_RCSID(0, "$NetBSD: isaclock.c,v 1.3 2003/07/15 03:35:49 lukem Exp $");
94
95 #include <sys/param.h>
96 #include <sys/systm.h>
97 #include <sys/callout.h>
98 #include <sys/time.h>
99 #include <sys/kernel.h>
100 #include <sys/device.h>
101
102 #include <machine/cpu.h>
103 #include <machine/intr.h>
104 #include <machine/pio.h>
105
106 #include <dev/isa/isareg.h>
107 #include <dev/isa/isavar.h>
108 #include <dev/ic/mc146818reg.h>
109 #include <dev/ic/i8253reg.h>
110 #include <sandpoint/isa/nvram.h>
111 #include <sandpoint/isa/spkrreg.h>
112
113 extern void disable_intr(void); /* In locore.S */
114 extern void enable_intr(void); /* In locore.S */
115
116 void sysbeepstop __P((void *));
117 void sysbeep __P((int, int));
118 void rtcinit __P((void));
119 int rtcget __P((mc_todregs *));
120 void rtcput __P((mc_todregs *));
121 static int yeartoday __P((int));
122 int hexdectodec __P((int));
123 int dectohexdec __P((int));
124
125 __inline u_int mc146818_read __P((void *, u_int));
126 __inline void mc146818_write __P((void *, u_int, u_int));
127
128 #define SECMIN ((unsigned)60) /* seconds per minute */
129 #define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */
130 #define SECDAY ((unsigned)(24*SECHOUR)) /* seconds per day */
131 #define SECYR ((unsigned)(365*SECDAY)) /* seconds per common year */
132
133 __inline u_int
134 mc146818_read(sc, reg)
135 void *sc; /* XXX use it? */
136 u_int reg;
137 {
138
139 isa_outb(IO_RTC, (u_char)reg);
140 return (isa_inb(IO_RTC+1));
141 }
142
143 __inline void
144 mc146818_write(sc, reg, datum)
145 void *sc; /* XXX use it? */
146 u_int reg, datum;
147 {
148
149 isa_outb(IO_RTC, reg);
150 isa_outb(IO_RTC+1, datum);
151 }
152
153 static int beeping;
154
155 void
156 sysbeepstop(arg)
157 void *arg;
158 {
159 /* disable counter 2 */
160 disable_intr();
161 isa_outb(PITAUX_PORT, isa_inb(PITAUX_PORT) & ~PIT_SPKR);
162 enable_intr();
163 beeping = 0;
164 }
165
166 void
167 sysbeep(pitch, period)
168 int pitch, period;
169 {
170 static struct callout sysbeep_ch = CALLOUT_INITIALIZER;
171 static int last_pitch;
172
173 if (beeping)
174 callout_stop(&sysbeep_ch);
175 if (pitch == 0 || period == 0) {
176 sysbeepstop(0);
177 last_pitch = 0;
178 return;
179 }
180 if (!beeping || last_pitch != pitch) {
181 disable_intr();
182 isa_outb(IO_TIMER1 + TIMER_MODE,
183 TIMER_SEL2 | TIMER_16BIT | TIMER_SQWAVE);
184 isa_outb(IO_TIMER1 + TIMER_CNTR2, TIMER_DIV(pitch) % 256);
185 isa_outb(IO_TIMER1 + TIMER_CNTR2, TIMER_DIV(pitch) / 256);
186 isa_outb(PITAUX_PORT, isa_inb(PITAUX_PORT) | PIT_SPKR); /* enable counter 2 */
187 enable_intr();
188 }
189 last_pitch = pitch;
190 beeping = 1;
191 callout_reset(&sysbeep_ch, period, sysbeepstop, NULL);
192 }
193
194 void
195 rtcinit()
196 {
197 static int first_rtcopen_ever = 1;
198
199 if (!first_rtcopen_ever)
200 return;
201 first_rtcopen_ever = 0;
202
203 mc146818_write(NULL, MC_REGA, /* XXX softc */
204 MC_BASE_32_KHz | MC_RATE_1024_Hz);
205 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */
206 }
207
208 int
209 rtcget(regs)
210 mc_todregs *regs;
211 {
212
213 rtcinit();
214 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
215 return (-1);
216 MC146818_GETTOD(NULL, regs); /* XXX softc */
217 return (0);
218 }
219
220 void
221 rtcput(regs)
222 mc_todregs *regs;
223 {
224
225 rtcinit();
226 MC146818_PUTTOD(NULL, regs); /* XXX softc */
227 }
228
229 static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
230
231 static int
232 yeartoday(year)
233 int year;
234 {
235
236 return ((year % 4) ? 365 : 366);
237 }
238
239 int
240 hexdectodec(n)
241 int n;
242 {
243
244 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
245 }
246
247 int
248 dectohexdec(n)
249 int n;
250 {
251
252 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
253 }
254
255 static int timeset;
256
257 /*
258 * Initialize the time of day register, based on the time base which is, e.g.
259 * from a filesystem.
260 */
261 void
262 inittodr(base)
263 time_t base;
264 {
265 mc_todregs rtclk;
266 time_t n;
267 int sec, min, hr, dom, mon, yr;
268 int i, days = 0;
269 int s;
270
271 /*
272 * We mostly ignore the suggested time and go for the RTC clock time
273 * stored in the CMOS RAM. If the time can't be obtained from the
274 * CMOS, or if the time obtained from the CMOS is 5 or more years
275 * less than the suggested time, we used the suggested time. (In
276 * the latter case, it's likely that the CMOS battery has died.)
277 */
278
279 if (base < 15*SECYR) { /* if before 1985, something's odd... */
280 printf("WARNING: preposterous time in file system\n");
281 /* read the system clock anyway */
282 base = 17*SECYR + 186*SECDAY + SECDAY/2;
283 }
284
285 s = splclock();
286 if (rtcget(&rtclk)) {
287 splx(s);
288 printf("WARNING: invalid time in clock chip\n");
289 goto fstime;
290 }
291 splx(s);
292
293 sec = hexdectodec(rtclk[MC_SEC]);
294 min = hexdectodec(rtclk[MC_MIN]);
295 hr = hexdectodec(rtclk[MC_HOUR]);
296 dom = hexdectodec(rtclk[MC_DOM]);
297 mon = hexdectodec(rtclk[MC_MONTH]);
298 yr = hexdectodec(rtclk[MC_YEAR]);
299 yr = (yr < 70) ? yr+100 : yr;
300
301 n = sec + 60 * min + 3600 * hr;
302 n += (dom - 1) * 3600 * 24;
303
304 if (yeartoday(yr) == 366)
305 month[1] = 29;
306 for (i = mon - 2; i >= 0; i--)
307 days += month[i];
308 month[1] = 28;
309 for (i = 70; i < yr; i++)
310 days += yeartoday(i);
311 n += days * 3600 * 24;
312
313 n += rtc_offset * 60;
314
315 if (base < n - 5*SECYR)
316 printf("WARNING: file system time much less than clock time\n");
317 else if (base > n + 5*SECYR) {
318 printf("WARNING: clock time much less than file system time\n");
319 printf("WARNING: using file system time\n");
320 goto fstime;
321 }
322
323 timeset = 1;
324 time.tv_sec = n;
325 time.tv_usec = 0;
326 return;
327
328 fstime:
329 timeset = 1;
330 time.tv_sec = base;
331 time.tv_usec = 0;
332 printf("WARNING: CHECK AND RESET THE DATE!\n");
333 }
334
335 /*
336 * Reset the clock.
337 */
338 void
339 resettodr()
340 {
341 mc_todregs rtclk;
342 time_t n;
343 int diff, i, j;
344 int s;
345
346 /*
347 * We might have been called by boot() due to a crash early
348 * on. Don't reset the clock chip in this case.
349 */
350 if (!timeset)
351 return;
352
353 s = splclock();
354 if (rtcget(&rtclk))
355 memset(&rtclk, 0, sizeof(rtclk));
356 splx(s);
357
358 diff = rtc_offset * 60;
359 n = (time.tv_sec - diff) % (3600 * 24); /* hrs+mins+secs */
360 rtclk[MC_SEC] = dectohexdec(n % 60);
361 n /= 60;
362 rtclk[MC_MIN] = dectohexdec(n % 60);
363 rtclk[MC_HOUR] = dectohexdec(n / 60);
364
365 n = (time.tv_sec - diff) / (3600 * 24); /* days */
366 rtclk[MC_DOW] = (n + 4) % 7; /* 1/1/70 is Thursday */
367
368 for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j))
369 n -= i;
370
371 rtclk[MC_YEAR] = dectohexdec(j - 1900);
372
373 if (i == 366)
374 month[1] = 29;
375 for (i = 0; n >= month[i]; i++)
376 n -= month[i];
377 month[1] = 28;
378 rtclk[MC_MONTH] = dectohexdec(++i);
379
380 rtclk[MC_DOM] = dectohexdec(++n);
381
382 s = splclock();
383 rtcput(&rtclk);
384 splx(s);
385 }
386