simmstat.c revision 1341:6d7c4f090a72
1157299Smarcel/*
2157299Smarcel * CDDL HEADER START
3157299Smarcel *
4157299Smarcel * The contents of this file are subject to the terms of the
5157299Smarcel * Common Development and Distribution License (the "License").
6157299Smarcel * You may not use this file except in compliance with the License.
7157299Smarcel *
8157299Smarcel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9157299Smarcel * or http://www.opensolaris.org/os/licensing.
10157299Smarcel * See the License for the specific language governing permissions
11157299Smarcel * and limitations under the License.
12157299Smarcel *
13157299Smarcel * When distributing Covered Code, include this CDDL HEADER in each
14157299Smarcel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15157299Smarcel * If applicable, add the following below this CDDL HEADER, with the
16157299Smarcel * fields enclosed by brackets "[]" replaced with your own identifying
17157299Smarcel * information: Portions Copyright [yyyy] [name of copyright owner]
18157299Smarcel *
19157299Smarcel * CDDL HEADER END
20157299Smarcel */
21157299Smarcel
22157299Smarcel/*
23157299Smarcel * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24157299Smarcel * Use is subject to license terms.
25157299Smarcel */
26157299Smarcel
27157299Smarcel#pragma ident	"%Z%%M%	%I%	%E% SMI"
28157299Smarcel
29157299Smarcel#include <sys/types.h>
30157299Smarcel#include <sys/conf.h>
31157299Smarcel#include <sys/ddi.h>
32157299Smarcel#include <sys/sunddi.h>
33157299Smarcel#include <sys/ddi_impldefs.h>
34157299Smarcel#include <sys/obpdefs.h>
35157299Smarcel#include <sys/cmn_err.h>
36157299Smarcel#include <sys/errno.h>
37157299Smarcel#include <sys/kmem.h>
38157299Smarcel#include <sys/debug.h>
39157299Smarcel#include <sys/sysmacros.h>
40157299Smarcel#include <sys/ivintr.h>
41157299Smarcel#include <sys/intr.h>
42157299Smarcel#include <sys/intreg.h>
43157299Smarcel#include <sys/autoconf.h>
44157299Smarcel#include <sys/modctl.h>
45157351Smarcel#include <sys/spl.h>
46157351Smarcel
47157351Smarcel#include <sys/fhc.h>
48157299Smarcel#include <sys/simmstat.h>
49157299Smarcel
50157299Smarcel/* Useful debugging Stuff */
51157299Smarcel#include <sys/nexusdebug.h>
52157299Smarcel
53157299Smarcel/*
54157299Smarcel * Function prototypes
55157299Smarcel */
56157299Smarcel
57157299Smarcelstatic int simmstat_attach(dev_info_t *, ddi_attach_cmd_t);
58157299Smarcel
59167822Smarcelstatic int simmstat_detach(dev_info_t *, ddi_detach_cmd_t);
60157299Smarcel
61157299Smarcelstatic void simmstat_add_kstats(struct simmstat_soft_state *);
62157299Smarcel
63157299Smarcelstatic int simmstat_kstat_update(kstat_t *, int);
64178600Smarcel
65178600Smarcel/*
66178600Smarcel * Configuration data structures
67178600Smarcel */
68178600Smarcelstatic struct cb_ops simmstat_cb_ops = {
69178600Smarcel	nulldev,			/* open */
70178600Smarcel	nulldev,			/* close */
71157299Smarcel	nulldev,			/* strategy */
72157299Smarcel	nulldev,			/* print */
73157299Smarcel	nodev,				/* dump */
74178600Smarcel	nulldev,			/* read */
75157299Smarcel	nulldev,			/* write */
76157299Smarcel	nulldev,			/* ioctl */
77157299Smarcel	nodev,				/* devmap */
78157299Smarcel	nodev,				/* mmap */
79157299Smarcel	nodev,				/* segmap */
80157299Smarcel	nochpoll,			/* poll */
81157299Smarcel	ddi_prop_op,			/* cb_prop_op */
82157299Smarcel	0,				/* streamtab */
83157299Smarcel	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
84157299Smarcel	CB_REV,				/* rev */
85157299Smarcel	nodev,				/* cb_aread */
86157299Smarcel	nodev				/* cb_awrite */
87157299Smarcel};
88157299Smarcel
89157299Smarcelstatic struct dev_ops simmstat_ops = {
90157299Smarcel	DEVO_REV,			/* rev */
91157299Smarcel	0,				/* refcnt  */
92157299Smarcel	ddi_no_info,			/* getinfo */
93157299Smarcel	nulldev,			/* identify */
94157340Smarcel	nulldev,			/* probe */
95157299Smarcel	simmstat_attach,		/* attach */
96	simmstat_detach,		/* detach */
97	nulldev,			/* reset */
98	&simmstat_cb_ops,		/* cb_ops */
99	(struct bus_ops *)0,		/* bus_ops */
100	nulldev				/* power */
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 v%I%",	/* 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
125static char _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