xref: /illumos-gate/usr/src/uts/sun4u/io/todm5819.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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  */
31 
32 #include <sys/types.h>
33 #include <sys/conf.h>
34 #include <sys/kmem.h>
35 #include <sys/open.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 
39 #include <sys/todm5819.h>
40 #include <sys/modctl.h>
41 #include <sys/stat.h>
42 #include <sys/clock.h>
43 #include <sys/reboot.h>
44 #include <sys/machsystm.h>
45 #include <sys/poll.h>
46 #include <sys/pbio.h>
47 
48 static timestruc_t	todm5819_get(void);
49 static void		todm5819_set(timestruc_t);
50 static uint_t		todm5819_set_watchdog_timer(uint_t);
51 static uint_t		todm5819_clear_watchdog_timer(void);
52 static void		todm5819_set_power_alarm(timestruc_t);
53 static void		todm5819_clear_power_alarm(void);
54 static uint64_t		todm5819_get_cpufrequency(void);
55 
56 extern uint64_t		find_cpufrequency(volatile uint8_t *);
57 
58 /*
59  * External variables
60  */
61 extern int		watchdog_enable;
62 extern int		watchdog_available;
63 extern int		boothowto;
64 
65 /*
66  * Global variables
67  */
68 int m5819_debug_flags;
69 
70 static todinfo_t	rtc_to_tod(struct rtc_t *);
71 static uint_t		read_rtc(struct rtc_t *);
72 static void		write_rtc_time(struct rtc_t *);
73 static void		write_rtc_alarm(struct rtc_t *);
74 
75 
76 static struct modlmisc modlmisc = {
77 	&mod_miscops, "tod module for ALI M5819",
78 };
79 
80 static struct modlinkage modlinkage = {
81 	MODREV_1, &modlmisc, NULL
82 };
83 
84 
85 int
86 _init(void)
87 {
88 	if (strcmp(tod_module_name, "todm5819") == 0 ||
89 		strcmp(tod_module_name, "m5819") == 0) {
90 		RTC_PUT8(RTC_B, (RTC_DM | RTC_HM));
91 
92 		tod_ops.tod_get = todm5819_get;
93 		tod_ops.tod_set = todm5819_set;
94 		tod_ops.tod_set_watchdog_timer =
95 			todm5819_set_watchdog_timer;
96 		tod_ops.tod_clear_watchdog_timer =
97 			todm5819_clear_watchdog_timer;
98 		tod_ops.tod_set_power_alarm = todm5819_set_power_alarm;
99 		tod_ops.tod_clear_power_alarm = todm5819_clear_power_alarm;
100 		tod_ops.tod_get_cpufrequency = todm5819_get_cpufrequency;
101 
102 		/*
103 		 * check if hardware watchdog timer is available and user
104 		 * enabled it.
105 		 */
106 		if (watchdog_enable) {
107 			if (!watchdog_available) {
108 				cmn_err(CE_WARN, "m5819: Hardware watchdog "
109 				    "unavailable");
110 			} else if (boothowto & RB_DEBUG) {
111 				cmn_err(CE_WARN, "m5819: Hardware watchdog "
112 				    "disabled [debugger]");
113 			}
114 		}
115 	}
116 
117 	return (mod_install(&modlinkage));
118 }
119 
120 int
121 _fini(void)
122 {
123 	if (strcmp(tod_module_name, "m5819") == 0 ||
124 		strcmp(tod_module_name, "todm5819") == 0) {
125 		return (EBUSY);
126 	} else {
127 		return (mod_remove(&modlinkage));
128 	}
129 }
130 
131 /*
132  * The loadable-module _info(9E) entry point
133  */
134 int
135 _info(struct modinfo *modinfop)
136 {
137 	return (mod_info(&modlinkage, modinfop));
138 }
139 
140 
141 /*
142  * Read the current time from the clock chip and convert to UNIX form.
143  * Assumes that the year in the clock chip is valid.
144  * Must be called with tod_lock held.
145  */
146 static timestruc_t
147 todm5819_get(void)
148 {
149 	int i;
150 	timestruc_t ts;
151 	struct rtc_t rtc;
152 
153 	ASSERT(MUTEX_HELD(&tod_lock));
154 
155 	/*
156 	 * Read from the tod, and if it isnt accessible wait
157 	 * before retrying.
158 	 */
159 	for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
160 		if (read_rtc(&rtc))
161 			break;
162 		drv_usecwait(TODM5819_UIP_WAIT_USEC);
163 	}
164 	if (i == TODM5819_UIP_RETRY_THRESH) {
165 		/*
166 		 * We couldnt read from the tod
167 		 */
168 		tod_fault_reset();
169 		return (hrestime);
170 	}
171 
172 	DPRINTF("todm5819_get: century=%d year=%d dom=%d hrs=%d\n",
173 		rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
174 
175 	ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc));
176 	ts.tv_nsec = 0;
177 	return (ts);
178 }
179 
180 static todinfo_t
181 rtc_to_tod(struct rtc_t *rtc)
182 {
183 	todinfo_t tod;
184 
185 	/*
186 	 * tod_year is base 1900 so this code needs to adjust the true
187 	 * year retrieved from the rtc's century and year fields.
188 	 */
189 	tod.tod_year	= rtc->rtc_year + (rtc->rtc_century * 100) - 1900;
190 	tod.tod_month	= rtc->rtc_mon;
191 	tod.tod_day	= rtc->rtc_dom;
192 	tod.tod_dow	= rtc->rtc_dow;
193 	tod.tod_hour	= rtc->rtc_hrs;
194 	tod.tod_min	= rtc->rtc_min;
195 	tod.tod_sec	= rtc->rtc_sec;
196 
197 	return (tod);
198 }
199 
200 uint_t
201 read_rtc(struct rtc_t *rtc)
202 {
203 	int s;
204 	uint_t rtc_readable = 0;
205 
206 	s = splhi();
207 	/*
208 	 * If UIP bit is not set we have at least 274us
209 	 * to read the values. Otherwise we have up to
210 	 * 336us to wait before we can read it
211 	 */
212 	if (!(RTC_GET8(RTC_A) & RTC_UIP)) {
213 		rtc_readable = 1;
214 
215 		rtc->rtc_sec = RTC_GET8(RTC_SEC);
216 		rtc->rtc_asec = RTC_GET8(RTC_ASEC);
217 		rtc->rtc_min = RTC_GET8(RTC_MIN);
218 		rtc->rtc_amin = RTC_GET8(RTC_AMIN);
219 
220 		rtc->rtc_hrs = RTC_GET8(RTC_HRS);
221 		rtc->rtc_ahrs = RTC_GET8(RTC_AHRS);
222 		rtc->rtc_dow = RTC_GET8(RTC_DOW);
223 		rtc->rtc_dom = RTC_GET8(RTC_DOM);
224 		rtc->rtc_adom = RTC_GET8(RTC_D) & 0x3f;
225 
226 		rtc->rtc_mon = RTC_GET8(RTC_MON);
227 		rtc->rtc_year = RTC_GET8(RTC_YEAR);
228 		rtc->rtc_century = RTC_GET8(RTC_CENTURY);
229 		rtc->rtc_amon = 0;
230 
231 		/* Clear wakeup data */
232 		rtc->apc_wdwr = 0;
233 		rtc->apc_wdmr = 0;
234 		rtc->apc_wmr = 0;
235 		rtc->apc_wyr = 0;
236 		rtc->apc_wcr = 0;
237 	}
238 	splx(s);
239 	return (rtc_readable);
240 }
241 
242 /*
243  * Write the specified time into the clock chip.
244  * Must be called with tod_lock held.
245  */
246 static void
247 todm5819_set(timestruc_t ts)
248 {
249 	struct rtc_t	rtc;
250 	todinfo_t tod = utc_to_tod(ts.tv_sec);
251 	int year;
252 
253 	ASSERT(MUTEX_HELD(&tod_lock));
254 
255 	/* tod_year is base 1900 so this code needs to adjust */
256 	year = 1900 + tod.tod_year;
257 	rtc.rtc_year	= year % 100;
258 	rtc.rtc_century = year / 100;
259 	rtc.rtc_mon	= (uint8_t)tod.tod_month;
260 	rtc.rtc_dom	= (uint8_t)tod.tod_day;
261 	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
262 	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
263 	rtc.rtc_min	= (uint8_t)tod.tod_min;
264 	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
265 	DPRINTF("todm5819_set: century=%d year=%d dom=%d hrs=%d\n",
266 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
267 
268 	write_rtc_time(&rtc);
269 }
270 
271 void
272 write_rtc_time(struct rtc_t *rtc)
273 {
274 	uint8_t	regb;
275 
276 	/*
277 	 * Freeze
278 	 */
279 	regb = RTC_GET8(RTC_B);
280 	RTC_PUT8(RTC_B, (regb | RTC_SET));
281 
282 	RTC_PUT8(RTC_SEC, (rtc->rtc_sec));
283 	RTC_PUT8(RTC_ASEC, (rtc->rtc_asec));
284 	RTC_PUT8(RTC_MIN, (rtc->rtc_min));
285 	RTC_PUT8(RTC_AMIN, (rtc->rtc_amin));
286 
287 	RTC_PUT8(RTC_HRS, (rtc->rtc_hrs));
288 	RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs));
289 	RTC_PUT8(RTC_DOW, (rtc->rtc_dow));
290 	RTC_PUT8(RTC_DOM, (rtc->rtc_dom));
291 
292 	RTC_PUT8(RTC_MON, (rtc->rtc_mon));
293 	RTC_PUT8(RTC_YEAR, (rtc->rtc_year));
294 	RTC_PUT8(RTC_CENTURY, (rtc->rtc_century));
295 
296 	/*
297 	 * Unfreeze
298 	 */
299 	RTC_PUT8(RTC_B, regb);
300 }
301 
302 
303 void
304 write_rtc_alarm(struct rtc_t *rtc)
305 {
306 	RTC_PUT8(RTC_ASEC, (rtc->rtc_asec));
307 	RTC_PUT8(RTC_AMIN, (rtc->rtc_amin));
308 	RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs));
309 	RTC_PUT8(RTC_D, (rtc->rtc_adom));
310 }
311 
312 /*
313  * program the rtc registers for alarm to go off at the specified time
314  */
315 static void
316 todm5819_set_power_alarm(timestruc_t ts)
317 {
318 	todinfo_t	tod;
319 	uint8_t		regb;
320 	struct rtc_t	rtc;
321 
322 	ASSERT(MUTEX_HELD(&tod_lock));
323 	tod = utc_to_tod(ts.tv_sec);
324 
325 	/*
326 	 * disable alarms
327 	 */
328 	regb = RTC_GET8(RTC_B);
329 	RTC_PUT8(RTC_B, (regb & ~RTC_AIE));
330 
331 
332 	rtc.rtc_asec = (uint8_t)tod.tod_sec;
333 	rtc.rtc_amin = (uint8_t)tod.tod_min;
334 	rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
335 	rtc.rtc_adom = (uint8_t)tod.tod_day;
336 
337 	write_rtc_alarm(&rtc);
338 	/*
339 	 * Enable alarm.
340 	 */
341 	RTC_PUT8(RTC_B, (regb | RTC_AIE));
342 }
343 
344 /*
345  * clear alarm interrupt
346  */
347 static void
348 todm5819_clear_power_alarm(void)
349 {
350 	uint8_t regb;
351 	ASSERT(MUTEX_HELD(&tod_lock));
352 
353 	regb = RTC_GET8(RTC_B);
354 	RTC_PUT8(RTC_B, (regb & ~RTC_AIE));
355 }
356 
357 /*
358  * Determine the cpu frequency by watching the TOD chip rollover twice.
359  * Cpu clock rate is determined by computing the ticks added (in tick register)
360  * during one second interval on TOD.
361  */
362 uint64_t
363 todm5819_get_cpufrequency(void)
364 {
365 	ASSERT(MUTEX_HELD(&tod_lock));
366 	M5819_ADDR_REG = RTC_SEC;
367 	return (find_cpufrequency(v_rtc_data_reg));
368 }
369 
370 
371 /*ARGSUSED*/
372 static uint_t
373 todm5819_set_watchdog_timer(uint_t timeoutval)
374 {
375 	ASSERT(MUTEX_HELD(&tod_lock));
376 	return (0);
377 }
378 
379 static uint_t
380 todm5819_clear_watchdog_timer(void)
381 {
382 	ASSERT(MUTEX_HELD(&tod_lock));
383 	return (0);
384 }
385