11708Sstevel /* 21708Sstevel * CDDL HEADER START 31708Sstevel * 41708Sstevel * The contents of this file are subject to the terms of the 51708Sstevel * Common Development and Distribution License (the "License"). 61708Sstevel * You may not use this file except in compliance with the License. 71708Sstevel * 81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91708Sstevel * or http://www.opensolaris.org/os/licensing. 101708Sstevel * See the License for the specific language governing permissions 111708Sstevel * and limitations under the License. 121708Sstevel * 131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each 141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151708Sstevel * If applicable, add the following below this CDDL HEADER, with the 161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 181708Sstevel * 191708Sstevel * CDDL HEADER END 201708Sstevel */ 211708Sstevel 221708Sstevel /* 23*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 241708Sstevel * Use is subject to license terms. 251708Sstevel */ 261708Sstevel 271708Sstevel 281708Sstevel #include <sys/types.h> 291708Sstevel #include <sys/conf.h> 301708Sstevel #include <sys/ddi.h> 311708Sstevel #include <sys/sunddi.h> 321708Sstevel #include <sys/ddi_impldefs.h> 331708Sstevel #include <sys/obpdefs.h> 341708Sstevel #include <sys/cmn_err.h> 351708Sstevel #include <sys/errno.h> 361708Sstevel #include <sys/kmem.h> 371708Sstevel #include <sys/debug.h> 381708Sstevel #include <sys/sysmacros.h> 391708Sstevel #include <sys/autoconf.h> 401708Sstevel #include <sys/modctl.h> 411708Sstevel 421708Sstevel #include <sys/fhc.h> 431708Sstevel #include <sys/sram.h> 441708Sstevel #include <sys/promif.h> 451708Sstevel 461708Sstevel /* Useful debugging Stuff */ 471708Sstevel #include <sys/nexusdebug.h> 481708Sstevel 491708Sstevel /* 501708Sstevel * Function protoypes 511708Sstevel */ 521708Sstevel 531708Sstevel static int sram_attach(dev_info_t *, ddi_attach_cmd_t); 541708Sstevel 551708Sstevel static int sram_detach(dev_info_t *, ddi_detach_cmd_t); 561708Sstevel 571708Sstevel static void sram_add_kstats(struct sram_soft_state *); 581708Sstevel 591708Sstevel /* 601708Sstevel * Configuration data structures 611708Sstevel */ 621708Sstevel static struct cb_ops sram_cb_ops = { 631708Sstevel nulldev, /* open */ 641708Sstevel nulldev, /* close */ 651708Sstevel nulldev, /* strategy */ 661708Sstevel nulldev, /* print */ 671708Sstevel nodev, /* dump */ 681708Sstevel nulldev, /* read */ 691708Sstevel nulldev, /* write */ 701708Sstevel nulldev, /* ioctl */ 711708Sstevel nodev, /* devmap */ 721708Sstevel nodev, /* mmap */ 731708Sstevel nodev, /* segmap */ 741708Sstevel nochpoll, /* poll */ 751708Sstevel ddi_prop_op, /* cb_prop_op */ 761708Sstevel 0, /* streamtab */ 771708Sstevel D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 781708Sstevel CB_REV, /* rev */ 791708Sstevel nodev, /* cb_aread */ 801708Sstevel nodev /* cb_awrite */ 811708Sstevel }; 821708Sstevel 831708Sstevel static struct dev_ops sram_ops = { 841708Sstevel DEVO_REV, /* rev */ 851708Sstevel 0, /* refcnt */ 861708Sstevel ddi_no_info, /* getinfo */ 871708Sstevel nulldev, /* identify */ 881708Sstevel nulldev, /* probe */ 891708Sstevel sram_attach, /* attach */ 901708Sstevel sram_detach, /* detach */ 911708Sstevel nulldev, /* reset */ 921708Sstevel &sram_cb_ops, /* cb_ops */ 931708Sstevel (struct bus_ops *)0, /* bus_ops */ 94*7656SSherry.Moore@Sun.COM nulldev, /* power */ 95*7656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 961708Sstevel }; 971708Sstevel 981708Sstevel 991708Sstevel /* 1001708Sstevel * Driver globals 1011708Sstevel */ 1021708Sstevel void *sramp; /* sram soft state hook */ 1031708Sstevel static struct kstat *resetinfo_ksp = NULL; 1041708Sstevel static int reset_info_created = 0; 1051708Sstevel 1061708Sstevel extern struct mod_ops mod_driverops; 1071708Sstevel 1081708Sstevel static struct modldrv modldrv = { 1091708Sstevel &mod_driverops, /* Type of module. This one is a driver */ 110*7656SSherry.Moore@Sun.COM "Sram Leaf", /* name of module */ 1111708Sstevel &sram_ops, /* driver ops */ 1121708Sstevel }; 1131708Sstevel 1141708Sstevel static struct modlinkage modlinkage = { 1151708Sstevel MODREV_1, 1161708Sstevel (void *)&modldrv, 1171708Sstevel NULL 1181708Sstevel }; 1191708Sstevel 1201708Sstevel #ifndef lint 1211708Sstevel char _depends_on[] = "drv/fhc"; 1221708Sstevel #endif /* lint */ 1231708Sstevel 1241708Sstevel /* 1251708Sstevel * These are the module initialization routines. 1261708Sstevel */ 1271708Sstevel 1281708Sstevel int 1291708Sstevel _init(void) 1301708Sstevel { 1311708Sstevel int error; 1321708Sstevel 1331708Sstevel if ((error = ddi_soft_state_init(&sramp, 1341708Sstevel sizeof (struct sram_soft_state), 1)) == 0 && 1351708Sstevel (error = mod_install(&modlinkage)) != 0) 1361708Sstevel ddi_soft_state_fini(&sramp); 1371708Sstevel return (error); 1381708Sstevel } 1391708Sstevel 1401708Sstevel int 1411708Sstevel _fini(void) 1421708Sstevel { 1431708Sstevel int error; 1441708Sstevel 1451708Sstevel if ((error = mod_remove(&modlinkage)) == 0) 1461708Sstevel ddi_soft_state_fini(&sramp); 1471708Sstevel return (error); 1481708Sstevel } 1491708Sstevel 1501708Sstevel int 1511708Sstevel _info(struct modinfo *modinfop) 1521708Sstevel { 1531708Sstevel return (mod_info(&modlinkage, modinfop)); 1541708Sstevel } 1551708Sstevel 1561708Sstevel static int 1571708Sstevel sram_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 1581708Sstevel { 1591708Sstevel int instance; 1601708Sstevel struct sram_soft_state *softsp; 1611708Sstevel 1621708Sstevel switch (cmd) { 1631708Sstevel case DDI_ATTACH: 1641708Sstevel break; 1651708Sstevel 1661708Sstevel case DDI_RESUME: 1671708Sstevel return (DDI_SUCCESS); 1681708Sstevel 1691708Sstevel default: 1701708Sstevel return (DDI_FAILURE); 1711708Sstevel } 1721708Sstevel 1731708Sstevel instance = ddi_get_instance(devi); 1741708Sstevel 1751708Sstevel if (ddi_soft_state_zalloc(sramp, instance) != DDI_SUCCESS) 1761708Sstevel return (DDI_FAILURE); 1771708Sstevel 1781708Sstevel softsp = ddi_get_soft_state(sramp, instance); 1791708Sstevel 1801708Sstevel /* Set the dip in the soft state */ 1811708Sstevel softsp->dip = devi; 1821708Sstevel 1831708Sstevel /* get the board number from this devices parent. */ 1841708Sstevel softsp->pdip = ddi_get_parent(softsp->dip); 1851708Sstevel if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip, 1861708Sstevel DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) { 1871708Sstevel cmn_err(CE_WARN, "sram%d: unable to retrieve %s property", 188*7656SSherry.Moore@Sun.COM instance, OBP_BOARDNUM); 1891708Sstevel goto bad; 1901708Sstevel } 1911708Sstevel 1921708Sstevel DPRINTF(SRAM_ATTACH_DEBUG, ("sram%d: devi= 0x%p\n, " 193*7656SSherry.Moore@Sun.COM " softsp=0x%p\n", instance, devi, softsp)); 1941708Sstevel 1951708Sstevel /* map in the registers for this device. */ 1961708Sstevel if (ddi_map_regs(softsp->dip, 0, 1971708Sstevel (caddr_t *)&softsp->sram_base, 0, 0)) { 1981708Sstevel cmn_err(CE_WARN, "sram%d: unable to map registers", 199*7656SSherry.Moore@Sun.COM instance); 2001708Sstevel goto bad; 2011708Sstevel } 2021708Sstevel 2031708Sstevel /* nothing to suspend/resume here */ 2041708Sstevel (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 205*7656SSherry.Moore@Sun.COM "pm-hardware-state", "no-suspend-resume"); 2061708Sstevel 2071708Sstevel /* create the kstats for this device. */ 2081708Sstevel sram_add_kstats(softsp); 2091708Sstevel 2101708Sstevel ddi_report_dev(devi); 2111708Sstevel 2121708Sstevel return (DDI_SUCCESS); 2131708Sstevel 2141708Sstevel bad: 2151708Sstevel ddi_soft_state_free(sramp, instance); 2161708Sstevel return (DDI_FAILURE); 2171708Sstevel } 2181708Sstevel 2191708Sstevel /* ARGSUSED */ 2201708Sstevel static int 2211708Sstevel sram_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 2221708Sstevel { 2231708Sstevel int instance; 2241708Sstevel struct sram_soft_state *softsp; 2251708Sstevel 2261708Sstevel /* get the instance of this devi */ 2271708Sstevel instance = ddi_get_instance(devi); 2281708Sstevel 2291708Sstevel /* get the soft state pointer for this device node */ 2301708Sstevel softsp = ddi_get_soft_state(sramp, instance); 2311708Sstevel 2321708Sstevel switch (cmd) { 2331708Sstevel case DDI_SUSPEND: 2341708Sstevel return (DDI_SUCCESS); 2351708Sstevel 2361708Sstevel case DDI_DETACH: 2371708Sstevel (void) fhc_bdlist_lock(softsp->board); 2381708Sstevel if (fhc_bd_detachable(softsp->board)) 2391708Sstevel break; 2401708Sstevel else 2411708Sstevel fhc_bdlist_unlock(); 2421708Sstevel /* FALLTHROUGH */ 2431708Sstevel 2441708Sstevel default: 2451708Sstevel return (DDI_FAILURE); 2461708Sstevel } 2471708Sstevel 2481708Sstevel fhc_bdlist_unlock(); 2491708Sstevel 2501708Sstevel /* 2511708Sstevel * We do not remove the kstat here. There is only one instance for 2521708Sstevel * the whole machine, and it must remain in existence while the 2531708Sstevel * system is running. 2541708Sstevel */ 2551708Sstevel 2561708Sstevel 2571708Sstevel /* unmap the registers */ 2581708Sstevel ddi_unmap_regs(softsp->dip, 0, 259*7656SSherry.Moore@Sun.COM (caddr_t *)&softsp->sram_base, 0, 0); 2601708Sstevel 2611708Sstevel /* free the soft state structure */ 2621708Sstevel ddi_soft_state_free(sramp, instance); 2631708Sstevel 2641708Sstevel ddi_prop_remove_all(devi); 2651708Sstevel 2661708Sstevel return (DDI_SUCCESS); 2671708Sstevel } 2681708Sstevel 2691708Sstevel /* 2701708Sstevel * The Reset-info structure passed up by POST has it's own kstat. 2711708Sstevel * It only needs to get created once. So the first sram instance 2721708Sstevel * that gets created will check for the OBP property 'reset-info' 2731708Sstevel * in the root node of the OBP device tree. If this property exists, 2741708Sstevel * then the reset-info kstat will get created. Otherwise it will 2751708Sstevel * not get created. This will inform users whether or not a fatal 2761708Sstevel * hardware reset has recently occurred. 2771708Sstevel */ 2781708Sstevel static void 2791708Sstevel sram_add_kstats(struct sram_soft_state *softsp) 2801708Sstevel { 2811708Sstevel int reset_size; /* size of data collected by POST */ 2821708Sstevel char *ksptr; /* memory pointer for byte copy */ 2831708Sstevel char *srptr; /* pointer to sram for byte copy */ 2841708Sstevel int i; 2851708Sstevel union { 2861708Sstevel char size[4]; /* copy in word byte-by-byte */ 2871708Sstevel uint_t len; 2881708Sstevel } rst_size; 2891708Sstevel 2901708Sstevel /* 2911708Sstevel * only one reset_info kstat per system, so don't create it if 2921708Sstevel * it exists already. 2931708Sstevel */ 2941708Sstevel if (reset_info_created) { 2951708Sstevel return; 2961708Sstevel } 2971708Sstevel 2981708Sstevel /* mark that this code has been run. */ 2991708Sstevel reset_info_created = 1; 3001708Sstevel 3011708Sstevel /* does the root node have a 'fatal-reset-info' property? */ 3021708Sstevel if (prom_getprop(prom_rootnode(), "fatal-reset-info", 3031708Sstevel (caddr_t)&softsp->offset) == -1) { 3041708Sstevel return; 3051708Sstevel } 3061708Sstevel 3071708Sstevel /* XXX - workaround for OBP bug */ 3081708Sstevel softsp->reset_info = softsp->sram_base + softsp->offset; 3091708Sstevel 3101708Sstevel /* 3111708Sstevel * First read size. In case FW has not word aligned structure, 3121708Sstevel * copy the unsigned int into a 4 byte union, then read it out as 3131708Sstevel * an inteeger. 3141708Sstevel */ 3151708Sstevel for (i = 0, srptr = softsp->reset_info; i < 4; i++) { 3161708Sstevel rst_size.size[i] = *srptr++; 3171708Sstevel } 3181708Sstevel reset_size = rst_size.len; 3191708Sstevel 3201708Sstevel /* 3211708Sstevel * If the reset size is zero, then POST did not 3221708Sstevel * record any info. 3231708Sstevel */ 3241708Sstevel if ((uint_t)reset_size == 0) { 3251708Sstevel return; 3261708Sstevel } 3271708Sstevel 3281708Sstevel /* Check for illegal size values. */ 3291708Sstevel if ((uint_t)reset_size > MX_RSTINFO_SZ) { 3301708Sstevel cmn_err(CE_NOTE, "sram%d: illegal " 331*7656SSherry.Moore@Sun.COM "reset_size: 0x%x", 332*7656SSherry.Moore@Sun.COM ddi_get_instance(softsp->dip), 333*7656SSherry.Moore@Sun.COM reset_size); 3341708Sstevel return; 3351708Sstevel } 3361708Sstevel 3371708Sstevel /* create the reset-info kstat */ 3381708Sstevel resetinfo_ksp = kstat_create("unix", 0, 339*7656SSherry.Moore@Sun.COM RESETINFO_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, 340*7656SSherry.Moore@Sun.COM reset_size, KSTAT_FLAG_PERSISTENT); 3411708Sstevel 3421708Sstevel if (resetinfo_ksp == NULL) { 3431708Sstevel cmn_err(CE_WARN, "sram%d: kstat_create failed", 344*7656SSherry.Moore@Sun.COM ddi_get_instance(softsp->dip)); 3451708Sstevel return; 3461708Sstevel } 3471708Sstevel 3481708Sstevel /* 3491708Sstevel * now copy the data into kstat. Don't use block 3501708Sstevel * copy, the local space sram does not support this. 3511708Sstevel */ 3521708Sstevel srptr = softsp->reset_info; 3531708Sstevel 3541708Sstevel ksptr = (char *)resetinfo_ksp->ks_data; 3551708Sstevel 3561708Sstevel for (i = 0; i < reset_size; i++) { 3571708Sstevel *ksptr++ = *srptr++; 3581708Sstevel } 3591708Sstevel 3601708Sstevel kstat_install(resetinfo_ksp); 3611708Sstevel } 362