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