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 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, (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 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