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 */
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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)157 _info(struct modinfo *modinfop)
158 {
159 return (mod_info(&modlinkage, modinfop));
160 }
161
162 static int
simmstat_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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, (void *)devi, (void *)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
simmstat_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
simmstat_add_kstats(struct simmstat_soft_state * softsp)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
simmstat_kstat_update(kstat_t * ksp,int rw)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