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