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