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 /*
23*19397407SSherry Moore  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel 
2803831d35Sstevel /*
2903831d35Sstevel  * PCI SBBC Device Driver that provides interfaces into
3003831d35Sstevel  * EPLD and IO-SRAM
3103831d35Sstevel  *
3203831d35Sstevel  */
3303831d35Sstevel #include <sys/types.h>
3403831d35Sstevel #include <sys/param.h>
3503831d35Sstevel #include <sys/errno.h>
3603831d35Sstevel #include <sys/file.h>
3703831d35Sstevel #include <sys/cmn_err.h>
3803831d35Sstevel #include <sys/stropts.h>
3903831d35Sstevel #include <sys/kmem.h>
4003831d35Sstevel #include <sys/sunndi.h>
4103831d35Sstevel #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
4203831d35Sstevel #include <sys/modctl.h>		/* for modldrv */
4303831d35Sstevel #include <sys/promif.h>
4403831d35Sstevel #include <sys/stat.h>
4503831d35Sstevel #include <sys/ddi.h>
4603831d35Sstevel 
4703831d35Sstevel #include <sys/serengeti.h>
4803831d35Sstevel #include <sys/sgsbbc_priv.h>
4903831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
5003831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h>
5103831d35Sstevel 
5203831d35Sstevel #ifdef DEBUG
5303831d35Sstevel /* debug flag */
5403831d35Sstevel uint_t sgsbbc_debug = 0;
5503831d35Sstevel #endif /* DEBUG */
5603831d35Sstevel 
5703831d35Sstevel /* driver entry point fn definitions */
5803831d35Sstevel static int	sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
5903831d35Sstevel static int	sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
6003831d35Sstevel 
6103831d35Sstevel /*
6203831d35Sstevel  * SBBC soft state hook
6303831d35Sstevel  */
6403831d35Sstevel static void    *sbbcp;
6503831d35Sstevel 
6603831d35Sstevel /*
6703831d35Sstevel  * Chosen IOSRAM
6803831d35Sstevel  */
6903831d35Sstevel struct chosen_iosram *master_iosram = NULL;
7003831d35Sstevel 
7103831d35Sstevel /*
7203831d35Sstevel  * define new iosram's sbbc and liked list of sbbc.
7303831d35Sstevel  */
7403831d35Sstevel struct sbbc_softstate *sgsbbc_instances = NULL;
7503831d35Sstevel 
7603831d35Sstevel /*
7703831d35Sstevel  * At attach time, check if the device is the 'chosen' node
7803831d35Sstevel  * if it is, set up the IOSRAM Solaris<->SC Comm tunnel
7903831d35Sstevel  * Its like 'Highlander' - there can be only one !
8003831d35Sstevel  */
8103831d35Sstevel static int	master_chosen = FALSE;
8203831d35Sstevel kmutex_t	chosen_lock;
8303831d35Sstevel 
8403831d35Sstevel /*
8503831d35Sstevel  * Local variable to save intr_in_enabled when the driver is suspended
8603831d35Sstevel  */
8703831d35Sstevel static uint32_t	intr_in_enabled;
8803831d35Sstevel 
8903831d35Sstevel /*
9003831d35Sstevel  * Local declarations
9103831d35Sstevel  */
9203831d35Sstevel static void	softsp_init(sbbc_softstate_t *, dev_info_t *);
9303831d35Sstevel static void	sbbc_chosen_init(sbbc_softstate_t *);
9403831d35Sstevel static void	sbbc_add_instance(sbbc_softstate_t *);
9503831d35Sstevel static void	sbbc_remove_instance(sbbc_softstate_t *);
9603831d35Sstevel static int	sbbc_find_dip(dev_info_t *, void *);
9703831d35Sstevel static void	sbbc_unmap_regs(sbbc_softstate_t *);
9803831d35Sstevel 
9903831d35Sstevel /*
10003831d35Sstevel  * ops stuff.
10103831d35Sstevel  */
10203831d35Sstevel static struct cb_ops sbbc_cb_ops = {
10303831d35Sstevel 	nodev,					/* cb_open */
10403831d35Sstevel 	nodev,					/* cb_close */
10503831d35Sstevel 	nodev,					/* cb_strategy */
10603831d35Sstevel 	nodev,					/* cb_print */
10703831d35Sstevel 	nodev,					/* cb_dump */
10803831d35Sstevel 	nodev,					/* cb_read */
10903831d35Sstevel 	nodev,					/* cb_write */
11003831d35Sstevel 	nodev,					/* cb_ioctl */
11103831d35Sstevel 	nodev,					/* cb_devmap */
11203831d35Sstevel 	nodev,					/* cb_mmap */
11303831d35Sstevel 	nodev,					/* cb_segmap */
11403831d35Sstevel 	nochpoll,				/* cb_chpoll */
11503831d35Sstevel 	ddi_prop_op,				/* cb_prop_op */
11603831d35Sstevel 	NULL,					/* cb_stream */
11703831d35Sstevel 	D_NEW | D_MP				/* cb_flag */
11803831d35Sstevel };
11903831d35Sstevel 
12003831d35Sstevel /*
12103831d35Sstevel  * Declare ops vectors for auto configuration.
12203831d35Sstevel  */
12303831d35Sstevel struct dev_ops  sbbc_ops = {
12403831d35Sstevel 	DEVO_REV,		/* devo_rev */
12503831d35Sstevel 	0,			/* devo_refcnt */
12603831d35Sstevel 	ddi_getinfo_1to1,	/* devo_getinfo */
12703831d35Sstevel 	nulldev,		/* devo_identify */
12803831d35Sstevel 	nulldev,		/* devo_probe */
12903831d35Sstevel 	sbbc_attach,		/* devo_attach */
13003831d35Sstevel 	sbbc_detach,		/* devo_detach */
13103831d35Sstevel 	nodev,			/* devo_reset */
13203831d35Sstevel 	&sbbc_cb_ops,		/* devo_cb_ops */
13303831d35Sstevel 	(struct bus_ops *)NULL,	/* devo_bus_ops */
134*19397407SSherry Moore 	nulldev,		/* devo_power */
135*19397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
13603831d35Sstevel };
13703831d35Sstevel 
13803831d35Sstevel /*
13903831d35Sstevel  * Loadable module support.
14003831d35Sstevel  */
14103831d35Sstevel extern struct mod_ops mod_driverops;
14203831d35Sstevel 
14303831d35Sstevel static struct modldrv modldrv = {
14403831d35Sstevel 	&mod_driverops,		/* type of module - driver */
145*19397407SSherry Moore 	"PCI SBBC",
14603831d35Sstevel 	&sbbc_ops,
14703831d35Sstevel };
14803831d35Sstevel 
14903831d35Sstevel static struct modlinkage modlinkage = {
15003831d35Sstevel 	MODREV_1,
15103831d35Sstevel 	(void *)&modldrv,
15203831d35Sstevel 	NULL
15303831d35Sstevel };
15403831d35Sstevel 
15503831d35Sstevel int
_init(void)15603831d35Sstevel _init(void)
15703831d35Sstevel {
15803831d35Sstevel 	int    error;
15903831d35Sstevel 
16003831d35Sstevel 	if ((error = ddi_soft_state_init(&sbbcp,
161*19397407SSherry Moore 	    sizeof (sbbc_softstate_t), 1)) != 0)
16203831d35Sstevel 		return (error);
16303831d35Sstevel 
16403831d35Sstevel 	if ((error = mod_install(&modlinkage)) != 0) {
16503831d35Sstevel 		ddi_soft_state_fini(&sbbcp);
16603831d35Sstevel 		return (error);
16703831d35Sstevel 	}
16803831d35Sstevel 
16903831d35Sstevel 	/*
17003831d35Sstevel 	 * Initialise the global 'chosen' IOSRAM mutex
17103831d35Sstevel 	 */
17203831d35Sstevel 	mutex_init(&chosen_lock, NULL, MUTEX_DEFAULT, NULL);
17303831d35Sstevel 
17403831d35Sstevel 	/*
17503831d35Sstevel 	 * Initialise the iosram driver
17603831d35Sstevel 	 */
17703831d35Sstevel 	iosram_init();
17803831d35Sstevel 
17903831d35Sstevel 	/*
18003831d35Sstevel 	 * Initialize the mailbox
18103831d35Sstevel 	 */
18203831d35Sstevel 	sbbc_mbox_init();
18303831d35Sstevel 
18403831d35Sstevel 	return (error);
18503831d35Sstevel 
18603831d35Sstevel }
18703831d35Sstevel 
18803831d35Sstevel int
_fini(void)18903831d35Sstevel _fini(void)
19003831d35Sstevel {
19103831d35Sstevel 	int    error;
19203831d35Sstevel 
19303831d35Sstevel 	if ((error = mod_remove(&modlinkage)) == 0)
19403831d35Sstevel 		ddi_soft_state_fini(&sbbcp);
19503831d35Sstevel 
19603831d35Sstevel 	master_chosen = FALSE;
19703831d35Sstevel 
19803831d35Sstevel 	mutex_destroy(&chosen_lock);
19903831d35Sstevel 
20003831d35Sstevel 	/*
20103831d35Sstevel 	 * remove the mailbox
20203831d35Sstevel 	 */
20303831d35Sstevel 	sbbc_mbox_fini();
20403831d35Sstevel 
20503831d35Sstevel 	/*
20603831d35Sstevel 	 * remove the iosram driver
20703831d35Sstevel 	 */
20803831d35Sstevel 	iosram_fini();
20903831d35Sstevel 
21003831d35Sstevel 	return (error);
21103831d35Sstevel }
21203831d35Sstevel 
21303831d35Sstevel int
_info(struct modinfo * modinfop)21403831d35Sstevel _info(struct modinfo *modinfop)
21503831d35Sstevel {
21603831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
21703831d35Sstevel }
21803831d35Sstevel 
21903831d35Sstevel static int
sbbc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)22003831d35Sstevel sbbc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
22103831d35Sstevel {
22203831d35Sstevel 	int			instance;
22303831d35Sstevel 	sbbc_softstate_t	*softsp;
22403831d35Sstevel 	uint32_t		*pci_intr_enable_reg;
22503831d35Sstevel 	int			len;
22603831d35Sstevel #ifdef	DEBUG
22703831d35Sstevel 	char			name[8];
22803831d35Sstevel #endif	/* DEBUG */
22903831d35Sstevel 
23003831d35Sstevel 	instance = ddi_get_instance(devi);
23103831d35Sstevel 
23203831d35Sstevel 	switch (cmd) {
23303831d35Sstevel 	case DDI_ATTACH:
23403831d35Sstevel 
23503831d35Sstevel 		if (ddi_soft_state_zalloc(sbbcp, instance) != 0)
23603831d35Sstevel 			return (DDI_FAILURE);
23703831d35Sstevel 
23803831d35Sstevel 		softsp = ddi_get_soft_state(sbbcp, instance);
23903831d35Sstevel 		softsp->sbbc_instance = instance;
24003831d35Sstevel 
24103831d35Sstevel 		/*
24203831d35Sstevel 		 * Set the dip in the soft state
24303831d35Sstevel 		 * And get interrupt cookies and initialize the
24403831d35Sstevel 		 * per instance mutex.
24503831d35Sstevel 		 */
24603831d35Sstevel 		softsp_init(softsp, devi);
24703831d35Sstevel 
24803831d35Sstevel 
24903831d35Sstevel 		/*
25003831d35Sstevel 		 * Verify that an 'interrupts' property exists for
25103831d35Sstevel 		 * this device. If not, this instance will be ignored.
25203831d35Sstevel 		 */
25303831d35Sstevel 		if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
254*19397407SSherry Moore 		    DDI_PROP_DONTPASS, "interrupts",
255*19397407SSherry Moore 		    &len) != DDI_PROP_SUCCESS) {
25603831d35Sstevel 			SBBC_ERR1(CE_WARN, "No 'interrupts' property for the "
257*19397407SSherry Moore 			    "SBBC instance %d\n", instance);
25803831d35Sstevel 			return (DDI_FAILURE);
25903831d35Sstevel 		}
26003831d35Sstevel 		/*
26103831d35Sstevel 		 * Add this instance to the sbbc chosen iosram list
26203831d35Sstevel 		 * so that it can be used for tunnel switch.
26303831d35Sstevel 		 */
26403831d35Sstevel 		mutex_enter(&chosen_lock);
26503831d35Sstevel 		softsp->sbbc_state = SBBC_STATE_INIT;
26603831d35Sstevel 		sbbc_add_instance(softsp);
26703831d35Sstevel 
26803831d35Sstevel 		/*
26903831d35Sstevel 		 * If this is the chosen IOSRAM and there is no master IOSRAM
27003831d35Sstevel 		 * yet, then let's set this instance as the master.
27103831d35Sstevel 		 * if there is a master alreay due to the previous tunnel switch
27203831d35Sstevel 		 * then keep as is even though this is the chosen.
27303831d35Sstevel 		 */
27403831d35Sstevel 		if (sgsbbc_iosram_is_chosen(softsp)) {
27503831d35Sstevel 			ASSERT(master_iosram);
27603831d35Sstevel 			softsp->iosram = master_iosram;
27703831d35Sstevel 			master_iosram->sgsbbc = softsp;
27803831d35Sstevel 
27903831d35Sstevel 			/* Do 'chosen' init only */
28003831d35Sstevel 			sbbc_chosen_init(softsp);
28103831d35Sstevel 		}
282