xref: /onnv-gate/usr/src/uts/sun4u/io/pmc.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright (c) 2001 by Sun Microsystems, Inc.
24*0Sstevel@tonic-gate  * All rights reserved.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * Driver for the Power Management Controller (logical unit 8) of the
31*0Sstevel@tonic-gate  * PC87317 SuperI/O chip. The PMC contains the hardware watchdog timer.
32*0Sstevel@tonic-gate  */
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include <sys/types.h>
35*0Sstevel@tonic-gate #include <sys/time.h>
36*0Sstevel@tonic-gate #include <sys/cmn_err.h>
37*0Sstevel@tonic-gate #include <sys/param.h>
38*0Sstevel@tonic-gate #include <sys/modctl.h>
39*0Sstevel@tonic-gate #include <sys/conf.h>
40*0Sstevel@tonic-gate #include <sys/stat.h>
41*0Sstevel@tonic-gate #include <sys/clock.h>
42*0Sstevel@tonic-gate #include <sys/reboot.h>
43*0Sstevel@tonic-gate #include <sys/ddi.h>
44*0Sstevel@tonic-gate #include <sys/sunddi.h>
45*0Sstevel@tonic-gate #include <sys/file.h>
46*0Sstevel@tonic-gate #include <sys/note.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate #ifdef	DEBUG
49*0Sstevel@tonic-gate int pmc_debug_flag = 0;
50*0Sstevel@tonic-gate #define	DPRINTF(ARGLIST) if (pmc_debug_flag) printf ARGLIST;
51*0Sstevel@tonic-gate #else
52*0Sstevel@tonic-gate #define	DPRINTF(ARGLIST)
53*0Sstevel@tonic-gate #endif /* DEBUG */
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate /* Driver soft state structure */
56*0Sstevel@tonic-gate typedef struct pmc {
57*0Sstevel@tonic-gate 	dev_info_t		*dip;
58*0Sstevel@tonic-gate 	ddi_acc_handle_t	pmc_handle;
59*0Sstevel@tonic-gate } pmc_t;
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate static void *pmc_soft_state;
62*0Sstevel@tonic-gate static int instance = -1;
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate /* dev_ops and cb_ops entry point function declarations */
65*0Sstevel@tonic-gate static int pmc_attach(dev_info_t *, ddi_attach_cmd_t);
66*0Sstevel@tonic-gate static int pmc_detach(dev_info_t *, ddi_detach_cmd_t);
67*0Sstevel@tonic-gate static int pmc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate /* hardware watchdog parameters */
70*0Sstevel@tonic-gate static uint_t pmc_set_watchdog_timer(uint_t);
71*0Sstevel@tonic-gate static uint_t pmc_clear_watchdog_timer(void);
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate extern volatile uint8_t	*v_pmc_addr_reg;
74*0Sstevel@tonic-gate extern volatile uint8_t	*v_pmc_data_reg;
75*0Sstevel@tonic-gate extern int		watchdog_enable;
76*0Sstevel@tonic-gate extern int		watchdog_available;
77*0Sstevel@tonic-gate extern int		watchdog_activated;
78*0Sstevel@tonic-gate extern int		boothowto;
79*0Sstevel@tonic-gate extern uint_t		watchdog_timeout_seconds;
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate /*
82*0Sstevel@tonic-gate  * Power Management Registers and values
83*0Sstevel@tonic-gate  */
84*0Sstevel@tonic-gate #define	PMC_WDTO	0x05	/* Watchdog Time Out */
85*0Sstevel@tonic-gate #define	PMC_CLEAR_WDTO	0x00
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate struct cb_ops pmc_cb_ops = {
88*0Sstevel@tonic-gate 	nodev,
89*0Sstevel@tonic-gate 	nodev,
90*0Sstevel@tonic-gate 	nodev,
91*0Sstevel@tonic-gate 	nodev,
92*0Sstevel@tonic-gate 	nodev,			/* dump */
93*0Sstevel@tonic-gate 	nodev,
94*0Sstevel@tonic-gate 	nodev,
95*0Sstevel@tonic-gate 	nodev,
96*0Sstevel@tonic-gate 	nodev,			/* devmap */
97*0Sstevel@tonic-gate 	nodev,
98*0Sstevel@tonic-gate 	nodev,
99*0Sstevel@tonic-gate 	nochpoll,
100*0Sstevel@tonic-gate 	ddi_prop_op,
101*0Sstevel@tonic-gate 	NULL,			/* for STREAMS drivers */
102*0Sstevel@tonic-gate 	D_NEW | D_MP,		/* driver compatibility flag */
103*0Sstevel@tonic-gate 	CB_REV,
104*0Sstevel@tonic-gate 	nodev,
105*0Sstevel@tonic-gate 	nodev
106*0Sstevel@tonic-gate };
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate static struct dev_ops pmc_dev_ops = {
109*0Sstevel@tonic-gate 	DEVO_REV,			/* driver build version */
110*0Sstevel@tonic-gate 	0,				/* device reference count */
111*0Sstevel@tonic-gate 	pmc_getinfo,
112*0Sstevel@tonic-gate 	nulldev,
113*0Sstevel@tonic-gate 	nulldev,			/* probe */
114*0Sstevel@tonic-gate 	pmc_attach,
115*0Sstevel@tonic-gate 	pmc_detach,
116*0Sstevel@tonic-gate 	nulldev,			/* reset */
117*0Sstevel@tonic-gate 	&pmc_cb_ops,
118*0Sstevel@tonic-gate 	(struct bus_ops *)NULL,
119*0Sstevel@tonic-gate 	nulldev				/* power */
120*0Sstevel@tonic-gate };
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate /* module configuration stuff */
123*0Sstevel@tonic-gate extern struct mod_ops mod_driverops;
124*0Sstevel@tonic-gate static struct modldrv modldrv = {
125*0Sstevel@tonic-gate 	&mod_driverops,
126*0Sstevel@tonic-gate 	"pmc driver %I%",
127*0Sstevel@tonic-gate 	&pmc_dev_ops
128*0Sstevel@tonic-gate };
129*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
130*0Sstevel@tonic-gate 	MODREV_1,
131*0Sstevel@tonic-gate 	&modldrv,
132*0Sstevel@tonic-gate 	0
133*0Sstevel@tonic-gate };
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate int
137*0Sstevel@tonic-gate _init(void)
138*0Sstevel@tonic-gate {
139*0Sstevel@tonic-gate 	int e;
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 	e = ddi_soft_state_init(&pmc_soft_state, sizeof (pmc_t), 1);
142*0Sstevel@tonic-gate 	if (e != 0) {
143*0Sstevel@tonic-gate 		DPRINTF(("_init: ddi_soft_state_init failed\n"));
144*0Sstevel@tonic-gate 		return (e);
145*0Sstevel@tonic-gate 	}
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate 	e = mod_install(&modlinkage);
148*0Sstevel@tonic-gate 	if (e != 0) {
149*0Sstevel@tonic-gate 		DPRINTF(("_init: mod_install failed\n"));
150*0Sstevel@tonic-gate 		ddi_soft_state_fini(&pmc_soft_state);
151*0Sstevel@tonic-gate 		return (e);
152*0Sstevel@tonic-gate 	}
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	if (v_pmc_addr_reg != NULL) {
155*0Sstevel@tonic-gate 		tod_ops.tod_set_watchdog_timer = pmc_set_watchdog_timer;
156*0Sstevel@tonic-gate 		tod_ops.tod_clear_watchdog_timer = pmc_clear_watchdog_timer;
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 		/*
159*0Sstevel@tonic-gate 		 * See if the user has enabled the watchdog timer, and if
160*0Sstevel@tonic-gate 		 * it's available.
161*0Sstevel@tonic-gate 		 */
162*0Sstevel@tonic-gate 		if (watchdog_enable) {
163*0Sstevel@tonic-gate 			if (!watchdog_available) {
164*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "pmc: Hardware watchdog "
165*0Sstevel@tonic-gate 					"unavailable");
166*0Sstevel@tonic-gate 			} else if (boothowto & RB_DEBUG) {
167*0Sstevel@tonic-gate 				watchdog_available = 0;
168*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "pmc: kernel debugger "
169*0Sstevel@tonic-gate 					"detected: hardware watchdog disabled");
170*0Sstevel@tonic-gate 			}
171*0Sstevel@tonic-gate 		}
172*0Sstevel@tonic-gate 	}
173*0Sstevel@tonic-gate 	return (e);
174*0Sstevel@tonic-gate }
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate int
177*0Sstevel@tonic-gate _fini(void)
178*0Sstevel@tonic-gate {
179*0Sstevel@tonic-gate 	int e;
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	if (v_pmc_addr_reg != NULL)
182*0Sstevel@tonic-gate 		return (DDI_FAILURE);
183*0Sstevel@tonic-gate 	else {
184*0Sstevel@tonic-gate 		e = mod_remove(&modlinkage);
185*0Sstevel@tonic-gate 		if (e != 0)
186*0Sstevel@tonic-gate 			return (e);
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 		ddi_soft_state_fini(&pmc_soft_state);
189*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
190*0Sstevel@tonic-gate 	}
191*0Sstevel@tonic-gate }
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate int
195*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
196*0Sstevel@tonic-gate {
197*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
198*0Sstevel@tonic-gate }
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate static int
201*0Sstevel@tonic-gate pmc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
202*0Sstevel@tonic-gate {
203*0Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 	pmc_t	*pmcp;
206*0Sstevel@tonic-gate 	int	instance;
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	switch (cmd) {
209*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
210*0Sstevel@tonic-gate 		instance = getminor((dev_t)arg);
211*0Sstevel@tonic-gate 		pmcp = (pmc_t *)ddi_get_soft_state(pmc_soft_state, instance);
212*0Sstevel@tonic-gate 		if (pmcp == NULL) {
213*0Sstevel@tonic-gate 			*result = (void *)NULL;
214*0Sstevel@tonic-gate 			return (DDI_FAILURE);
215*0Sstevel@tonic-gate 		}
216*0Sstevel@tonic-gate 		*result = (void *)pmcp->dip;
217*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
220*0Sstevel@tonic-gate 		*result = (void *)getminor((dev_t)arg);
221*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	default:
224*0Sstevel@tonic-gate 		return (DDI_FAILURE);
225*0Sstevel@tonic-gate 	}
226*0Sstevel@tonic-gate }
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate static int
230*0Sstevel@tonic-gate pmc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
231*0Sstevel@tonic-gate {
232*0Sstevel@tonic-gate 	pmc_t	*pmcp;
233*0Sstevel@tonic-gate 	uint_t	wd_timout;
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate 	switch (cmd) {
236*0Sstevel@tonic-gate 	case DDI_ATTACH:
237*0Sstevel@tonic-gate 		break;
238*0Sstevel@tonic-gate 	case DDI_RESUME:
239*0Sstevel@tonic-gate 		if (v_pmc_addr_reg != NULL && watchdog_enable) {
240*0Sstevel@tonic-gate 			int ret = 0;
241*0Sstevel@tonic-gate 			wd_timout = watchdog_timeout_seconds;
242*0Sstevel@tonic-gate 			mutex_enter(&tod_lock);
243*0Sstevel@tonic-gate 			ret = tod_ops.tod_set_watchdog_timer(wd_timout);
244*0Sstevel@tonic-gate 			mutex_exit(&tod_lock);
245*0Sstevel@tonic-gate 			if (ret == 0)
246*0Sstevel@tonic-gate 				return (DDI_FAILURE);
247*0Sstevel@tonic-gate 		}
248*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
249*0Sstevel@tonic-gate 	default:
250*0Sstevel@tonic-gate 		return (DDI_FAILURE);
251*0Sstevel@tonic-gate 	}
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	if (instance != -1) {
254*0Sstevel@tonic-gate 		DPRINTF(("pmc_attach: Another instance is already attached."));
255*0Sstevel@tonic-gate 		return (DDI_FAILURE);
256*0Sstevel@tonic-gate 	}
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(pmc_soft_state, instance) != DDI_SUCCESS) {
261*0Sstevel@tonic-gate 		DPRINTF(("pmc_attach: Failed to allocate soft state."));
262*0Sstevel@tonic-gate 		return (DDI_FAILURE);
263*0Sstevel@tonic-gate 	}
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 	pmcp = (pmc_t *)ddi_get_soft_state(pmc_soft_state, instance);
266*0Sstevel@tonic-gate 	pmcp->dip = dip;
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
269*0Sstevel@tonic-gate }
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate static int
272*0Sstevel@tonic-gate pmc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
273*0Sstevel@tonic-gate {
274*0Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	pmc_t	*pmcp;
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	switch (cmd) {
279*0Sstevel@tonic-gate 	case DDI_DETACH:
280*0Sstevel@tonic-gate 		/* allow detach if no hardware watchdog */
281*0Sstevel@tonic-gate 		if (v_pmc_addr_reg == NULL || !watchdog_activated) {
282*0Sstevel@tonic-gate 			pmcp = (pmc_t *)ddi_get_soft_state(pmc_soft_state,
283*0Sstevel@tonic-gate 				instance);
284*0Sstevel@tonic-gate 			if (pmcp == NULL)
285*0Sstevel@tonic-gate 				return (ENXIO);
286*0Sstevel@tonic-gate 			ddi_soft_state_free(pmc_soft_state, instance);
287*0Sstevel@tonic-gate 			return (DDI_SUCCESS);
288*0Sstevel@tonic-gate 		} else
289*0Sstevel@tonic-gate 			return (DDI_FAILURE);
290*0Sstevel@tonic-gate 	case DDI_SUSPEND:
291*0Sstevel@tonic-gate 		if (v_pmc_addr_reg != NULL && watchdog_activated) {
292*0Sstevel@tonic-gate 			mutex_enter(&tod_lock);
293*0Sstevel@tonic-gate 			(void) tod_ops.tod_clear_watchdog_timer();
294*0Sstevel@tonic-gate 			mutex_exit(&tod_lock);
295*0Sstevel@tonic-gate 		}
296*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
297*0Sstevel@tonic-gate 	default:
298*0Sstevel@tonic-gate 		return (DDI_FAILURE);
299*0Sstevel@tonic-gate 	}
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate }
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate /*
304*0Sstevel@tonic-gate  * Set the hardware watchdog timer; returning what we set it to.
305*0Sstevel@tonic-gate  */
306*0Sstevel@tonic-gate static uint_t
307*0Sstevel@tonic-gate pmc_set_watchdog_timer(uint_t timeoutval)
308*0Sstevel@tonic-gate {
309*0Sstevel@tonic-gate 	uint_t timeoutval_minutes;
310*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	/* sanity checks */
313*0Sstevel@tonic-gate 	if (watchdog_enable == 0 || watchdog_available == 0 ||
314*0Sstevel@tonic-gate 	    timeoutval == 0)
315*0Sstevel@tonic-gate 		return (0);
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 	/*
318*0Sstevel@tonic-gate 	 * Historically the timer has been counted out in seconds.
319*0Sstevel@tonic-gate 	 * The PC87317 counts the timeout in minutes. The default
320*0Sstevel@tonic-gate 	 * timeout is 10 seconds; the least we can do is one minute.
321*0Sstevel@tonic-gate 	 */
322*0Sstevel@tonic-gate 	timeoutval_minutes = (timeoutval + 59) / 60;
323*0Sstevel@tonic-gate 	if (timeoutval_minutes > UINT8_MAX)
324*0Sstevel@tonic-gate 		return (0);
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 	*v_pmc_addr_reg = (uint8_t)PMC_WDTO;
327*0Sstevel@tonic-gate 	*v_pmc_data_reg = (uint8_t)timeoutval_minutes;
328*0Sstevel@tonic-gate 	watchdog_activated = 1;
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 	/* we'll still return seconds */
331*0Sstevel@tonic-gate 	return (timeoutval_minutes * 60);
332*0Sstevel@tonic-gate }
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate /*
335*0Sstevel@tonic-gate  * Clear the hardware watchdog timer; returning what it was set to.
336*0Sstevel@tonic-gate  */
337*0Sstevel@tonic-gate static uint_t
338*0Sstevel@tonic-gate pmc_clear_watchdog_timer(void)
339*0Sstevel@tonic-gate {
340*0Sstevel@tonic-gate 	uint_t	wd_timeout;
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
343*0Sstevel@tonic-gate 	if (watchdog_activated == 0)
344*0Sstevel@tonic-gate 		return (0);
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	*v_pmc_addr_reg = (uint8_t)PMC_WDTO;
347*0Sstevel@tonic-gate 	wd_timeout = (uint_t)*v_pmc_data_reg;
348*0Sstevel@tonic-gate 	*v_pmc_data_reg = (uint8_t)PMC_CLEAR_WDTO;
349*0Sstevel@tonic-gate 	watchdog_activated = 0;
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 	/* return seconds */
352*0Sstevel@tonic-gate 	return (wd_timeout * 60);
353*0Sstevel@tonic-gate }
354