xref: /netbsd/sys/arch/next68k/next68k/rtc.c (revision 6550d01e)
1 /*      $NetBSD: rtc.c,v 1.15 2009/12/12 14:44:09 tsutsui 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.15 2009/12/12 14:44:09 tsutsui 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 volatile u_int *scr2 = (u_int *)NEXT_P_SCR2; /* will get memory mapped in rtc_init */
61 
62 static int gettime_old(todr_chip_handle_t, struct clock_ymdhms *);
63 static int settime_old(todr_chip_handle_t, struct clock_ymdhms *);
64 static int gettime_new(todr_chip_handle_t, struct timeval *);
65 static int settime_new(todr_chip_handle_t, struct timeval *);
66 
67 /*
68  * NB: This code should probably be converted to a _true_ device, then this
69  * initialization could happen in attach.  The printf could get fixed then,
70  * too.
71  */
72 void
73 rtc_init(void)
74 {
75 	static struct todr_chip_handle tch;
76 	u_char val;
77 
78 	scr2 = (u_int *)IIOV(NEXT_P_SCR2);
79 	val = rtc_read(RTC_STATUS);
80 	new_clock = (val & RTC_NEW_CLOCK) ? 1 : 0;
81 
82 	printf("Looks like a %s clock chip.\n",
83 			(new_clock?
84 					"MCS1850 (new style)":
85 					"MC68HC68T1 (old style)"));
86 
87 #ifdef RTC_DEBUG
88 	rtc_print();
89 #endif
90 
91 	if (new_clock) {
92 		tch.todr_gettime = gettime_new;
93 		tch.todr_settime = settime_new;
94 		tch.todr_gettime_ymdhms = NULL;
95 		tch.todr_settime_ymdhms = NULL;
96 	} else {
97 		tch.todr_gettime_ymdhms = gettime_old;
98 		tch.todr_settime_ymdhms = settime_old;
99 		tch.todr_gettime = NULL;
100 		tch.todr_settime = NULL;
101 	}
102 	tch.todr_setwen = NULL;
103 
104 	todr_attach(&tch);
105 }
106 
107 void
108 rtc_print(void)
109 {
110 
111 #define RTC_PRINT(x)	printf("\t%16s= 0x%02x\n",#x, rtc_read(x))
112 
113 	if (new_clock) {
114 		RTC_PRINT(RTC_RAM);
115 		RTC_PRINT(RTC_CNTR0);
116 		RTC_PRINT(RTC_CNTR1);
117 		RTC_PRINT(RTC_CNTR2);
118 		RTC_PRINT(RTC_CNTR3);
119 		RTC_PRINT(RTC_ALARM0);
120 		RTC_PRINT(RTC_ALARM1);
121 		RTC_PRINT(RTC_ALARM2);
122 		RTC_PRINT(RTC_ALARM3);
123 		RTC_PRINT(RTC_STATUS);
124 		RTC_PRINT(RTC_CONTROL);
125 	} else {
126 		RTC_PRINT(RTC_RAM);
127 		RTC_PRINT(RTC_SEC);
128 		RTC_PRINT(RTC_MIN);
129 		RTC_PRINT(RTC_HRS);
130 		RTC_PRINT(RTC_DAY);
131 		RTC_PRINT(RTC_DATE);
132 		RTC_PRINT(RTC_MON);
133 		RTC_PRINT(RTC_YR);
134 		RTC_PRINT(RTC_ALARM_SEC);
135 		RTC_PRINT(RTC_ALARM_MIN);
136 		RTC_PRINT(RTC_ALARM_HR);
137 		RTC_PRINT(RTC_STATUS);
138 		RTC_PRINT(RTC_CONTROL);
139 		RTC_PRINT(RTC_INTRCTL);
140 	}
141 }
142 
143 
144 u_char
145 rtc_read(u_char reg)
146 {
147 	int i;
148 	u_int tmp;
149 	u_char val;
150 
151 	*scr2 = (*scr2 & ~(SCR2_RTDATA | SCR2_RTCLK)) | SCR2_RTCE;
152 	DELAY(1);
153 
154 	val = reg;
155 	for (i=0; i<8; i++) {
156 		tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
157 		if (val & 0x80)
158 			tmp |= SCR2_RTDATA;
159 
160 		*scr2 = tmp;
161 		DELAY(1);
162 		*scr2 = tmp | SCR2_RTCLK;
163 		DELAY(1);
164 		*scr2 = tmp;
165 		DELAY(1);
166 
167 		val <<= 1;
168 	}
169 
170 	val = 0;			/* should be anyway */
171 	for (i=0; i<8; i++) {
172 		val <<= 1;
173 
174 		tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
175 
176 		*scr2 = tmp | SCR2_RTCLK;
177 		DELAY(1);
178 		*scr2 = tmp;
179 		DELAY(1);
180 
181 		if (*scr2 & SCR2_RTDATA)
182 			val |= 1;
183 	}
184 
185 	*scr2 &= ~(SCR2_RTDATA|SCR2_RTCLK|SCR2_RTCE);
186 	DELAY(1);
187 
188 	return val;
189 }
190 
191 void
192 rtc_write(u_char reg, u_char v)
193 {
194 	int i;
195 	u_int tmp;
196 	u_char val;
197 
198 	*scr2 = (*scr2 & ~(SCR2_RTDATA | SCR2_RTCLK)) | SCR2_RTCE;
199 	DELAY(1);
200 
201 	val = reg|RTC_WRITE;
202 
203 	for (i=0; i<8; i++) {
204 		tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
205 		if (val & 0x80)
206 			tmp |= SCR2_RTDATA;
207 
208 		*scr2 = tmp;
209 		DELAY(1);
210 		*scr2 = tmp | SCR2_RTCLK;
211 		DELAY(1);
212 		*scr2 = tmp;
213 		DELAY(1);
214 
215 		val <<= 1;
216 	}
217 
218 	DELAY(1);
219 
220 	for (i=0; i<8; i++) {
221 		tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK);
222 		if (v & 0x80)
223 			tmp |= SCR2_RTDATA;
224 
225 		*scr2 = tmp;
226 		DELAY(1);
227 		*scr2 = tmp | SCR2_RTCLK;
228 		DELAY(1);
229 		*scr2 = tmp;
230 		DELAY(1);
231 
232 		v <<= 1;
233 	}
234 
235 	*scr2 &= ~(SCR2_RTDATA|SCR2_RTCLK|SCR2_RTCE);
236 	DELAY(1);
237 }
238 
239 void
240 poweroff(void)
241 {
242 	int reg, t;
243 
244 	if(new_clock) {
245 		reg = RTC_CNTR3;
246 	} else {
247 		reg = RTC_CNTR0;
248 	}
249 
250 	t = rtc_read(reg);	/* seconds */
251 	/* wait for clock to tick */
252 	while(t == rtc_read(reg));
253 
254 	DELAY(850000);	/* hardware bug workaround ? */
255 
256 	if(new_clock) {
257 		reg = RTC_CONTROL;
258 	} else {
259 		reg = RTC_INTRCTL;
260 	}
261 
262 	rtc_write(reg, rtc_read(reg)|(RTC_PDOWN));
263 
264 	printf("....................."); /* @@@ work around some sort of bug. */
265 
266 	panic("Failed to poweroff!");
267 }
268 
269 
270 int
271 gettime_old(todr_chip_handle_t tch, struct clock_ymdhms *dt)
272 {
273 	u_char h, y;
274 
275 	struct clock_ymdhms val;
276 	y = FROMBCD(rtc_read(RTC_YR));
277 	if (y >= 69) {
278 		dt->dt_year = 1900+y;
279 	} else {
280 		dt->dt_year = 2000+y;
281 	}
282 
283 	dt->dt_mon	= FROMBCD(rtc_read(RTC_MON)&0x1f);
284 	dt->dt_day	= FROMBCD(rtc_read(RTC_DATE)&0x3f);
285 	dt->dt_wday = FROMBCD(rtc_read(RTC_DAY)&0x7);
286 
287 	h = rtc_read(RTC_HRS);
288 	if (h & 0x80) {			/* time is am/pm format */
289 		dt->dt_hour = FROMBCD(h&0x1f);
290 		if (h & 0x20) { /* pm */
291 			if (dt->dt_hour < 12) dt->dt_hour += 12;
292 		} else {  /* am */
293 			if (dt->dt_hour == 12) dt->dt_hour = 0;
294 		}
295 	} else {	/* time is 24 hour format */
296 		val.dt_hour = FROMBCD(h & 0x3f);
297 	}
298 
299 	dt->dt_min	= FROMBCD(rtc_read(RTC_MIN)&0x7f);
300 	dt->dt_sec	= FROMBCD(rtc_read(RTC_SEC)&0x7f);
301 
302 	return 0;
303 }
304 
305 int
306 settime_old(todr_chip_handle_t tcr, struct clock_ymdhms *dt)
307 {
308 	u_char h;
309 
310 	/* Stop the clock */
311 	rtc_write(RTC_CONTROL,rtc_read(RTC_CONTROL) & ~RTC_START);
312 
313 #ifdef RTC_DEBUG
314 	printf("Setting RTC to 0x%08x.  Regs before:\n",secs);
315 	rtc_print();
316 #endif
317 
318 	rtc_write(RTC_SEC,TOBCD(dt->dt_sec));
319 	rtc_write(RTC_MIN,TOBCD(dt->dt_min));
320 	h = rtc_read(RTC_HRS);
321 	if (h & 0x80) {		/* time is am/pm format */
322 		if (dt->dt_hour == 0) {
323 			rtc_write(RTC_HRS,TOBCD(12)|0x80);
324 		} else if (dt->dt_hour < 12) {	/* am */
325 			rtc_write(RTC_HRS,TOBCD(dt->dt_hour)|0x80);
326 		} else if (dt->dt_hour == 12) {
327 				rtc_write(RTC_HRS,TOBCD(12)|0x80|0x20);
328 		} else 		/* pm */
329 			rtc_write(RTC_HRS,TOBCD(dt->dt_hour-12)|0x80|0x20);
330 	} else {	/* time is 24 hour format */
331 			rtc_write(RTC_HRS,TOBCD(dt->dt_hour));
332 	}
333 	rtc_write(RTC_DAY,TOBCD(dt->dt_wday));
334 	rtc_write(RTC_DATE,TOBCD(dt->dt_day));
335 	rtc_write(RTC_MON,TOBCD(dt->dt_mon));
336 	rtc_write(RTC_YR,TOBCD(dt->dt_year%100));
337 
338 #ifdef RTC_DEBUG
339 	printf("Regs after:\n",secs);
340 	rtc_print();
341 #endif
342 
343 	/* restart the clock */
344 	rtc_write(RTC_CONTROL,rtc_read(RTC_CONTROL) | RTC_START);
345 	return 0;
346 }
347 
348 int
349 gettime_new(todr_chip_handle_t tch, struct timeval *tvp)
350 {
351 	tvp->tv_sec = rtc_read(RTC_CNTR0) << 24 |
352 			rtc_read(RTC_CNTR1) << 16 |
353 			rtc_read(RTC_CNTR2) << 8	 |
354 			rtc_read(RTC_CNTR3);
355 	return 0;
356 }
357 
358 int
359 settime_new(todr_chip_handle_t tch, struct timeval *tvp)
360 {
361 
362 	/* Stop the clock */
363 	rtc_write(RTC_CONTROL,rtc_read(RTC_CONTROL) & ~RTC_START);
364 
365 #ifdef RTC_DEBUG
366 	printf("Setting RTC to 0x%08x.  Regs before:\n",tvp->tv_sec);
367 	rtc_print();
368 #endif
369 
370 	rtc_write(RTC_CNTR0, (tvp->tv_sec >> 24) & 0xff);
371 	rtc_write(RTC_CNTR1, (tvp->tv_sec >> 16) & 0xff);
372 	rtc_write(RTC_CNTR2, (tvp->tv_sec >> 8) & 0xff);
373 	rtc_write(RTC_CNTR3, (tvp->tv_sec) & 0xff);
374 
375 #ifdef RTC_DEBUG
376 	printf("Regs after:\n",secs);
377 	rtc_print();
378 #endif
379 
380 	/* restart the clock */
381 	rtc_write(RTC_CONTROL,rtc_read(RTC_CONTROL) | RTC_START);
382 
383 	return 0;
384 }
385