xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/sram.c (revision 89b43686)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
25*89b43686SBayard Bell  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
2603831d35Sstevel  */
2703831d35Sstevel 
2803831d35Sstevel 
2903831d35Sstevel #include <sys/types.h>
3003831d35Sstevel #include <sys/conf.h>
3103831d35Sstevel #include <sys/ddi.h>
3203831d35Sstevel #include <sys/sunddi.h>
3303831d35Sstevel #include <sys/ddi_impldefs.h>
3403831d35Sstevel #include <sys/obpdefs.h>
3503831d35Sstevel #include <sys/cmn_err.h>
3603831d35Sstevel #include <sys/errno.h>
3703831d35Sstevel #include <sys/kmem.h>
3803831d35Sstevel #include <sys/debug.h>
3903831d35Sstevel #include <sys/sysmacros.h>
4003831d35Sstevel #include <sys/autoconf.h>
4103831d35Sstevel #include <sys/modctl.h>
4203831d35Sstevel 
4303831d35Sstevel #include <sys/fhc.h>
4403831d35Sstevel #include <sys/sram.h>
4503831d35Sstevel #include <sys/promif.h>
4603831d35Sstevel 
4703831d35Sstevel /* Useful debugging Stuff */
4803831d35Sstevel #include <sys/nexusdebug.h>
4903831d35Sstevel 
5003831d35Sstevel /*
5103831d35Sstevel  * Function protoypes
5203831d35Sstevel  */
5303831d35Sstevel 
5403831d35Sstevel static int sram_attach(dev_info_t *, ddi_attach_cmd_t);
5503831d35Sstevel 
5603831d35Sstevel static int sram_detach(dev_info_t *, ddi_detach_cmd_t);
5703831d35Sstevel 
5803831d35Sstevel static void sram_add_kstats(struct sram_soft_state *);
5903831d35Sstevel 
6003831d35Sstevel /*
6103831d35Sstevel  * Configuration data structures
6203831d35Sstevel  */
6303831d35Sstevel static struct cb_ops sram_cb_ops = {
6403831d35Sstevel 	nulldev,			/* open */
6503831d35Sstevel 	nulldev,			/* close */
6603831d35Sstevel 	nulldev,			/* strategy */
6703831d35Sstevel 	nulldev,			/* print */
6803831d35Sstevel 	nodev,				/* dump */
6903831d35Sstevel 	nulldev,			/* read */
7003831d35Sstevel 	nulldev,			/* write */
7103831d35Sstevel 	nulldev,			/* ioctl */
7203831d35Sstevel 	nodev,				/* devmap */
7303831d35Sstevel 	nodev,				/* mmap */
7403831d35Sstevel 	nodev,				/* segmap */
7503831d35Sstevel 	nochpoll,			/* poll */
7603831d35Sstevel 	ddi_prop_op,			/* cb_prop_op */
7703831d35Sstevel 	0,				/* streamtab */
7803831d35Sstevel 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
7903831d35Sstevel 	CB_REV,				/* rev */
8003831d35Sstevel 	nodev,				/* cb_aread */
8103831d35Sstevel 	nodev				/* cb_awrite */
8203831d35Sstevel };
8303831d35Sstevel 
8403831d35Sstevel static struct dev_ops sram_ops = {
8503831d35Sstevel 	DEVO_REV,			/* rev */
8603831d35Sstevel 	0,				/* refcnt  */
8703831d35Sstevel 	ddi_no_info,			/* getinfo */
8803831d35Sstevel 	nulldev,			/* identify */
8903831d35Sstevel 	nulldev,			/* probe */
9003831d35Sstevel 	sram_attach,			/* attach */
9103831d35Sstevel 	sram_detach,			/* detach */
9203831d35Sstevel 	nulldev,			/* reset */
9303831d35Sstevel 	&sram_cb_ops,			/* cb_ops */
9403831d35Sstevel 	(struct bus_ops *)0,		/* bus_ops */
9519397407SSherry Moore 	nulldev,			/* power */
9619397407SSherry Moore 	ddi_quiesce_not_needed,			/* quiesce */
9703831d35Sstevel };
9803831d35Sstevel 
9903831d35Sstevel 
10003831d35Sstevel /*
10103831d35Sstevel  * Driver globals
10203831d35Sstevel  */
10303831d35Sstevel void *sramp;			/* sram soft state hook */
10403831d35Sstevel static struct kstat *resetinfo_ksp = NULL;
10503831d35Sstevel static int reset_info_created = 0;
10603831d35Sstevel 
10703831d35Sstevel extern struct mod_ops mod_driverops;
10803831d35Sstevel 
10903831d35Sstevel static struct modldrv modldrv = {
11003831d35Sstevel 	&mod_driverops,		/* Type of module.  This one is a driver */
11119397407SSherry Moore 	"Sram Leaf",		/* name of module */
11203831d35Sstevel 	&sram_ops,		/* driver ops */
11303831d35Sstevel };
11403831d35Sstevel 
11503831d35Sstevel static struct modlinkage modlinkage = {
11603831d35Sstevel 	MODREV_1,
11703831d35Sstevel 	(void *)&modldrv,
11803831d35Sstevel 	NULL
11903831d35Sstevel };
12003831d35Sstevel 
12103831d35Sstevel /*
12203831d35Sstevel  * These are the module initialization routines.
12303831d35Sstevel  */
12403831d35Sstevel 
12503831d35Sstevel int
_init(void)12603831d35Sstevel _init(void)
12703831d35Sstevel {
12803831d35Sstevel 	int error;
12903831d35Sstevel 
13003831d35Sstevel 	if ((error = ddi_soft_state_init(&sramp,
13103831d35Sstevel 	    sizeof (struct sram_soft_state), 1)) == 0 &&
13203831d35Sstevel 	    (error = mod_install(&modlinkage)) != 0)
13303831d35Sstevel 		ddi_soft_state_fini(&sramp);
13403831d35Sstevel 	return (error);
13503831d35Sstevel }
13603831d35Sstevel 
13703831d35Sstevel int
_fini(void)13803831d35Sstevel _fini(void)
13903831d35Sstevel {
14003831d35Sstevel 	int error;
14103831d35Sstevel 
14203831d35Sstevel 	if ((error = mod_remove(&modlinkage)) == 0)
14303831d35Sstevel 		ddi_soft_state_fini(&sramp);
14403831d35Sstevel 	return (error);
14503831d35Sstevel }
14603831d35Sstevel 
14703831d35Sstevel int
_info(struct modinfo * modinfop)14803831d35Sstevel _info(struct modinfo *modinfop)
14903831d35Sstevel {
15003831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
15103831d35Sstevel }
15203831d35Sstevel 
15303831d35Sstevel static int
sram_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)15403831d35Sstevel sram_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
15503831d35Sstevel {
15603831d35Sstevel 	int instance;
15703831d35Sstevel 	struct sram_soft_state *softsp;
15803831d35Sstevel 
15903831d35Sstevel 	switch (cmd) {
16003831d35Sstevel 	case DDI_ATTACH:
16103831d35Sstevel 		break;
16203831d35Sstevel 
16303831d35Sstevel 	case DDI_RESUME:
16403831d35Sstevel 		return (DDI_SUCCESS);
16503831d35Sstevel 
16603831d35Sstevel 	default:
16703831d35Sstevel 		return (DDI_FAILURE);
16803831d35Sstevel 	}
16903831d35Sstevel 
17003831d35Sstevel 	instance = ddi_get_instance(devi);
17103831d35Sstevel 
17203831d35Sstevel 	if (ddi_soft_state_zalloc(sramp, instance) != DDI_SUCCESS)
17303831d35Sstevel 		return (DDI_FAILURE);
17403831d35Sstevel 
17503831d35Sstevel 	softsp = ddi_get_soft_state(sramp, instance);
17603831d35Sstevel 
17703831d35Sstevel 	/* Set the dip in the soft state */
17803831d35Sstevel 	softsp->dip = devi;
17903831d35Sstevel 
18003831d35Sstevel 	/* get the board number from this devices parent. */
18103831d35Sstevel 	softsp->pdip = ddi_get_parent(softsp->dip);
18203831d35Sstevel 	if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
18303831d35Sstevel 	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
18403831d35Sstevel 		cmn_err(CE_WARN, "sram%d: unable to retrieve %s property",
18503831d35Sstevel 		    instance, OBP_BOARDNUM);
18603831d35Sstevel 		goto bad;
18703831d35Sstevel 	}
18803831d35Sstevel 
18903831d35Sstevel 	DPRINTF(SRAM_ATTACH_DEBUG, ("sram%d: devi= 0x%p\n, "
19007d06da5SSurya Prakki 	    " softsp=0x%p\n", instance, (void *)devi, (void *)softsp));
19103831d35Sstevel 
19203831d35Sstevel 	/* map in the registers for this device. */
19303831d35Sstevel 	if (ddi_map_regs(softsp->dip, 0,
19403831d35Sstevel 	    (caddr_t *)&softsp->sram_base, 0, 0)) {
19503831d35Sstevel 		cmn_err(CE_WARN, "sram%d: unable to map registers",
19603831d35Sstevel 		    instance);
19703831d35Sstevel 		goto bad;
19803831d35Sstevel 	}
19903831d35Sstevel 
20003831d35Sstevel 	/* nothing to suspend/resume here */
20103831d35Sstevel 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
20203831d35Sstevel 	    "pm-hardware-state", "no-suspend-resume");
20303831d35Sstevel 
20403831d35Sstevel 	/* create the kstats for this device. */
20503831d35Sstevel 	sram_add_kstats(softsp);
20603831d35Sstevel 
20703831d35Sstevel 	ddi_report_dev(devi);
20803831d35Sstevel 
20903831d35Sstevel 	return (DDI_SUCCESS);
21003831d35Sstevel 
21103831d35Sstevel bad:
21203831d35Sstevel 	ddi_soft_state_free(sramp, instance);
21303831d35Sstevel 	return (DDI_FAILURE);
21403831d35Sstevel }
21503831d35Sstevel 
21603831d35Sstevel /* ARGSUSED */
21703831d35Sstevel static int
sram_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)21803831d35Sstevel sram_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
21903831d35Sstevel {
22003831d35Sstevel 	int instance;
22103831d35Sstevel 	struct sram_soft_state *softsp;
22203831d35Sstevel 
22303831d35Sstevel 	/* get the instance of this devi */
22403831d35Sstevel 	instance = ddi_get_instance(devi);
22503831d35Sstevel 
22603831d35Sstevel 	/* get the soft state pointer for this device node */
22703831d35Sstevel 	softsp = ddi_get_soft_state(sramp, instance);
22803831d35Sstevel 
22903831d35Sstevel 	switch (cmd) {
23003831d35Sstevel 	case DDI_SUSPEND:
23103831d35Sstevel 		return (DDI_SUCCESS);
23203831d35Sstevel 
23303831d35Sstevel 	case DDI_DETACH:
23403831d35Sstevel 		(void) fhc_bdlist_lock(softsp->board);
23503831d35Sstevel 		if (fhc_bd_detachable(softsp->board))
23603831d35Sstevel 			break;
23703831d35Sstevel 		else
23803831d35Sstevel 			fhc_bdlist_unlock();
23903831d35Sstevel 		/* FALLTHROUGH */
24003831d35Sstevel 
24103831d35Sstevel 	default:
24203831d35Sstevel 		return (DDI_FAILURE);
24303831d35Sstevel 	}
24403831d35Sstevel 
24503831d35Sstevel 	fhc_bdlist_unlock();
24603831d35Sstevel 
24703831d35Sstevel 	/*
24803831d35Sstevel 	 * We do not remove the kstat here. There is only one instance for
24903831d35Sstevel 	 * the whole machine, and it must remain in existence while the
25003831d35Sstevel 	 * system is running.
25103831d35Sstevel 	 */
25203831d35Sstevel 
25303831d35Sstevel 
25403831d35Sstevel 	/* unmap the registers */
25503831d35Sstevel 	ddi_unmap_regs(softsp->dip, 0,
25603831d35Sstevel 	    (caddr_t *)&softsp->sram_base, 0, 0);
25703831d35Sstevel 
25803831d35Sstevel 	/* free the soft state structure */
25903831d35Sstevel 	ddi_soft_state_free(sramp, instance);
26003831d35Sstevel 
26103831d35Sstevel 	ddi_prop_remove_all(devi);
26203831d35Sstevel 
26303831d35Sstevel 	return (DDI_SUCCESS);
26403831d35Sstevel }
26503831d35Sstevel 
26603831d35Sstevel /*
26703831d35Sstevel  * The Reset-info structure passed up by POST has it's own kstat.
26803831d35Sstevel  * It only needs to get created once. So the first sram instance
26903831d35Sstevel  * that gets created will check for the OBP property 'reset-info'
27003831d35Sstevel  * in the root node of the OBP device tree. If this property exists,
27103831d35Sstevel  * then the reset-info kstat will get created. Otherwise it will
27203831d35Sstevel  * not get created. This will inform users whether or not a fatal
27303831d35Sstevel  * hardware reset has recently occurred.
27403831d35Sstevel  */
27503831d35Sstevel static void
sram_add_kstats(struct sram_soft_state * softsp)27603831d35Sstevel sram_add_kstats(struct sram_soft_state *softsp)
27703831d35Sstevel {
27803831d35Sstevel 	int reset_size;		/* size of data collected by POST */
27903831d35Sstevel 	char *ksptr;		/* memory pointer for byte copy */
28003831d35Sstevel 	char *srptr;		/* pointer to sram for byte copy */
28103831d35Sstevel 	int i;
28203831d35Sstevel 	union  {
28303831d35Sstevel 		char size[4];	/* copy in word byte-by-byte */
28403831d35Sstevel 		uint_t len;
28503831d35Sstevel 	} rst_size;
28603831d35Sstevel 
28703831d35Sstevel 	/*
28803831d35Sstevel 	 * only one reset_info kstat per system, so don't create it if
28903831d35Sstevel 	 * it exists already.
29003831d35Sstevel 	 */
29103831d35Sstevel 	if (reset_info_created) {
29203831d35Sstevel 		return;
29303831d35Sstevel 	}
29403831d35Sstevel 
29503831d35Sstevel 	/* mark that this code has been run. */
29603831d35Sstevel 	reset_info_created = 1;
29703831d35Sstevel 
29803831d35Sstevel 	/* does the root node have a 'fatal-reset-info' property? */
29903831d35Sstevel 	if (prom_getprop(prom_rootnode(), "fatal-reset-info",
30003831d35Sstevel 	    (caddr_t)&softsp->offset) == -1) {
30103831d35Sstevel 		return;
30203831d35Sstevel 	}
30303831d35Sstevel 
30403831d35Sstevel 	/* XXX - workaround for OBP bug */
30503831d35Sstevel 	softsp->reset_info = softsp->sram_base + softsp->offset;
30603831d35Sstevel 
30703831d35Sstevel 	/*
30803831d35Sstevel 	 * First read size. In case FW has not word aligned structure,
30903831d35Sstevel 	 * copy the unsigned int into a 4 byte union, then read it out as
31003831d35Sstevel 	 * an inteeger.
31103831d35Sstevel 	 */
31203831d35Sstevel 	for (i = 0, srptr = softsp->reset_info; i < 4; i++) {
31303831d35Sstevel 		rst_size.size[i] = *srptr++;
31403831d35Sstevel 	}
31503831d35Sstevel 	reset_size = rst_size.len;
31603831d35Sstevel 
31703831d35Sstevel 	/*
31803831d35Sstevel 	 * If the reset size is zero, then POST did not
31903831d35Sstevel 	 * record any info.
32003831d35Sstevel 	 */
32103831d35Sstevel 	if ((uint_t)reset_size == 0) {
32203831d35Sstevel 		return;
32303831d35Sstevel 	}
32403831d35Sstevel 
32503831d35Sstevel 	/* Check for illegal size values. */
32603831d35Sstevel 	if ((uint_t)reset_size > MX_RSTINFO_SZ) {
32703831d35Sstevel 		cmn_err(CE_NOTE, "sram%d: illegal "
32803831d35Sstevel 		    "reset_size: 0x%x",
32903831d35Sstevel 		    ddi_get_instance(softsp->dip),
33003831d35Sstevel 		    reset_size);
33103831d35Sstevel 		return;
33203831d35Sstevel 	}
33303831d35Sstevel 
33403831d35Sstevel 	/* create the reset-info kstat */
33503831d35Sstevel 	resetinfo_ksp = kstat_create("unix", 0,
33603831d35Sstevel 	    RESETINFO_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
33703831d35Sstevel 	    reset_size, KSTAT_FLAG_PERSISTENT);
33803831d35Sstevel 
33903831d35Sstevel 	if (resetinfo_ksp == NULL) {
34003831d35Sstevel 		cmn_err(CE_WARN, "sram%d: kstat_create failed",
34103831d35Sstevel 		    ddi_get_instance(softsp->dip));
34203831d35Sstevel 		return;
34303831d35Sstevel 	}
34403831d35Sstevel 
34503831d35Sstevel 	/*
34603831d35Sstevel 	 * now copy the data into kstat. Don't use block
34703831d35Sstevel 	 * copy, the local space sram does not support this.
34803831d35Sstevel 	 */
34903831d35Sstevel 	srptr = softsp->reset_info;
35003831d35Sstevel 
35103831d35Sstevel 	ksptr = (char *)resetinfo_ksp->ks_data;
35203831d35Sstevel 
35303831d35Sstevel 	for (i = 0; i < reset_size; i++) {
35403831d35Sstevel 		*ksptr++ = *srptr++;
35503831d35Sstevel 	}
35603831d35Sstevel 
35703831d35Sstevel 	kstat_install(resetinfo_ksp);
35803831d35Sstevel }
359