129949e86Sstevel /* 229949e86Sstevel * CDDL HEADER START 329949e86Sstevel * 429949e86Sstevel * The contents of this file are subject to the terms of the 529949e86Sstevel * Common Development and Distribution License (the "License"). 629949e86Sstevel * You may not use this file except in compliance with the License. 729949e86Sstevel * 829949e86Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 929949e86Sstevel * or http://www.opensolaris.org/os/licensing. 1029949e86Sstevel * See the License for the specific language governing permissions 1129949e86Sstevel * and limitations under the License. 1229949e86Sstevel * 1329949e86Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1429949e86Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1529949e86Sstevel * If applicable, add the following below this CDDL HEADER, with the 1629949e86Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1729949e86Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1829949e86Sstevel * 1929949e86Sstevel * CDDL HEADER END 2029949e86Sstevel */ 2129949e86Sstevel 2229949e86Sstevel /* 2307d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2429949e86Sstevel * Use is subject to license terms. 25*89b43686SBayard Bell * Copyright (c) 2011 Bayard G. Bell. All rights reserved. 2629949e86Sstevel */ 2729949e86Sstevel 2829949e86Sstevel 2929949e86Sstevel #include <sys/types.h> 3029949e86Sstevel #include <sys/conf.h> 3129949e86Sstevel #include <sys/ddi.h> 3229949e86Sstevel #include <sys/sunddi.h> 3329949e86Sstevel #include <sys/ddi_impldefs.h> 3429949e86Sstevel #include <sys/obpdefs.h> 3529949e86Sstevel #include <sys/cmn_err.h> 3629949e86Sstevel #include <sys/errno.h> 3729949e86Sstevel #include <sys/kmem.h> 3829949e86Sstevel #include <sys/debug.h> 3929949e86Sstevel #include <sys/sysmacros.h> 4029949e86Sstevel #include <sys/ivintr.h> 4129949e86Sstevel #include <sys/intr.h> 4229949e86Sstevel #include <sys/intreg.h> 4329949e86Sstevel #include <sys/autoconf.h> 4429949e86Sstevel #include <sys/modctl.h> 4529949e86Sstevel #include <sys/spl.h> 4629949e86Sstevel 4729949e86Sstevel #include <sys/fhc.h> 4829949e86Sstevel #include <sys/simmstat.h> 4929949e86Sstevel 5029949e86Sstevel /* Useful debugging Stuff */ 5129949e86Sstevel #include <sys/nexusdebug.h> 5229949e86Sstevel 5329949e86Sstevel /* 5429949e86Sstevel * Function prototypes 5529949e86Sstevel */ 5629949e86Sstevel 5729949e86Sstevel static int simmstat_attach(dev_info_t *, ddi_attach_cmd_t); 5829949e86Sstevel 5929949e86Sstevel static int simmstat_detach(dev_info_t *, ddi_detach_cmd_t); 6029949e86Sstevel 6129949e86Sstevel static void simmstat_add_kstats(struct simmstat_soft_state *); 6229949e86Sstevel 6329949e86Sstevel static int simmstat_kstat_update(kstat_t *, int); 6429949e86Sstevel 6529949e86Sstevel /* 6629949e86Sstevel * Configuration data structures 6729949e86Sstevel */ 6829949e86Sstevel static struct cb_ops simmstat_cb_ops = { 6929949e86Sstevel nulldev, /* open */ 7029949e86Sstevel nulldev, /* close */ 7129949e86Sstevel nulldev, /* strategy */ 7229949e86Sstevel nulldev, /* print */ 7329949e86Sstevel nodev, /* dump */ 7429949e86Sstevel nulldev, /* read */ 7529949e86Sstevel nulldev, /* write */ 7629949e86Sstevel nulldev, /* ioctl */ 7729949e86Sstevel nodev, /* devmap */ 7829949e86Sstevel nodev, /* mmap */ 7929949e86Sstevel nodev, /* segmap */ 8029949e86Sstevel nochpoll, /* poll */ 8129949e86Sstevel ddi_prop_op, /* cb_prop_op */ 8229949e86Sstevel 0, /* streamtab */ 8329949e86Sstevel D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 8429949e86Sstevel CB_REV, /* rev */ 8529949e86Sstevel nodev, /* cb_aread */ 8629949e86Sstevel nodev /* cb_awrite */ 8729949e86Sstevel }; 8829949e86Sstevel 8929949e86Sstevel static struct dev_ops simmstat_ops = { 9029949e86Sstevel DEVO_REV, /* rev */ 9129949e86Sstevel 0, /* refcnt */ 9229949e86Sstevel ddi_no_info, /* getinfo */ 9329949e86Sstevel nulldev, /* identify */ 9429949e86Sstevel nulldev, /* probe */ 9529949e86Sstevel simmstat_attach, /* attach */ 9629949e86Sstevel simmstat_detach, /* detach */ 9729949e86Sstevel nulldev, /* reset */ 9829949e86Sstevel &simmstat_cb_ops, /* cb_ops */ 9929949e86Sstevel (struct bus_ops *)0, /* bus_ops */ 10019397407SSherry Moore nulldev, /* power */ 10119397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 10229949e86Sstevel }; 10329949e86Sstevel 10429949e86Sstevel static uint_t simmstat_reg_read_delay_us = 10; 10529949e86Sstevel 10629949e86Sstevel /* 10729949e86Sstevel * Driver globals 10829949e86Sstevel */ 10929949e86Sstevel void *simmstatp; 11029949e86Sstevel 11129949e86Sstevel extern struct mod_ops mod_driverops; 11229949e86Sstevel 11329949e86Sstevel static struct modldrv modldrv = { 11429949e86Sstevel &mod_driverops, /* module type, this one is a driver */ 11519397407SSherry Moore "SIMM-status Leaf", /* module name */ 11629949e86Sstevel &simmstat_ops, /* driver ops */ 11729949e86Sstevel }; 11829949e86Sstevel 11929949e86Sstevel static struct modlinkage modlinkage = { 12029949e86Sstevel MODREV_1, /* rev */ 12129949e86Sstevel (void *)&modldrv, 12229949e86Sstevel NULL 12329949e86Sstevel }; 12429949e86Sstevel 12529949e86Sstevel /* 12629949e86Sstevel * These are the module initialization routines. 12729949e86Sstevel */ 12829949e86Sstevel 12929949e86Sstevel int 13029949e86Sstevel _init(void) 13129949e86Sstevel { 13229949e86Sstevel int error; 13329949e86Sstevel 13429949e86Sstevel if ((error = ddi_soft_state_init(&simmstatp, 13529949e86Sstevel sizeof (struct simmstat_soft_state), 1)) != 0) 13629949e86Sstevel return (error); 13729949e86Sstevel 13829949e86Sstevel return (mod_install(&modlinkage)); 13929949e86Sstevel } 14029949e86Sstevel 14129949e86Sstevel int 14229949e86Sstevel _fini(void) 14329949e86Sstevel { 14429949e86Sstevel int error; 14529949e86Sstevel 14629949e86Sstevel if ((error = mod_remove(&modlinkage)) != 0) 14729949e86Sstevel return (error); 14829949e86Sstevel 14929949e86Sstevel ddi_soft_state_fini(&simmstatp); 15029949e86Sstevel return (0); 15129949e86Sstevel } 15229949e86Sstevel 15329949e86Sstevel int 15429949e86Sstevel _info(struct modinfo *modinfop) 15529949e86Sstevel { 15629949e86Sstevel return (mod_info(&modlinkage, modinfop)); 15729949e86Sstevel } 15829949e86Sstevel 15929949e86Sstevel static int 16029949e86Sstevel simmstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 16129949e86Sstevel { 16229949e86Sstevel struct simmstat_soft_state *softsp; 16329949e86Sstevel int instance; 16429949e86Sstevel 16529949e86Sstevel switch (cmd) { 16629949e86Sstevel case DDI_ATTACH: 16729949e86Sstevel break; 16829949e86Sstevel 16929949e86Sstevel case DDI_RESUME: 17029949e86Sstevel return (DDI_SUCCESS); 17129949e86Sstevel 17229949e86Sstevel default: 17329949e86Sstevel return (DDI_FAILURE); 17429949e86Sstevel } 17529949e86Sstevel 17629949e86Sstevel instance = ddi_get_instance(devi); 17729949e86Sstevel 17829949e86Sstevel if (ddi_soft_state_zalloc(simmstatp, instance) != DDI_SUCCESS) 17929949e86Sstevel return (DDI_FAILURE); 18029949e86Sstevel 18129949e86Sstevel softsp = ddi_get_soft_state(simmstatp, instance); 18229949e86Sstevel 18329949e86Sstevel /* Set the dip in the soft state */ 18429949e86Sstevel softsp->dip = devi; 18529949e86Sstevel 18629949e86Sstevel /* Get the board number from this nodes parent device. */ 18729949e86Sstevel softsp->pdip = ddi_get_parent(softsp->dip); 18829949e86Sstevel if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip, 18929949e86Sstevel DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) { 19029949e86Sstevel cmn_err(CE_WARN, "simmstat%d: unable to retrieve %s property", 19129949e86Sstevel instance, OBP_BOARDNUM); 19229949e86Sstevel goto bad; 19329949e86Sstevel } 19429949e86Sstevel 19529949e86Sstevel DPRINTF(SIMMSTAT_ATTACH_DEBUG, ("simmstat%d: devi= 0x%p\n, " 19607d06da5SSurya Prakki " softsp=0x%p\n", instance, (void *)devi, (void *)softsp)); 19729949e86Sstevel 19829949e86Sstevel /* map in the registers for this device. */ 19929949e86Sstevel if (ddi_map_regs(softsp->dip, 0, 20029949e86Sstevel (caddr_t *)&softsp->simmstat_base, 0, 0)) { 20129949e86Sstevel cmn_err(CE_WARN, "simmstat%d: unable to map registers", 20229949e86Sstevel instance); 20329949e86Sstevel goto bad; 20429949e86Sstevel } 20529949e86Sstevel 20629949e86Sstevel /* nothing to suspend/resume here */ 20729949e86Sstevel (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 20829949e86Sstevel "pm-hardware-state", "no-suspend-resume"); 20929949e86Sstevel 21029949e86Sstevel /* create the kstats for this device */ 21129949e86Sstevel simmstat_add_kstats(softsp); 21229949e86Sstevel 21329949e86Sstevel ddi_report_dev(devi); 21429949e86Sstevel 21529949e86Sstevel return (DDI_SUCCESS); 21629949e86Sstevel 21729949e86Sstevel bad: 21829949e86Sstevel ddi_soft_state_free(simmstatp, instance); 21929949e86Sstevel return (DDI_FAILURE); 22029949e86Sstevel } 22129949e86Sstevel 22229949e86Sstevel /* ARGSUSED */ 22329949e86Sstevel static int 22429949e86Sstevel simmstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 22529949e86Sstevel { 22629949e86Sstevel int instance; 22729949e86Sstevel struct simmstat_soft_state *softsp; 22829949e86Sstevel 22929949e86Sstevel /* get the instance of this devi */ 23029949e86Sstevel instance = ddi_get_instance(devi); 23129949e86Sstevel 23229949e86Sstevel /* get the soft state pointer for this device node */ 23329949e86Sstevel softsp = ddi_get_soft_state(simmstatp, instance); 23429949e86Sstevel 23529949e86Sstevel switch (cmd) { 23629949e86Sstevel case DDI_SUSPEND: 23729949e86Sstevel return (DDI_SUCCESS); 23829949e86Sstevel 23929949e86Sstevel case DDI_DETACH: 24029949e86Sstevel (void) fhc_bdlist_lock(softsp->board); 24129949e86Sstevel if (fhc_bd_detachable(softsp->board)) 24229949e86Sstevel break; 24329949e86Sstevel else 24429949e86Sstevel fhc_bdlist_unlock(); 24529949e86Sstevel /* FALLTHROUGH */ 24629949e86Sstevel 24729949e86Sstevel default: 24829949e86Sstevel return (DDI_FAILURE); 24929949e86Sstevel } 25029949e86Sstevel 25129949e86Sstevel fhc_bdlist_unlock(); 25229949e86Sstevel 25329949e86Sstevel /* remove the kstat for this board */ 25429949e86Sstevel kstat_delete(softsp->simmstat_ksp); 25529949e86Sstevel 25629949e86Sstevel /* unmap the registers */ 25729949e86Sstevel ddi_unmap_regs(softsp->dip, 0, 25829949e86Sstevel (caddr_t *)&softsp->simmstat_base, 0, 0); 25929949e86Sstevel 26029949e86Sstevel /* free up the soft state */ 26129949e86Sstevel ddi_soft_state_free(simmstatp, instance); 26229949e86Sstevel ddi_prop_remove_all(devi); 26329949e86Sstevel 26429949e86Sstevel return (DDI_SUCCESS); 26529949e86Sstevel } 26629949e86Sstevel 26729949e86Sstevel static void 26829949e86Sstevel simmstat_add_kstats(struct simmstat_soft_state *softsp) 26929949e86Sstevel { 27029949e86Sstevel struct kstat *simmstat_ksp; 27129949e86Sstevel 27229949e86Sstevel if ((simmstat_ksp = kstat_create("unix", softsp->board, 27329949e86Sstevel SIMMSTAT_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, 27429949e86Sstevel SIMM_COUNT, KSTAT_FLAG_PERSISTENT)) == NULL) { 27529949e86Sstevel cmn_err(CE_WARN, "simmstat%d: kstat_create failed", 27629949e86Sstevel ddi_get_instance(softsp->dip)); 27729949e86Sstevel } 27829949e86Sstevel 27929949e86Sstevel simmstat_ksp->ks_update = simmstat_kstat_update; 28029949e86Sstevel simmstat_ksp->ks_private = (void *)softsp; 28129949e86Sstevel softsp->simmstat_ksp = simmstat_ksp; 28229949e86Sstevel kstat_install(simmstat_ksp); 28329949e86Sstevel } 28429949e86Sstevel 28529949e86Sstevel /* 28629949e86Sstevel * Kstats only need ks_update functions when they change dynamically 28729949e86Sstevel * at run time. 28829949e86Sstevel * In the case of the simmstat registers, they contain battery 28929949e86Sstevel * information for NVSIMMs. These need to be updated whenever a 29029949e86Sstevel * kstat_read asks for the data. There is currently no plan to 29129949e86Sstevel * ship NVSIMMs on this platform, but this support must be present. 29229949e86Sstevel */ 29329949e86Sstevel 29429949e86Sstevel static int 29529949e86Sstevel simmstat_kstat_update(kstat_t *ksp, int rw) 29629949e86Sstevel { 29729949e86Sstevel struct simmstat_soft_state *softsp; 29829949e86Sstevel volatile char *statp; /* pointer to hardware register */ 29929949e86Sstevel char *kstatp; /* pointer to kstat data buffer */ 30029949e86Sstevel int i; 30129949e86Sstevel 30229949e86Sstevel kstatp = (char *)ksp->ks_data; 30329949e86Sstevel softsp = (struct simmstat_soft_state *)ksp->ks_private; 30429949e86Sstevel 30529949e86Sstevel statp = (char *)softsp->simmstat_base; 30629949e86Sstevel 30729949e86Sstevel /* this is a read-only kstat. Bail out on a write */ 30829949e86Sstevel if (rw == KSTAT_WRITE) { 30929949e86Sstevel return (EACCES); 31029949e86Sstevel } else { 31129949e86Sstevel 31229949e86Sstevel /* 31329949e86Sstevel * copy current status of hardware into the kstat 31429949e86Sstevel * structure. 31529949e86Sstevel */ 31629949e86Sstevel for (i = 0; i < SIMM_COUNT; i++, statp++, kstatp++) { 31729949e86Sstevel *kstatp = *statp; 31829949e86Sstevel DELAY(simmstat_reg_read_delay_us); 31929949e86Sstevel } 32029949e86Sstevel } 32129949e86Sstevel return (0); 32229949e86Sstevel } 323