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