xref: /titanic_50/usr/src/uts/sun4u/io/todblade.c (revision 3db86aab554edbb4244c8d1a1c90f152eee768af)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 #include <sys/types.h>
31 #include <sys/conf.h>
32 #include <sys/kmem.h>
33 #include <sys/open.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 
37 #include <sys/todm5819.h>
38 #include <sys/modctl.h>
39 #include <sys/stat.h>
40 #include <sys/clock.h>
41 #include <sys/reboot.h>
42 #include <sys/machsystm.h>
43 #include <sys/poll.h>
44 #include <sys/pbio.h>
45 #include <sys/lom_priv.h>
46 
47 #define	WDOG_ON 1
48 #define	WDOG_OFF 0
49 
50 static timestruc_t	todbl_get(void);
51 static void		todbl_set(timestruc_t);
52 static uint_t		todbl_set_watchdog_timer(uint_t);
53 static uint_t		todbl_clear_watchdog_timer(void);
54 static void		todbl_set_power_alarm(timestruc_t);
55 static void		todbl_clear_power_alarm(void);
56 static uint64_t		todbl_get_cpufrequency(void);
57 
58 static todinfo_t	rtc_to_tod(struct rtc_t *);
59 static uint_t		read_rtc(struct rtc_t *);
60 static void		write_rtc_time(struct rtc_t *);
61 static uint_t		configure_wdog(uint8_t new_state);
62 
63 extern uint64_t		find_cpufrequency(volatile uint8_t *);
64 
65 /*
66  * External variables
67  */
68 extern int	watchdog_enable;
69 extern int	watchdog_available;
70 extern int	watchdog_activated;
71 extern uint_t   watchdog_timeout_seconds;
72 extern int	boothowto;
73 extern void	(*bsc_drv_func_ptr)(struct bscv_idi_info *);
74 
75 /*
76  * Global variables
77  */
78 int m5819_debug_flags;
79 uint8_t wdog_reset_on_timeout = 1;
80 static clock_t last_pat_lbt;
81 
82 
83 static struct modlmisc modlmisc = {
84 	&mod_miscops, "todblade module v%I%",
85 };
86 
87 static struct modlinkage modlinkage = {
88 	MODREV_1, &modlmisc, NULL
89 };
90 
91 
92 int
93 _init(void)
94 {
95 	if (strcmp(tod_module_name, "todblade") == 0) {
96 		RTC_PUT8(RTC_B, (RTC_DM | RTC_HM));
97 
98 		tod_ops.tod_get = todbl_get;
99 		tod_ops.tod_set = todbl_set;
100 		tod_ops.tod_set_watchdog_timer =
101 			todbl_set_watchdog_timer;
102 		tod_ops.tod_clear_watchdog_timer =
103 			todbl_clear_watchdog_timer;
104 		tod_ops.tod_set_power_alarm = todbl_set_power_alarm;
105 		tod_ops.tod_clear_power_alarm = todbl_clear_power_alarm;
106 		tod_ops.tod_get_cpufrequency = todbl_get_cpufrequency;
107 
108 		if (watchdog_enable && (boothowto & RB_DEBUG)) {
109 				watchdog_available = 0;
110 				cmn_err(CE_WARN, "todblade: kernel debugger "
111 				    "detected: hardware watchdog disabled");
112 		}
113 	}
114 	return (mod_install(&modlinkage));
115 }
116 
117 int
118 _fini(void)
119 {
120 	if (strcmp(tod_module_name, "todblade") == 0) {
121 		return (EBUSY);
122 	} else {
123 		return (mod_remove(&modlinkage));
124 	}
125 }
126 
127 /*
128  * The loadable-module _info(9E) entry point
129  */
130 int
131 _info(struct modinfo *modinfop)
132 {
133 	return (mod_info(&modlinkage, modinfop));
134 }
135 
136 
137 /*
138  * Read the current time from the clock chip and convert to UNIX form.
139  * Assumes that the year in the clock chip is valid.
140  * Must be called with tod_lock held.
141  */
142 static timestruc_t
143 todbl_get(void)
144 {
145 	int i;
146 	timestruc_t ts;
147 	struct rtc_t rtc;
148 	struct bscv_idi_info bscv_info;
149 
150 	ASSERT(MUTEX_HELD(&tod_lock));
151 
152 	/*
153 	 * We must check that the value of watchdog enable hasnt changed
154 	 * as its a user knob for turning it on and off
155 	 */
156 	if (watchdog_available) {
157 		if (watchdog_activated && !watchdog_enable) {
158 			(void) configure_wdog(WDOG_OFF);
159 		} else if (!watchdog_activated && watchdog_enable) {
160 			(void) configure_wdog(WDOG_ON);
161 		} else if (watchdog_activated &&
162 			    (ddi_get_lbolt() - last_pat_lbt) >=
163 			    SEC_TO_TICK(1)) {
164 			/*
165 			 * PAT THE WATCHDOG!!
166 			 * We dont want to accelerate the pat frequency
167 			 * when userland calls to the TOD_GET_DATE ioctl
168 			 * pass through here.
169 			 */
170 			bscv_info.type = BSCV_IDI_WDOG_PAT;
171 			bscv_info.data = NULL;
172 			bscv_info.size = 0;
173 			if (bsc_drv_func_ptr != NULL) {
174 				(*bsc_drv_func_ptr)(&bscv_info);
175 				last_pat_lbt = ddi_get_lbolt();
176 			}
177 		}
178 	}
179 
180 	/*
181 	 * Read from the tod, and if it isnt accessible wait
182 	 * before retrying.
183 	 */
184 	for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
185 		if (read_rtc(&rtc))
186 			break;
187 		drv_usecwait(TODM5819_UIP_WAIT_USEC);
188 	}
189 	if (i == TODM5819_UIP_RETRY_THRESH) {
190 		/*
191 		 * We couldnt read from the tod
192 		 */
193 		tod_fault_reset();
194 		return (hrestime);
195 	}
196 
197 	DPRINTF("todbl_get: century=%d year=%d dom=%d hrs=%d\n",
198 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
199 
200 	ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc));
201 	ts.tv_nsec = 0;
202 	return (ts);
203 }
204 
205 static todinfo_t
206 rtc_to_tod(struct rtc_t *rtc)
207 {
208 	todinfo_t tod;
209 
210 	/*
211 	 * tod_year is base 1900 so this code needs to adjust the true
212 	 * year retrieved from the rtc's century and year fields.
213 	 */
214 	tod.tod_year	= rtc->rtc_year + (rtc->rtc_century * 100) - 1900;
215 	tod.tod_month	= rtc->rtc_mon;
216 	tod.tod_day	= rtc->rtc_dom;
217 	tod.tod_dow	= rtc->rtc_dow;
218 	tod.tod_hour	= rtc->rtc_hrs;
219 	tod.tod_min	= rtc->rtc_min;
220 	tod.tod_sec	= rtc->rtc_sec;
221 
222 	return (tod);
223 }
224 
225 
226 static uint_t
227 read_rtc(struct rtc_t *rtc)
228 {
229 	int s;
230 	uint_t rtc_readable = 0;
231 
232 	s = splhi();
233 	/*
234 	 * If UIP bit is not set we have at least 274us
235 	 * to read the values.
236 	 */
237 	if (!(RTC_GET8(RTC_A) & RTC_UIP)) {
238 		rtc_readable = 1;
239 
240 		rtc->rtc_sec = RTC_GET8(RTC_SEC);
241 		rtc->rtc_asec = RTC_GET8(RTC_ASEC);
242 		rtc->rtc_min = RTC_GET8(RTC_MIN);
243 		rtc->rtc_amin = RTC_GET8(RTC_AMIN);
244 
245 		rtc->rtc_hrs = RTC_GET8(RTC_HRS);
246 		rtc->rtc_ahrs = RTC_GET8(RTC_AHRS);
247 		rtc->rtc_dow = RTC_GET8(RTC_DOW);
248 		rtc->rtc_dom = RTC_GET8(RTC_DOM);
249 		rtc->rtc_adom = RTC_GET8(RTC_D) & 0x3f;
250 
251 		rtc->rtc_mon = RTC_GET8(RTC_MON);
252 		rtc->rtc_year = RTC_GET8(RTC_YEAR);
253 		rtc->rtc_century = RTC_GET8(RTC_CENTURY);
254 		rtc->rtc_amon = 0;
255 
256 		/* Clear wakeup data */
257 		rtc->apc_wdwr = 0;
258 		rtc->apc_wdmr = 0;
259 		rtc->apc_wmr = 0;
260 		rtc->apc_wyr = 0;
261 		rtc->apc_wcr = 0;
262 	}
263 
264 	splx(s);
265 	return (rtc_readable);
266 }
267 
268 /*
269  * Write the specified time into the clock chip.
270  * Must be called with tod_lock held.
271  */
272 static void
273 todbl_set(timestruc_t ts)
274 {
275 	struct rtc_t	rtc;
276 	todinfo_t tod = utc_to_tod(ts.tv_sec);
277 	struct bscv_idi_info bscv_info;
278 	int year;
279 
280 	ASSERT(MUTEX_HELD(&tod_lock));
281 
282 	/* tod_year is base 1900 so this code needs to adjust */
283 	year = 1900 + tod.tod_year;
284 	rtc.rtc_year	= year % 100;
285 	rtc.rtc_century = year / 100;
286 	rtc.rtc_mon	= (uint8_t)tod.tod_month;
287 	rtc.rtc_dom	= (uint8_t)tod.tod_day;
288 	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
289 	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
290 	rtc.rtc_min	= (uint8_t)tod.tod_min;
291 	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
292 	DPRINTF("todbl_set: century=%d year=%d dom=%d hrs=%d\n",
293 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
294 
295 	write_rtc_time(&rtc);
296 
297 	/*
298 	 * Because of a generic solaris problem where calls to stime()
299 	 * starve calls to tod_get(), we need to check to see when the
300 	 * watchdog was last patted and pat it if necessary.
301 	 */
302 	if (watchdog_activated &&
303 	    (ddi_get_lbolt() - last_pat_lbt) >= SEC_TO_TICK(1)) {
304 		/*
305 		 * Pat the watchdog!
306 		 */
307 		bscv_info.type = BSCV_IDI_WDOG_PAT;
308 		bscv_info.data = NULL;
309 		bscv_info.size = 0;
310 		if (bsc_drv_func_ptr != NULL) {
311 			(*bsc_drv_func_ptr)(&bscv_info);
312 			last_pat_lbt = ddi_get_lbolt();
313 		}
314 	}
315 }
316 
317 static void
318 write_rtc_time(struct rtc_t *rtc)
319 {
320 	uint8_t	regb;
321 
322 	/*
323 	 * Freeze
324 	 */
325 	regb = RTC_GET8(RTC_B);
326 	RTC_PUT8(RTC_B, (regb | RTC_SET));
327 
328 	RTC_PUT8(RTC_SEC, (rtc->rtc_sec));
329 	RTC_PUT8(RTC_ASEC, (rtc->rtc_asec));
330 	RTC_PUT8(RTC_MIN, (rtc->rtc_min));
331 	RTC_PUT8(RTC_AMIN, (rtc->rtc_amin));
332 
333 	RTC_PUT8(RTC_HRS, (rtc->rtc_hrs));
334 	RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs));
335 	RTC_PUT8(RTC_DOW, (rtc->rtc_dow));
336 	RTC_PUT8(RTC_DOM, (rtc->rtc_dom));
337 
338 	RTC_PUT8(RTC_MON, (rtc->rtc_mon));
339 	RTC_PUT8(RTC_YEAR, (rtc->rtc_year));
340 	RTC_PUT8(RTC_CENTURY, (rtc->rtc_century));
341 
342 	/*
343 	 * Unfreeze
344 	 */
345 	RTC_PUT8(RTC_B, regb);
346 }
347 
348 
349 
350 /*
351  * The TOD alarm functionality is not supported on our platform
352  * as the interrupt is not wired, so do nothing.
353  */
354 /*ARGSUSED*/
355 static void
356 todbl_set_power_alarm(timestruc_t ts)
357 {
358 	ASSERT(MUTEX_HELD(&tod_lock));
359 }
360 
361 /*
362  * clear alarm interrupt
363  */
364 static void
365 todbl_clear_power_alarm(void)
366 {
367 	ASSERT(MUTEX_HELD(&tod_lock));
368 }
369 
370 /*
371  * Determine the cpu frequency by watching the TOD chip rollover twice.
372  * Cpu clock rate is determined by computing the ticks added (in tick register)
373  * during one second interval on TOD.
374  */
375 uint64_t
376 todbl_get_cpufrequency(void)
377 {
378 	ASSERT(MUTEX_HELD(&tod_lock));
379 	M5819_ADDR_REG = RTC_SEC;
380 	return (find_cpufrequency(v_rtc_data_reg));
381 }
382 
383 
384 static uint_t
385 todbl_set_watchdog_timer(uint_t timeoutval)
386 {
387 	/*
388 	 * We get started during kernel intilaisation only
389 	 * if watchdog_enable is set.
390 	 */
391 	ASSERT(MUTEX_HELD(&tod_lock));
392 
393 	if (watchdog_available && (!watchdog_activated ||
394 	    (watchdog_activated && (timeoutval != watchdog_timeout_seconds)))) {
395 		watchdog_timeout_seconds = timeoutval;
396 		if (configure_wdog(WDOG_ON))
397 			return (watchdog_timeout_seconds);
398 	}
399 	return (0);
400 }
401 
402 static uint_t
403 todbl_clear_watchdog_timer(void)
404 {
405 	/*
406 	 * The core kernel will call us here to disable the wdog when:
407 	 * 1. we're panicing
408 	 * 2. we're entering debug
409 	 * 3. we're rebooting
410 	 */
411 	ASSERT(MUTEX_HELD(&tod_lock));
412 
413 	if (watchdog_available && watchdog_activated) {
414 		watchdog_enable = 0;
415 		if (!configure_wdog(WDOG_OFF))
416 			return (0);
417 	}
418 	return (watchdog_timeout_seconds);
419 }
420 
421 static uint_t
422 configure_wdog(uint8_t new_state)
423 {
424 	bscv_wdog_t wdog_cmd;
425 	struct bscv_idi_info bscv_info;
426 
427 	if (new_state == WDOG_ON || new_state == WDOG_OFF) {
428 
429 		wdog_cmd.enable_wdog = new_state;
430 		wdog_cmd.wdog_timeout_s = watchdog_timeout_seconds;
431 		wdog_cmd.reset_system_on_timeout = wdog_reset_on_timeout;
432 		bscv_info.type = BSCV_IDI_WDOG_CFG;
433 		bscv_info.data = &wdog_cmd;
434 		bscv_info.size = sizeof (wdog_cmd);
435 
436 		if (bsc_drv_func_ptr != NULL) {
437 			watchdog_activated = new_state;
438 			(*bsc_drv_func_ptr)(&bscv_info);
439 			return (1);
440 		}
441 	}
442 	return (0);
443 
444 }
445