simmstat.c revision 7656:2621e50fdf4a
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/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28#include <sys/types.h>
29#include <sys/conf.h>
30#include <sys/ddi.h>
31#include <sys/sunddi.h>
32#include <sys/ddi_impldefs.h>
33#include <sys/obpdefs.h>
34#include <sys/cmn_err.h>
35#include <sys/errno.h>
36#include <sys/kmem.h>
37#include <sys/debug.h>
38#include <sys/sysmacros.h>
39#include <sys/ivintr.h>
40#include <sys/intr.h>
41#include <sys/intreg.h>
42#include <sys/autoconf.h>
43#include <sys/modctl.h>
44#include <sys/spl.h>
45
46#include <sys/fhc.h>
47#include <sys/simmstat.h>
48
49/* Useful debugging Stuff */
50#include <sys/nexusdebug.h>
51
52/*
53 * Function prototypes
54 */
55
56static int simmstat_attach(dev_info_t *, ddi_attach_cmd_t);
57
58static int simmstat_detach(dev_info_t *, ddi_detach_cmd_t);
59
60static void simmstat_add_kstats(struct simmstat_soft_state *);
61
62static int simmstat_kstat_update(kstat_t *, int);
63
64/*
65 * Configuration data structures
66 */
67static struct cb_ops simmstat_cb_ops = {
68	nulldev,			/* open */
69	nulldev,			/* close */
70	nulldev,			/* strategy */
71	nulldev,			/* print */
72	nodev,				/* dump */
73	nulldev,			/* read */
74	nulldev,			/* write */
75	nulldev,			/* ioctl */
76	nodev,				/* devmap */
77	nodev,				/* mmap */
78	nodev,				/* segmap */
79	nochpoll,			/* poll */
80	ddi_prop_op,			/* cb_prop_op */
81	0,				/* streamtab */
82	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
83	CB_REV,				/* rev */
84	nodev,				/* cb_aread */
85	nodev				/* cb_awrite */
86};
87
88static struct dev_ops simmstat_ops = {
89	DEVO_REV,			/* rev */
90	0,				/* refcnt  */
91	ddi_no_info,			/* getinfo */
92	nulldev,			/* identify */
93	nulldev,			/* probe */
94	simmstat_attach,		/* attach */
95	simmstat_detach,		/* detach */
96	nulldev,			/* reset */
97	&simmstat_cb_ops,		/* cb_ops */
98	(struct bus_ops *)0,		/* bus_ops */
99	nulldev,			/* power */
100	ddi_quiesce_not_needed,			/* quiesce */
101};
102
103static uint_t simmstat_reg_read_delay_us = 10;
104
105/*
106 * Driver globals
107 */
108void *simmstatp;
109
110extern struct mod_ops mod_driverops;
111
112static struct modldrv modldrv = {
113	&mod_driverops,			/* module type, this one is a driver */
114	"SIMM-status Leaf",		/* module name */
115	&simmstat_ops,			/* driver ops */
116};
117
118static struct modlinkage modlinkage = {
119	MODREV_1,		/* rev */
120	(void *)&modldrv,
121	NULL
122};
123
124#ifndef	lint
125char _depends_on[] = "drv/fhc";
126#endif	/* lint */
127
128/*
129 * These are the module initialization routines.
130 */
131
132int
133_init(void)
134{
135	int error;
136
137	if ((error = ddi_soft_state_init(&simmstatp,
138	    sizeof (struct simmstat_soft_state), 1)) != 0)
139		return (error);
140
141	return (mod_install(&modlinkage));
142}
143
144int
145_fini(void)
146{
147	int error;
148
149	if ((error = mod_remove(&modlinkage)) != 0)
150		return (error);
151
152	ddi_soft_state_fini(&simmstatp);
153	return (0);
154}
155
156int
157_info(struct modinfo *modinfop)
158{
159	return (mod_info(&modlinkage, modinfop));
160}
161
162static int
163simmstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
164{
165	struct simmstat_soft_state *softsp;
166	int instance;
167
168	switch (cmd) {
169	case DDI_ATTACH:
170		break;
171
172	case DDI_RESUME:
173		return (DDI_SUCCESS);
174
175	default:
176		return (DDI_FAILURE);
177	}
178
179	instance = ddi_get_instance(devi);
180
181	if (ddi_soft_state_zalloc(simmstatp, instance) != DDI_SUCCESS)
182		return (DDI_FAILURE);
183
184	softsp = ddi_get_soft_state(simmstatp, instance);
185
186	/* Set the dip in the soft state */
187	softsp->dip = devi;
188
189	/* Get the board number from this nodes parent device. */
190	softsp->pdip = ddi_get_parent(softsp->dip);
191	if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
192	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
193		cmn_err(CE_WARN, "simmstat%d: unable to retrieve %s property",
194		    instance, OBP_BOARDNUM);
195		goto bad;
196	}
197
198	DPRINTF(SIMMSTAT_ATTACH_DEBUG, ("simmstat%d: devi= 0x%p\n, "
199	    " softsp=0x%p\n", instance, devi, softsp));
200
201	/* map in the registers for this device. */
202	if (ddi_map_regs(softsp->dip, 0,
203	    (caddr_t *)&softsp->simmstat_base, 0, 0)) {
204		cmn_err(CE_WARN, "simmstat%d: unable to map registers",
205		    instance);
206		goto bad;
207	}
208
209	/* nothing to suspend/resume here */
210	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
211	    "pm-hardware-state", "no-suspend-resume");
212
213	/* create the kstats for this device */
214	simmstat_add_kstats(softsp);
215
216	ddi_report_dev(devi);
217
218	return (DDI_SUCCESS);
219
220bad:
221	ddi_soft_state_free(simmstatp, instance);
222	return (DDI_FAILURE);
223}
224
225/* ARGSUSED */
226static int
227simmstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
228{
229	int instance;
230	struct simmstat_soft_state *softsp;
231
232	/* get the instance of this devi */
233	instance = ddi_get_instance(devi);
234
235	/* get the soft state pointer for this device node */
236	softsp = ddi_get_soft_state(simmstatp, instance);
237
238	switch (cmd) {
239	case DDI_SUSPEND:
240		return (DDI_SUCCESS);
241
242	case DDI_DETACH:
243		(void) fhc_bdlist_lock(softsp->board);
244		if (fhc_bd_detachable(softsp->board))
245			break;
246		else
247			fhc_bdlist_unlock();
248		/* FALLTHROUGH */
249
250	default:
251		return (DDI_FAILURE);
252	}
253
254	fhc_bdlist_unlock();
255
256	/* remove the kstat for this board */
257	kstat_delete(softsp->simmstat_ksp);
258
259	/* unmap the registers */
260	ddi_unmap_regs(softsp->dip, 0,
261	    (caddr_t *)&softsp->simmstat_base, 0, 0);
262
263	/* free up the soft state */
264	ddi_soft_state_free(simmstatp, instance);
265	ddi_prop_remove_all(devi);
266
267	return (DDI_SUCCESS);
268}
269
270static void
271simmstat_add_kstats(struct simmstat_soft_state *softsp)
272{
273	struct kstat *simmstat_ksp;
274
275	if ((simmstat_ksp = kstat_create("unix", softsp->board,
276	    SIMMSTAT_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
277	    SIMM_COUNT, KSTAT_FLAG_PERSISTENT)) == NULL) {
278		cmn_err(CE_WARN, "simmstat%d: kstat_create failed",
279		    ddi_get_instance(softsp->dip));
280	}
281
282	simmstat_ksp->ks_update = simmstat_kstat_update;
283	simmstat_ksp->ks_private = (void *)softsp;
284	softsp->simmstat_ksp = simmstat_ksp;
285	kstat_install(simmstat_ksp);
286}
287
288/*
289 * Kstats only need ks_update functions when they change dynamically
290 * at run time.
291 * In the case of the simmstat registers, they contain battery
292 * information for NVSIMMs. These need to be updated whenever a
293 * kstat_read asks for the data. There is currently no plan to
294 * ship NVSIMMs on this platform, but this support must be present.
295 */
296
297static int
298simmstat_kstat_update(kstat_t *ksp, int rw)
299{
300	struct simmstat_soft_state *softsp;
301	volatile char *statp;	/* pointer to hardware register */
302	char *kstatp;		/* pointer to kstat data buffer */
303	int i;
304
305	kstatp = (char *)ksp->ks_data;
306	softsp = (struct simmstat_soft_state *)ksp->ks_private;
307
308	statp = (char *)softsp->simmstat_base;
309
310	/* this is a read-only kstat. Bail out on a write */
311	if (rw == KSTAT_WRITE) {
312		return (EACCES);
313	} else {
314
315		/*
316		 * copy current status of hardware into the kstat
317		 * structure.
318		 */
319		for (i = 0; i < SIMM_COUNT; i++, statp++, kstatp++) {
320			*kstatp = *statp;
321			DELAY(simmstat_reg_read_delay_us);
322		}
323	}
324	return (0);
325}
326