xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/simmstat.c (revision b07ce584f4e28873b8927d7f83d9d3275a0f3ed2)
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 
56 static int simmstat_attach(dev_info_t *, ddi_attach_cmd_t);
57 
58 static int simmstat_detach(dev_info_t *, ddi_detach_cmd_t);
59 
60 static void simmstat_add_kstats(struct simmstat_soft_state *);
61 
62 static int simmstat_kstat_update(kstat_t *, int);
63 
64 /*
65  * Configuration data structures
66  */
67 static 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 
88 static 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 
103 static uint_t simmstat_reg_read_delay_us = 10;
104 
105 /*
106  * Driver globals
107  */
108 void *simmstatp;
109 
110 extern struct mod_ops mod_driverops;
111 
112 static 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 
118 static struct modlinkage modlinkage = {
119 	MODREV_1,		/* rev */
120 	(void *)&modldrv,
121 	NULL
122 };
123 
124 #ifndef	lint
125 char _depends_on[] = "drv/fhc";
126 #endif	/* lint */
127 
128 /*
129  * These are the module initialization routines.
130  */
131 
132 int
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 
144 int
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 
156 int
157 _info(struct modinfo *modinfop)
158 {
159 	return (mod_info(&modlinkage, modinfop));
160 }
161 
162 static int
163 simmstat_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 
220 bad:
221 	ddi_soft_state_free(simmstatp, instance);
222 	return (DDI_FAILURE);
223 }
224 
225 /* ARGSUSED */
226 static int
227 simmstat_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 
270 static void
271 simmstat_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 
297 static int
298 simmstat_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