1 /* $NetBSD: rtc.c,v 1.20 2023/10/19 22:07:13 andvar Exp $ */
2 /*
3 * Copyright (c) 1998 Darrin Jewell
4 * Copyright (c) 1997 Rolf Grossmann
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Rolf Grossmann.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /* These haven't been tested to see how they interact with NeXTstep's
34 * interpretation of the rtc.
35 */
36
37 /* Now using this in the kernel. This should be turned into a device
38 * Darrin B Jewell <jewell@mit.edu> Tue Jan 27 20:59:25 1998
39 */
40
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: rtc.c,v 1.20 2023/10/19 22:07:13 andvar Exp $");
43
44 #include <sys/param.h>
45 #include <sys/systm.h> /* for panic */
46
47 #include <machine/bus.h>
48 #include <machine/cpu.h>
49
50 #include <dev/clock_subr.h>
51
52 #include <next68k/next68k/rtc.h>
53
54 #include <next68k/dev/clockreg.h>
55 #include <next68k/dev/intiovar.h>
56
57 /* #define RTC_DEBUG */
58
59 u_char new_clock;
60
61 /* will get memory mapped in rtc_init */
62 volatile u_int *scr2 = (u_int *)NEXT_P_SCR2;
63
64 static int gettime_old(todr_chip_handle_t, struct clock_ymdhms *);
65 static int settime_old(todr_chip_handle_t, struct clock_ymdhms *);
66 static int gettime_new(todr_chip_handle_t, struct timeval *);
67 static int settime_new(todr_chip_handle_t, struct timeval *);
68
69 /*
70 * NB: This code should probably be converted to a _true_ device, then this
71 * initialization could happen in attach. The printf could get fixed then,
72 * too.
73 */
74 void
rtc_init(void)75 rtc_init(void)
76 {
77 static struct todr_chip_handle tch;
78 uint8_t val;
79
80 scr2 = (u_int *)IIOV(NEXT_P_SCR2);
81 val = rtc_read(RTC_STATUS);
82 new_clock = (val & RTC_NEW_CLOCK) ? 1 : 0;
83
84 printf("Looks like a %s clock chip.\n", new_clock ?
85 "MCS1850 (new style)" :
86 "MC68HC68T1 (old style)");
87
88 #ifdef RTC_DEBUG
89 rtc_print();
90 #endif
91
92 if (new_clock) {
93 tch.todr_gettime = gettime_new;
94 tch.todr_settime = settime_new;
95 tch.todr_gettime_ymdhms = NULL;
96 tch.todr_settime_ymdhms = NULL;
97 } else {
98 tch.todr_gettime_ymdhms = gettime_old;
99 tch.todr_settime_ymdhms = settime_old;
100 tch.todr_gettime = NULL;
101 tch.todr_settime = NULL;
102 }
103 tch.todr_setwen = NULL;
104
105 todr_attach(&tch);
106 }
107
108 void
rtc_print(void)109 rtc_print(void)
110 {
111
112 #define RTC_PRINT(x) printf("\t%16s= 0x%02x\n",#x, rtc_read(x))
113
114 if (new_clock) {
115 RTC_PRINT(RTC_RAM);
116 RTC_PRINT(RTC_CNTR0);
117 RTC_PRINT(RTC_CNTR1);
118 RTC_PRINT(RTC_CNTR2);
119 RTC_PRINT(RTC_CNTR3);
120 RTC_PRINT(RTC_ALARM0);
121 RTC_PRINT(RTC_ALARM1);
122 RTC_PRINT(RTC_ALARM2);
123 RTC_PRINT(RTC_ALARM3);
124 RTC_PRINT(RTC_STATUS);
125 RTC_PRINT(RTC_CONTROL);
126 } else {
127 RTC_PRINT(RTC_RAM);
128 RTC_PRINT(RTC_SEC);
129 RTC_PRINT(RTC_MIN);
130 RTC_PRINT(RTC_HRS);
131 RTC_PRINT(RTC_DAY);
132 RTC_PRINT(RTC_DATE);
133 RTC_PRINT(RTC_MON);
134 RTC_PRINT(RTC_YR);
135 RTC_PRINT(RTC_ALARM_SEC);
136 RTC_PRINT(RTC_ALARM_MIN);
137 RTC_PRINT(RTC_ALARM_HR);
138 RTC_PRINT(RTC_STATUS);
139 RTC_PRINT(RTC_CONTROL);
140 RTC_PRINT(RTC_INTRCTL);
141 }
142 }
143
144
145 uint8_t
rtc_read(uint8_t reg)146 rtc_read(uint8_t reg)
147 {
148 int i;
149 u_int tmp;
150 uint8_t val;
151
152 *scr2 = (*scr2 & ~(SCR2_RTDATA | SCR2_RTCLK)) | SCR2_RTCE;
153 DELAY(1);
154
155 val = reg;
156 for (i = 0; i < 8; i++) {
157 tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
158 if ((val & 0x80) != 0)
159 tmp |= SCR2_RTDATA;
160
161 *scr2 = tmp;
162 DELAY(1);
163 *scr2 = tmp | SCR2_RTCLK;
164 DELAY(1);
165 *scr2 = tmp;
166 DELAY(1);
167
168 val <<= 1;
169 }
170
171 val = 0; /* should be anyway */
172 for (i = 0; i < 8; i++) {
173 val <<= 1;
174
175 tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
176
177 *scr2 = tmp | SCR2_RTCLK;
178 DELAY(1);
179 *scr2 = tmp;
180 DELAY(1);
181
182 if ((*scr2 & SCR2_RTDATA) != 0)
183 val |= 1;
184 }
185
186 *scr2 &= ~(SCR2_RTDATA | SCR2_RTCLK | SCR2_RTCE);
187 DELAY(1);
188
189 return val;
190 }
191
192 void
rtc_write(uint8_t reg,uint8_t v)193 rtc_write(uint8_t reg, uint8_t v)
194 {
195 int i;
196 u_int tmp;
197 uint8_t val;
198
199 *scr2 = (*scr2 & ~(SCR2_RTDATA | SCR2_RTCLK)) | SCR2_RTCE;
200 DELAY(1);
201
202 val = reg | RTC_WRITE;
203
204 for (i = 0; i < 8; i++) {
205 tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
206 if ((val & 0x80) != 0)
207 tmp |= SCR2_RTDATA;
208
209 *scr2 = tmp;
210 DELAY(1);
211 *scr2 = tmp | SCR2_RTCLK;
212 DELAY(1);
213 *scr2 = tmp;
214 DELAY(1);
215
216 val <<= 1;
217 }
218
219 DELAY(1);
220
221 for (i = 0; i < 8; i++) {
222 tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
223 if ((v & 0x80) != 0)
224 tmp |= SCR2_RTDATA;
225
226 *scr2 = tmp;
227 DELAY(1);
228 *scr2 = tmp | SCR2_RTCLK;
229 DELAY(1);
230 *scr2 = tmp;
231 DELAY(1);
232
233 v <<= 1;
234 }
235
236 *scr2 &= ~(SCR2_RTDATA | SCR2_RTCLK | SCR2_RTCE);
237 DELAY(1);
238 }
239
240 void
poweroff(void)241 poweroff(void)
242 {
243 int reg, t;
244
245 if(new_clock) {
246 reg = RTC_CNTR3;
247 } else {
248 reg = RTC_CNTR0;
249 }
250
251 t = rtc_read(reg); /* seconds */
252 /* wait for clock to tick */
253 while(t == rtc_read(reg))
254 continue;
255
256 DELAY(850000); /* hardware bug workaround ? */
257
258 if (new_clock) {
259 reg = RTC_CONTROL;
260 } else {
261 reg = RTC_INTRCTL;
262 }
263
264 rtc_write(reg, rtc_read(reg)|(RTC_PDOWN));
265
266 printf("....................."); /* @@@ work around some sort of bug. */
267
268 panic("Failed to poweroff!");
269 }
270
271
272 int
gettime_old(todr_chip_handle_t tch,struct clock_ymdhms * dt)273 gettime_old(todr_chip_handle_t tch, struct clock_ymdhms *dt)
274 {
275 uint8_t h, y;
276
277 y = bcdtobin(rtc_read(RTC_YR));
278 if (y >= 69) {
279 dt->dt_year = 1900 + y;
280 } else {
281 dt->dt_year = 2000 + y;
282 }
283
284 dt->dt_mon = bcdtobin(rtc_read(RTC_MON) & 0x1f);
285 dt->dt_day = bcdtobin(rtc_read(RTC_DATE) & 0x3f);
286 dt->dt_wday = bcdtobin(rtc_read(RTC_DAY) & 0x07);
287
288 h = rtc_read(RTC_HRS);
289 if ((h & 0x80) != 0) {
290 /* time is am/pm format */
291 dt->dt_hour = bcdtobin(h & 0x1f);
292 if ((h & 0x20) != 0) {
293 /* pm */
294 if (dt->dt_hour < 12)
295 dt->dt_hour += 12;
296 } else {
297 /* am */
298 if (dt->dt_hour == 12)
299 dt->dt_hour = 0;
300 }
301 #ifdef notdef
302 } else {
303 /* time is 24 hour format */
304 struct clock_ymdhms val;
305 val.dt_hour = bcdtobin(h & 0x3f);
306 #endif
307 }
308
309 dt->dt_min = bcdtobin(rtc_read(RTC_MIN) & 0x7f);
310 dt->dt_sec = bcdtobin(rtc_read(RTC_SEC) & 0x7f);
311
312 return 0;
313 }
314
315 int
settime_old(todr_chip_handle_t tcr,struct clock_ymdhms * dt)316 settime_old(todr_chip_handle_t tcr, struct clock_ymdhms *dt)
317 {
318 uint8_t h;
319
320 /* Stop the clock */
321 rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) & ~RTC_START);
322
323 #ifdef RTC_DEBUG
324 printf("Regs before:\n");
325 rtc_print();
326 #endif
327
328 rtc_write(RTC_SEC, bintobcd(dt->dt_sec));
329 rtc_write(RTC_MIN, bintobcd(dt->dt_min));
330 h = rtc_read(RTC_HRS);
331 if ((h & 0x80) != 0) {
332 /* time is am/pm format */
333 if (dt->dt_hour == 0) {
334 rtc_write(RTC_HRS, bintobcd(12) | 0x80);
335 } else if (dt->dt_hour < 12) {
336 /* am */
337 rtc_write(RTC_HRS, bintobcd(dt->dt_hour) | 0x80);
338 } else if (dt->dt_hour == 12) {
339 rtc_write(RTC_HRS, bintobcd(12) | 0x80 | 0x20);
340 } else {
341 /* pm */
342 rtc_write(RTC_HRS,
343 bintobcd(dt->dt_hour - 12) | 0x80 | 0x20);
344 }
345 } else {
346 /* time is 24 hour format */
347 rtc_write(RTC_HRS, bintobcd(dt->dt_hour));
348 }
349 rtc_write(RTC_DAY, bintobcd(dt->dt_wday));
350 rtc_write(RTC_DATE, bintobcd(dt->dt_day));
351 rtc_write(RTC_MON, bintobcd(dt->dt_mon));
352 rtc_write(RTC_YR, bintobcd(dt->dt_year % 100));
353
354 #ifdef RTC_DEBUG
355 printf("Regs after:\n");
356 rtc_print();
357 #endif
358
359 /* restart the clock */
360 rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) | RTC_START);
361 return 0;
362 }
363
364 int
gettime_new(todr_chip_handle_t tch,struct timeval * tvp)365 gettime_new(todr_chip_handle_t tch, struct timeval *tvp)
366 {
367 tvp->tv_sec =
368 rtc_read(RTC_CNTR0) << 24 |
369 rtc_read(RTC_CNTR1) << 16 |
370 rtc_read(RTC_CNTR2) << 8 |
371 rtc_read(RTC_CNTR3);
372 return 0;
373 }
374
375 int
settime_new(todr_chip_handle_t tch,struct timeval * tvp)376 settime_new(todr_chip_handle_t tch, struct timeval *tvp)
377 {
378
379 /* Stop the clock */
380 rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) & ~RTC_START);
381
382 #ifdef RTC_DEBUG
383 printf("Setting RTC to 0x%08llx. Regs before:\n", tvp->tv_sec);
384 rtc_print();
385 #endif
386
387 rtc_write(RTC_CNTR0, (tvp->tv_sec >> 24) & 0xff);
388 rtc_write(RTC_CNTR1, (tvp->tv_sec >> 16) & 0xff);
389 rtc_write(RTC_CNTR2, (tvp->tv_sec >> 8) & 0xff);
390 rtc_write(RTC_CNTR3, (tvp->tv_sec) & 0xff);
391
392 #ifdef RTC_DEBUG
393 printf("Regs after:\n");
394 rtc_print();
395 #endif
396
397 /* restart the clock */
398 rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) | RTC_START);
399
400 return 0;
401 }
402