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 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 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 154 _info(struct modinfo *modinfop) 155 { 156 return (mod_info(&modlinkage, modinfop)); 157 } 158 159 static int 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 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 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 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