xref: /illumos-gate/usr/src/uts/sun4u/io/todm5819p_rmc.c (revision 6d3b960aa395b3d268922a1938aed248a213725b)
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 
27 /*
28  * tod driver module for ALI M5819P part
29  */
30 
31 #include <sys/types.h>
32 #include <sys/conf.h>
33 #include <sys/kmem.h>
34 #include <sys/open.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 
38 #include <sys/todm5819p.h>
39 #include <sys/rmc_comm_dp.h>
40 #include <sys/rmc_comm_drvintf.h>
41 #include <sys/modctl.h>
42 #include <sys/stat.h>
43 #include <sys/clock.h>
44 #include <sys/reboot.h>
45 #include <sys/machsystm.h>
46 
47 static timestruc_t	todm5819p_rmc_get(void);
48 static void		todm5819p_rmc_set(timestruc_t);
49 static uint_t		todm5819p_rmc_set_watchdog_timer(uint_t);
50 static uint_t		todm5819p_rmc_clear_watchdog_timer(void);
51 static void		todm5819p_rmc_set_power_alarm(timestruc_t);
52 static void		todm5819p_rmc_clear_power_alarm(void);
53 static uint64_t		todm5819p_rmc_get_cpufrequency(void);
54 
55 extern uint64_t		find_cpufrequency(volatile uint8_t *);
56 
57 /*
58  * External variables
59  */
60 extern int	watchdog_enable;
61 extern int	watchdog_available;
62 extern int	boothowto;
63 
64 /*
65  * Global variables
66  */
67 int m5819p_debug_flags;
68 
69 /*
70  * Module linkage information for the kernel.
71  */
72 static struct modlmisc modlmisc = {
73 	&mod_miscops, "tod module for ALI M5819P"
74 };
75 
76 static struct modlinkage modlinkage = {
77 	MODREV_1, (void *)&modlmisc, NULL
78 };
79 
80 static todinfo_t rtc_to_tod(struct rtc_t *);
81 static void read_rtc(struct rtc_t *);
82 static void write_rtc_time(struct rtc_t *);
83 static void write_rtc_alarm(struct rtc_t *);
84 
85 
86 int
87 _init(void)
88 {
89 	if (strcmp(tod_module_name, "todm5819p_rmc") == 0) {
90 		M5819P_ADDR_REG = RTC_B;
91 		M5819P_DATA_REG = (RTC_DM | RTC_HM);
92 
93 		tod_ops.tod_get = todm5819p_rmc_get;
94 		tod_ops.tod_set = todm5819p_rmc_set;
95 
96 		tod_ops.tod_set_watchdog_timer =
97 		    todm5819p_rmc_set_watchdog_timer;
98 		tod_ops.tod_clear_watchdog_timer =
99 		    todm5819p_rmc_clear_watchdog_timer;
100 		tod_ops.tod_set_power_alarm = todm5819p_rmc_set_power_alarm;
101 		tod_ops.tod_clear_power_alarm = todm5819p_rmc_clear_power_alarm;
102 		tod_ops.tod_get_cpufrequency = todm5819p_rmc_get_cpufrequency;
103 		if (boothowto & RB_DEBUG) {
104 			cmn_err(CE_WARN, "todm5819p_rmc: kernel debugger "
105 			    "detected: hardware watchdog disabled");
106 		}
107 	}
108 
109 	return (mod_install(&modlinkage));
110 }
111 
112 int
113 _fini(void)
114 {
115 	if (strcmp(tod_module_name, "todm5819p_rmc") == 0)
116 		return (EBUSY);
117 
118 	return (mod_remove(&modlinkage));
119 }
120 
121 /*
122  * The loadable-module _info(9E) entry point
123  */
124 int
125 _info(struct modinfo *modinfop)
126 {
127 	return (mod_info(&modlinkage, modinfop));
128 }
129 
130 
131 /*
132  * Read the current time from the clock chip and convert to UNIX form.
133  * Assumes that the year in the clock chip is valid.
134  * Must be called with tod_lock held.
135  */
136 static timestruc_t
137 todm5819p_rmc_get(void)
138 {
139 	int i;
140 	int s;
141 	timestruc_t ts;
142 	struct rtc_t rtc;
143 
144 	ASSERT(MUTEX_HELD(&tod_lock));
145 
146 	/* set the hw watchdog timer if it's been activated */
147 	if (watchdog_activated) {
148 		int ret = 0;
149 		ret = tod_ops.tod_set_watchdog_timer(0);
150 		/*
151 		 * The empty set_watchdog routine returns a 0. So if a
152 		 * coded routine fails we will look for a -1 for a failure.
153 		 */
154 		if (ret == -1)
155 			cmn_err(CE_WARN, "todm5819p: failed to set hardware "
156 			    "watchdog timer.");
157 	}
158 
159 	/*
160 	 * Read current time from the tod. If the tod isn't accessible, wait and
161 	 * retry.
162 	 * Run critical in the time critical section to avoid being interrupted
163 	 */
164 	for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
165 		s = ddi_enter_critical();
166 		M5819P_ADDR_REG = RTC_A;
167 		if (!(M5819P_DATA_REG & RTC_UIP)) {
168 			read_rtc(&rtc);
169 			ddi_exit_critical(s);
170 			break;
171 		}
172 		ddi_exit_critical(s);
173 		drv_usecwait(TODM5819_UIP_WAIT_USEC);
174 	}
175 	if (i == TODM5819_UIP_RETRY_THRESH) {
176 		/*
177 		 * tod is inaccessible: just return current software time
178 		 */
179 		tod_status_set(TOD_GET_FAILED);
180 		return (hrestime);
181 	}
182 
183 	/* read was successful so ensure failure flag is clear */
184 	tod_status_clear(TOD_GET_FAILED);
185 
186 	ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc));
187 	ts.tv_nsec = 0;
188 	return (ts);
189 }
190 
191 static todinfo_t
192 rtc_to_tod(struct rtc_t *rtc)
193 {
194 	todinfo_t tod;
195 
196 	/*
197 	 * tod_year is base 1900 so this code needs to adjust the true year
198 	 * retrieved from the rtc's century and year fields.
199 	 */
200 	tod.tod_year	= rtc->rtc_year + (rtc->rtc_century * 100) - 1900;
201 	tod.tod_month	= rtc->rtc_mon;
202 	tod.tod_day	= rtc->rtc_dom;
203 	tod.tod_dow	= rtc->rtc_dow;
204 	tod.tod_hour	= rtc->rtc_hrs;
205 	tod.tod_min	= rtc->rtc_min;
206 	tod.tod_sec	= rtc->rtc_sec;
207 
208 	return (tod);
209 }
210 
211 static void
212 read_rtc(struct rtc_t *rtc)
213 {
214 	M5819P_ADDR_REG = RTC_SEC;
215 	rtc->rtc_sec = M5819P_DATA_REG;
216 	M5819P_ADDR_REG = RTC_ASEC;
217 	rtc->rtc_asec = M5819P_DATA_REG;
218 	M5819P_ADDR_REG = RTC_MIN;
219 	rtc->rtc_min = M5819P_DATA_REG;
220 	M5819P_ADDR_REG = RTC_AMIN;
221 	rtc->rtc_amin = M5819P_DATA_REG;
222 	M5819P_ADDR_REG = RTC_HRS;
223 	rtc->rtc_hrs = M5819P_DATA_REG;
224 	M5819P_ADDR_REG = RTC_AHRS;
225 	rtc->rtc_ahrs = M5819P_DATA_REG;
226 	M5819P_ADDR_REG = RTC_DOW;
227 	rtc->rtc_dow = M5819P_DATA_REG;
228 	M5819P_ADDR_REG = RTC_DOM;
229 	rtc->rtc_dom = M5819P_DATA_REG;
230 	M5819P_ADDR_REG = RTC_MON;
231 	rtc->rtc_mon = M5819P_DATA_REG;
232 	M5819P_ADDR_REG = RTC_YEAR;
233 	rtc->rtc_year = M5819P_DATA_REG;
234 	M5819P_ADDR_REG = RTC_CENTURY;
235 	rtc->rtc_century = M5819P_DATA_REG;
236 
237 	/* Read date alarm */
238 	M5819P_ADDR_REG = RTC_ADOM_REG;
239 	rtc->rtc_adom = (M5819P_DATA_REG) & RTC_ADOM;
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 todm5819p_rmc_set(timestruc_t ts)
248 {
249 	struct rtc_t	rtc;
250 	todinfo_t tod = utc_to_tod(ts.tv_sec);
251 	int year;
252 	rmc_comm_msg_t request;
253 	dp_set_date_time_t set_time_msg;
254 
255 	ASSERT(MUTEX_HELD(&tod_lock));
256 
257 	/* tod_year is base 1900 so this code needs to adjust */
258 	year = 1900 + tod.tod_year;
259 	rtc.rtc_year	= year % 100;
260 	rtc.rtc_century = year / 100;
261 	rtc.rtc_mon	= (uint8_t)tod.tod_month;
262 	rtc.rtc_dom	= (uint8_t)tod.tod_day;
263 	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
264 	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
265 	rtc.rtc_min	= (uint8_t)tod.tod_min;
266 	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
267 
268 	write_rtc_time(&rtc);
269 
270 	set_time_msg.year	= year - 1900;
271 	set_time_msg.month	= tod.tod_month - 1;
272 	set_time_msg.day	= tod.tod_day;
273 	set_time_msg.hour	= tod.tod_hour;
274 	set_time_msg.minute	= tod.tod_min;
275 	set_time_msg.second	= tod.tod_sec;
276 
277 	request.msg_type = DP_SET_DATE_TIME;
278 	request.msg_len = sizeof (set_time_msg);
279 	request.msg_buf = (caddr_t)&set_time_msg;
280 
281 	(void) rmc_comm_request_nowait(&request, 0);
282 }
283 
284 void
285 write_rtc_time(struct rtc_t *rtc)
286 {
287 	uint8_t	regb;
288 	int	i;
289 
290 	/*
291 	 * Freeze
292 	 */
293 	M5819P_ADDR_REG = RTC_B;
294 	regb = M5819P_DATA_REG;
295 	M5819P_DATA_REG = (regb | RTC_SET);
296 
297 	/*
298 	 * If an update cycle is in progress wait for the UIP flag to
299 	 * clear.  If we write whilst UIP is still set there is a slight
300 	 * but real possibility of corrupting the RTC date and time
301 	 * registers.
302 	 *
303 	 * The expected wait is one internal cycle of the chip.  We could
304 	 * simply spin but this may hang a CPU if we were to have a broken
305 	 * RTC chip where UIP is stuck, so we use a retry loop instead.
306 	 * No critical section is needed here as the UIP flag will not be
307 	 * re-asserted until we clear RTC_SET.
308 	 */
309 	M5819P_ADDR_REG = RTC_A;
310 	for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
311 		if (!(M5819P_DATA_REG & RTC_UIP)) {
312 			break;
313 		}
314 		drv_usecwait(TODM5819_UIP_WAIT_USEC);
315 	}
316 	if (i < TODM5819_UIP_RETRY_THRESH) {
317 		M5819P_ADDR_REG = RTC_SEC;
318 		M5819P_DATA_REG = rtc->rtc_sec;
319 		M5819P_ADDR_REG = RTC_MIN;
320 		M5819P_DATA_REG = rtc->rtc_min;
321 		M5819P_ADDR_REG = RTC_HRS;
322 		M5819P_DATA_REG = rtc->rtc_hrs;
323 		M5819P_ADDR_REG = RTC_DOW;
324 		M5819P_DATA_REG = rtc->rtc_dow;
325 		M5819P_ADDR_REG = RTC_DOM;
326 		M5819P_DATA_REG = rtc->rtc_dom;
327 		M5819P_ADDR_REG = RTC_MON;
328 		M5819P_DATA_REG = rtc->rtc_mon;
329 		M5819P_ADDR_REG = RTC_YEAR;
330 		M5819P_DATA_REG = rtc->rtc_year;
331 		M5819P_ADDR_REG = RTC_CENTURY;
332 		M5819P_DATA_REG = rtc->rtc_century;
333 	} else {
334 		cmn_err(CE_WARN, "todm5819p_rmc: Could not write the RTC\n");
335 	}
336 
337 	/*
338 	 * Unfreeze
339 	 */
340 	M5819P_ADDR_REG = RTC_B;
341 	M5819P_DATA_REG = regb;
342 }
343 
344 void
345 write_rtc_alarm(struct rtc_t *rtc)
346 {
347 	M5819P_ADDR_REG = RTC_ASEC;
348 	M5819P_DATA_REG = rtc->rtc_asec;
349 	M5819P_ADDR_REG = RTC_AMIN;
350 	M5819P_DATA_REG = rtc->rtc_amin;
351 	M5819P_ADDR_REG = RTC_AHRS;
352 	M5819P_DATA_REG = rtc->rtc_ahrs;
353 
354 	M5819P_ADDR_REG = RTC_ADOM_REG;
355 	M5819P_DATA_REG = rtc->rtc_adom;
356 }
357 
358 /*
359  * program the rtc registers for alarm to go off at the specified time
360  */
361 static void
362 todm5819p_rmc_set_power_alarm(timestruc_t ts)
363 {
364 	todinfo_t	tod;
365 	uint8_t		regb;
366 	struct rtc_t	rtc;
367 
368 	ASSERT(MUTEX_HELD(&tod_lock));
369 	tod = utc_to_tod(ts.tv_sec);
370 
371 	/*
372 	 * disable alarms and clear AF flag by reading reg C
373 	 */
374 	M5819P_ADDR_REG = RTC_B;
375 	regb = M5819P_DATA_REG;
376 	M5819P_DATA_REG = regb & ~RTC_AIE;
377 	M5819P_ADDR_REG = RTC_C;
378 	(void) M5819P_DATA_REG;
379 
380 	rtc.rtc_asec = (uint8_t)tod.tod_sec;
381 	rtc.rtc_amin = (uint8_t)tod.tod_min;
382 	rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
383 	rtc.rtc_adom = (uint8_t)tod.tod_day;
384 
385 	/*
386 	 * Write alarm values and enable alarm
387 	 */
388 	write_rtc_alarm(&rtc);
389 
390 	M5819P_ADDR_REG = RTC_B;
391 	M5819P_DATA_REG = regb | RTC_AIE;
392 }
393 
394 /*
395  * clear alarm interrupt
396  */
397 static void
398 todm5819p_rmc_clear_power_alarm(void)
399 {
400 	uint8_t regb;
401 
402 	ASSERT(MUTEX_HELD(&tod_lock));
403 
404 	M5819P_ADDR_REG = RTC_B;
405 	regb = M5819P_DATA_REG;
406 	M5819P_DATA_REG = regb & ~RTC_AIE;
407 }
408 
409 /*
410  * Determine the cpu frequency by watching the TOD chip rollover twice.
411  * Cpu clock rate is determined by computing the ticks added (in tick register)
412  * during one second interval on TOD.
413  */
414 uint64_t
415 todm5819p_rmc_get_cpufrequency(void)
416 {
417 	ASSERT(MUTEX_HELD(&tod_lock));
418 	M5819P_ADDR_REG = RTC_SEC;
419 	return (find_cpufrequency(v_rtc_data_reg));
420 }
421 
422 /*ARGSUSED*/
423 static uint_t
424 todm5819p_rmc_set_watchdog_timer(uint_t timeoutval)
425 {
426 	ASSERT(MUTEX_HELD(&tod_lock));
427 	return (0);
428 }
429 
430 static uint_t
431 todm5819p_rmc_clear_watchdog_timer(void)
432 {
433 	ASSERT(MUTEX_HELD(&tod_lock));
434 	return (0);
435 }
436