1*0ed1bf01Scheloha /* $OpenBSD: clock.c,v 1.42 2023/09/17 14:50:50 cheloha Exp $ */
2f5df1827Smickey /* $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $ */
3f5df1827Smickey
4f5df1827Smickey /*-
5f5df1827Smickey * Copyright (c) 1993, 1994 Charles M. Hannum.
6f5df1827Smickey * Copyright (c) 1990 The Regents of the University of California.
7f5df1827Smickey * All rights reserved.
8f5df1827Smickey *
9f5df1827Smickey * This code is derived from software contributed to Berkeley by
10f5df1827Smickey * William Jolitz and Don Ahn.
11f5df1827Smickey *
12f5df1827Smickey * Redistribution and use in source and binary forms, with or without
13f5df1827Smickey * modification, are permitted provided that the following conditions
14f5df1827Smickey * are met:
15f5df1827Smickey * 1. Redistributions of source code must retain the above copyright
16f5df1827Smickey * notice, this list of conditions and the following disclaimer.
17f5df1827Smickey * 2. Redistributions in binary form must reproduce the above copyright
18f5df1827Smickey * notice, this list of conditions and the following disclaimer in the
19f5df1827Smickey * documentation and/or other materials provided with the distribution.
20c5217b0aSjsg * 3. Neither the name of the University nor the names of its contributors
21f5df1827Smickey * may be used to endorse or promote products derived from this software
22f5df1827Smickey * without specific prior written permission.
23f5df1827Smickey *
24f5df1827Smickey * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25f5df1827Smickey * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26f5df1827Smickey * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27f5df1827Smickey * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28f5df1827Smickey * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29f5df1827Smickey * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30f5df1827Smickey * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31f5df1827Smickey * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32f5df1827Smickey * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33f5df1827Smickey * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34f5df1827Smickey * SUCH DAMAGE.
35f5df1827Smickey *
36f5df1827Smickey * @(#)clock.c 7.2 (Berkeley) 5/12/91
37f5df1827Smickey */
38f5df1827Smickey /*
39f5df1827Smickey * Mach Operating System
40f5df1827Smickey * Copyright (c) 1991,1990,1989 Carnegie Mellon University
41f5df1827Smickey * All Rights Reserved.
42f5df1827Smickey *
43f5df1827Smickey * Permission to use, copy, modify and distribute this software and its
44f5df1827Smickey * documentation is hereby granted, provided that both the copyright
45f5df1827Smickey * notice and this permission notice appear in all copies of the
46f5df1827Smickey * software, derivative works or modified versions, and any portions
47f5df1827Smickey * thereof, and that both notices appear in supporting documentation.
48f5df1827Smickey *
49f5df1827Smickey * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50f5df1827Smickey * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
51f5df1827Smickey * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52f5df1827Smickey *
53f5df1827Smickey * Carnegie Mellon requests users of this software to return to
54f5df1827Smickey *
55f5df1827Smickey * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
56f5df1827Smickey * School of Computer Science
57f5df1827Smickey * Carnegie Mellon University
58f5df1827Smickey * Pittsburgh PA 15213-3890
59f5df1827Smickey *
60f5df1827Smickey * any improvements or extensions that they make and grant Carnegie Mellon
61f5df1827Smickey * the rights to redistribute these changes.
62f5df1827Smickey */
63f5df1827Smickey /*
64f5df1827Smickey Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
65f5df1827Smickey
66f5df1827Smickey All Rights Reserved
67f5df1827Smickey
68f5df1827Smickey Permission to use, copy, modify, and distribute this software and
69f5df1827Smickey its documentation for any purpose and without fee is hereby
70f5df1827Smickey granted, provided that the above copyright notice appears in all
71f5df1827Smickey copies and that both the copyright notice and this permission notice
72f5df1827Smickey appear in supporting documentation, and that the name of Intel
73f5df1827Smickey not be used in advertising or publicity pertaining to distribution
74f5df1827Smickey of the software without specific, written prior permission.
75f5df1827Smickey
76f5df1827Smickey INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77f5df1827Smickey INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78f5df1827Smickey IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79f5df1827Smickey CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80f5df1827Smickey LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81f5df1827Smickey NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82f5df1827Smickey WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83f5df1827Smickey */
84f5df1827Smickey
85f5df1827Smickey /*
86f5df1827Smickey * Primitive clock interrupt routines.
87f5df1827Smickey */
88f5df1827Smickey
896905cb4aSkettenis /* #define CLOCK_DEBUG */
90f5df1827Smickey
91f5df1827Smickey #include <sys/param.h>
92f5df1827Smickey #include <sys/systm.h>
939e761879Scheloha #include <sys/clockintr.h>
94f5df1827Smickey #include <sys/time.h>
95f5df1827Smickey #include <sys/kernel.h>
96f5df1827Smickey #include <sys/timeout.h>
9707e9c1faSotto #include <sys/timetc.h>
98f5df1827Smickey
99f5df1827Smickey #include <machine/cpu.h>
100f5df1827Smickey #include <machine/intr.h>
101f5df1827Smickey #include <machine/pio.h>
102f5df1827Smickey #include <machine/cpufunc.h>
103f5df1827Smickey
1046905cb4aSkettenis #include <dev/clock_subr.h>
105f5df1827Smickey #include <dev/isa/isareg.h>
106f5df1827Smickey #include <dev/isa/isavar.h>
107f5df1827Smickey #include <dev/ic/mc146818reg.h>
108f5df1827Smickey #include <dev/ic/i8253reg.h>
1095be2bff2Sderaadt #include <amd64/isa/nvram.h>
110f5df1827Smickey
11107e9c1faSotto /* Timecounter on the i8254 */
11207e9c1faSotto u_int32_t i8254_lastcount;
11307e9c1faSotto u_int32_t i8254_offset;
11407e9c1faSotto int i8254_ticked;
11507e9c1faSotto u_int i8254_get_timecount(struct timecounter *tc);
11607e9c1faSotto
11707e9c1faSotto u_int i8254_simple_get_timecount(struct timecounter *tc);
11807e9c1faSotto
11907e9c1faSotto static struct timecounter i8254_timecounter = {
1208611d3cdScheloha .tc_get_timecount = i8254_get_timecount,
1218611d3cdScheloha .tc_counter_mask = ~0u,
1228611d3cdScheloha .tc_frequency = TIMER_FREQ,
1238611d3cdScheloha .tc_name = "i8254",
1248611d3cdScheloha .tc_quality = 0,
1258611d3cdScheloha .tc_priv = NULL,
1268611d3cdScheloha .tc_user = 0,
12707e9c1faSotto };
12807e9c1faSotto
129f5df1827Smickey int clockintr(void *);
130f5df1827Smickey int rtcintr(void *);
131f5df1827Smickey int gettick(void);
132f5df1827Smickey void rtcdrain(void *v);
133f5df1827Smickey int rtcget(mc_todregs *);
134f5df1827Smickey void rtcput(mc_todregs *);
135f5df1827Smickey int bcdtobin(int);
136f5df1827Smickey int bintobcd(int);
137f5df1827Smickey
13823f8a50cSjsg u_int mc146818_read(void *, u_int);
13923f8a50cSjsg void mc146818_write(void *, u_int, u_int);
140f5df1827Smickey
14123f8a50cSjsg u_int
mc146818_read(void * sc,u_int reg)1422bb6026aSjsg mc146818_read(void *sc, u_int reg)
143f5df1827Smickey {
144f5df1827Smickey outb(IO_RTC, reg);
145f5df1827Smickey DELAY(1);
146f5df1827Smickey return (inb(IO_RTC+1));
147f5df1827Smickey }
148f5df1827Smickey
14923f8a50cSjsg void
mc146818_write(void * sc,u_int reg,u_int datum)1502bb6026aSjsg mc146818_write(void *sc, u_int reg, u_int datum)
151f5df1827Smickey {
152f5df1827Smickey outb(IO_RTC, reg);
153f5df1827Smickey DELAY(1);
154f5df1827Smickey outb(IO_RTC+1, datum);
155f5df1827Smickey DELAY(1);
156f5df1827Smickey }
157f5df1827Smickey
15807e9c1faSotto struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
15907e9c1faSotto
160b5b9857bSart u_long rtclock_tval;
161f5df1827Smickey
162f5df1827Smickey void
startclocks(void)16394ce6677Sderaadt startclocks(void)
164f5df1827Smickey {
16594ce6677Sderaadt mtx_enter(&timer_mutex);
16694ce6677Sderaadt rtclock_tval = TIMER_DIV(hz);
16794ce6677Sderaadt i8254_startclock();
16894ce6677Sderaadt mtx_leave(&timer_mutex);
169f5df1827Smickey }
170f5df1827Smickey
171f5df1827Smickey int
clockintr(void * frame)1729e761879Scheloha clockintr(void *frame)
173f5df1827Smickey {
17407e9c1faSotto if (timecounter->tc_get_timecount == i8254_get_timecount) {
17507e9c1faSotto if (i8254_ticked) {
17607e9c1faSotto i8254_ticked = 0;
17707e9c1faSotto } else {
17807e9c1faSotto i8254_offset += rtclock_tval;
17907e9c1faSotto i8254_lastcount = 0;
18007e9c1faSotto }
18107e9c1faSotto }
18207e9c1faSotto
1839e761879Scheloha clockintr_dispatch(frame);
184f5df1827Smickey
185f5df1827Smickey return 1;
186f5df1827Smickey }
187f5df1827Smickey
188f5df1827Smickey int
rtcintr(void * frame)1899e761879Scheloha rtcintr(void *frame)
190f5df1827Smickey {
191f5df1827Smickey u_int stat = 0;
192f5df1827Smickey
193f5df1827Smickey /*
194f5df1827Smickey * If rtcintr is 'late', next intr may happen immediately.
195f5df1827Smickey * Get them all. (Also, see comment in cpu_initclocks().)
196f5df1827Smickey */
1979e761879Scheloha while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
198f5df1827Smickey stat = 1;
1999e761879Scheloha
2009e761879Scheloha if (stat)
2019e761879Scheloha clockintr_dispatch(frame);
202f5df1827Smickey
203f5df1827Smickey return (stat);
204f5df1827Smickey }
205f5df1827Smickey
206f5df1827Smickey int
gettick(void)2072bb6026aSjsg gettick(void)
208f5df1827Smickey {
2090db9c031Skettenis u_long s;
210f5df1827Smickey u_char lo, hi;
211f5df1827Smickey
212f5df1827Smickey /* Don't want someone screwing with the counter while we're here. */
21307e9c1faSotto mtx_enter(&timer_mutex);
2140db9c031Skettenis s = intr_disable();
215f5df1827Smickey /* Select counter 0 and latch it. */
216f5df1827Smickey outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
217f5df1827Smickey lo = inb(IO_TIMER1+TIMER_CNTR0);
218f5df1827Smickey hi = inb(IO_TIMER1+TIMER_CNTR0);
2190db9c031Skettenis intr_restore(s);
22007e9c1faSotto mtx_leave(&timer_mutex);
221f5df1827Smickey return ((hi << 8) | lo);
222f5df1827Smickey }
223f5df1827Smickey
224f5df1827Smickey /*
225f5df1827Smickey * Wait "n" microseconds.
226f5df1827Smickey * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
227f5df1827Smickey * Note: timer had better have been programmed before this is first used!
228f5df1827Smickey * (Note that we use `rate generator' mode, which counts at 1:1; `square
229f5df1827Smickey * wave' mode counts at 2:1).
230f5df1827Smickey */
231f5df1827Smickey void
i8254_delay(int n)232b5b9857bSart i8254_delay(int n)
233f5df1827Smickey {
234f5df1827Smickey int limit, tick, otick;
235f807562cSderaadt static const int delaytab[26] = {
236f807562cSderaadt 0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
237f807562cSderaadt 12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
238f807562cSderaadt 24, 25, 27, 28, 29, 30,
239f807562cSderaadt };
240f807562cSderaadt
241f5df1827Smickey /*
242f5df1827Smickey * Read the counter first, so that the rest of the setup overhead is
243f5df1827Smickey * counted.
244f5df1827Smickey */
245f5df1827Smickey otick = gettick();
246f5df1827Smickey
247f807562cSderaadt if (n <= 25)
248f807562cSderaadt n = delaytab[n];
249f807562cSderaadt else {
2502fbcb6d2Scheloha /* Force 64-bit math to avoid 32-bit overflow if possible. */
2512fbcb6d2Scheloha n = (int64_t)n * TIMER_FREQ / 1000000;
252f807562cSderaadt }
253f5df1827Smickey
254f5df1827Smickey limit = TIMER_FREQ / hz;
255f5df1827Smickey
256f5df1827Smickey while (n > 0) {
257f5df1827Smickey tick = gettick();
258f5df1827Smickey if (tick > otick)
259f5df1827Smickey n -= limit - (tick - otick);
260f5df1827Smickey else
261f5df1827Smickey n -= otick - tick;
262f5df1827Smickey otick = tick;
263f5df1827Smickey }
264f5df1827Smickey }
265f5df1827Smickey
266f5df1827Smickey void
rtcdrain(void * v)267f5df1827Smickey rtcdrain(void *v)
268f5df1827Smickey {
269f5df1827Smickey struct timeout *to = (struct timeout *)v;
270f5df1827Smickey
271f5df1827Smickey if (to != NULL)
272f5df1827Smickey timeout_del(to);
273f5df1827Smickey
2742b2a6d92Sjasper /* Drain any un-acknowledged RTC interrupts. */
275f5df1827Smickey while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
276f5df1827Smickey ; /* Nothing. */
277f5df1827Smickey }
278f5df1827Smickey
279f5df1827Smickey void
i8254_initclocks(void)2802bb6026aSjsg i8254_initclocks(void)
281f5df1827Smickey {
2829e761879Scheloha i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
2839e761879Scheloha
284f5df1827Smickey stathz = 128;
2859e761879Scheloha profhz = 1024; /* XXX does not divide into 1 billion */
28611d1f9b2Scheloha }
2879e761879Scheloha
28811d1f9b2Scheloha void
i8254_start_both_clocks(void)28911d1f9b2Scheloha i8254_start_both_clocks(void)
29011d1f9b2Scheloha {
2919e761879Scheloha clockintr_cpu_init(NULL);
292f5df1827Smickey
293587afcd5Skettenis /*
294587afcd5Skettenis * While the clock interrupt handler isn't really MPSAFE, the
295587afcd5Skettenis * i8254 can't really be used as a clock on a true MP system.
296587afcd5Skettenis */
297587afcd5Skettenis isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
298587afcd5Skettenis clockintr, 0, "clock");
299587afcd5Skettenis isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
300587afcd5Skettenis rtcintr, 0, "rtc");
30194ce6677Sderaadt
30294ce6677Sderaadt rtcstart(); /* start the mc146818 clock */
30394ce6677Sderaadt }
30494ce6677Sderaadt
30594ce6677Sderaadt void
rtcstart(void)30694ce6677Sderaadt rtcstart(void)
30794ce6677Sderaadt {
30894ce6677Sderaadt static struct timeout rtcdrain_timeout;
309f5df1827Smickey
310f5df1827Smickey mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
311f5df1827Smickey mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
312f5df1827Smickey
313f5df1827Smickey /*
314f5df1827Smickey * On a number of i386 systems, the rtc will fail to start when booting
315f5df1827Smickey * the system. This is due to us missing to acknowledge an interrupt
316f5df1827Smickey * during early stages of the boot process. If we do not acknowledge
317f5df1827Smickey * the interrupt, the rtc clock will not generate further interrupts.
318f5df1827Smickey * To solve this, once interrupts are enabled, use a timeout (once)
319f5df1827Smickey * to drain any un-acknowledged rtc interrupt(s).
320f5df1827Smickey */
321f5df1827Smickey timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
322f5df1827Smickey timeout_add(&rtcdrain_timeout, 1);
323f5df1827Smickey }
324f5df1827Smickey
3253191b86aSkettenis void
rtcstop(void)3263191b86aSkettenis rtcstop(void)
3273191b86aSkettenis {
3283191b86aSkettenis mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
3293191b86aSkettenis }
3303191b86aSkettenis
331f5df1827Smickey int
rtcget(mc_todregs * regs)3322bb6026aSjsg rtcget(mc_todregs *regs)
333f5df1827Smickey {
334f5df1827Smickey if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
335f5df1827Smickey return (-1);
336f5df1827Smickey MC146818_GETTOD(NULL, regs); /* XXX softc */
337f5df1827Smickey return (0);
338f5df1827Smickey }
339f5df1827Smickey
340f5df1827Smickey void
rtcput(mc_todregs * regs)3412bb6026aSjsg rtcput(mc_todregs *regs)
342f5df1827Smickey {
343f5df1827Smickey MC146818_PUTTOD(NULL, regs); /* XXX softc */
344f5df1827Smickey }
345f5df1827Smickey
346f5df1827Smickey int
bcdtobin(int n)3472bb6026aSjsg bcdtobin(int n)
348f5df1827Smickey {
349f5df1827Smickey return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
350f5df1827Smickey }
351f5df1827Smickey
352f5df1827Smickey int
bintobcd(int n)3532bb6026aSjsg bintobcd(int n)
354f5df1827Smickey {
355f5df1827Smickey return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
356f5df1827Smickey }
357f5df1827Smickey
358f5df1827Smickey /*
359f5df1827Smickey * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
360f5df1827Smickey * to be called at splclock()
361f5df1827Smickey */
362f5df1827Smickey static int cmoscheck(void);
363f5df1827Smickey static int
cmoscheck(void)3642bb6026aSjsg cmoscheck(void)
365f5df1827Smickey {
366f5df1827Smickey int i;
367f5df1827Smickey unsigned short cksum = 0;
368f5df1827Smickey
369f5df1827Smickey for (i = 0x10; i <= 0x2d; i++)
370f5df1827Smickey cksum += mc146818_read(NULL, i); /* XXX softc */
371f5df1827Smickey
372f5df1827Smickey return (cksum == (mc146818_read(NULL, 0x2e) << 8)
373f5df1827Smickey + mc146818_read(NULL, 0x2f));
374f5df1827Smickey }
375f5df1827Smickey
376f5df1827Smickey /*
377f5df1827Smickey * patchable to control century byte handling:
378f5df1827Smickey * 1: always update
379f5df1827Smickey * -1: never touch
380f5df1827Smickey * 0: try to figure out itself
381f5df1827Smickey */
382f5df1827Smickey int rtc_update_century = 0;
383f5df1827Smickey
384f5df1827Smickey /*
385f5df1827Smickey * Expand a two-digit year as read from the clock chip
386f5df1827Smickey * into full width.
387f5df1827Smickey * Being here, deal with the CMOS century byte.
388f5df1827Smickey */
389f5df1827Smickey static int centb = NVRAM_CENTURY;
390f5df1827Smickey static int clock_expandyear(int);
391f5df1827Smickey static int
clock_expandyear(int clockyear)3922bb6026aSjsg clock_expandyear(int clockyear)
393f5df1827Smickey {
394f5df1827Smickey int s, clockcentury, cmoscentury;
395f5df1827Smickey
396f5df1827Smickey clockcentury = (clockyear < 70) ? 20 : 19;
397f5df1827Smickey clockyear += 100 * clockcentury;
398f5df1827Smickey
399f5df1827Smickey if (rtc_update_century < 0)
400f5df1827Smickey return (clockyear);
401f5df1827Smickey
402f5df1827Smickey s = splclock();
403f5df1827Smickey if (cmoscheck())
404f5df1827Smickey cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
405f5df1827Smickey else
406f5df1827Smickey cmoscentury = 0;
407f5df1827Smickey splx(s);
4086b03d5deSjcs if (!cmoscentury)
409f5df1827Smickey return (clockyear);
4106b03d5deSjcs
411f5df1827Smickey cmoscentury = bcdtobin(cmoscentury);
412f5df1827Smickey
413f5df1827Smickey if (cmoscentury != clockcentury) {
414f5df1827Smickey /* XXX note: saying "century is 20" might confuse the naive. */
415f5df1827Smickey printf("WARNING: NVRAM century is %d but RTC year is %d\n",
416f5df1827Smickey cmoscentury, clockyear);
417f5df1827Smickey
418f5df1827Smickey /* Kludge to roll over century. */
419f5df1827Smickey if ((rtc_update_century > 0) ||
420f5df1827Smickey ((cmoscentury == 19) && (clockcentury == 20) &&
421f5df1827Smickey (clockyear == 2000))) {
422f5df1827Smickey printf("WARNING: Setting NVRAM century to %d\n",
423f5df1827Smickey clockcentury);
424f5df1827Smickey s = splclock();
425f5df1827Smickey mc146818_write(NULL, centb, bintobcd(clockcentury));
426f5df1827Smickey splx(s);
427f5df1827Smickey }
428f5df1827Smickey } else if (cmoscentury == 19 && rtc_update_century == 0)
429f5df1827Smickey rtc_update_century = 1; /* will update later in resettodr() */
430f5df1827Smickey
431f5df1827Smickey return (clockyear);
432f5df1827Smickey }
433f5df1827Smickey
4346905cb4aSkettenis int
rtcgettime(struct todr_chip_handle * handle,struct timeval * tv)4356905cb4aSkettenis rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
436f5df1827Smickey {
437f5df1827Smickey mc_todregs rtclk;
438f5df1827Smickey struct clock_ymdhms dt;
439f5df1827Smickey int s;
44007e9c1faSotto
441f5df1827Smickey s = splclock();
442f5df1827Smickey if (rtcget(&rtclk)) {
443f5df1827Smickey splx(s);
4446905cb4aSkettenis return EINVAL;
445f5df1827Smickey }
446f5df1827Smickey splx(s);
4476905cb4aSkettenis
4486905cb4aSkettenis #ifdef CLOCK_DEBUG
449f5df1827Smickey printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
450f5df1827Smickey rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
451f5df1827Smickey rtclk[MC_SEC]);
452f5df1827Smickey #endif
453f5df1827Smickey
454f5df1827Smickey dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
455f5df1827Smickey dt.dt_min = bcdtobin(rtclk[MC_MIN]);
456f5df1827Smickey dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
457f5df1827Smickey dt.dt_day = bcdtobin(rtclk[MC_DOM]);
458f5df1827Smickey dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
459f5df1827Smickey dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
460f5df1827Smickey
4616905cb4aSkettenis tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
4626905cb4aSkettenis tv->tv_usec = 0;
4636905cb4aSkettenis return 0;
464f5df1827Smickey }
465f5df1827Smickey
4666905cb4aSkettenis int
rtcsettime(struct todr_chip_handle * handle,struct timeval * tv)4676905cb4aSkettenis rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
468f5df1827Smickey {
469f5df1827Smickey mc_todregs rtclk;
470f5df1827Smickey struct clock_ymdhms dt;
471db1dd8d3Scheloha int century, s;
472f5df1827Smickey
473f5df1827Smickey s = splclock();
474f5df1827Smickey if (rtcget(&rtclk))
475f5df1827Smickey memset(&rtclk, 0, sizeof(rtclk));
476f5df1827Smickey splx(s);
477f5df1827Smickey
478a5dde40eSkettenis clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
479f5df1827Smickey
480f5df1827Smickey rtclk[MC_SEC] = bintobcd(dt.dt_sec);
481f5df1827Smickey rtclk[MC_MIN] = bintobcd(dt.dt_min);
482f5df1827Smickey rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
483f5df1827Smickey rtclk[MC_DOW] = dt.dt_wday + 1;
484f5df1827Smickey rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
485f5df1827Smickey rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
486f5df1827Smickey rtclk[MC_DOM] = bintobcd(dt.dt_day);
487f5df1827Smickey
4886905cb4aSkettenis #ifdef CLOCK_DEBUG
489f5df1827Smickey printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
490f5df1827Smickey rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
491f5df1827Smickey #endif
4926905cb4aSkettenis
493f5df1827Smickey s = splclock();
494f5df1827Smickey rtcput(&rtclk);
495f5df1827Smickey if (rtc_update_century > 0) {
496f5df1827Smickey century = bintobcd(dt.dt_year / 100);
497f5df1827Smickey mc146818_write(NULL, centb, century); /* XXX softc */
498f5df1827Smickey }
499f5df1827Smickey splx(s);
5006905cb4aSkettenis return 0;
5016905cb4aSkettenis }
5026905cb4aSkettenis
5036905cb4aSkettenis struct todr_chip_handle rtc_todr;
5046905cb4aSkettenis
5056905cb4aSkettenis void
rtcinit(void)5066905cb4aSkettenis rtcinit(void)
5076905cb4aSkettenis {
5086905cb4aSkettenis rtc_todr.todr_gettime = rtcgettime;
5096905cb4aSkettenis rtc_todr.todr_settime = rtcsettime;
5104b3ced23Skettenis rtc_todr.todr_quality = 0;
5114b3ced23Skettenis todr_attach(&rtc_todr);
512f5df1827Smickey }
513f5df1827Smickey
514f5df1827Smickey void
setstatclockrate(int arg)5152bb6026aSjsg setstatclockrate(int arg)
516f5df1827Smickey {
5174237b508Sgerhard if (initclock_func == i8254_initclocks) {
518f5df1827Smickey if (arg == stathz)
5194237b508Sgerhard mc146818_write(NULL, MC_REGA,
5204237b508Sgerhard MC_BASE_32_KHz | MC_RATE_128_Hz);
521f5df1827Smickey else
5224237b508Sgerhard mc146818_write(NULL, MC_REGA,
5234237b508Sgerhard MC_BASE_32_KHz | MC_RATE_1024_Hz);
5244237b508Sgerhard }
525f5df1827Smickey }
52607e9c1faSotto
52707e9c1faSotto void
i8254_inittimecounter(void)52807e9c1faSotto i8254_inittimecounter(void)
52907e9c1faSotto {
53007e9c1faSotto tc_init(&i8254_timecounter);
53107e9c1faSotto }
53207e9c1faSotto
53307e9c1faSotto /*
53407e9c1faSotto * If we're using lapic to drive hardclock, we can use a simpler
53507e9c1faSotto * algorithm for the i8254 timecounters.
53607e9c1faSotto */
53707e9c1faSotto void
i8254_inittimecounter_simple(void)53807e9c1faSotto i8254_inittimecounter_simple(void)
53907e9c1faSotto {
54007e9c1faSotto i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
54107e9c1faSotto i8254_timecounter.tc_counter_mask = 0x7fff;
54207e9c1faSotto i8254_timecounter.tc_frequency = TIMER_FREQ;
54307e9c1faSotto
54407e9c1faSotto mtx_enter(&timer_mutex);
54594ce6677Sderaadt rtclock_tval = 0x8000;
54694ce6677Sderaadt i8254_startclock();
54707e9c1faSotto mtx_leave(&timer_mutex);
54807e9c1faSotto
54907e9c1faSotto tc_init(&i8254_timecounter);
55007e9c1faSotto }
55107e9c1faSotto
55294ce6677Sderaadt void
i8254_startclock(void)55394ce6677Sderaadt i8254_startclock(void)
55494ce6677Sderaadt {
55594ce6677Sderaadt u_long tval = rtclock_tval;
55694ce6677Sderaadt
55794ce6677Sderaadt outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
55894ce6677Sderaadt outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
55994ce6677Sderaadt outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
56094ce6677Sderaadt }
56194ce6677Sderaadt
56207e9c1faSotto u_int
i8254_simple_get_timecount(struct timecounter * tc)56307e9c1faSotto i8254_simple_get_timecount(struct timecounter *tc)
56407e9c1faSotto {
56507e9c1faSotto return (rtclock_tval - gettick());
56607e9c1faSotto }
56707e9c1faSotto
56807e9c1faSotto u_int
i8254_get_timecount(struct timecounter * tc)56907e9c1faSotto i8254_get_timecount(struct timecounter *tc)
57007e9c1faSotto {
57107e9c1faSotto u_char hi, lo;
57207e9c1faSotto u_int count;
5730db9c031Skettenis u_long s;
57407e9c1faSotto
5750db9c031Skettenis s = intr_disable();
57607e9c1faSotto
57707e9c1faSotto outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
57807e9c1faSotto lo = inb(IO_TIMER1+TIMER_CNTR0);
57907e9c1faSotto hi = inb(IO_TIMER1+TIMER_CNTR0);
58007e9c1faSotto
58107e9c1faSotto count = rtclock_tval - ((hi << 8) | lo);
58207e9c1faSotto
58307e9c1faSotto if (count < i8254_lastcount) {
58407e9c1faSotto i8254_ticked = 1;
58507e9c1faSotto i8254_offset += rtclock_tval;
58607e9c1faSotto }
58707e9c1faSotto i8254_lastcount = count;
58807e9c1faSotto count += i8254_offset;
5890db9c031Skettenis
5900db9c031Skettenis intr_restore(s);
59107e9c1faSotto
59207e9c1faSotto return (count);
59307e9c1faSotto }
594