1 /* $OpenBSD: clock.c,v 1.42 2023/09/17 14:50:50 cheloha Exp $ */
2 /* $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $ */
3
4 /*-
5 * Copyright (c) 1993, 1994 Charles M. Hannum.
6 * Copyright (c) 1990 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * William Jolitz and Don Ahn.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)clock.c 7.2 (Berkeley) 5/12/91
37 */
38 /*
39 * Mach Operating System
40 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
41 * All Rights Reserved.
42 *
43 * Permission to use, copy, modify and distribute this software and its
44 * documentation is hereby granted, provided that both the copyright
45 * notice and this permission notice appear in all copies of the
46 * software, derivative works or modified versions, and any portions
47 * thereof, and that both notices appear in supporting documentation.
48 *
49 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
51 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52 *
53 * Carnegie Mellon requests users of this software to return to
54 *
55 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
56 * School of Computer Science
57 * Carnegie Mellon University
58 * Pittsburgh PA 15213-3890
59 *
60 * any improvements or extensions that they make and grant Carnegie Mellon
61 * the rights to redistribute these changes.
62 */
63 /*
64 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
65
66 All Rights Reserved
67
68 Permission to use, copy, modify, and distribute this software and
69 its documentation for any purpose and without fee is hereby
70 granted, provided that the above copyright notice appears in all
71 copies and that both the copyright notice and this permission notice
72 appear in supporting documentation, and that the name of Intel
73 not be used in advertising or publicity pertaining to distribution
74 of the software without specific, written prior permission.
75
76 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83 */
84
85 /*
86 * Primitive clock interrupt routines.
87 */
88
89 /* #define CLOCK_DEBUG */
90
91 #include <sys/param.h>
92 #include <sys/systm.h>
93 #include <sys/clockintr.h>
94 #include <sys/time.h>
95 #include <sys/kernel.h>
96 #include <sys/timeout.h>
97 #include <sys/timetc.h>
98
99 #include <machine/cpu.h>
100 #include <machine/intr.h>
101 #include <machine/pio.h>
102 #include <machine/cpufunc.h>
103
104 #include <dev/clock_subr.h>
105 #include <dev/isa/isareg.h>
106 #include <dev/isa/isavar.h>
107 #include <dev/ic/mc146818reg.h>
108 #include <dev/ic/i8253reg.h>
109 #include <amd64/isa/nvram.h>
110
111 /* Timecounter on the i8254 */
112 u_int32_t i8254_lastcount;
113 u_int32_t i8254_offset;
114 int i8254_ticked;
115 u_int i8254_get_timecount(struct timecounter *tc);
116
117 u_int i8254_simple_get_timecount(struct timecounter *tc);
118
119 static struct timecounter i8254_timecounter = {
120 .tc_get_timecount = i8254_get_timecount,
121 .tc_counter_mask = ~0u,
122 .tc_frequency = TIMER_FREQ,
123 .tc_name = "i8254",
124 .tc_quality = 0,
125 .tc_priv = NULL,
126 .tc_user = 0,
127 };
128
129 int clockintr(void *);
130 int rtcintr(void *);
131 int gettick(void);
132 void rtcdrain(void *v);
133 int rtcget(mc_todregs *);
134 void rtcput(mc_todregs *);
135 int bcdtobin(int);
136 int bintobcd(int);
137
138 u_int mc146818_read(void *, u_int);
139 void mc146818_write(void *, u_int, u_int);
140
141 u_int
mc146818_read(void * sc,u_int reg)142 mc146818_read(void *sc, u_int reg)
143 {
144 outb(IO_RTC, reg);
145 DELAY(1);
146 return (inb(IO_RTC+1));
147 }
148
149 void
mc146818_write(void * sc,u_int reg,u_int datum)150 mc146818_write(void *sc, u_int reg, u_int datum)
151 {
152 outb(IO_RTC, reg);
153 DELAY(1);
154 outb(IO_RTC+1, datum);
155 DELAY(1);
156 }
157
158 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
159
160 u_long rtclock_tval;
161
162 void
startclocks(void)163 startclocks(void)
164 {
165 mtx_enter(&timer_mutex);
166 rtclock_tval = TIMER_DIV(hz);
167 i8254_startclock();
168 mtx_leave(&timer_mutex);
169 }
170
171 int
clockintr(void * frame)172 clockintr(void *frame)
173 {
174 if (timecounter->tc_get_timecount == i8254_get_timecount) {
175 if (i8254_ticked) {
176 i8254_ticked = 0;
177 } else {
178 i8254_offset += rtclock_tval;
179 i8254_lastcount = 0;
180 }
181 }
182
183 clockintr_dispatch(frame);
184
185 return 1;
186 }
187
188 int
rtcintr(void * frame)189 rtcintr(void *frame)
190 {
191 u_int stat = 0;
192
193 /*
194 * If rtcintr is 'late', next intr may happen immediately.
195 * Get them all. (Also, see comment in cpu_initclocks().)
196 */
197 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
198 stat = 1;
199
200 if (stat)
201 clockintr_dispatch(frame);
202
203 return (stat);
204 }
205
206 int
gettick(void)207 gettick(void)
208 {
209 u_long s;
210 u_char lo, hi;
211
212 /* Don't want someone screwing with the counter while we're here. */
213 mtx_enter(&timer_mutex);
214 s = intr_disable();
215 /* Select counter 0 and latch it. */
216 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
217 lo = inb(IO_TIMER1+TIMER_CNTR0);
218 hi = inb(IO_TIMER1+TIMER_CNTR0);
219 intr_restore(s);
220 mtx_leave(&timer_mutex);
221 return ((hi << 8) | lo);
222 }
223
224 /*
225 * Wait "n" microseconds.
226 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
227 * Note: timer had better have been programmed before this is first used!
228 * (Note that we use `rate generator' mode, which counts at 1:1; `square
229 * wave' mode counts at 2:1).
230 */
231 void
i8254_delay(int n)232 i8254_delay(int n)
233 {
234 int limit, tick, otick;
235 static const int delaytab[26] = {
236 0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
237 12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
238 24, 25, 27, 28, 29, 30,
239 };
240
241 /*
242 * Read the counter first, so that the rest of the setup overhead is
243 * counted.
244 */
245 otick = gettick();
246
247 if (n <= 25)
248 n = delaytab[n];
249 else {
250 /* Force 64-bit math to avoid 32-bit overflow if possible. */
251 n = (int64_t)n * TIMER_FREQ / 1000000;
252 }
253
254 limit = TIMER_FREQ / hz;
255
256 while (n > 0) {
257 tick = gettick();
258 if (tick > otick)
259 n -= limit - (tick - otick);
260 else
261 n -= otick - tick;
262 otick = tick;
263 }
264 }
265
266 void
rtcdrain(void * v)267 rtcdrain(void *v)
268 {
269 struct timeout *to = (struct timeout *)v;
270
271 if (to != NULL)
272 timeout_del(to);
273
274 /* Drain any un-acknowledged RTC interrupts. */
275 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
276 ; /* Nothing. */
277 }
278
279 void
i8254_initclocks(void)280 i8254_initclocks(void)
281 {
282 i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
283
284 stathz = 128;
285 profhz = 1024; /* XXX does not divide into 1 billion */
286 }
287
288 void
i8254_start_both_clocks(void)289 i8254_start_both_clocks(void)
290 {
291 clockintr_cpu_init(NULL);
292
293 /*
294 * While the clock interrupt handler isn't really MPSAFE, the
295 * i8254 can't really be used as a clock on a true MP system.
296 */
297 isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
298 clockintr, 0, "clock");
299 isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
300 rtcintr, 0, "rtc");
301
302 rtcstart(); /* start the mc146818 clock */
303 }
304
305 void
rtcstart(void)306 rtcstart(void)
307 {
308 static struct timeout rtcdrain_timeout;
309
310 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
311 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
312
313 /*
314 * On a number of i386 systems, the rtc will fail to start when booting
315 * the system. This is due to us missing to acknowledge an interrupt
316 * during early stages of the boot process. If we do not acknowledge
317 * the interrupt, the rtc clock will not generate further interrupts.
318 * To solve this, once interrupts are enabled, use a timeout (once)
319 * to drain any un-acknowledged rtc interrupt(s).
320 */
321 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
322 timeout_add(&rtcdrain_timeout, 1);
323 }
324
325 void
rtcstop(void)326 rtcstop(void)
327 {
328 mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
329 }
330
331 int
rtcget(mc_todregs * regs)332 rtcget(mc_todregs *regs)
333 {
334 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
335 return (-1);
336 MC146818_GETTOD(NULL, regs); /* XXX softc */
337 return (0);
338 }
339
340 void
rtcput(mc_todregs * regs)341 rtcput(mc_todregs *regs)
342 {
343 MC146818_PUTTOD(NULL, regs); /* XXX softc */
344 }
345
346 int
bcdtobin(int n)347 bcdtobin(int n)
348 {
349 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
350 }
351
352 int
bintobcd(int n)353 bintobcd(int n)
354 {
355 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
356 }
357
358 /*
359 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
360 * to be called at splclock()
361 */
362 static int cmoscheck(void);
363 static int
cmoscheck(void)364 cmoscheck(void)
365 {
366 int i;
367 unsigned short cksum = 0;
368
369 for (i = 0x10; i <= 0x2d; i++)
370 cksum += mc146818_read(NULL, i); /* XXX softc */
371
372 return (cksum == (mc146818_read(NULL, 0x2e) << 8)
373 + mc146818_read(NULL, 0x2f));
374 }
375
376 /*
377 * patchable to control century byte handling:
378 * 1: always update
379 * -1: never touch
380 * 0: try to figure out itself
381 */
382 int rtc_update_century = 0;
383
384 /*
385 * Expand a two-digit year as read from the clock chip
386 * into full width.
387 * Being here, deal with the CMOS century byte.
388 */
389 static int centb = NVRAM_CENTURY;
390 static int clock_expandyear(int);
391 static int
clock_expandyear(int clockyear)392 clock_expandyear(int clockyear)
393 {
394 int s, clockcentury, cmoscentury;
395
396 clockcentury = (clockyear < 70) ? 20 : 19;
397 clockyear += 100 * clockcentury;
398
399 if (rtc_update_century < 0)
400 return (clockyear);
401
402 s = splclock();
403 if (cmoscheck())
404 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
405 else
406 cmoscentury = 0;
407 splx(s);
408 if (!cmoscentury)
409 return (clockyear);
410
411 cmoscentury = bcdtobin(cmoscentury);
412
413 if (cmoscentury != clockcentury) {
414 /* XXX note: saying "century is 20" might confuse the naive. */
415 printf("WARNING: NVRAM century is %d but RTC year is %d\n",
416 cmoscentury, clockyear);
417
418 /* Kludge to roll over century. */
419 if ((rtc_update_century > 0) ||
420 ((cmoscentury == 19) && (clockcentury == 20) &&
421 (clockyear == 2000))) {
422 printf("WARNING: Setting NVRAM century to %d\n",
423 clockcentury);
424 s = splclock();
425 mc146818_write(NULL, centb, bintobcd(clockcentury));
426 splx(s);
427 }
428 } else if (cmoscentury == 19 && rtc_update_century == 0)
429 rtc_update_century = 1; /* will update later in resettodr() */
430
431 return (clockyear);
432 }
433
434 int
rtcgettime(struct todr_chip_handle * handle,struct timeval * tv)435 rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
436 {
437 mc_todregs rtclk;
438 struct clock_ymdhms dt;
439 int s;
440
441 s = splclock();
442 if (rtcget(&rtclk)) {
443 splx(s);
444 return EINVAL;
445 }
446 splx(s);
447
448 #ifdef CLOCK_DEBUG
449 printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
450 rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
451 rtclk[MC_SEC]);
452 #endif
453
454 dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
455 dt.dt_min = bcdtobin(rtclk[MC_MIN]);
456 dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
457 dt.dt_day = bcdtobin(rtclk[MC_DOM]);
458 dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
459 dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
460
461 tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
462 tv->tv_usec = 0;
463 return 0;
464 }
465
466 int
rtcsettime(struct todr_chip_handle * handle,struct timeval * tv)467 rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
468 {
469 mc_todregs rtclk;
470 struct clock_ymdhms dt;
471 int century, s;
472
473 s = splclock();
474 if (rtcget(&rtclk))
475 memset(&rtclk, 0, sizeof(rtclk));
476 splx(s);
477
478 clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
479
480 rtclk[MC_SEC] = bintobcd(dt.dt_sec);
481 rtclk[MC_MIN] = bintobcd(dt.dt_min);
482 rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
483 rtclk[MC_DOW] = dt.dt_wday + 1;
484 rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
485 rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
486 rtclk[MC_DOM] = bintobcd(dt.dt_day);
487
488 #ifdef CLOCK_DEBUG
489 printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
490 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
491 #endif
492
493 s = splclock();
494 rtcput(&rtclk);
495 if (rtc_update_century > 0) {
496 century = bintobcd(dt.dt_year / 100);
497 mc146818_write(NULL, centb, century); /* XXX softc */
498 }
499 splx(s);
500 return 0;
501 }
502
503 struct todr_chip_handle rtc_todr;
504
505 void
rtcinit(void)506 rtcinit(void)
507 {
508 rtc_todr.todr_gettime = rtcgettime;
509 rtc_todr.todr_settime = rtcsettime;
510 rtc_todr.todr_quality = 0;
511 todr_attach(&rtc_todr);
512 }
513
514 void
setstatclockrate(int arg)515 setstatclockrate(int arg)
516 {
517 if (initclock_func == i8254_initclocks) {
518 if (arg == stathz)
519 mc146818_write(NULL, MC_REGA,
520 MC_BASE_32_KHz | MC_RATE_128_Hz);
521 else
522 mc146818_write(NULL, MC_REGA,
523 MC_BASE_32_KHz | MC_RATE_1024_Hz);
524 }
525 }
526
527 void
i8254_inittimecounter(void)528 i8254_inittimecounter(void)
529 {
530 tc_init(&i8254_timecounter);
531 }
532
533 /*
534 * If we're using lapic to drive hardclock, we can use a simpler
535 * algorithm for the i8254 timecounters.
536 */
537 void
i8254_inittimecounter_simple(void)538 i8254_inittimecounter_simple(void)
539 {
540 i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
541 i8254_timecounter.tc_counter_mask = 0x7fff;
542 i8254_timecounter.tc_frequency = TIMER_FREQ;
543
544 mtx_enter(&timer_mutex);
545 rtclock_tval = 0x8000;
546 i8254_startclock();
547 mtx_leave(&timer_mutex);
548
549 tc_init(&i8254_timecounter);
550 }
551
552 void
i8254_startclock(void)553 i8254_startclock(void)
554 {
555 u_long tval = rtclock_tval;
556
557 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
558 outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
559 outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
560 }
561
562 u_int
i8254_simple_get_timecount(struct timecounter * tc)563 i8254_simple_get_timecount(struct timecounter *tc)
564 {
565 return (rtclock_tval - gettick());
566 }
567
568 u_int
i8254_get_timecount(struct timecounter * tc)569 i8254_get_timecount(struct timecounter *tc)
570 {
571 u_char hi, lo;
572 u_int count;
573 u_long s;
574
575 s = intr_disable();
576
577 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
578 lo = inb(IO_TIMER1+TIMER_CNTR0);
579 hi = inb(IO_TIMER1+TIMER_CNTR0);
580
581 count = rtclock_tval - ((hi << 8) | lo);
582
583 if (count < i8254_lastcount) {
584 i8254_ticked = 1;
585 i8254_offset += rtclock_tval;
586 }
587 i8254_lastcount = count;
588 count += i8254_offset;
589
590 intr_restore(s);
591
592 return (count);
593 }
594