simmstat.c revision 1341:6d7c4f090a72
1157299Smarcel/* 2157299Smarcel * CDDL HEADER START 3157299Smarcel * 4157299Smarcel * The contents of this file are subject to the terms of the 5157299Smarcel * Common Development and Distribution License (the "License"). 6157299Smarcel * You may not use this file except in compliance with the License. 7157299Smarcel * 8157299Smarcel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9157299Smarcel * or http://www.opensolaris.org/os/licensing. 10157299Smarcel * See the License for the specific language governing permissions 11157299Smarcel * and limitations under the License. 12157299Smarcel * 13157299Smarcel * When distributing Covered Code, include this CDDL HEADER in each 14157299Smarcel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15157299Smarcel * If applicable, add the following below this CDDL HEADER, with the 16157299Smarcel * fields enclosed by brackets "[]" replaced with your own identifying 17157299Smarcel * information: Portions Copyright [yyyy] [name of copyright owner] 18157299Smarcel * 19157299Smarcel * CDDL HEADER END 20157299Smarcel */ 21157299Smarcel 22157299Smarcel/* 23157299Smarcel * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24157299Smarcel * Use is subject to license terms. 25157299Smarcel */ 26157299Smarcel 27157299Smarcel#pragma ident "%Z%%M% %I% %E% SMI" 28157299Smarcel 29157299Smarcel#include <sys/types.h> 30157299Smarcel#include <sys/conf.h> 31157299Smarcel#include <sys/ddi.h> 32157299Smarcel#include <sys/sunddi.h> 33157299Smarcel#include <sys/ddi_impldefs.h> 34157299Smarcel#include <sys/obpdefs.h> 35157299Smarcel#include <sys/cmn_err.h> 36157299Smarcel#include <sys/errno.h> 37157299Smarcel#include <sys/kmem.h> 38157299Smarcel#include <sys/debug.h> 39157299Smarcel#include <sys/sysmacros.h> 40157299Smarcel#include <sys/ivintr.h> 41157299Smarcel#include <sys/intr.h> 42157299Smarcel#include <sys/intreg.h> 43157299Smarcel#include <sys/autoconf.h> 44157299Smarcel#include <sys/modctl.h> 45157351Smarcel#include <sys/spl.h> 46157351Smarcel 47157351Smarcel#include <sys/fhc.h> 48157299Smarcel#include <sys/simmstat.h> 49157299Smarcel 50157299Smarcel/* Useful debugging Stuff */ 51157299Smarcel#include <sys/nexusdebug.h> 52157299Smarcel 53157299Smarcel/* 54157299Smarcel * Function prototypes 55157299Smarcel */ 56157299Smarcel 57157299Smarcelstatic int simmstat_attach(dev_info_t *, ddi_attach_cmd_t); 58157299Smarcel 59167822Smarcelstatic int simmstat_detach(dev_info_t *, ddi_detach_cmd_t); 60157299Smarcel 61157299Smarcelstatic void simmstat_add_kstats(struct simmstat_soft_state *); 62157299Smarcel 63157299Smarcelstatic int simmstat_kstat_update(kstat_t *, int); 64178600Smarcel 65178600Smarcel/* 66178600Smarcel * Configuration data structures 67178600Smarcel */ 68178600Smarcelstatic struct cb_ops simmstat_cb_ops = { 69178600Smarcel nulldev, /* open */ 70178600Smarcel nulldev, /* close */ 71157299Smarcel nulldev, /* strategy */ 72157299Smarcel nulldev, /* print */ 73157299Smarcel nodev, /* dump */ 74178600Smarcel nulldev, /* read */ 75157299Smarcel nulldev, /* write */ 76157299Smarcel nulldev, /* ioctl */ 77157299Smarcel nodev, /* devmap */ 78157299Smarcel nodev, /* mmap */ 79157299Smarcel nodev, /* segmap */ 80157299Smarcel nochpoll, /* poll */ 81157299Smarcel ddi_prop_op, /* cb_prop_op */ 82157299Smarcel 0, /* streamtab */ 83157299Smarcel D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 84157299Smarcel CB_REV, /* rev */ 85157299Smarcel nodev, /* cb_aread */ 86157299Smarcel nodev /* cb_awrite */ 87157299Smarcel}; 88157299Smarcel 89157299Smarcelstatic struct dev_ops simmstat_ops = { 90157299Smarcel DEVO_REV, /* rev */ 91157299Smarcel 0, /* refcnt */ 92157299Smarcel ddi_no_info, /* getinfo */ 93157299Smarcel nulldev, /* identify */ 94157340Smarcel nulldev, /* probe */ 95157299Smarcel 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}; 102 103static uint_t simmstat_reg_read_delay_us = 10; 104 105/* 106 * Driver globals 107 */ 108void *simmstatp; 109 110extern struct mod_ops mod_driverops; 111 112static struct modldrv modldrv = { 113 &mod_driverops, /* module type, this one is a driver */ 114 "SIMM-status Leaf v%I%", /* module name */ 115 &simmstat_ops, /* driver ops */ 116}; 117 118static struct modlinkage modlinkage = { 119 MODREV_1, /* rev */ 120 (void *)&modldrv, 121 NULL 122}; 123 124#ifndef lint 125static char _depends_on[] = "drv/fhc"; 126#endif /* lint */ 127 128/* 129 * These are the module initialization routines. 130 */ 131 132int 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 144int 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 156int 157_info(struct modinfo *modinfop) 158{ 159 return (mod_info(&modlinkage, modinfop)); 160} 161 162static int 163simmstat_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, devi, 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 220bad: 221 ddi_soft_state_free(simmstatp, instance); 222 return (DDI_FAILURE); 223} 224 225/* ARGSUSED */ 226static int 227simmstat_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 270static void 271simmstat_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 297static int 298simmstat_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