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 2009 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
_init(void)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,
87 "Hardware watchdog unavailable");
88 } else if (boothowto & RB_DEBUG) {
89 cmn_err(CE_WARN, "Hardware watchdog disabled"
90 " [debugger]");
91 }
92 }
93 }
94
95 return (mod_install(&modlinkage));
96 }
97
98 int
_fini(void)99 _fini(void)
100 {
101 if (strcmp(tod_module_name, "todmostek") == 0)
102 return (EBUSY);
103 else
104 return (mod_remove(&modlinkage));
105 }
106
107 int
_info(struct modinfo * modinfop)108 _info(struct modinfo *modinfop)
109 {
110 return (mod_info(&modlinkage, modinfop));
111 }
112
113 /*
114 * Read the current time from the clock chip and convert to UNIX form.
115 * Assumes that the year in the clock chip is valid.
116 * Must be called with tod_lock held.
117 */
118 static timestruc_t
todm_get(void)119 todm_get(void)
120 {
121 timestruc_t ts;
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 return (ts);
151 }
152
153 /*
154 * Write the specified time into the clock chip.
155 * Must be called with tod_lock held.
156 */
157 /* ARGSUSED */
158 static void
todm_set(timestruc_t ts)159 todm_set(timestruc_t ts)
160 {
161 todinfo_t tod = utc_to_tod(ts.tv_sec);
162
163 ASSERT(MUTEX_HELD(&tod_lock));
164
165 CLOCK->clk_ctrl |= CLK_CTRL_WRITE; /* allow writes */
166 CLOCK->clk_year = BYTE_TO_BCD(tod.tod_year - YRBASE);
167 CLOCK->clk_month = BYTE_TO_BCD(tod.tod_month);
168 CLOCK->clk_day = BYTE_TO_BCD(tod.tod_day);
169 CLOCK->clk_weekday = BYTE_TO_BCD(tod.tod_dow);
170 CLOCK->clk_hour = BYTE_TO_BCD(tod.tod_hour);
171 CLOCK->clk_min = BYTE_TO_BCD(tod.tod_min);
172 CLOCK->clk_sec = BYTE_TO_BCD(tod.tod_sec);
173 CLOCK->clk_ctrl &= ~CLK_CTRL_WRITE; /* load values */
174 }
175
176
177 /*
178 * Program the watchdog timer shadow register with the specified value.
179 * Setting the timer to zero value means no watchdog timeout.
180 */
181 static uint_t
todm_set_watchdog_timer(uint_t timeoutval)182 todm_set_watchdog_timer(uint_t timeoutval)
183 {
184 ASSERT(MUTEX_HELD(&tod_lock));
185
186 if (watchdog_enable == 0 || watchdog_available == 0 ||
187 (boothowto & RB_DEBUG))
188 return (0);
189
190 watchdog_timeout = timeoutval;
191 watchdog_bits = CLK_WATCHDOG_BITS(timeoutval);
192 watchdog_activated = 1;
193
194 return (timeoutval);
195 }
196
197 /*
198 * Clear the hardware timer register. Also zero out the watchdog timer
199 * shadow register.
200 */
201 static uint_t
todm_clear_watchdog_timer(void)202 todm_clear_watchdog_timer(void)
203 {
204 ASSERT(MUTEX_HELD(&tod_lock));
205
206 if (watchdog_activated == 0)
207 return (0);
208
209 CLOCK->clk_watchdog = 0;
210
211 watchdog_bits = 0;
212 watchdog_activated = 0;
213 return (watchdog_timeout);
214 }
215
216 /*
217 * program the tod registers for alarm to go off at the specified time
218 */
219 static void
todm_set_power_alarm(timestruc_t ts)220 todm_set_power_alarm(timestruc_t ts)
221 {
222 todinfo_t tod;
223 uchar_t c;
224
225 ASSERT(MUTEX_HELD(&tod_lock));
226 tod = utc_to_tod(ts.tv_sec);
227
228 c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */
229 #ifdef lint
230 CLOCK->clk_flags = c;
231 #endif
232 CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */
233
234 CLOCK->clk_day &= ~CLK_FREQT; /* keep Freqency Test bit cleared */
235
236 CLOCK->clk_alm_day = BYTE_TO_BCD(tod.tod_day);
237 CLOCK->clk_alm_hours = BYTE_TO_BCD(tod.tod_hour);
238 CLOCK->clk_alm_mins = BYTE_TO_BCD(tod.tod_min);
239 CLOCK->clk_alm_secs = BYTE_TO_BCD(tod.tod_sec);
240
241 CLOCK->clk_interrupts |= CLK_ALARM_ENABLE; /* enable alarm intr */
242 }
243
244 /*
245 * clear alarm interrupt
246 */
247 static void
todm_clear_power_alarm()248 todm_clear_power_alarm()
249 {
250 uchar_t c;
251
252 ASSERT(MUTEX_HELD(&tod_lock));
253
254 c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */
255
256 #ifdef lint
257 CLOCK->clk_flags = c;
258 #endif
259
260 CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */
261 }
262
263 /*
264 * Determine the cpu frequency by watching the TOD chip rollover twice.
265 * Cpu clock rate is determined by computing the ticks added (in tick register)
266 * during one second interval on TOD.
267 */
268 uint64_t
todm_get_cpufrequency(void)269 todm_get_cpufrequency(void)
270 {
271 ASSERT(MUTEX_HELD(&tod_lock));
272
273 return (find_cpufrequency(&(TIMECHECK_CLOCK->clk_sec)));
274 }
275