xref: /illumos-gate/usr/src/uts/sun4u/io/todmostek.c (revision 88294e09b5c27cbb12b6735e2fb247a86b76666d)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * tod driver module for Mostek M48T59 part
28  */
29 
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/sysmacros.h>
33 #include <sys/systm.h>
34 #include <sys/errno.h>
35 #include <sys/modctl.h>
36 #include <sys/autoconf.h>
37 #include <sys/debug.h>
38 #include <sys/clock.h>
39 #include <sys/todmostek.h>
40 #include <sys/reboot.h>
41 #include <sys/cmn_err.h>
42 #include <sys/cpuvar.h>
43 
44 static timestruc_t	todm_get(void);
45 static void		todm_set(timestruc_t);
46 static uint_t		todm_set_watchdog_timer(uint_t);
47 static uint_t		todm_clear_watchdog_timer(void);
48 static void		todm_set_power_alarm(timestruc_t);
49 static void		todm_clear_power_alarm(void);
50 static uint64_t		todm_get_cpufrequency(void);
51 
52 static uchar_t watchdog_bits = 0;
53 static uint_t watchdog_timeout;
54 
55 extern uint64_t find_cpufrequency(volatile uchar_t *);
56 
57 /*
58  * Module linkage information for the kernel.
59  */
60 static struct modlmisc modlmisc = {
61 	&mod_miscops, "tod module for Mostek M48T59"
62 };
63 
64 static struct modlinkage modlinkage = {
65 	MODREV_1, (void *)&modlmisc, NULL
66 };
67 
68 int
69 _init(void)
70 {
71 	if (strcmp(tod_module_name, "todmostek") == 0) {
72 		tod_ops.tod_get = todm_get;
73 		tod_ops.tod_set = todm_set;
74 		tod_ops.tod_set_watchdog_timer = todm_set_watchdog_timer;
75 		tod_ops.tod_clear_watchdog_timer = todm_clear_watchdog_timer;
76 		tod_ops.tod_set_power_alarm = todm_set_power_alarm;
77 		tod_ops.tod_clear_power_alarm = todm_clear_power_alarm;
78 		tod_ops.tod_get_cpufrequency = todm_get_cpufrequency;
79 
80 		/*
81 		 * check if hardware watchdog timer is available and user
82 		 * enabled it.
83 		 */
84 		if (watchdog_enable) {
85 			if (!watchdog_available) {
86 			    cmn_err(CE_WARN, "Hardware watchdog unavailable");
87 			} else if (boothowto & RB_DEBUG) {
88 			    cmn_err(CE_WARN, "Hardware watchdog disabled"
89 				" [debugger]");
90 			}
91 		}
92 	}
93 
94 	return (mod_install(&modlinkage));
95 }
96 
97 int
98 _fini(void)
99 {
100 	if (strcmp(tod_module_name, "todmostek") == 0)
101 		return (EBUSY);
102 	else
103 		return (mod_remove(&modlinkage));
104 }
105 
106 int
107 _info(struct modinfo *modinfop)
108 {
109 	return (mod_info(&modlinkage, modinfop));
110 }
111 
112 /*
113  * Read the current time from the clock chip and convert to UNIX form.
114  * Assumes that the year in the clock chip is valid.
115  * Must be called with tod_lock held.
116  */
117 static timestruc_t
118 todm_get(void)
119 {
120 	timestruc_t ts;
121 #ifndef	MPSAS
122 	todinfo_t tod;
123 	int s;
124 
125 	ASSERT(MUTEX_HELD(&tod_lock));
126 
127 	s = splhi();
128 
129 	CLOCK->clk_ctrl |= CLK_CTRL_READ;
130 	tod.tod_year	= BCD_TO_BYTE(CLOCK->clk_year) + YRBASE;
131 	tod.tod_month	= BCD_TO_BYTE(CLOCK->clk_month & 0x1f);
132 	tod.tod_day	= BCD_TO_BYTE(CLOCK->clk_day & 0x3f);
133 	tod.tod_dow	= BCD_TO_BYTE(CLOCK->clk_weekday & 0x7);
134 	tod.tod_hour	= BCD_TO_BYTE(CLOCK->clk_hour & 0x3f);
135 	tod.tod_min	= BCD_TO_BYTE(CLOCK->clk_min & 0x7f);
136 	tod.tod_sec	= BCD_TO_BYTE(CLOCK->clk_sec & 0x7f);
137 	CLOCK->clk_ctrl &= ~CLK_CTRL_READ;
138 
139 	splx(s);
140 
141 	/*
142 	 * Apparently the m48t59 doesn't quite do what the spec sheet says.
143 	 * The spec says reading WRD will reset the timer but that doesn't work.
144 	 * So we need to reload timeout each time we want to reset the timer.
145 	 */
146 	CLOCK->clk_watchdog = watchdog_bits;
147 
148 	ts.tv_sec = tod_to_utc(tod);
149 	ts.tv_nsec = 0;
150 #else
151 	ts.tv_sec = 0;
152 	ts.tv_nsec = 0;
153 #endif
154 	return (ts);
155 }
156 
157 /*
158  * Write the specified time into the clock chip.
159  * Must be called with tod_lock held.
160  */
161 /* ARGSUSED */
162 static void
163 todm_set(timestruc_t ts)
164 {
165 #ifndef	MPSAS
166 	todinfo_t tod = utc_to_tod(ts.tv_sec);
167 
168 	ASSERT(MUTEX_HELD(&tod_lock));
169 
170 	CLOCK->clk_ctrl |= CLK_CTRL_WRITE;	/* allow writes */
171 	CLOCK->clk_year		= BYTE_TO_BCD(tod.tod_year - YRBASE);
172 	CLOCK->clk_month	= BYTE_TO_BCD(tod.tod_month);
173 	CLOCK->clk_day		= BYTE_TO_BCD(tod.tod_day);
174 	CLOCK->clk_weekday	= BYTE_TO_BCD(tod.tod_dow);
175 	CLOCK->clk_hour		= BYTE_TO_BCD(tod.tod_hour);
176 	CLOCK->clk_min		= BYTE_TO_BCD(tod.tod_min);
177 	CLOCK->clk_sec		= BYTE_TO_BCD(tod.tod_sec);
178 	CLOCK->clk_ctrl &= ~CLK_CTRL_WRITE;	/* load values */
179 #endif
180 }
181 
182 
183 /*
184  * Program the watchdog timer shadow register with the specified value.
185  * Setting the timer to zero value means no watchdog timeout.
186  */
187 static uint_t
188 todm_set_watchdog_timer(uint_t timeoutval)
189 {
190 	ASSERT(MUTEX_HELD(&tod_lock));
191 
192 	if (watchdog_enable == 0 || watchdog_available == 0 ||
193 		(boothowto & RB_DEBUG))
194 			return (0);
195 
196 	watchdog_timeout = timeoutval;
197 	watchdog_bits = CLK_WATCHDOG_BITS(timeoutval);
198 	watchdog_activated = 1;
199 
200 	return (timeoutval);
201 }
202 
203 /*
204  * Clear the hardware timer register. Also zero out the watchdog timer
205  * shadow register.
206  */
207 static uint_t
208 todm_clear_watchdog_timer(void)
209 {
210 	ASSERT(MUTEX_HELD(&tod_lock));
211 
212 	if (watchdog_activated == 0)
213 		return (0);
214 
215 #ifndef	MPSAS
216 	CLOCK->clk_watchdog = 0;
217 #endif /* MPSAS */
218 
219 	watchdog_bits = 0;
220 	watchdog_activated = 0;
221 	return (watchdog_timeout);
222 }
223 
224 /*
225  * program the tod registers for alarm to go off at the specified time
226  */
227 static void
228 todm_set_power_alarm(timestruc_t ts)
229 {
230 #ifndef	MPSAS
231 	todinfo_t	tod;
232 	uchar_t	c;
233 
234 	ASSERT(MUTEX_HELD(&tod_lock));
235 	tod = utc_to_tod(ts.tv_sec);
236 
237 	c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */
238 #ifdef lint
239 	CLOCK->clk_flags = c;
240 #endif
241 	CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */
242 
243 	CLOCK->clk_day &= ~CLK_FREQT; /* keep Freqency Test bit cleared */
244 
245 	CLOCK->clk_alm_day = BYTE_TO_BCD(tod.tod_day);
246 	CLOCK->clk_alm_hours = BYTE_TO_BCD(tod.tod_hour);
247 	CLOCK->clk_alm_mins = BYTE_TO_BCD(tod.tod_min);
248 	CLOCK->clk_alm_secs = BYTE_TO_BCD(tod.tod_sec);
249 
250 	CLOCK->clk_interrupts |= CLK_ALARM_ENABLE; /* enable alarm intr */
251 #endif /* MPSAS */
252 }
253 
254 /*
255  * clear alarm interrupt
256  */
257 static void
258 todm_clear_power_alarm()
259 {
260 #ifndef	MPSAS
261 	uchar_t	c;
262 
263 	ASSERT(MUTEX_HELD(&tod_lock));
264 
265 	c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */
266 
267 #ifdef lint
268 	CLOCK->clk_flags = c;
269 #endif
270 
271 	CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */
272 #endif /* MPSAS */
273 }
274 
275 /*
276  * Determine the cpu frequency by watching the TOD chip rollover twice.
277  * Cpu clock rate is determined by computing the ticks added (in tick register)
278  * during one second interval on TOD.
279  */
280 uint64_t
281 todm_get_cpufrequency(void)
282 {
283 #ifndef	MPSAS
284 	ASSERT(MUTEX_HELD(&tod_lock));
285 
286 	return (find_cpufrequency(&(TIMECHECK_CLOCK->clk_sec)));
287 #else
288 	return (cpunodes[CPU->cpu_id].clock_freq);
289 #endif /* MPSAS */
290 }
291