mcclock.c revision 1.32 1 /* $NetBSD: mcclock.c,v 1.32 2025/09/07 21:45:15 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
5 * All rights reserved.
6 *
7 * Author: Chris G. Demetriou
8 *
9 * Permission to use, copy, modify and distribute this software and
10 * its documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation.
14 *
15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18 *
19 * Carnegie Mellon requests users of this software to return to
20 *
21 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
22 * School of Computer Science
23 * Carnegie Mellon University
24 * Pittsburgh PA 15213-3890
25 *
26 * any improvements or extensions that they make and grant Carnegie the
27 * rights to redistribute these changes.
28 */
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: mcclock.c,v 1.32 2025/09/07 21:45:15 thorpej Exp $");
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 #include <dev/clock_subr.h>
38
39 #include <dev/dec/clockvar.h>
40 #include <dev/dec/mcclockvar.h>
41 #include <dev/ic/mc146818reg.h>
42
43 /*
44 * XXX default rate is machine-dependent.
45 */
46 #ifdef __alpha__
47 #define MC_DEFAULTHZ 1024
48 #endif
49 #ifdef pmax
50 #define MC_DEFAULTHZ 256
51 #endif
52
53
54 void mcclock_init(device_t);
55 int mcclock_get(todr_chip_handle_t, struct timeval *);
56 int mcclock_set(todr_chip_handle_t, struct timeval *);
57
58 const struct clockfns mcclock_clockfns = {
59 mcclock_init,
60 };
61
62 #define mc146818_write(sc, reg, datum) \
63 (*(sc)->sc_busfns->mc_bf_write)(sc, reg, datum)
64 #define mc146818_read(sc, reg) \
65 (*(sc)->sc_busfns->mc_bf_read)(sc, reg)
66
67 void
68 mcclock_attach(struct mcclock_softc *sc, const struct mcclock_busfns *busfns)
69 {
70
71 aprint_normal(": mc146818 or compatible");
72
73 sc->sc_busfns = busfns;
74
75 /* Turn interrupts off, just in case. */
76 mc146818_write(sc, MC_REGB, MC_REGB_BINARY | MC_REGB_24HR);
77
78 clockattach(sc->sc_dev, &mcclock_clockfns);
79
80 sc->sc_todr.todr_gettime = mcclock_get;
81 sc->sc_todr.todr_settime = mcclock_set;
82 KASSERT(sc->sc_dev != NULL);
83 sc->sc_todr.todr_dev = sc->sc_dev;
84 todr_attach(&sc->sc_todr);
85 }
86
87 void
88 mcclock_init(device_t dev)
89 {
90 struct mcclock_softc *sc = device_private(dev);
91 int rate;
92
93 again:
94 switch (hz) {
95 case 32:
96 rate = MC_BASE_32_KHz | MC_RATE_32_Hz;
97 break;
98 case 64:
99 rate = MC_BASE_32_KHz | MC_RATE_64_Hz;
100 break;
101 case 128:
102 rate = MC_BASE_32_KHz | MC_RATE_128_Hz;
103 break;
104 case 256:
105 rate = MC_BASE_32_KHz | MC_RATE_256_Hz;
106 break;
107 case 512:
108 rate = MC_BASE_32_KHz | MC_RATE_512_Hz;
109 break;
110 case 1024:
111 rate = MC_BASE_32_KHz | MC_RATE_1024_Hz;
112 break;
113 case 2048:
114 rate = MC_BASE_32_KHz | MC_RATE_2048_Hz;
115 break;
116 case 4096:
117 rate = MC_BASE_32_KHz | MC_RATE_4096_Hz;
118 break;
119 case 8192:
120 rate = MC_BASE_32_KHz | MC_RATE_8192_Hz;
121 break;
122 case 16384:
123 rate = MC_BASE_4_MHz | MC_RATE_1;
124 break;
125 case 32768:
126 rate = MC_BASE_4_MHz | MC_RATE_2;
127 break;
128 default:
129 printf("%s: Cannot get %d Hz clock; using %d Hz\n",
130 device_xname(dev), hz, MC_DEFAULTHZ);
131 hz = MC_DEFAULTHZ;
132 goto again;
133 }
134 mc146818_write(sc, MC_REGA, rate);
135 mc146818_write(sc, MC_REGB,
136 MC_REGB_PIE | MC_REGB_SQWE | MC_REGB_BINARY | MC_REGB_24HR);
137 }
138
139 /*
140 * Experiments (and passing years) show that Decstation PROMS
141 * assume the kernel uses the clock chip as a time-of-year clock.
142 * The PROM assumes the clock is always set to 1972 or 1973, and contains
143 * time-of-year in seconds. The PROM checks the clock at boot time,
144 * and if it's outside that range, sets it to 1972-01-01.
145 *
146 * XXX should be at the mc146818 layer?
147 */
148
149 /*
150 * Get the time of day, based on the clock's value and/or the base value.
151 */
152 int
153 mcclock_get(todr_chip_handle_t tch, struct timeval *tvp)
154 {
155 struct mcclock_softc *sc = device_private(tch->todr_dev);
156 uint32_t yearsecs;
157 mc_todregs regs;
158 int s;
159 struct clock_ymdhms dt;
160
161 s = splclock();
162 MC146818_GETTOD(sc, ®s)
163 splx(s);
164
165 dt.dt_sec = regs[MC_SEC];
166 dt.dt_min = regs[MC_MIN];
167 dt.dt_hour = regs[MC_HOUR];
168 dt.dt_day = regs[MC_DOM];
169 dt.dt_mon = regs[MC_MONTH];
170 dt.dt_year = 1972;
171
172 yearsecs = clock_ymdhms_to_secs(&dt) - (72 - 70) * SECS_PER_COMMON_YEAR;
173
174 /*
175 * Take the actual year from the filesystem if possible;
176 * allow for 2 days of clock loss and 363 days of clock gain.
177 */
178 dt.dt_year = 1972; /* or MINYEAR or base/SECS_PER_COMMON_YEAR+1970... */
179 dt.dt_mon = 1;
180 dt.dt_day = 1;
181 dt.dt_hour = 0;
182 dt.dt_min = 0;
183 dt.dt_sec = 0;
184 for(;;) {
185 tvp->tv_sec = yearsecs + clock_ymdhms_to_secs(&dt);
186 if (tvp->tv_sec > tch->todr_base_time - 2 * SECS_PER_DAY)
187 break;
188 dt.dt_year++;
189 }
190
191 tvp->tv_usec = 0;
192 return 0;
193 }
194
195 /*
196 * Reset the TODR based on the time value.
197 */
198 int
199 mcclock_set(todr_chip_handle_t tch, struct timeval *tvp)
200 {
201 struct mcclock_softc *sc = device_private(tch->todr_dev);
202 struct clock_ymdhms dt;
203 uint32_t yearsecs;
204 mc_todregs regs;
205 int s;
206
207 /*
208 * calculate seconds relative to this year
209 */
210 clock_secs_to_ymdhms(tvp->tv_sec, &dt); /* get the year */
211 dt.dt_mon = 1;
212 dt.dt_day = 1;
213 dt.dt_hour = 0;
214 dt.dt_min = 0;
215 dt.dt_sec = 0;
216 yearsecs = tvp->tv_sec - clock_ymdhms_to_secs(&dt);
217
218 #define first72 ((72 - 70) * SECS_PER_COMMON_YEAR)
219 clock_secs_to_ymdhms(first72 + yearsecs, &dt);
220
221 #ifdef DEBUG
222 if (dt.dt_year != 1972)
223 printf("resettodr: botch (%d, %" PRId64 ")\n",
224 yearsecs, time_second);
225 #endif
226
227 s = splclock();
228 MC146818_GETTOD(sc, ®s);
229 splx(s);
230
231 regs[MC_SEC] = dt.dt_sec;
232 regs[MC_MIN] = dt.dt_min;
233 regs[MC_HOUR] = dt.dt_hour;
234 regs[MC_DOW] = dt.dt_wday;
235 regs[MC_DOM] = dt.dt_day;
236 regs[MC_MONTH] = dt.dt_mon;
237 regs[MC_YEAR] = dt.dt_year - 1900; /* rt clock wants 2 digits */
238
239 s = splclock();
240 MC146818_PUTTOD(sc, ®s);
241 splx(s);
242
243 return 0;
244 }
245