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*11311SSurya.Prakki@Sun.COM * Copyright 2009 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 */
947656SSherry.Moore@Sun.COM nulldev, /* power */
957656SSherry.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 */
1107656SSherry.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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)1511708Sstevel _info(struct modinfo *modinfop)
1521708Sstevel {
1531708Sstevel return (mod_info(&modlinkage, modinfop));
1541708Sstevel }
1551708Sstevel
1561708Sstevel static int
sram_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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",
1887656SSherry.Moore@Sun.COM instance, OBP_BOARDNUM);
1891708Sstevel goto bad;
1901708Sstevel }
1911708Sstevel
1921708Sstevel DPRINTF(SRAM_ATTACH_DEBUG, ("sram%d: devi= 0x%p\n, "
193*11311SSurya.Prakki@Sun.COM " softsp=0x%p\n", instance, (void *)devi, (void *)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",
1997656SSherry.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,
2057656SSherry.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
sram_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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,
2597656SSherry.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
sram_add_kstats(struct sram_soft_state * softsp)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 "
3317656SSherry.Moore@Sun.COM "reset_size: 0x%x",
3327656SSherry.Moore@Sun.COM ddi_get_instance(softsp->dip),
3337656SSherry.Moore@Sun.COM reset_size);
3341708Sstevel return;
3351708Sstevel }
3361708Sstevel
3371708Sstevel /* create the reset-info kstat */
3381708Sstevel resetinfo_ksp = kstat_create("unix", 0,
3397656SSherry.Moore@Sun.COM RESETINFO_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
3407656SSherry.Moore@Sun.COM reset_size, KSTAT_FLAG_PERSISTENT);
3411708Sstevel
3421708Sstevel if (resetinfo_ksp == NULL) {
3431708Sstevel cmn_err(CE_WARN, "sram%d: kstat_create failed",
3447656SSherry.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