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