xref: /titanic_41/usr/src/uts/i86pc/io/todpc_subr.c (revision 0a05e7057ae5537db2da83492d375e6524599463)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
32 /*	  All Rights Reserved	*/
33 
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 #include <sys/param.h>
37 #include <sys/time.h>
38 #include <sys/systm.h>
39 
40 #include <sys/cpuvar.h>
41 #include <sys/clock.h>
42 #include <sys/debug.h>
43 #include <sys/rtc.h>
44 #include <sys/archsystm.h>
45 #include <sys/sysmacros.h>
46 #include <sys/lockstat.h>
47 #include <sys/stat.h>
48 #include <sys/sunddi.h>
49 
50 #include <sys/acpi/acpi.h>
51 #include <sys/acpica.h>
52 
53 static int todpc_rtcget(unsigned char *buf);
54 static void todpc_rtcput(unsigned char *buf);
55 
56 #define	CLOCK_RES	1000		/* 1 microsec in nanosecs */
57 
58 int clock_res = CLOCK_RES;
59 
60 /*
61  * The minimum sleep time till an alarm can be fired.
62  * This can be tuned in /etc/system, but if the value is too small,
63  * there is a danger that it will be missed if it takes too long to
64  * get from the set point to sleep.  Or that it can fire quickly, and
65  * generate a power spike on the hardware.  And small values are
66  * probably only usefull for test setups.
67  */
68 int clock_min_alarm = 4;
69 
70 /*
71  * Machine-dependent clock routines.
72  */
73 
74 extern long gmt_lag;
75 
76 struct rtc_offset {
77 	int8_t	loaded;
78 	uint8_t	day_alrm;
79 	uint8_t mon_alrm;
80 	uint8_t	century;
81 };
82 
83 static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
84 
85 
86 /*
87  * Entry point for ACPI to pass RTC or other clock values that
88  * are useful to TOD.
89  */
90 void
91 pc_tod_set_rtc_offsets(FADT_DESCRIPTOR *fadt) {
92 	int		ok = 0;
93 
94 	/*
95 	 * ASSERT is for debugging, but we don't want the machine
96 	 * falling over because for some reason we didn't get a valid
97 	 * pointer.
98 	 */
99 	ASSERT(fadt);
100 	if (fadt == NULL) {
101 		return;
102 	}
103 
104 	if (fadt->DayAlrm) {
105 		pc_rtc_offset.day_alrm = fadt->DayAlrm;
106 		ok = 1;
107 	}
108 
109 	if (fadt->MonAlrm) {
110 		pc_rtc_offset.mon_alrm = fadt->MonAlrm;
111 		ok = 1;
112 	}
113 
114 	if (fadt->Century) {
115 		pc_rtc_offset.century = fadt->Century;
116 		ok = 1;
117 	}
118 
119 	pc_rtc_offset.loaded = ok;
120 }
121 
122 
123 /*
124  * Write the specified time into the clock chip.
125  * Must be called with tod_lock held.
126  */
127 /*ARGSUSED*/
128 static void
129 todpc_set(tod_ops_t *top, timestruc_t ts)
130 {
131 	todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl());
132 	struct rtc_t rtc;
133 
134 	ASSERT(MUTEX_HELD(&tod_lock));
135 
136 	if (todpc_rtcget((unsigned char *)&rtc))
137 		return;
138 
139 	/*
140 	 * rtc bytes are in binary-coded decimal, so we have to convert.
141 	 * We assume that we wrap the rtc year back to zero at 2000.
142 	 */
143 	/* LINTED: YRBASE = 0 for x86 */
144 	tod.tod_year -= YRBASE;
145 	if (tod.tod_year >= 100) {
146 		tod.tod_year -= 100;
147 		rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */
148 	} else
149 		rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */
150 	rtc.rtc_yr	= BYTE_TO_BCD(tod.tod_year);
151 	rtc.rtc_mon	= BYTE_TO_BCD(tod.tod_month);
152 	rtc.rtc_dom	= BYTE_TO_BCD(tod.tod_day);
153 	/* dow < 10, so no conversion */
154 	rtc.rtc_dow	= (unsigned char)tod.tod_dow;
155 	rtc.rtc_hr	= BYTE_TO_BCD(tod.tod_hour);
156 	rtc.rtc_min	= BYTE_TO_BCD(tod.tod_min);
157 	rtc.rtc_sec	= BYTE_TO_BCD(tod.tod_sec);
158 
159 	todpc_rtcput((unsigned char *)&rtc);
160 }
161 
162 /*
163  * Read the current time from the clock chip and convert to UNIX form.
164  * Assumes that the year in the clock chip is valid.
165  * Must be called with tod_lock held.
166  */
167 /*ARGSUSED*/
168 static timestruc_t
169 todpc_get(tod_ops_t *top)
170 {
171 	timestruc_t ts;
172 	todinfo_t tod;
173 	struct rtc_t rtc;
174 	int compute_century;
175 	static int century_warn = 1; /* only warn once, not each time called */
176 	static int range_warn = 1;
177 
178 	ASSERT(MUTEX_HELD(&tod_lock));
179 
180 	if (todpc_rtcget((unsigned char *)&rtc)) {
181 		ts.tv_sec = 0;
182 		ts.tv_nsec = 0;
183 		tod_fault_reset();
184 		return (ts);
185 	}
186 
187 	/* assume that we wrap the rtc year back to zero at 2000 */
188 	tod.tod_year	= BCD_TO_BYTE(rtc.rtc_yr);
189 	if (tod.tod_year < 69) {
190 		if (range_warn && tod.tod_year > 38) {
191 			cmn_err(CE_WARN, "hardware real-time clock is out "
192 			    "of range -- time needs to be reset");
193 			range_warn = 0;
194 		}
195 		tod.tod_year += 100 + YRBASE; /* 20xx year */
196 		compute_century = 20;
197 	} else {
198 		/* LINTED: YRBASE = 0 for x86 */
199 		tod.tod_year += YRBASE; /* 19xx year */
200 		compute_century = 19;
201 	}
202 	if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
203 		cmn_err(CE_NOTE,
204 		    "The hardware real-time clock appears to have the "
205 		    "wrong century: %d.\nSolaris will still operate "
206 		    "correctly, but other OS's/firmware agents may "
207 		    "not.\nUse date(1) to set the date to the current "
208 		    "time to correct the RTC.",
209 		    BCD_TO_BYTE(rtc.rtc_century));
210 		century_warn = 0;
211 	}
212 	tod.tod_month	= BCD_TO_BYTE(rtc.rtc_mon);
213 	tod.tod_day	= BCD_TO_BYTE(rtc.rtc_dom);
214 	tod.tod_dow	= rtc.rtc_dow;	/* dow < 10, so no conversion needed */
215 	tod.tod_hour	= BCD_TO_BYTE(rtc.rtc_hr);
216 	tod.tod_min	= BCD_TO_BYTE(rtc.rtc_min);
217 	tod.tod_sec	= BCD_TO_BYTE(rtc.rtc_sec);
218 
219 	ts.tv_sec = tod_to_utc(tod) + ggmtl();
220 	ts.tv_nsec = 0;
221 
222 	return (ts);
223 }
224 
225 #include <sys/promif.h>
226 /*
227  * Write the specified wakeup alarm into the clock chip.
228  * Must be called with tod_lock held.
229  */
230 void
231 /*ARGSUSED*/
232 todpc_setalarm(tod_ops_t *top, int nsecs)
233 {
234 	struct rtc_t rtc;
235 	int delta, asec, amin, ahr, adom, amon;
236 	int day_alrm = pc_rtc_offset.day_alrm;
237 	int mon_alrm = pc_rtc_offset.mon_alrm;
238 
239 	ASSERT(MUTEX_HELD(&tod_lock));
240 
241 	/* A delay of zero is not allowed */
242 	if (nsecs == 0)
243 		return;
244 
245 	/* Make sure that we delay no less than the minimum time */
246 	if (nsecs < clock_min_alarm)
247 		nsecs = clock_min_alarm;
248 
249 	if (todpc_rtcget((unsigned char *)&rtc))
250 		return;
251 
252 	/*
253 	 * Compute alarm secs, mins and hrs, and where appropriate, dom
254 	 * and mon.  rtc bytes are in binary-coded decimal, so we have
255 	 * to convert.
256 	 */
257 	delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
258 	asec = delta % 60;
259 
260 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
261 	amin = delta % 60;
262 
263 	delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
264 	ahr  = delta % 24;
265 
266 	if (day_alrm == 0 && delta >= 24) {
267 		prom_printf("No day alarm - set to end of today!\n");
268 		asec = 59;
269 		amin = 59;
270 		ahr  = 23;
271 	} else {
272 		int mon = BCD_TO_BYTE(rtc.rtc_mon);
273 		static int dpm[] =
274 		    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
275 
276 		adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
277 
278 		if (mon_alrm == 0) {
279 			if (adom > dpm[mon]) {
280 				prom_printf("No mon alarm - "
281 				    "set to end of current month!\n");
282 				asec = 59;
283 				amin = 59;
284 				ahr  = 23;
285 				adom = dpm[mon];
286 			}
287 		} else {
288 			for (amon = mon;
289 			    amon <= 12 && adom > dpm[amon]; amon++) {
290 				adom -= dpm[amon];
291 			}
292 			if (amon > 12) {
293 				prom_printf("Alarm too far in future - "
294 				    "set to end of current year!\n");
295 				asec = 59;
296 				amin = 59;
297 				ahr  = 23;
298 				adom = dpm[12];
299 				amon = 12;
300 			}
301 			rtc.rtc_amon = BYTE_TO_BCD(amon);
302 		}
303 
304 		rtc.rtc_adom = BYTE_TO_BCD(adom);
305 	}
306 
307 	rtc.rtc_asec = BYTE_TO_BCD(asec);
308 	rtc.rtc_amin = BYTE_TO_BCD(amin);
309 	rtc.rtc_ahr  = BYTE_TO_BCD(ahr);
310 
311 	rtc.rtc_statusb |= RTC_AIE;	/* Enable alarm interrupt */
312 
313 	todpc_rtcput((unsigned char *)&rtc);
314 }
315 
316 /*
317  * Clear an alarm.  This is effectively setting an alarm of 0.
318  */
319 void
320 /*ARGSUSED*/
321 todpc_clralarm(tod_ops_t *top)
322 {
323 	mutex_enter(&tod_lock);
324 	todpc_setalarm(top, 0);
325 	mutex_exit(&tod_lock);
326 }
327 
328 /*
329  * Routine to read contents of real time clock to the specified buffer.
330  * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
331  * else 0.
332  * The routine will busy wait for the Update-In-Progress flag to clear.
333  * On completion of the reads the Seconds register is re-read and the
334  * UIP flag is rechecked to confirm that an clock update did not occur
335  * during the accesses.  Routine will error exit after 256 attempts.
336  * (See bugid 1158298.)
337  * Routine returns RTC_NREG (which is 15) bytes of data, as given in the
338  * technical reference.  This data includes both time and status registers.
339  */
340 
341 static int
342 todpc_rtcget(unsigned char *buf)
343 {
344 	unsigned char	reg;
345 	int		i;
346 	int		retries = 256;
347 	unsigned char	*rawp;
348 	unsigned char	century = RTC_CENTURY;
349 	unsigned char	day_alrm;
350 	unsigned char	mon_alrm;
351 
352 	ASSERT(MUTEX_HELD(&tod_lock));
353 
354 	day_alrm = pc_rtc_offset.day_alrm;
355 	mon_alrm = pc_rtc_offset.mon_alrm;
356 	if (pc_rtc_offset.century != 0) {
357 		century = pc_rtc_offset.century;
358 	}
359 
360 	outb(RTC_ADDR, RTC_D);		/* check if clock valid */
361 	reg = inb(RTC_DATA);
362 	if ((reg & RTC_VRT) == 0)
363 		return (ENXIO);
364 
365 checkuip:
366 	if (retries-- < 0)
367 		return (EAGAIN);
368 	outb(RTC_ADDR, RTC_A);		/* check if update in progress */
369 	reg = inb(RTC_DATA);
370 	if (reg & RTC_UIP) {
371 		tenmicrosec();
372 		goto checkuip;
373 	}
374 
375 	for (i = 0, rawp = buf; i < RTC_NREG; i++) {
376 		outb(RTC_ADDR, i);
377 		*rawp++ = inb(RTC_DATA);
378 	}
379 	outb(RTC_ADDR, century); /* do century */
380 	((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
381 
382 	if (day_alrm > 0) {
383 		outb(RTC_ADDR, day_alrm);
384 		((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
385 	}
386 	if (mon_alrm > 0) {
387 		outb(RTC_ADDR, mon_alrm);
388 		((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
389 	}
390 
391 	outb(RTC_ADDR, 0);		/* re-read Seconds register */
392 	reg = inb(RTC_DATA);
393 	if (reg != ((struct rtc_t *)buf)->rtc_sec ||
394 	    (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP))
395 		/* update occured during reads */
396 		goto checkuip;
397 
398 	return (0);
399 }
400 
401 /*
402  * This routine writes the contents of the given buffer to the real time
403  * clock.  It is given RTC_NREGP bytes of data, which are the 10 bytes used
404  * to write the time and set the alarm.  It should be called with the priority
405  * raised to 5.
406  */
407 static void
408 todpc_rtcput(unsigned char *buf)
409 {
410 	unsigned char	reg;
411 	int		i;
412 	unsigned char	century = RTC_CENTURY;
413 	unsigned char	day_alrm = pc_rtc_offset.day_alrm;
414 	unsigned char	mon_alrm = pc_rtc_offset.mon_alrm;
415 
416 	if (pc_rtc_offset.century != 0) {
417 		century = pc_rtc_offset.century;
418 	}
419 
420 	outb(RTC_ADDR, RTC_B);
421 	reg = inb(RTC_DATA);
422 	outb(RTC_ADDR, RTC_B);
423 	outb(RTC_DATA, reg | RTC_SET);	/* allow time set now */
424 	for (i = 0; i < RTC_NREGP; i++) { /* set the time */
425 		outb(RTC_ADDR, i);
426 		outb(RTC_DATA, buf[i]);
427 	}
428 	outb(RTC_ADDR, century); /* do century */
429 	outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
430 
431 	if (day_alrm > 0) {
432 		outb(RTC_ADDR, day_alrm);
433 		outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_adom);
434 	}
435 	if (mon_alrm > 0) {
436 		outb(RTC_ADDR, mon_alrm);
437 		outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
438 	}
439 
440 	outb(RTC_ADDR, RTC_B);
441 	reg = inb(RTC_DATA);
442 	outb(RTC_ADDR, RTC_B);
443 	outb(RTC_DATA, reg & ~RTC_SET);	/* allow time update */
444 }
445 
446 static tod_ops_t todpc_ops = {
447 	TOD_OPS_VERSION,
448 	todpc_get,
449 	todpc_set,
450 	NULL,
451 	NULL,
452 	todpc_setalarm,
453 	todpc_clralarm,
454 	NULL
455 };
456 
457 /*
458  * Initialize for the default TOD ops vector for use on hardware.
459  */
460 
461 tod_ops_t *tod_ops = &todpc_ops;
462