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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * tod driver module for Mostek M48T59 part
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>
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);
52 static uchar_t watchdog_bits
= 0;
53 static uint_t watchdog_timeout
;
55 extern uint64_t find_cpufrequency(volatile uchar_t
*);
58 * Module linkage information for the kernel.
60 static struct modlmisc modlmisc
= {
61 &mod_miscops
, "tod module for Mostek M48T59"
64 static struct modlinkage modlinkage
= {
65 MODREV_1
, (void *)&modlmisc
, NULL
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
;
81 * check if hardware watchdog timer is available and user
84 if (watchdog_enable
) {
85 if (!watchdog_available
) {
87 "Hardware watchdog unavailable");
88 } else if (boothowto
& RB_DEBUG
) {
89 cmn_err(CE_WARN
, "Hardware watchdog disabled"
95 return (mod_install(&modlinkage
));
101 if (strcmp(tod_module_name
, "todmostek") == 0)
104 return (mod_remove(&modlinkage
));
108 _info(struct modinfo
*modinfop
)
110 return (mod_info(&modlinkage
, modinfop
));
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.
125 ASSERT(MUTEX_HELD(&tod_lock
));
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
;
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.
146 CLOCK
->clk_watchdog
= watchdog_bits
;
148 ts
.tv_sec
= tod_to_utc(tod
);
154 * Write the specified time into the clock chip.
155 * Must be called with tod_lock held.
159 todm_set(timestruc_t ts
)
161 todinfo_t tod
= utc_to_tod(ts
.tv_sec
);
163 ASSERT(MUTEX_HELD(&tod_lock
));
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 */
178 * Program the watchdog timer shadow register with the specified value.
179 * Setting the timer to zero value means no watchdog timeout.
182 todm_set_watchdog_timer(uint_t timeoutval
)
184 ASSERT(MUTEX_HELD(&tod_lock
));
186 if (watchdog_enable
== 0 || watchdog_available
== 0 ||
187 (boothowto
& RB_DEBUG
))
190 watchdog_timeout
= timeoutval
;
191 watchdog_bits
= CLK_WATCHDOG_BITS(timeoutval
);
192 watchdog_activated
= 1;
198 * Clear the hardware timer register. Also zero out the watchdog timer
202 todm_clear_watchdog_timer(void)
204 ASSERT(MUTEX_HELD(&tod_lock
));
206 if (watchdog_activated
== 0)
209 CLOCK
->clk_watchdog
= 0;
212 watchdog_activated
= 0;
213 return (watchdog_timeout
);
217 * program the tod registers for alarm to go off at the specified time
220 todm_set_power_alarm(timestruc_t ts
)
225 ASSERT(MUTEX_HELD(&tod_lock
));
226 tod
= utc_to_tod(ts
.tv_sec
);
228 c
= CLOCK
->clk_flags
; /* clear alarm intr flag by reading the reg */
230 CLOCK
->clk_flags
= c
;
232 CLOCK
->clk_interrupts
&= ~CLK_ALARM_ENABLE
; /* disable alarm intr */
234 CLOCK
->clk_day
&= ~CLK_FREQT
; /* keep Freqency Test bit cleared */
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
);
241 CLOCK
->clk_interrupts
|= CLK_ALARM_ENABLE
; /* enable alarm intr */
245 * clear alarm interrupt
248 todm_clear_power_alarm()
252 ASSERT(MUTEX_HELD(&tod_lock
));
254 c
= CLOCK
->clk_flags
; /* clear alarm intr flag by reading the reg */
257 CLOCK
->clk_flags
= c
;
260 CLOCK
->clk_interrupts
&= ~CLK_ALARM_ENABLE
; /* disable alarm intr */
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.
269 todm_get_cpufrequency(void)
271 ASSERT(MUTEX_HELD(&tod_lock
));
273 return (find_cpufrequency(&(TIMECHECK_CLOCK
->clk_sec
)));