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