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