xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/sghsc.c (revision 2bc987325e3ded1865bff043128661815c4690b9)
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.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel 
2803831d35Sstevel /*
2903831d35Sstevel  *
3003831d35Sstevel  *	Serengeti CompactPCI Hot Swap Controller Driver.
3103831d35Sstevel  *
3203831d35Sstevel  */
3303831d35Sstevel 
3403831d35Sstevel #include <sys/types.h>
3503831d35Sstevel #include <sys/cmn_err.h>
3603831d35Sstevel #include <sys/kmem.h>
3703831d35Sstevel #include <sys/errno.h>
3803831d35Sstevel #include <sys/cpuvar.h>
3903831d35Sstevel #include <sys/open.h>
4003831d35Sstevel #include <sys/stat.h>
4103831d35Sstevel #include <sys/conf.h>
4203831d35Sstevel #include <sys/ddi.h>
4303831d35Sstevel #include <sys/sunddi.h>
4403831d35Sstevel #include <sys/modctl.h>
4503831d35Sstevel #include <sys/ksynch.h>
4603831d35Sstevel #include <sys/pci.h>
4703831d35Sstevel #include <sys/serengeti.h>
4803831d35Sstevel #include <sys/sghsc.h>
4903831d35Sstevel #include <sys/promif.h>
5003831d35Sstevel 
5103831d35Sstevel /*
5203831d35Sstevel  * Debug flags
5303831d35Sstevel  */
5403831d35Sstevel 
5503831d35Sstevel int	sghsc_configure_ack = 0;
5603831d35Sstevel int	cpci_enable = 1;
5703831d35Sstevel #ifdef	DEBUG
5803831d35Sstevel #define	SGHSC_DEBUG
5903831d35Sstevel #endif
6003831d35Sstevel 
6103831d35Sstevel #ifdef	SGHSC_DEBUG
6203831d35Sstevel int	sghsc_debug = 0;
6303831d35Sstevel #define	DEBUGF(level, args) \
6403831d35Sstevel 	{ if (sghsc_debug >= (level)) cmn_err args; }
6503831d35Sstevel #define	DEBUGON  sghsc_debug = 3
6603831d35Sstevel #define	DEBUGOFF sghsc_debug = 0
6703831d35Sstevel #else
6803831d35Sstevel #define	DEBUGF(level, args)	/* nothing */
6903831d35Sstevel #define	DEBUGON
7003831d35Sstevel #define	DEBUGOFF
7103831d35Sstevel #endif
7203831d35Sstevel 
7303831d35Sstevel /*
7403831d35Sstevel  * Global data
7503831d35Sstevel  */
7603831d35Sstevel static void *sghsc_state;		/* soft state */
7703831d35Sstevel static sghsc_rb_head_t sghsc_rb_header;	/* ring buffer header */
7803831d35Sstevel 
7903831d35Sstevel /*
8003831d35Sstevel  * Definitions for events thread (outside interrupt context), mutex and
8103831d35Sstevel  * condition variable.
8203831d35Sstevel  */
8303831d35Sstevel static kthread_t *sghsc_event_thread;
8403831d35Sstevel static kmutex_t sghsc_event_thread_mutex;
8503831d35Sstevel static kcondvar_t sghsc_event_thread_cv;
8603831d35Sstevel static boolean_t sghsc_event_thread_exit = B_FALSE;
8703831d35Sstevel 
8803831d35Sstevel static struct cb_ops sghsc_cb_ops = {
8903831d35Sstevel 	nodev,			/* open */
9003831d35Sstevel 	nodev,			/* close */
9103831d35Sstevel 	nodev,			/* strategy */
9203831d35Sstevel 	nodev,			/* print */
9303831d35Sstevel 	nodev,			/* dump */
9403831d35Sstevel 	nodev,			/* read */
9503831d35Sstevel 	nodev,			/* write */
9603831d35Sstevel 	nodev,			/* ioctl */
9703831d35Sstevel 	nodev,			/* devmap */
9803831d35Sstevel 	nodev,			/* mmap */
9903831d35Sstevel 	nodev,			/* segmap */
10003831d35Sstevel 	nochpoll,		/* poll */
10103831d35Sstevel 	ddi_prop_op,		/* prop_op */
10203831d35Sstevel 	0,			/* streamtab  */
10303831d35Sstevel 	D_NEW | D_MP,		/* Driver compatibility flag */
10403831d35Sstevel 	CB_REV,			/* rev */
10503831d35Sstevel 	nodev,			/* int (*cb_aread)() */
10603831d35Sstevel 	nodev			/* int (*cb_awrite)() */
10703831d35Sstevel };
10803831d35Sstevel 
10903831d35Sstevel /*
11003831d35Sstevel  * Function prototype for dev_ops
11103831d35Sstevel  */
11203831d35Sstevel 
11303831d35Sstevel static int sghsc_attach(dev_info_t *, ddi_attach_cmd_t);
11403831d35Sstevel static int sghsc_detach(dev_info_t *, ddi_detach_cmd_t);
11503831d35Sstevel 
11603831d35Sstevel static struct dev_ops sghsc_dev_ops = {
11703831d35Sstevel 	DEVO_REV,		/* devo_rev, */
11803831d35Sstevel 	0,			/* refcnt  */
11903831d35Sstevel 	nulldev,		/* get_dev_info */
12003831d35Sstevel 	nulldev,		/* identify */
12103831d35Sstevel 	nulldev,		/* probe */
12203831d35Sstevel 	sghsc_attach,		/* attach */
12303831d35Sstevel 	sghsc_detach,		/* detach */
12403831d35Sstevel 	nodev,			/* reset */
12503831d35Sstevel 	&sghsc_cb_ops,		/* driver operations */
12619397407SSherry Moore 	(struct bus_ops *)0,	/* no bus operations */
12719397407SSherry Moore 	NULL,			/* power */
12819397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
12903831d35Sstevel };
13003831d35Sstevel 
13103831d35Sstevel static struct modldrv modldrv = {
13203831d35Sstevel 	&mod_driverops,
13319397407SSherry Moore 	"Serengeti CompactPCI HSC",
13403831d35Sstevel 	&sghsc_dev_ops,
13503831d35Sstevel };
13603831d35Sstevel 
13703831d35Sstevel static struct modlinkage modlinkage = {
13803831d35Sstevel 	MODREV_1,
13903831d35Sstevel 	&modldrv,
14003831d35Sstevel 	NULL
14103831d35Sstevel };
14203831d35Sstevel 
14303831d35Sstevel /*
14403831d35Sstevel  * Function prototype for HP support
14503831d35Sstevel  */
14603831d35Sstevel static int sghsc_connect(caddr_t, hpc_slot_t slot, void *, uint_t);
14703831d35Sstevel static int sghsc_disconnect(caddr_t, hpc_slot_t, void *, uint_t);
14803831d35Sstevel static int sghsc_control(caddr_t, hpc_slot_t, int, caddr_t);
14903831d35Sstevel 
15003831d35Sstevel /*
15103831d35Sstevel  * Function prototypes for internal functions
15203831d35Sstevel  */
15303831d35Sstevel static int sghsc_register_slots(sghsc_t *, int);
15403831d35Sstevel static int sghsc_get_slotnum(sghsc_t *, hpc_slot_t);
15503831d35Sstevel static int sghsc_scctl(int, int, int, int, int *);
15603831d35Sstevel static void sghsc_freemem(sghsc_t *);
15703831d35Sstevel static hpc_slot_t sghsc_find_sloth(int, int, int);
15803831d35Sstevel static sghsc_t *sghsc_find_softstate(int, int, int);
15903831d35Sstevel static int sghsc_led_state(sghsc_t *, hpc_slot_t, int, hpc_led_info_t *);
16003831d35Sstevel static void sghsc_rb_setup(sghsc_rb_head_t *);
16103831d35Sstevel static void sghsc_rb_teardown(sghsc_rb_head_t *);
16203831d35Sstevel static int sghsc_rb_get(sghsc_rb_head_t *, sghsc_event_t *);
16303831d35Sstevel static int sghsc_rb_put(sghsc_rb_head_t *, sghsc_event_t *);
16403831d35Sstevel 
16503831d35Sstevel /*
16603831d35Sstevel  * Patchable timeout value
16703831d35Sstevel  */
16803831d35Sstevel int sghsc_mbx_timeout = SGHSC_MBX_TIMEOUT;
16903831d35Sstevel 
17003831d35Sstevel /*
17103831d35Sstevel  * Data for self-identification. This will help enumerate all soft states.
17203831d35Sstevel  */
17303831d35Sstevel static int sghsc_maxinst;
17403831d35Sstevel 
17503831d35Sstevel /*
17603831d35Sstevel  * Six slot boat and four slot boats are different in topology (slot to
17703831d35Sstevel  * bus assignment) and here we should have 2 separate maps (the first 3
17803831d35Sstevel  * slots have the same topology). The map is in the "delta" form. Logical
17903831d35Sstevel  * slots correspond to indexes in the map.
18003831d35Sstevel  */
18103831d35Sstevel static sdesc_t four_slot_wib_bd[] = {
18203831d35Sstevel 	0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */
18303831d35Sstevel 	1, 0, 2, 0,		/* logical/physical slot 1 - paroli2 */
18403831d35Sstevel 	1, 0, 0, 0,		/* logical/physical slot 2 - paroli0 */
18503831d35Sstevel 	0, 7, 1, HPC_SLOT_TYPE_CPCI  /* logical/physical slot 3 - Schizo0/B */
18603831d35Sstevel };
18703831d35Sstevel static sdesc_t four_slot_bd[] = {
18803831d35Sstevel 	0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */
18903831d35Sstevel 	1, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 1 - Schizo1/A */
19003831d35Sstevel 	0, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 2 - Schizo0/B */
19103831d35Sstevel 	1, 7, 1, HPC_SLOT_TYPE_CPCI  /* logical/physical slot 3 - Schizo1/B */
19203831d35Sstevel };
19303831d35Sstevel static sdesc_t six_slot_wib_bd[] = {
19403831d35Sstevel 	0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */
19503831d35Sstevel 	1, 0, 2, 0,		/* logical/physical slot 1 - paroli2 */
19603831d35Sstevel 	1, 0, 0, 0,		/* logical/physical slot 2 - paroli0 */
19703831d35Sstevel 	0, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 3 - Schizo0/B */
19803831d35Sstevel 	0, 7, 2, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 4 - Schizo0/B */
19903831d35Sstevel 	0, 7, 3, HPC_SLOT_TYPE_CPCI  /* logical/physical slot 5 - Schizo0/B */
20003831d35Sstevel };
20103831d35Sstevel static sdesc_t six_slot_bd[] = {
20203831d35Sstevel 	0, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 0 - Schizo0/A */
20303831d35Sstevel 	1, 6, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 1 - Schizo1/A */
20403831d35Sstevel 	0, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 2 - Schizo0/B */
20503831d35Sstevel 	0, 7, 2, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 3 - Schizo0/B */
20603831d35Sstevel 	1, 7, 1, HPC_SLOT_TYPE_CPCI, /* logical/physical slot 4 - Schizo1/B */
20703831d35Sstevel 	1, 7, 2, HPC_SLOT_TYPE_CPCI  /* logical/physical slot 5 - Schizo1/B */
20803831d35Sstevel };
20903831d35Sstevel 
21003831d35Sstevel /*
21103831d35Sstevel  * DR event handlers
21203831d35Sstevel  * We want to register the event handlers once for all instances. In the
21303831d35Sstevel  * other hand we have register them after the sghsc has been attached.
21403831d35Sstevel  * event_initialize gives us the logic of only registering the events only
21503831d35Sstevel  * once. The event thread will do all the work when called from interrupts.
21603831d35Sstevel  */
21703831d35Sstevel int sghsc_event_init = 0;
21803831d35Sstevel static uint_t sghsc_event_handler(char *);
21903831d35Sstevel static void sghsc_event_thread_code(void);
22003831d35Sstevel 
22103831d35Sstevel /*
22203831d35Sstevel  * DR event msg and payload
22303831d35Sstevel  */
22403831d35Sstevel static sbbc_msg_t event_msg;
22503831d35Sstevel static sghsc_event_t payload;
22603831d35Sstevel 
22703831d35Sstevel /*
22803831d35Sstevel  * Event lock and state
22903831d35Sstevel  */
23003831d35Sstevel static kmutex_t sghsc_event_lock;
23103831d35Sstevel int sghsc_event_state;
23203831d35Sstevel 
23303831d35Sstevel int
_init(void)23403831d35Sstevel _init(void)
23503831d35Sstevel {
23603831d35Sstevel 	int error;
23703831d35Sstevel 
23803831d35Sstevel 	sghsc_maxinst = 0;
23903831d35Sstevel 
24003831d35Sstevel 	if ((error = ddi_soft_state_init(&sghsc_state,
24103831d35Sstevel 	    sizeof (sghsc_t), 1)) != 0)
24203831d35Sstevel 		return (error);
24303831d35Sstevel 
24403831d35Sstevel 	if ((error = mod_install(&modlinkage)) != 0) {
24503831d35Sstevel 		ddi_soft_state_fini(&sghsc_state);
24603831d35Sstevel 		return (error);
24703831d35Sstevel 	}
24803831d35Sstevel 
24903831d35Sstevel 	sghsc_rb_header.buf = NULL;
25003831d35Sstevel 
25103831d35Sstevel 	mutex_init(&sghsc_event_thread_mutex, NULL, MUTEX_DRIVER, NULL);
25203831d35Sstevel 	cv_init(&sghsc_event_thread_cv, NULL, CV_DRIVER, NULL);
25303831d35Sstevel 
25403831d35Sstevel 	return (error);
25503831d35Sstevel }
25603831d35Sstevel 
25703831d35Sstevel int
_fini(void)25803831d35Sstevel _fini(void)
25903831d35Sstevel {
26003831d35Sstevel 	int error;
26103831d35Sstevel 
26203831d35Sstevel 	if ((error = mod_remove(&modlinkage)) != 0)
26303831d35Sstevel 		return (error);
26403831d35Sstevel 	/*
26503831d35Sstevel 	 * Unregister the event handler
26603831d35Sstevel 	 */
26707d06da5SSurya Prakki 	(void) sbbc_mbox_unreg_intr(MBOX_EVENT_CPCI_ENUM, sghsc_event_handler);
26803831d35Sstevel 	mutex_destroy(&sghsc_event_lock);
26903831d35Sstevel 
27003831d35Sstevel 	/*
27103831d35Sstevel 	 * Kill the event thread if it is running.
27203831d35Sstevel 	 */
27303831d35Sstevel 	if (sghsc_event_thread != NULL) {
27403831d35Sstevel 		mutex_enter(&sghsc_event_thread_mutex);
27503831d35Sstevel 		sghsc_event_thread_exit = B_TRUE;
27603831d35Sstevel 		/*
27703831d35Sstevel 		 * Goes to the thread at once.
27803831d35Sstevel 		 */
27903831d35Sstevel 		cv_signal(&sghsc_event_thread_cv);
28003831d35Sstevel 		/*
28103831d35Sstevel 		 * Waiting for the response from the thread.
28203831d35Sstevel 		 */
28303831d35Sstevel 		cv_wait(&sghsc_event_thread_cv, &sghsc_event_thread_mutex);
28403831d35Sstevel 		mutex_exit(&sghsc_event_thread_mutex);
28503831d35Sstevel 		sghsc_event_thread = NULL;
28603831d35Sstevel 	}
28703831d35Sstevel 	mutex_destroy(&sghsc_event_thread_mutex);
28803831d35Sstevel 	cv_destroy(&sghsc_event_thread_cv);
28903831d35Sstevel 
29003831d35Sstevel 	/*
29103831d35Sstevel 	 * tear down shared, global ring buffer now that it is safe to
29203831d35Sstevel 	 * do so because sghsc_event_handler has been unregistered and
29303831d35Sstevel 	 * sghsc_event_thread_code has exited
29403831d35Sstevel 	 */
29503831d35Sstevel 	sghsc_rb_teardown(&sghsc_rb_header);
29603831d35Sstevel 
29703831d35Sstevel 	sghsc_maxinst = 0;
29803831d35Sstevel 	ddi_soft_state_fini(&sghsc_state);
29903831d35Sstevel 
30003831d35Sstevel 	return (0);
30103831d35Sstevel }
30203831d35Sstevel 
30303831d35Sstevel int
_info(struct modinfo * modinfop)30403831d35Sstevel _info(struct modinfo *modinfop)
30503831d35Sstevel {
30603831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
30703831d35Sstevel }
30803831d35Sstevel 
30903831d35Sstevel /*
31003831d35Sstevel  * sghsc_attach()
31103831d35Sstevel  */
31203831d35Sstevel /* ARGSUSED */
31303831d35Sstevel static int
sghsc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)31403831d35Sstevel sghsc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
31503831d35Sstevel {
31603831d35Sstevel 	sghsc_t *sghsc;
31703831d35Sstevel 	uint_t instance;
31803831d35Sstevel 	uint_t portid;
31903831d35Sstevel 	int rc;
32003831d35Sstevel 	int board_type = 0;
32103831d35Sstevel 
32203831d35Sstevel 	instance = ddi_get_instance(dip);
32303831d35Sstevel 
32403831d35Sstevel 	switch (cmd) {
32503831d35Sstevel 		case DDI_RESUME:
32603831d35Sstevel 			return (DDI_SUCCESS);
32703831d35Sstevel 
32803831d35Sstevel 		case DDI_ATTACH:
32903831d35Sstevel 			break;
33003831d35Sstevel 		default:
33103831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: unsupported cmd %d",
33203831d35Sstevel 			    instance, cmd);
33303831d35Sstevel 			return (DDI_FAILURE);
33403831d35Sstevel 	}
33503831d35Sstevel 
33603831d35Sstevel 	DEBUGF(1, (CE_NOTE, "attach sghsc driver. "));
33703831d35Sstevel 
33803831d35Sstevel 	/* Fetch Safari Extended Agent ID of this device. */
33903831d35Sstevel 	portid = (uint_t)ddi_getprop(DDI_DEV_T_ANY, dip,
34003831d35Sstevel 	    DDI_PROP_DONTPASS, "portid", -1);
34103831d35Sstevel 
34203831d35Sstevel 	if (!SG_PORTID_IS_IO_TYPE(portid)) {
34303831d35Sstevel 		cmn_err(CE_WARN, "sghsc%d: property %s out of bounds %d\n",
34403831d35Sstevel 		    instance, "portid", portid);
34503831d35Sstevel 		return (DDI_FAILURE);
34603831d35Sstevel 	}
34703831d35Sstevel 
34803831d35Sstevel 	if (ddi_soft_state_zalloc(sghsc_state, instance) != DDI_SUCCESS)
34903831d35Sstevel 		return (DDI_FAILURE);
35003831d35Sstevel 
35103831d35Sstevel 	sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance);
35203831d35Sstevel 
35303831d35Sstevel 	sghsc->sghsc_dip = dip;
35403831d35Sstevel 	sghsc->sghsc_instance = instance;
35503831d35Sstevel 	sghsc->sghsc_board = SG_PORTID_TO_BOARD_NUM(portid);
35603831d35Sstevel 	sghsc->sghsc_node_id = SG_PORTID_TO_NODEID(portid);
35703831d35Sstevel 	sghsc->sghsc_portid = portid;
35803831d35Sstevel 
35903831d35Sstevel 	ddi_set_driver_private(dip, sghsc);
36003831d35Sstevel 
36103831d35Sstevel 	mutex_init(SGHSC_MUTEX(sghsc), NULL, MUTEX_DRIVER, NULL);
36203831d35Sstevel 
36303831d35Sstevel 	rc = sghsc_scctl(SGHSC_GET_NUM_SLOTS, sghsc->sghsc_node_id,
36403831d35Sstevel 	    sghsc->sghsc_board, 0, (int *)&sghsc->sghsc_num_slots);
36503831d35Sstevel 
36603831d35Sstevel 	if (rc) {
36703831d35Sstevel 		cmn_err(CE_WARN, "sghsc%d: unable to size node %d / board %d",
36803831d35Sstevel 		    instance, sghsc->sghsc_node_id, sghsc->sghsc_board);
36903831d35Sstevel 		goto cleanup_stage2;
37003831d35Sstevel 	}
37103831d35Sstevel 
37203831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc%d: node %d / board %d  has %d slots",
37303831d35Sstevel 	    instance, sghsc->sghsc_node_id, sghsc->sghsc_board,
37403831d35Sstevel 	    sghsc->sghsc_num_slots));
37503831d35Sstevel 
37603831d35Sstevel 	switch (sghsc->sghsc_num_slots) {
37703831d35Sstevel 		case 4:
37803831d35Sstevel 		case 6:
37903831d35Sstevel 			rc = 0;
38003831d35Sstevel 			break;
38103831d35Sstevel 		default:
38203831d35Sstevel 			rc = -1;
38303831d35Sstevel 			break;
38403831d35Sstevel 	}
38503831d35Sstevel 
38603831d35Sstevel 	if (rc) {
38703831d35Sstevel 		cmn_err(CE_WARN, "sghsc%d: wrong num of slots %d for node %d"
38803831d35Sstevel 		    " / board %d", instance, sghsc->sghsc_num_slots,
38903831d35Sstevel 		    sghsc->sghsc_node_id, sghsc->sghsc_board);
39003831d35Sstevel 		goto cleanup_stage2;
39103831d35Sstevel 	}
39203831d35Sstevel 
39303831d35Sstevel 	rc = sghsc_scctl(SGHSC_GET_CPCI_BOARD_TYPE, sghsc->sghsc_node_id,
39403831d35Sstevel 	    sghsc->sghsc_board, 0, &board_type);
39503831d35Sstevel 
39603831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc%d: node %d / board %d is type %d",
39703831d35Sstevel 	    instance, sghsc->sghsc_node_id, sghsc->sghsc_board, board_type));
39803831d35Sstevel 
39903831d35Sstevel 	sghsc->sghsc_slot_table = (sghsc_slot_t *)kmem_zalloc((size_t)
40003831d35Sstevel 	    (sghsc->sghsc_num_slots * sizeof (sghsc_slot_t)), KM_SLEEP);
40103831d35Sstevel 
40203831d35Sstevel 
40303831d35Sstevel 	if (sghsc_register_slots(sghsc, board_type) != DDI_SUCCESS) {
40403831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_register_slots"
40503831d35Sstevel 		    " failed for node %d / board %d",
40603831d35Sstevel 		    instance, sghsc->sghsc_node_id, sghsc->sghsc_board));
40703831d35Sstevel 		goto cleanup;
40803831d35Sstevel 	}
40903831d35Sstevel 
41003831d35Sstevel 	if (sghsc_connect((caddr_t)sghsc, 0, 0, SGHSC_ALL_SLOTS_ENABLE)
41103831d35Sstevel 	    != HPC_SUCCESS) {
41203831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_connect failed for"
41303831d35Sstevel 		    " node %d / board %d", instance, sghsc->sghsc_node_id,
41403831d35Sstevel 		    sghsc->sghsc_board));
41503831d35Sstevel 		goto cleanup;
41603831d35Sstevel 	}
41703831d35Sstevel 
41803831d35Sstevel 
41903831d35Sstevel 	if (sghsc_event_init == 0) {
42003831d35Sstevel 
42103831d35Sstevel 		/*
42203831d35Sstevel 		 * allocate shared, global ring buffer before registering
42303831d35Sstevel 		 * sghsc_event_handler and before starting
42403831d35Sstevel 		 * sghsc_event_thread_code
42503831d35Sstevel 		 */
42603831d35Sstevel 		sghsc_rb_setup(&sghsc_rb_header);
42703831d35Sstevel 
42803831d35Sstevel 		/*
42903831d35Sstevel 		 * Regiter cpci DR event handler
43003831d35Sstevel 		 *
43103831d35Sstevel 		 */
43203831d35Sstevel 		mutex_init(&sghsc_event_lock,  NULL, MUTEX_DRIVER, NULL);
43303831d35Sstevel 		event_msg.msg_buf = (caddr_t)&payload;
43403831d35Sstevel 		event_msg.msg_len = sizeof (payload);
43503831d35Sstevel 		rc = sbbc_mbox_reg_intr(MBOX_EVENT_CPCI_ENUM,
43603831d35Sstevel 		    sghsc_event_handler, &event_msg,
43703831d35Sstevel 		    (uint_t *)&sghsc_event_state, &sghsc_event_lock);
43803831d35Sstevel 
43903831d35Sstevel 		if (rc != 0)
44003831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: failed to register events"
44103831d35Sstevel 			    " for node %d", instance, sghsc->sghsc_node_id);
44203831d35Sstevel 
44303831d35Sstevel 		sghsc_event_init = 1;
44403831d35Sstevel 
44503831d35Sstevel 		/*
44603831d35Sstevel 		 * Create the event thread if it is not already created.
44703831d35Sstevel 		 */
44803831d35Sstevel 		if (sghsc_event_thread == NULL) {
44903831d35Sstevel 			DEBUGF(1, (CE_NOTE, "sghsc: creating event thread"
45003831d35Sstevel 			    "for node %d", sghsc->sghsc_node_id));
45103831d35Sstevel 			sghsc_event_thread = thread_create(NULL, 0,
45203831d35Sstevel 			    sghsc_event_thread_code, NULL, 0, &p0,
45303831d35Sstevel 			    TS_RUN, minclsyspri);
45403831d35Sstevel 		}
45503831d35Sstevel 	}
45603831d35Sstevel 
45703831d35Sstevel 	ddi_report_dev(dip);
45803831d35Sstevel 
45903831d35Sstevel 	/*
46003831d35Sstevel 	 * Grossly bump up the instance counter. We may have holes inside.
46103831d35Sstevel 	 */
46203831d35Sstevel 	sghsc_maxinst++;
46303831d35Sstevel 	sghsc->sghsc_valid = 1;
46403831d35Sstevel 
46503831d35Sstevel 	return (DDI_SUCCESS);
46603831d35Sstevel 
46703831d35Sstevel cleanup:
46803831d35Sstevel 	/*
46903831d35Sstevel 	 * Free up allocated resources and return error
47003831d35Sstevel 	 * sghsc_register_slots => unregister all slots
47103831d35Sstevel 	 */
47203831d35Sstevel 	sghsc_freemem(sghsc);
47303831d35Sstevel 
47403831d35Sstevel cleanup_stage2:
47503831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc%d: attach failed for node %d",
47603831d35Sstevel 	    instance, sghsc->sghsc_node_id));
47703831d35Sstevel 	mutex_destroy(SGHSC_MUTEX(sghsc));
47803831d35Sstevel 	ddi_set_driver_private(dip, NULL);
47903831d35Sstevel 	ddi_soft_state_free(sghsc_state, instance);
48003831d35Sstevel 	return (DDI_FAILURE);
48103831d35Sstevel }
48203831d35Sstevel 
48303831d35Sstevel /*
48403831d35Sstevel  * detach(9E)
48503831d35Sstevel  */
48603831d35Sstevel /* ARGSUSED */
48703831d35Sstevel static int
sghsc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)48803831d35Sstevel sghsc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
48903831d35Sstevel {
49003831d35Sstevel 	sghsc_t *sghsc;
49103831d35Sstevel 	int instance;
49203831d35Sstevel 	int i;
49303831d35Sstevel 
49403831d35Sstevel 	instance = ddi_get_instance(dip);
49503831d35Sstevel 	sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance);
49603831d35Sstevel 
49703831d35Sstevel 	if (sghsc == NULL)
49803831d35Sstevel 		return (DDI_FAILURE);
49903831d35Sstevel 
50003831d35Sstevel 	switch (cmd) {
50103831d35Sstevel 		case DDI_DETACH:
50203831d35Sstevel 		/*
50303831d35Sstevel 		 * We don't allow to detach in case the pci nexus
50403831d35Sstevel 		 * didn't run pcihp_uninit(). The buses should be
50503831d35Sstevel 		 * unregistered by now, otherwise slot info will be
50603831d35Sstevel 		 * corrupted on the next 'cfgadm'.
50703831d35Sstevel 		 */
50803831d35Sstevel 		for (i = 0; i < sghsc->sghsc_num_slots; i++) {
50903831d35Sstevel 			if (sghsc->sghsc_slot_table[i].handle &&
51003831d35Sstevel 			    hpc_bus_registered(
51103831d35Sstevel 			    sghsc->sghsc_slot_table[i].handle)) {
51203831d35Sstevel 				cmn_err(CE_WARN,
51303831d35Sstevel 				    "sghsc: must detach buses first");
51403831d35Sstevel 				return (DDI_FAILURE);
51503831d35Sstevel 			}
51603831d35Sstevel 		}
51703831d35Sstevel 
51803831d35Sstevel 		if (mutex_tryenter(&sghsc_event_thread_mutex) == 0)
51903831d35Sstevel 			return (EBUSY);
52003831d35Sstevel 
52103831d35Sstevel 		sghsc->sghsc_valid = 0;
52203831d35Sstevel 		sghsc_freemem(sghsc);
52303831d35Sstevel 		mutex_destroy(SGHSC_MUTEX(sghsc));
52403831d35Sstevel 		ddi_set_driver_private(dip, NULL);
52503831d35Sstevel 		ddi_soft_state_free(sghsc_state, instance);
52603831d35Sstevel 
52703831d35Sstevel 		/*
52803831d35Sstevel 		 * Grossly decrement the counter. We may have holes inside.
52903831d35Sstevel 		 */
53003831d35Sstevel 		if (instance == (sghsc_maxinst - 1))
53103831d35Sstevel 			sghsc_maxinst--;
53203831d35Sstevel 		mutex_exit(&sghsc_event_thread_mutex);
53303831d35Sstevel 		return (DDI_SUCCESS);
53403831d35Sstevel 
53503831d35Sstevel 		case DDI_SUSPEND:
53603831d35Sstevel 		return (DDI_SUCCESS);
53703831d35Sstevel 
53803831d35Sstevel 		default:
53903831d35Sstevel 		return (DDI_FAILURE);
54003831d35Sstevel 	}
54103831d35Sstevel }
54203831d35Sstevel 
54303831d35Sstevel 
54403831d35Sstevel /*
54503831d35Sstevel  * Set up and register slot 0 to num_slots with hotplug
54603831d35Sstevel  *     framework
54703831d35Sstevel  * 	Assume SGHSC_MUTEX is held
54803831d35Sstevel  *
54903831d35Sstevel  * Return val: DDI_SUCCESS
55003831d35Sstevel  *	       DDI_FAILURE
55103831d35Sstevel  */
55203831d35Sstevel static int
sghsc_register_slots(sghsc_t * sghsc,int board_type)55303831d35Sstevel sghsc_register_slots(sghsc_t *sghsc, int board_type)
55403831d35Sstevel {
55503831d35Sstevel 	int  i;
55603831d35Sstevel 	dev_info_t	*dip = sghsc->sghsc_dip;
55703831d35Sstevel 	hpc_slot_ops_t	*slot_ops = NULL;
55803831d35Sstevel 	sdesc_t 	*slot2bus;
55903831d35Sstevel 
56003831d35Sstevel 
56103831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc%d: slot table has %d entries for "
56203831d35Sstevel 	    "node %d / board %d", sghsc->sghsc_instance, sghsc->sghsc_num_slots,
56303831d35Sstevel 	    sghsc->sghsc_node_id, sghsc->sghsc_board));
56403831d35Sstevel 
56503831d35Sstevel 	if ((cpci_enable == 0) || (sg_prom_cpci_dr_check() != 0))
56603831d35Sstevel 		return (DDI_SUCCESS);
56703831d35Sstevel 
56803831d35Sstevel 	if (sghsc->sghsc_slot_table == NULL)
56903831d35Sstevel 		return (DDI_FAILURE);
57003831d35Sstevel 
57103831d35Sstevel 	switch (board_type) {
57203831d35Sstevel 		/*
57303831d35Sstevel 		 * If the GET_CPCI_BOARD_TYPE request failed, board type
57403831d35Sstevel 		 * will be NO_BOARD_TYPE.  In that case, assume it is an
57503831d35Sstevel 		 * io boat and make board type determination based on the
57603831d35Sstevel 		 * number of slots.
57703831d35Sstevel 		 */
57803831d35Sstevel 		case NO_BOARD_TYPE:
57903831d35Sstevel 		case CPCI_BOARD:
58003831d35Sstevel 		case SP_CPCI_BOARD:
58103831d35Sstevel 			switch (sghsc->sghsc_num_slots) {
58203831d35Sstevel 			case 4:
58303831d35Sstevel 				slot2bus = four_slot_bd;
58403831d35Sstevel 				break;
58503831d35Sstevel 			case 6:
58603831d35Sstevel 				slot2bus = six_slot_bd;
58703831d35Sstevel 				break;
58803831d35Sstevel 			default:
58903831d35Sstevel 				cmn_err(CE_WARN, "sghsc%d: unknown size %d for"
59003831d35Sstevel 				    " node %d / board %d",
59103831d35Sstevel 				    sghsc->sghsc_instance,
59203831d35Sstevel 				    sghsc->sghsc_num_slots,
59303831d35Sstevel 				    sghsc->sghsc_node_id, sghsc->sghsc_board);
59403831d35Sstevel 				break;
59503831d35Sstevel 			}
59603831d35Sstevel 			break;
59703831d35Sstevel 		case WCI_CPCI_BOARD:
59803831d35Sstevel 			slot2bus = four_slot_wib_bd;
59903831d35Sstevel 			break;
60003831d35Sstevel 		case WCI_SP_CPCI_BOARD:
60103831d35Sstevel 			slot2bus = six_slot_wib_bd;
60203831d35Sstevel 			break;
60303831d35Sstevel 		default:
60403831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: unknown type %d  for"
60503831d35Sstevel 			    " node %d / board %d", sghsc->sghsc_instance,
60603831d35Sstevel 			    board_type, sghsc->sghsc_node_id,
60703831d35Sstevel 			    sghsc->sghsc_board);
60803831d35Sstevel 			return (DDI_FAILURE);
60903831d35Sstevel 	}
61003831d35Sstevel 
61103831d35Sstevel 	/*
61203831d35Sstevel 	 * constructing the slot table array and register the
61303831d35Sstevel 	 * slot with the HPS
61403831d35Sstevel 	 * we don't depend on the .conf file
61503831d35Sstevel 	 */
61603831d35Sstevel 	for (i = 0; i < sghsc->sghsc_num_slots; i++) {
61703831d35Sstevel 		char	*nexuspath;
61803831d35Sstevel 		hpc_slot_info_t  *slot_info;
61903831d35Sstevel 		uint32_t base_id;
62003831d35Sstevel 
62103831d35Sstevel 		/*
62203831d35Sstevel 		 * Some kind of black list may be needed
62303831d35Sstevel 		 */
62403831d35Sstevel 
62503831d35Sstevel 		/*
62603831d35Sstevel 		 * Need to talk to SC and get slot info and set slot state:
62703831d35Sstevel 		 * 1. slot status
62803831d35Sstevel 		 * 2. slot capabilities
62903831d35Sstevel 		 * 3. LED status
63003831d35Sstevel 		 * 4. get bus num
63103831d35Sstevel 		 */
63203831d35Sstevel 
63303831d35Sstevel 		/*
63403831d35Sstevel 		 * fill up nexuspath, extended id is used instead of the
63503831d35Sstevel 		 * local one, the node id is encoded in the path twice.
63603831d35Sstevel 		 */
63703831d35Sstevel 		base_id = sghsc->sghsc_portid & SGHSC_SAFARI_ID_EVEN;
63803831d35Sstevel 		nexuspath = sghsc->sghsc_slot_table[i].nexus_path;
63903831d35Sstevel 
64007d06da5SSurya Prakki 		(void) sprintf(nexuspath, SGHSC_PATH, sghsc->sghsc_node_id,
64103831d35Sstevel 		    (base_id + slot2bus[i].agent_delta), slot2bus[i].off);
64203831d35Sstevel 		sghsc->sghsc_slot_table[i].pci_device_num =
64303831d35Sstevel 		    slot2bus[i].pcidev;
64403831d35Sstevel 
64503831d35Sstevel 		/*
64603831d35Sstevel 		 * fill up slot_info
64703831d35Sstevel 		 */
64803831d35Sstevel 		slot_info = &sghsc->sghsc_slot_table[i].slot_info;
64903831d35Sstevel 
65003831d35Sstevel 		slot_info->version = HPC_SLOT_INFO_VERSION;
65103831d35Sstevel 		slot_info->slot_type = slot2bus[i].slot_type;
65203831d35Sstevel 		/* capabilities need to be discovered via SC */
65303831d35Sstevel 		slot_info->pci_slot_capabilities = HPC_SLOT_64BITS;
65403831d35Sstevel 		slot_info->pci_dev_num = slot2bus[i].pcidev;
65503831d35Sstevel 
65607d06da5SSurya Prakki 		(void) sprintf(slot_info->pci_slot_name,
65703831d35Sstevel 		    "sg%dslot%d", sghsc->sghsc_board, i);
65803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "pci_slot_name is %s at pci_dev_num %d"
65903831d35Sstevel 		    " on node %d / board %d", slot_info->pci_slot_name,
66003831d35Sstevel 		    slot_info->pci_dev_num, sghsc->sghsc_node_id,
66103831d35Sstevel 		    sghsc->sghsc_board));
66203831d35Sstevel 
66303831d35Sstevel 		/*
66403831d35Sstevel 		 * allocate and fill up slot_ops
66503831d35Sstevel 		 */
66603831d35Sstevel 		slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
66703831d35Sstevel 		sghsc->sghsc_slot_table[i].slot_ops = slot_ops;
66803831d35Sstevel 
66903831d35Sstevel 		/* assign slot ops for HPS */
67003831d35Sstevel 		slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
67103831d35Sstevel 		slot_ops->hpc_op_connect = sghsc_connect;
67203831d35Sstevel 		slot_ops->hpc_op_disconnect = sghsc_disconnect;
67303831d35Sstevel 		slot_ops->hpc_op_insert = nodev;
67403831d35Sstevel 		slot_ops->hpc_op_remove = nodev;
67503831d35Sstevel 		slot_ops->hpc_op_control = sghsc_control;
67603831d35Sstevel 
67703831d35Sstevel 		/*
67803831d35Sstevel 		 * HA (Full Hot Swap) is the default mode of operation
67903831d35Sstevel 		 * but the type of the board is set conservstively as
68003831d35Sstevel 		 * sghsc has no way of knowing it. The HP Framwork will
68103831d35Sstevel 		 * overwrite the value set at boot time.
68203831d35Sstevel 		 */
68303831d35Sstevel 		sghsc->sghsc_slot_table[i].flags = SGHSC_SLOT_AUTO_CFG_EN;
68403831d35Sstevel 		sghsc->sghsc_slot_table[i].board_type = HPC_BOARD_UNKNOWN;
68503831d35Sstevel 
68603831d35Sstevel 		/* Only register CPCI slots */
68703831d35Sstevel 		if (slot_info->slot_type != HPC_SLOT_TYPE_CPCI) {
68803831d35Sstevel 			DEBUGF(1, (CE_NOTE, "sghsc_register_slots: "
68903831d35Sstevel 			    "slot %d is non-cpci", i));
69003831d35Sstevel 			continue;
69103831d35Sstevel 		}
69203831d35Sstevel 
69303831d35Sstevel 		/*
69403831d35Sstevel 		 *  register slots
69503831d35Sstevel 		 */
69603831d35Sstevel 		if ((hpc_slot_register(dip, nexuspath, slot_info,
69703831d35Sstevel 		    &sghsc->sghsc_slot_table[i].handle,
69803831d35Sstevel 		    slot_ops, (caddr_t)sghsc, 0)) != 0) {
69903831d35Sstevel 
70003831d35Sstevel 			/*
70103831d35Sstevel 			 * return failure and let attach()
70203831d35Sstevel 			 * do the cleanup
70303831d35Sstevel 			 */
70403831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: Slot <%s> failed during HPS"
70503831d35Sstevel 			    " registration process for node %d / board %d",
70603831d35Sstevel 			    sghsc->sghsc_instance, slot_info->pci_slot_name,
70703831d35Sstevel 			    sghsc->sghsc_node_id, sghsc->sghsc_board);
70803831d35Sstevel 			return (DDI_FAILURE);
70903831d35Sstevel 		}
71003831d35Sstevel 
71103831d35Sstevel 	}
71203831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc registered successfully for"
71303831d35Sstevel 	    " node %d / board %d", sghsc->sghsc_node_id, sghsc->sghsc_board));
71403831d35Sstevel 	return (DDI_SUCCESS);
71503831d35Sstevel }
71603831d35Sstevel 
71703831d35Sstevel /*
71803831d35Sstevel  * Connecting a slot or all slots
71903831d35Sstevel  *	State Diagram:
72003831d35Sstevel  *	     states
72103831d35Sstevel  *	hw bits		EMPTY	DISCONNECT	CONNECT
72203831d35Sstevel  *	slot_enable	 NO	   NO		  YES
72303831d35Sstevel  *	card_present	 NO	   YES		  YES
72403831d35Sstevel  *	slot_switch	 N/A	   NO/YES	  YES
72503831d35Sstevel  *
72603831d35Sstevel  * Return val:	HPC_SUCCESS if the slot(s) are enabled
72703831d35Sstevel  * 		HPC_ERR_FAILED if the slot can't be enabled
72803831d35Sstevel  */
72903831d35Sstevel /* ARGSUSED */
73003831d35Sstevel static int
sghsc_connect(caddr_t op_arg,hpc_slot_t sloth,void * data,uint_t flag)73103831d35Sstevel sghsc_connect(caddr_t op_arg, hpc_slot_t sloth, void *data,
73203831d35Sstevel     uint_t flag)
73303831d35Sstevel {
73403831d35Sstevel 	int i = 0;
73503831d35Sstevel 	sghsc_t *sghsc = (sghsc_t *)op_arg;
73603831d35Sstevel 	int rc;
73703831d35Sstevel 	int result;
73803831d35Sstevel 	int	slot_num = sghsc_get_slotnum(sghsc, sloth);
73903831d35Sstevel 
74003831d35Sstevel 	switch (flag) {
74103831d35Sstevel 
74203831d35Sstevel 		case SGHSC_ALL_SLOTS_ENABLE:
74303831d35Sstevel 		for (i = 0; i < sghsc->sghsc_num_slots; i++) {
74403831d35Sstevel 			/*
74503831d35Sstevel 			 * All slots will be marked 'empty' as HP Framework
74603831d35Sstevel 			 * will try to connect those which have no kernel node.
74703831d35Sstevel 			 */
74803831d35Sstevel 			sghsc->sghsc_slot_table[i].slot_status =
74903831d35Sstevel 			    HPC_SLOT_EMPTY;
75003831d35Sstevel 		}
75103831d35Sstevel 
75203831d35Sstevel 		return (HPC_SUCCESS);
75303831d35Sstevel 	}
75403831d35Sstevel 
75503831d35Sstevel 	if (slot_num == -1)
75603831d35Sstevel 		return (HPC_ERR_INVALID);
75703831d35Sstevel 
75803831d35Sstevel 	SGHSC_MUTEX_ENTER(sghsc);
75903831d35Sstevel 
76003831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc%d: connecting logical slot%d for"
76103831d35Sstevel 	    " node %d / board %d", sghsc->sghsc_instance, slot_num,
76203831d35Sstevel 	    sghsc->sghsc_node_id, sghsc->sghsc_board));
76303831d35Sstevel 
76403831d35Sstevel 	/*
76503831d35Sstevel 	 * Powering an empty slot is highly illegal so far
76603831d35Sstevel 	 * (before SC implemented a constant poll). Otherwise
76703831d35Sstevel 	 * it breaks ddi framework and HP. The workaround
76803831d35Sstevel 	 * is to check for a card first.
76903831d35Sstevel 	 */
77003831d35Sstevel 	rc = sghsc_scctl(SGHSC_GET_SLOT_STATUS, sghsc->sghsc_node_id,
77103831d35Sstevel 	    sghsc->sghsc_board, slot_num, &result);
77203831d35Sstevel 
77303831d35Sstevel 	if (rc == ETIMEDOUT) {
77403831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
77503831d35Sstevel 		return (HPC_ERR_FAILED);
77603831d35Sstevel 	}
77703831d35Sstevel 
77803831d35Sstevel 	if (rc) {
77903831d35Sstevel 		cmn_err(CE_NOTE, "sghsc%d: unable to stat slot %d for"
78003831d35Sstevel 		    " node %d / board %d", sghsc->sghsc_instance, slot_num,
78103831d35Sstevel 		    sghsc->sghsc_node_id, sghsc->sghsc_board);
78203831d35Sstevel 		sghsc->sghsc_slot_table[i].slot_status = HPC_SLOT_UNKNOWN;
78303831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
78403831d35Sstevel 		return (HPC_ERR_FAILED);
78503831d35Sstevel 	}
78603831d35Sstevel 
78703831d35Sstevel 
78803831d35Sstevel 	if ((result >> CPCI_STAT_SLOT_EMPTY_SHIFT) & ONE_BIT) {
78903831d35Sstevel 		sghsc->sghsc_slot_table[i].slot_status = HPC_SLOT_EMPTY;
79003831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
79103831d35Sstevel 		return (HPC_ERR_FAILED);
79203831d35Sstevel 	}
79303831d35Sstevel 
79403831d35Sstevel 	rc = sghsc_scctl(SGHSC_SET_SLOT_POWER_ON, sghsc->sghsc_node_id,
79503831d35Sstevel 	    sghsc->sghsc_board, slot_num, &result);
79603831d35Sstevel 	if (rc) {
79703831d35Sstevel 		cmn_err(CE_WARN, "sghsc%d: unable to poweron slot %d for"
79803831d35Sstevel 		    " node %d / board %d", sghsc->sghsc_instance,
79903831d35Sstevel 		    slot_num, sghsc->sghsc_node_id, sghsc->sghsc_board);
80003831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
80103831d35Sstevel 		return (HPC_ERR_FAILED);
80203831d35Sstevel 	} else {
80303831d35Sstevel 		sghsc->sghsc_slot_table[slot_num].slot_status =
80403831d35Sstevel 		    HPC_SLOT_CONNECTED;
80503831d35Sstevel 	}
80603831d35Sstevel 
80703831d35Sstevel 	SGHSC_MUTEX_EXIT(sghsc);
80803831d35Sstevel 
80903831d35Sstevel 	return (HPC_SUCCESS);
81003831d35Sstevel }
81103831d35Sstevel 
81203831d35Sstevel 
81303831d35Sstevel /*
81403831d35Sstevel  * Disconnecting a slot or slots
81503831d35Sstevel  *
81603831d35Sstevel  * return:  HPC_SUCCESS if slot(s) are successfully disconnected
81703831d35Sstevel  *          HPC_ERR_FAILED if slot(s) can't be disconnected
81803831d35Sstevel  *
81903831d35Sstevel  */
82003831d35Sstevel /* ARGSUSED */
82103831d35Sstevel static int
sghsc_disconnect(caddr_t op_arg,hpc_slot_t sloth,void * data,uint_t flag)82203831d35Sstevel sghsc_disconnect(caddr_t op_arg, hpc_slot_t sloth, void *data,
82303831d35Sstevel     uint_t flag)
82403831d35Sstevel {
82503831d35Sstevel 	sghsc_t *sghsc = (sghsc_t *)op_arg;
82603831d35Sstevel 	int rc;
82703831d35Sstevel 	int result;
82803831d35Sstevel 	int slot_num = sghsc_get_slotnum(sghsc, sloth);
82903831d35Sstevel 
83003831d35Sstevel 	switch (flag) {
83103831d35Sstevel 		case SGHSC_ALL_SLOTS_DISABLE:
83203831d35Sstevel 		return (HPC_SUCCESS);
83303831d35Sstevel 
83403831d35Sstevel 	}
83503831d35Sstevel 
83603831d35Sstevel 	if (slot_num == -1)
83703831d35Sstevel 		return (HPC_ERR_INVALID);
83803831d35Sstevel 
83903831d35Sstevel 	SGHSC_MUTEX_ENTER(sghsc);
84003831d35Sstevel 
84103831d35Sstevel 	/*
84203831d35Sstevel 	 * Disconnecting an empty or disconnected slot
84303831d35Sstevel 	 * does't make sense.
84403831d35Sstevel 	 */
84503831d35Sstevel 	if (sghsc->sghsc_slot_table[slot_num].slot_status !=
84603831d35Sstevel 	    HPC_SLOT_CONNECTED) {
84703831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
84803831d35Sstevel 		return (HPC_SUCCESS);
84903831d35Sstevel 	}
85003831d35Sstevel 
85103831d35Sstevel 	rc = sghsc_scctl(SGHSC_SET_SLOT_POWER_OFF, sghsc->sghsc_node_id,
85203831d35Sstevel 	    sghsc->sghsc_board, slot_num, &result);
85303831d35Sstevel 	if (rc) {
85403831d35Sstevel 		cmn_err(CE_WARN, "sghsc%d: unable to poweroff slot %d for"
85503831d35Sstevel 		    " node %d / board %d", sghsc->sghsc_instance,
85603831d35Sstevel 		    slot_num, sghsc->sghsc_node_id, sghsc->sghsc_board);
85703831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
85803831d35Sstevel 		return (HPC_ERR_FAILED);
85903831d35Sstevel 	} else {
86003831d35Sstevel 		sghsc->sghsc_slot_table[slot_num].slot_status =
86103831d35Sstevel 		    HPC_SLOT_DISCONNECTED;
86203831d35Sstevel 	}
86303831d35Sstevel 
86403831d35Sstevel 	SGHSC_MUTEX_EXIT(sghsc);
86503831d35Sstevel 
86603831d35Sstevel 	return (HPC_SUCCESS);
86703831d35Sstevel }
86803831d35Sstevel 
86903831d35Sstevel /*
87003831d35Sstevel  * Entry point from the hotplug framework to do
87103831d35Sstevel  *   the main hotplug operations
87203831d35Sstevel  * Return val:	HPC_SUCCESS  success on ops
87303831d35Sstevel  *		HPC_NOT_SUPPORTED not supported feature
87403831d35Sstevel  *		HPC_ERR_FAILED	ops failed
87503831d35Sstevel  */
87603831d35Sstevel /*ARGSUSED*/
87703831d35Sstevel static int
sghsc_control(caddr_t op_arg,hpc_slot_t sloth,int request,caddr_t arg)87803831d35Sstevel sghsc_control(caddr_t op_arg, hpc_slot_t sloth, int request,
87903831d35Sstevel     caddr_t arg)
88003831d35Sstevel {
88103831d35Sstevel 	sghsc_t *sghsc = (sghsc_t *)op_arg;
88203831d35Sstevel 	int slot = sghsc_get_slotnum(sghsc, sloth);
88303831d35Sstevel 	int error = HPC_SUCCESS;
88403831d35Sstevel 	int rc;
88503831d35Sstevel 	int result;
88603831d35Sstevel 
88703831d35Sstevel 	if ((sghsc == NULL) || (slot < 0) ||
88803831d35Sstevel 	    (slot >= sghsc->sghsc_num_slots)) {
88903831d35Sstevel 		cmn_err(CE_WARN, "sghsc%d: sghsc_control fails with slot = %d"
89003831d35Sstevel 		    " max = %d, sloth = 0x%p for node %d / board %d",
89103831d35Sstevel 		    sghsc->sghsc_instance, slot, sghsc->sghsc_num_slots,
89203831d35Sstevel 		    sloth, sghsc->sghsc_node_id, sghsc->sghsc_board);
89303831d35Sstevel 		return (HPC_ERR_INVALID);
89403831d35Sstevel 	}
89503831d35Sstevel 
89603831d35Sstevel 	SGHSC_MUTEX_ENTER(sghsc);
89703831d35Sstevel 
89803831d35Sstevel 	switch (request) {
89903831d35Sstevel 	case HPC_CTRL_GET_LED_STATE: {
90003831d35Sstevel 		/* arg == hpc_led_info_t */
90103831d35Sstevel 
90203831d35Sstevel 		hpc_led_info_t *ledinfo;
90303831d35Sstevel 
90403831d35Sstevel 		ledinfo = (hpc_led_info_t *)arg;
90503831d35Sstevel 
90603831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
90703831d35Sstevel 		    " HPC_CTRL_GET_LED_STATE for node %d / board %d slot %d",
90803831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
90903831d35Sstevel 		    sghsc->sghsc_board, slot));
91003831d35Sstevel 
91103831d35Sstevel 		switch (ledinfo->led) {
91203831d35Sstevel 		case HPC_POWER_LED:
91303831d35Sstevel 		case HPC_ATTN_LED:
91403831d35Sstevel 		case HPC_FAULT_LED:
91503831d35Sstevel 		case HPC_ACTIVE_LED:
91603831d35Sstevel 			error = sghsc_led_state(sghsc, sloth,
91703831d35Sstevel 			    HPC_CTRL_GET_LED_STATE, ledinfo);
91803831d35Sstevel 			break;
91903831d35Sstevel 		default:
92003831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: sghsc_control"
92103831d35Sstevel 			    " HPC_CTRL_GET_LED_STATE "
92203831d35Sstevel 			    " unknown led state %d for node %d / board %d"
92303831d35Sstevel 			    " slot handle 0x%p", sghsc->sghsc_instance,
92403831d35Sstevel 			    ledinfo->led, sghsc->sghsc_node_id,
92503831d35Sstevel 			    sghsc->sghsc_board, sloth);
92603831d35Sstevel 			error = HPC_ERR_NOTSUPPORTED;
92703831d35Sstevel 			break;
92803831d35Sstevel 		}
92903831d35Sstevel 
93003831d35Sstevel 		break;
93103831d35Sstevel 	}
93203831d35Sstevel 
93303831d35Sstevel 	case HPC_CTRL_SET_LED_STATE: {
93403831d35Sstevel 		/* arg == hpc_led_info_t */
93503831d35Sstevel 		hpc_led_info_t *ledinfo;
93603831d35Sstevel 
93703831d35Sstevel 		ledinfo = (hpc_led_info_t *)arg;
93803831d35Sstevel 
93903831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
94003831d35Sstevel 		    " HPC_CTRL_SET_LED_STATE for node %d / board %d slot %d",
94103831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
94203831d35Sstevel 		    sghsc->sghsc_board, slot));
94303831d35Sstevel 
94403831d35Sstevel 		switch (ledinfo->led) {
94503831d35Sstevel 		case HPC_POWER_LED:
94603831d35Sstevel 		case HPC_ATTN_LED:
94703831d35Sstevel 		case HPC_FAULT_LED:
94803831d35Sstevel 		case HPC_ACTIVE_LED:
94903831d35Sstevel 			DEBUGF(1, (CE_NOTE, "sghsc:"
95003831d35Sstevel 			    " LED writing not supported "));
95103831d35Sstevel 			break;
95203831d35Sstevel 
95303831d35Sstevel 		default:
95403831d35Sstevel 			DEBUGF(1, (CE_NOTE, "sghsc:"
95503831d35Sstevel 			    " LED not supported "));
95603831d35Sstevel 			error = HPC_ERR_NOTSUPPORTED;
95703831d35Sstevel 		}
95803831d35Sstevel 		break;
95903831d35Sstevel 	}
96003831d35Sstevel 
96103831d35Sstevel 	case HPC_CTRL_GET_SLOT_STATE: {
96203831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
96303831d35Sstevel 		    " HPC_CTRL_GET_SLOT_STATE for node %d / board %d slot %d",
96403831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
96503831d35Sstevel 		    sghsc->sghsc_board, slot));
96603831d35Sstevel 
96703831d35Sstevel 		/*
96803831d35Sstevel 		 * Send mailbox cmd to SC to query the latest state
96903831d35Sstevel 		 */
97003831d35Sstevel 		rc = sghsc_scctl(SGHSC_GET_SLOT_STATUS, sghsc->sghsc_node_id,
97103831d35Sstevel 		    sghsc->sghsc_board, slot, &result);
97203831d35Sstevel 
97303831d35Sstevel 		if (rc == ETIMEDOUT) {
97403831d35Sstevel 			error = HPC_ERR_FAILED;
97503831d35Sstevel 			break;
97603831d35Sstevel 		}
97703831d35Sstevel 
97803831d35Sstevel 		if (rc) {
97903831d35Sstevel 			cmn_err(CE_NOTE, "sghsc%d: unable to stat slot %d for "
98003831d35Sstevel 			    "node %d / board %d", sghsc->sghsc_instance, slot,
98103831d35Sstevel 			    sghsc->sghsc_node_id, sghsc->sghsc_board);
98203831d35Sstevel 			sghsc->sghsc_slot_table[slot].slot_status =
98303831d35Sstevel 			    HPC_SLOT_UNKNOWN;
98403831d35Sstevel 			*(hpc_slot_state_t *)arg = HPC_SLOT_UNKNOWN;
98503831d35Sstevel 			break;
98603831d35Sstevel 		}
98703831d35Sstevel 
98803831d35Sstevel 		/*
98903831d35Sstevel 		 * Update the cached state if needed. Initally all
99003831d35Sstevel 		 * slots are marked as empty for the Hot Plug Framwork.
99103831d35Sstevel 		 */
99203831d35Sstevel 		if ((result >> CPCI_STAT_SLOT_EMPTY_SHIFT) & ONE_BIT) {
99303831d35Sstevel 			sghsc->sghsc_slot_table[slot].slot_status =
99403831d35Sstevel 			    HPC_SLOT_EMPTY;
99503831d35Sstevel 		} else if ((result >> CPCI_STAT_POWER_ON_SHIFT) & ONE_BIT) {
99603831d35Sstevel 			sghsc->sghsc_slot_table[slot].slot_status =
99703831d35Sstevel 			    HPC_SLOT_CONNECTED;
99803831d35Sstevel 		} else if (sghsc->sghsc_slot_table[slot].slot_status ==
99903831d35Sstevel 		    HPC_SLOT_EMPTY ||
100003831d35Sstevel 		    sghsc->sghsc_slot_table[slot].slot_status ==
100103831d35Sstevel 		    HPC_SLOT_UNKNOWN) {
100203831d35Sstevel 			sghsc->sghsc_slot_table[slot].slot_status =
100303831d35Sstevel 			    HPC_SLOT_DISCONNECTED;
100403831d35Sstevel 		}
100503831d35Sstevel 		/*
100603831d35Sstevel 		 * No change
100703831d35Sstevel 		 */
100803831d35Sstevel 		*(hpc_slot_state_t *)arg =
100903831d35Sstevel 		    sghsc->sghsc_slot_table[slot].slot_status;
101003831d35Sstevel 
101103831d35Sstevel 		break;
101203831d35Sstevel 	}
101303831d35Sstevel 
101403831d35Sstevel 	case HPC_CTRL_DEV_CONFIGURED:
101503831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
101603831d35Sstevel 		    " HPC_CTRL_DEV_CONFIGURED for node %d / board %d slot %d",
101703831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
101803831d35Sstevel 		    sghsc->sghsc_board, slot));
101903831d35Sstevel 
102003831d35Sstevel 		if (sghsc_configure_ack)
102103831d35Sstevel 			cmn_err(CE_NOTE, "sghsc%d:"
102203831d35Sstevel 			    " node %d / board %d slot %d configured",
102303831d35Sstevel 			    sghsc->sghsc_instance, sghsc->sghsc_node_id,
102403831d35Sstevel 			    sghsc->sghsc_board, slot);
102503831d35Sstevel 		/*
102603831d35Sstevel 		 * This is important to tell SC:
102703831d35Sstevel 		 * "start looking for ENUMs"
102803831d35Sstevel 		 */
102903831d35Sstevel 		if (sghsc->sghsc_slot_table[slot].flags &
103003831d35Sstevel 		    SGHSC_SLOT_AUTO_CFG_EN)
103103831d35Sstevel 			(void) sghsc_scctl(SGHSC_SET_ENUM_CLEARED,
103203831d35Sstevel 			    sghsc->sghsc_node_id, sghsc->sghsc_board,
103303831d35Sstevel 			    slot, &result);
103403831d35Sstevel 
103503831d35Sstevel 		break;
103603831d35Sstevel 
103703831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
103803831d35Sstevel 		/*
103903831d35Sstevel 		 * due to unclean drivers, unconfigure may leave
104003831d35Sstevel 		 * some state on card, configure may actually
104103831d35Sstevel 		 * use these invalid values. therefore, may force
104203831d35Sstevel 		 * disconnect.
104303831d35Sstevel 		 */
104403831d35Sstevel 
104503831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control "
104603831d35Sstevel 		    "HPC_CTRL_DEV_UNCONFIGURED for node %d / board %d slot %d",
104703831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
104803831d35Sstevel 		    sghsc->sghsc_board, slot));
104903831d35Sstevel 
105003831d35Sstevel 		SGHSC_MUTEX_EXIT(sghsc);
105103831d35Sstevel 		if (sghsc_disconnect(op_arg, sloth, 0,
105203831d35Sstevel 		    0) != HPC_SUCCESS) {
105303831d35Sstevel 			DEBUGF(1, (CE_NOTE, "sghsc_control: "
105403831d35Sstevel 			    "disconnect failed"));
105503831d35Sstevel 			error = HPC_ERR_FAILED;
105603831d35Sstevel 		}
105703831d35Sstevel 
105803831d35Sstevel 		cmn_err(CE_NOTE, "sghsc%d: node %d / board %d "
105903831d35Sstevel 		    "slot %d unconfigured", sghsc->sghsc_instance,
106003831d35Sstevel 		    sghsc->sghsc_node_id, sghsc->sghsc_board, slot);
106103831d35Sstevel 		return (error);
106203831d35Sstevel 
106303831d35Sstevel 
106403831d35Sstevel 	case HPC_CTRL_GET_BOARD_TYPE: {
106503831d35Sstevel 		/* arg = hpc_board_type_t */
106603831d35Sstevel 
106703831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
106803831d35Sstevel 		    " HPC_CTRL_GET_BOARD_TYPE for node %d / board %d slot %d",
106903831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
107003831d35Sstevel 		    sghsc->sghsc_board, slot));
107103831d35Sstevel 
107203831d35Sstevel 		*(hpc_board_type_t *)arg =
107303831d35Sstevel 		    sghsc->sghsc_slot_table[slot].board_type;
107403831d35Sstevel 
107503831d35Sstevel 		break;
107603831d35Sstevel 	}
107703831d35Sstevel 
107803831d35Sstevel 	case HPC_CTRL_ENABLE_AUTOCFG:
107903831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
108003831d35Sstevel 		    " HPC_CTRL_ENABLE_AUTOCFG for node %d / board %d slot %d",
108103831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
108203831d35Sstevel 		    sghsc->sghsc_board, slot));
108303831d35Sstevel 
108403831d35Sstevel 		sghsc->sghsc_slot_table[slot].flags |= SGHSC_SLOT_AUTO_CFG_EN;
108503831d35Sstevel 		(void) hpc_slot_event_notify(sloth, HPC_EVENT_ENABLE_ENUM,
108603831d35Sstevel 		    HPC_EVENT_NORMAL);
108703831d35Sstevel 
108803831d35Sstevel 		/*
108903831d35Sstevel 		 * Tell SC to start looking for ENUMs on this slot.
109003831d35Sstevel 		 */
109103831d35Sstevel 		rc = sghsc_scctl(SGHSC_SET_ENUM_CLEARED, sghsc->sghsc_node_id,
109203831d35Sstevel 		    sghsc->sghsc_board, slot, &result);
109303831d35Sstevel 
109403831d35Sstevel 		if (rc)
109503831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: unable to arm ENUM for"
109603831d35Sstevel 			    " node %d / board %d, slot %d",
109703831d35Sstevel 			    sghsc->sghsc_instance, sghsc->sghsc_node_id,
109803831d35Sstevel 			    sghsc->sghsc_board, slot);
109903831d35Sstevel 		break;
110003831d35Sstevel 
110103831d35Sstevel 	case HPC_CTRL_DISABLE_AUTOCFG:
110203831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc%d: sghsc_control"
110303831d35Sstevel 		    " HPC_CTRL_DISABLE_AUTOCFG for node %d / board %d slot %d",
110403831d35Sstevel 		    sghsc->sghsc_instance, sghsc->sghsc_node_id,
110503831d35Sstevel 		    sghsc->sghsc_board, slot));
110603831d35Sstevel 
110703831d35Sstevel 		sghsc->sghsc_slot_table[slot].flags &= ~SGHSC_SLOT_AUTO_CFG_EN;
110803831d35Sstevel 		(void) hpc_slot_event_notify(sloth, HPC_EVENT_DISABLE_ENUM,
110903831d35Sstevel 		    HPC_EVENT_NORMAL);
111003831d35Sstevel 		break;
111103831d35Sstevel 
111203831d35Sstevel 	case HPC_CTRL_DISABLE_SLOT:
111303831d35Sstevel 	case HPC_CTRL_ENABLE_SLOT:
111403831d35Sstevel 		break;
111503831d35Sstevel 
111603831d35Sstevel 	/*  need to add support for enable/disable_ENUM */
111703831d35Sstevel 	case HPC_CTRL_DISABLE_ENUM:
111803831d35Sstevel 	case HPC_CTRL_ENABLE_ENUM:
111903831d35Sstevel 	default:
112003831d35Sstevel 		DEBUGF(1, (CE_CONT, "sghsc%d: sghsc_control "
112103831d35Sstevel 		    "request (0x%x) not supported", sghsc->sghsc_instance,
112203831d35Sstevel 		    request));
112303831d35Sstevel 
112403831d35Sstevel 		/* invalid request */
112503831d35Sstevel 		error = HPC_ERR_NOTSUPPORTED;
112603831d35Sstevel 	}
112703831d35Sstevel 
112803831d35Sstevel 	SGHSC_MUTEX_EXIT(sghsc);
112903831d35Sstevel 
113003831d35Sstevel 	return (error);
113103831d35Sstevel }
113203831d35Sstevel 
113303831d35Sstevel /*
113403831d35Sstevel  * Read/write slot's led
113503831d35Sstevel  *	Assume MUTEX_HELD
113603831d35Sstevel  *
113703831d35Sstevel  * return:  HPC_SUCCESS if the led's status is avaiable,
113803831d35Sstevel  *          SC return status otherwise.
113903831d35Sstevel  */
114003831d35Sstevel static int
sghsc_led_state(sghsc_t * sghsc,hpc_slot_t sloth,int op,hpc_led_info_t * ledinfo)114103831d35Sstevel sghsc_led_state(sghsc_t *sghsc, hpc_slot_t sloth, int op,
114203831d35Sstevel     hpc_led_info_t *ledinfo)
114303831d35Sstevel {
114403831d35Sstevel 	int rval;
114503831d35Sstevel 	int slot_num;
114603831d35Sstevel 	int result;
114703831d35Sstevel 
114803831d35Sstevel 	slot_num = sghsc_get_slotnum(sghsc, sloth);
114903831d35Sstevel 	rval = sghsc_scctl(SGHSC_GET_SLOT_STATUS, sghsc->sghsc_node_id,
115003831d35Sstevel 	    sghsc->sghsc_board, slot_num, &result);
115103831d35Sstevel 	if (rval != HPC_SUCCESS)
115203831d35Sstevel 		return (rval);
115303831d35Sstevel 
115403831d35Sstevel 	switch (op) {
115503831d35Sstevel 	case HPC_CTRL_GET_LED_STATE:
115603831d35Sstevel 		switch (ledinfo->led) {
115703831d35Sstevel 		case HPC_POWER_LED:
115803831d35Sstevel 			if ((result >> CPCI_STAT_LED_POWER_SHIFT) & ONE_BIT)
115903831d35Sstevel 				ledinfo->state = HPC_LED_ON;
116003831d35Sstevel 			else
116103831d35Sstevel 				ledinfo->state = HPC_LED_OFF;
116203831d35Sstevel 			break;
116303831d35Sstevel 
116403831d35Sstevel 		case HPC_ATTN_LED:
116503831d35Sstevel 		case HPC_FAULT_LED:
116603831d35Sstevel 			if ((result >> CPCI_STAT_LED_FAULT_SHIFT) & ONE_BIT)
116703831d35Sstevel 				ledinfo->state = HPC_LED_ON;
116803831d35Sstevel 			else
116903831d35Sstevel 				ledinfo->state = HPC_LED_OFF;
117003831d35Sstevel 			break;
117103831d35Sstevel 
117203831d35Sstevel 		case HPC_ACTIVE_LED:
117303831d35Sstevel 			if ((result >> CPCI_STAT_LED_HP_SHIFT) & ONE_BIT)
117403831d35Sstevel 				ledinfo->state = HPC_LED_ON;
117503831d35Sstevel 			else
117603831d35Sstevel 				ledinfo->state = HPC_LED_OFF;
117703831d35Sstevel 			break;
117803831d35Sstevel 		}
117903831d35Sstevel 
118003831d35Sstevel 		break;
118103831d35Sstevel 
118203831d35Sstevel 	case HPC_CTRL_SET_LED_STATE:
118303831d35Sstevel 		return (HPC_ERR_NOTSUPPORTED);
118403831d35Sstevel 	}
118503831d35Sstevel 
118603831d35Sstevel 	return (HPC_SUCCESS);
118703831d35Sstevel }
118803831d35Sstevel 
118903831d35Sstevel /*
119003831d35Sstevel  * sghsc_get_slotnum()
119103831d35Sstevel  *	get slot number from the slot handle
119203831d35Sstevel  * returns non-negative value to indicate slot number
119303831d35Sstevel  *	  -1 for failure
119403831d35Sstevel  */
119503831d35Sstevel static int
sghsc_get_slotnum(sghsc_t * sghsc,hpc_slot_t sloth)119603831d35Sstevel sghsc_get_slotnum(sghsc_t *sghsc, hpc_slot_t sloth)
119703831d35Sstevel {
119803831d35Sstevel 	int i;
119903831d35Sstevel 
120003831d35Sstevel 	if (sloth == NULL || sghsc == NULL)
120103831d35Sstevel 		return (-1);
120203831d35Sstevel 
120303831d35Sstevel 	for (i = 0; i < sghsc->sghsc_num_slots; i++) {
120403831d35Sstevel 
120503831d35Sstevel 		if (sghsc->sghsc_slot_table[i].handle == sloth)
120603831d35Sstevel 			return (i);
120703831d35Sstevel 	}
120803831d35Sstevel 
120903831d35Sstevel 	return (-1);
121003831d35Sstevel 
121103831d35Sstevel }
121203831d35Sstevel 
121303831d35Sstevel /*
121403831d35Sstevel  * sghsc_scctl()
121503831d35Sstevel  *      mailbox interface
121603831d35Sstevel  *
121703831d35Sstevel  * return result code from mailbox operation
121803831d35Sstevel  */
121903831d35Sstevel static int
sghsc_scctl(int cmd,int node_id,int board,int slot,int * resultp)122003831d35Sstevel sghsc_scctl(int cmd, int node_id, int board, int slot, int *resultp)
122103831d35Sstevel {
122203831d35Sstevel 	int		ret = 0xbee;
122303831d35Sstevel 	bitcmd_info_t	cmd_info, *cmd_infop = &cmd_info;
122403831d35Sstevel 	bitcmd_resp_t	cmd_info_r, *cmd_info_r_p = &cmd_info_r;
122503831d35Sstevel 	sbbc_msg_t	request, *reqp = &request;
122603831d35Sstevel 	sbbc_msg_t	response, *resp = &response;
122703831d35Sstevel 
122803831d35Sstevel 	cmd_infop->cmd_id = 0x01234567;
122903831d35Sstevel 	cmd_infop->node_id = node_id;
123003831d35Sstevel 	cmd_infop->board = board;
123103831d35Sstevel 	cmd_infop->slot = slot;
123203831d35Sstevel 
123303831d35Sstevel 	reqp->msg_type.type = CPCI_MBOX;
123403831d35Sstevel 	reqp->msg_status = 0xeeeeffff;
123503831d35Sstevel 	reqp->msg_len = sizeof (cmd_info);
123603831d35Sstevel 	reqp->msg_bytes = 8;
123703831d35Sstevel 	reqp->msg_buf = (caddr_t)cmd_infop;
123803831d35Sstevel 	reqp->msg_data[0] = 0;
123903831d35Sstevel 	reqp->msg_data[1] = 0;
124003831d35Sstevel 
124103831d35Sstevel 	bzero(resp, sizeof (*resp));
124203831d35Sstevel 	bzero(cmd_info_r_p, sizeof (*cmd_info_r_p));
124303831d35Sstevel 
124403831d35Sstevel 	resp->msg_buf = (caddr_t)cmd_info_r_p;
124503831d35Sstevel 	resp->msg_len = sizeof (cmd_info_r);
124603831d35Sstevel 
124703831d35Sstevel 	resp->msg_type.type = CPCI_MBOX;
124803831d35Sstevel 	resp->msg_bytes = 8;
124903831d35Sstevel 	resp->msg_status = 0xddddffff;
125003831d35Sstevel 
125103831d35Sstevel 	switch (cmd) {
125203831d35Sstevel 	case SGHSC_GET_SLOT_STATUS:
125303831d35Sstevel 		reqp->msg_type.sub_type = CPCI_GET_SLOT_STATUS;
125403831d35Sstevel 		resp->msg_type.sub_type = CPCI_GET_SLOT_STATUS;
125503831d35Sstevel 		reqp->msg_len -= 4;
125603831d35Sstevel 		break;
125703831d35Sstevel 	case SGHSC_GET_NUM_SLOTS:
125803831d35Sstevel 		reqp->msg_type.sub_type = CPCI_GET_NUM_SLOTS;
125903831d35Sstevel 		resp->msg_type.sub_type = CPCI_GET_NUM_SLOTS;
126003831d35Sstevel 		reqp->msg_len -= 8;
126103831d35Sstevel 		break;
126203831d35Sstevel 	case SGHSC_SET_SLOT_STATUS_RESET:
126303831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_STATUS;
126403831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_STATUS;
126503831d35Sstevel 		cmd_infop->info = CPCI_SET_STATUS_SLOT_RESET;
126603831d35Sstevel 		break;
126703831d35Sstevel 	case SGHSC_SET_SLOT_STATUS_READY:
126803831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_STATUS;
126903831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_STATUS;
127003831d35Sstevel 		cmd_infop->info = CPCI_SET_STATUS_SLOT_READY;
127103831d35Sstevel 		break;
127203831d35Sstevel 	case SGHSC_SET_SLOT_FAULT_LED_ON:
127303831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
127403831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
127503831d35Sstevel 		cmd_infop->info = CPCI_SET_FAULT_LED_ON;
127603831d35Sstevel 		break;
127703831d35Sstevel 	case SGHSC_SET_SLOT_FAULT_LED_OFF:
127803831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
127903831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
128003831d35Sstevel 		cmd_infop->info = CPCI_SET_FAULT_LED_OFF;
128103831d35Sstevel 		break;
128203831d35Sstevel 	case SGHSC_SET_SLOT_FAULT_LED_KEEP:
128303831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
128403831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
128503831d35Sstevel 		cmd_infop->info = CPCI_SET_FAULT_LED_KEEP;
128603831d35Sstevel 		break;
128703831d35Sstevel 	case SGHSC_SET_SLOT_FAULT_LED_TOGGLE:
128803831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
128903831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_FAULT_LED;
129003831d35Sstevel 		cmd_infop->info = CPCI_SET_FAULT_LED_TOGGLE;
129103831d35Sstevel 		break;
129203831d35Sstevel 	case SGHSC_SET_SLOT_POWER_OFF:
129303831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_POWER;
129403831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_POWER;
129503831d35Sstevel 		cmd_infop->info = CPCI_POWER_OFF;
129603831d35Sstevel 		break;
129703831d35Sstevel 	case SGHSC_SET_SLOT_POWER_ON:
129803831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_SLOT_POWER;
129903831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_SLOT_POWER;
130003831d35Sstevel 		cmd_infop->info = CPCI_POWER_ON;
130103831d35Sstevel 		break;
130203831d35Sstevel 	case SGHSC_GET_CPCI_BOARD_TYPE:
130303831d35Sstevel 		reqp->msg_type.sub_type = CPCI_BOARD_TYPE;
130403831d35Sstevel 		resp->msg_type.sub_type = CPCI_BOARD_TYPE;
130503831d35Sstevel 		reqp->msg_len -= 8;
130603831d35Sstevel 		break;
130703831d35Sstevel 	case SGHSC_SET_ENUM_CLEARED:
130803831d35Sstevel 		reqp->msg_type.sub_type = CPCI_SET_ENUM_CLEARED;
130903831d35Sstevel 		resp->msg_type.sub_type = CPCI_SET_ENUM_CLEARED;
131003831d35Sstevel 		break;
131103831d35Sstevel 	default:
131203831d35Sstevel 		cmn_err(CE_WARN, "sghsc: unrecognized action code 0x%x\n",
131303831d35Sstevel 		    cmd);
131403831d35Sstevel 	}
131503831d35Sstevel 
131603831d35Sstevel 	DEBUGF(1, (CE_NOTE,
131703831d35Sstevel 	    "sghsc: sending mbox command type=%d subtype=0x%x size=%d buf=%p",
131803831d35Sstevel 	    reqp->msg_type.type, reqp->msg_type.sub_type,
131907d06da5SSurya Prakki 	    reqp->msg_len, (void *)reqp->msg_buf));
132003831d35Sstevel 
132103831d35Sstevel 	DEBUGF(1, (CE_NOTE,
132203831d35Sstevel 	    "sghsc: sending buf  cmd_id=0x%x node_id=0x%x board=0x%x "
132303831d35Sstevel 	    "slot=0x%x info=0x%x", cmd_infop->cmd_id, cmd_infop->node_id,
132403831d35Sstevel 	    cmd_infop->board, cmd_infop->slot, cmd_infop->info));
132503831d35Sstevel 
132603831d35Sstevel 
132703831d35Sstevel 	ret = sbbc_mbox_request_response(reqp, resp, sghsc_mbx_timeout);
132803831d35Sstevel 
132903831d35Sstevel 	/*
133003831d35Sstevel 	 * The resp->msg_status field may contain an SC error or a common
133103831d35Sstevel 	 * error such as ETIMEDOUT.
133203831d35Sstevel 	 */
133303831d35Sstevel 	if ((ret != 0) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
133403831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: mailbox command error = 0x%x, "
133503831d35Sstevel 		    "status = 0x%x", ret, resp->msg_status));
133603831d35Sstevel 		return (-1);
133703831d35Sstevel 	}
133803831d35Sstevel 
133903831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: reply request status=0x%x",
134003831d35Sstevel 	    reqp->msg_status));
134103831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: reply resp status=0x%x",
134203831d35Sstevel 	    resp->msg_status));
134303831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: reply buf  cmd_id=0x%x result=0x%x\n",
134403831d35Sstevel 	    cmd_info_r_p->cmd_id, cmd_info_r_p->result));
134503831d35Sstevel 
134603831d35Sstevel #ifdef DEBUG_EXTENDED
134703831d35Sstevel 	if (cmd == SGHSC_GET_NUM_SLOTS) {
134803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc:  node %d / board %d has %d slots",
134919397407SSherry Moore 		    cmd_infop->node_id, cmd_infop->board,
135019397407SSherry Moore 		    cmd_info_r_p->result));
135103831d35Sstevel 		*resultp = cmd_info_r_p->result;
135203831d35Sstevel 		return (0);
135303831d35Sstevel 	}
135403831d35Sstevel 
135503831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_POWER_ON_SHIFT) & ONE_BIT)
135603831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: cpower on"));
135703831d35Sstevel 
135803831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_LED_POWER_SHIFT) & ONE_BIT)
135903831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: power led on"));
136003831d35Sstevel 
136103831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_LED_FAULT_SHIFT) & ONE_BIT)
136203831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: fault led on"));
136303831d35Sstevel 
136403831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_LED_HP_SHIFT) & ONE_BIT)
136503831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: remove(hp) led on"));
136603831d35Sstevel 
136703831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_SLOT_EMPTY_SHIFT) & ONE_BIT)
136803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: slot empty"));
136903831d35Sstevel 
137003831d35Sstevel 	tmp = ((cmd_info_r_p->result >> CPCI_STAT_HOT_SWAP_STATUS_SHIFT) &
137103831d35Sstevel 	    THREE_BITS);
137203831d35Sstevel 	if (tmp)
137303831d35Sstevel 		DEBUGF(1, (CE_NOTE,
137403831d35Sstevel 		    "sghsc: slot condition(hot swap status) is 0x%x", tmp));
137503831d35Sstevel 
137603831d35Sstevel 	if (cmd_info_r_p->result & CPCI_GET_STAT_SLOT_HZ_CAP)
137703831d35Sstevel 		DEBUGF(1, (CE_NOTE,
137803831d35Sstevel 		    "sghsc: freq cap %x", cmd_info_r_p->result &
137903831d35Sstevel 		    CPCI_GET_STAT_SLOT_HZ_CAP));
138003831d35Sstevel 
138103831d35Sstevel 	if (cmd_info_r_p->result & CPCI_GET_STAT_SLOT_HZ_SET)
138203831d35Sstevel 		DEBUGF(1, (CE_NOTE,
138303831d35Sstevel 		    "sghsc: freq setting %x", cmd_info_r_p->result &
138403831d35Sstevel 		    CPCI_GET_STAT_SLOT_HZ_SET));
138503831d35Sstevel 
138603831d35Sstevel 
138703831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_HEALTHY_SHIFT) & ONE_BIT)
138803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: healthy"));
138903831d35Sstevel 
139003831d35Sstevel 	if ((cmd_info_r_p->result >> CPCI_STAT_RESET_SHIFT) & ONE_BIT)
139103831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: in reset"));
139203831d35Sstevel 
139303831d35Sstevel 	if (cmd_info_r_p->result & CPCI_GET_STAT_POWER_GOOD)
139403831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: power good"));
139503831d35Sstevel 
139603831d35Sstevel 	if (cmd_info_r_p->result & CPCI_GET_STAT_POWER_FAULT)
139703831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: power fault"));
139803831d35Sstevel 
139903831d35Sstevel 	if (cmd_info_r_p->result & CPCI_GET_STAT_PCI_PRESENT)
140003831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: pci present"));
140103831d35Sstevel #endif
140203831d35Sstevel 
140303831d35Sstevel 	*resultp = cmd_info_r_p->result;
140403831d35Sstevel 	return (0);
140503831d35Sstevel }
140603831d35Sstevel 
140703831d35Sstevel 
140803831d35Sstevel /*
140903831d35Sstevel  * sghsc_freemem()
141003831d35Sstevel  *	deallocates memory resources
141103831d35Sstevel  *
141203831d35Sstevel  */
141303831d35Sstevel static void
sghsc_freemem(sghsc_t * sghsc)141403831d35Sstevel sghsc_freemem(sghsc_t *sghsc)
141503831d35Sstevel {
141603831d35Sstevel 	int i;
141703831d35Sstevel 
141803831d35Sstevel 	/*
141903831d35Sstevel 	 * Free up allocated resources
142003831d35Sstevel 	 * sghsc_register_slots => unregister all slots
142103831d35Sstevel 	 */
142203831d35Sstevel 	for (i = 0; i < sghsc->sghsc_num_slots; i++) {
142303831d35Sstevel 		if (sghsc->sghsc_slot_table[i].slot_ops)
142403831d35Sstevel 			hpc_free_slot_ops(sghsc->sghsc_slot_table[i].slot_ops);
142503831d35Sstevel 		if (sghsc->sghsc_slot_table[i].handle)
142607d06da5SSurya Prakki 			(void) hpc_slot_unregister(
142707d06da5SSurya Prakki 			    &sghsc->sghsc_slot_table[i].handle);
142803831d35Sstevel 	}
142903831d35Sstevel 
143003831d35Sstevel 	/* finally free up slot_table */
143103831d35Sstevel 	kmem_free(sghsc->sghsc_slot_table,
143203831d35Sstevel 	    (size_t)(sghsc->sghsc_num_slots * sizeof (sghsc_slot_t)));
143303831d35Sstevel 
143403831d35Sstevel }
143503831d35Sstevel 
143603831d35Sstevel /*
143703831d35Sstevel  * sghsc_find_sloth()
143803831d35Sstevel  *      Find slot handle by node id, board number and slot numbert
143903831d35Sstevel  * Returns slot handle or 0 if slot not found.
144003831d35Sstevel  */
144103831d35Sstevel static hpc_slot_t
sghsc_find_sloth(int node_id,int board,int slot)144203831d35Sstevel sghsc_find_sloth(int node_id, int board, int slot)
144303831d35Sstevel {
144403831d35Sstevel 	int instance;
144503831d35Sstevel 	sghsc_t *sghsc;
144603831d35Sstevel 
144703831d35Sstevel 	for (instance = 0; instance < sghsc_maxinst; instance++) {
144803831d35Sstevel 		sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance);
144903831d35Sstevel 
145003831d35Sstevel 		if (sghsc == NULL || sghsc->sghsc_node_id != node_id ||
145103831d35Sstevel 		    sghsc->sghsc_board != board)
145203831d35Sstevel 			continue;
145303831d35Sstevel 
145403831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc_find_sloth on board %d at node %d"
145503831d35Sstevel 		    " slot %d", board, node_id, slot))
145603831d35Sstevel 
145703831d35Sstevel 		if (sghsc->sghsc_num_slots < (slot + 1)) {
145803831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: slot data corruption at"
145903831d35Sstevel 			    "node %d / board %d", instance, node_id, board);
146003831d35Sstevel 			return (NULL);
146103831d35Sstevel 		}
146203831d35Sstevel 
146303831d35Sstevel 		if (sghsc->sghsc_valid == 0)
146403831d35Sstevel 			return (NULL);
146503831d35Sstevel 
146603831d35Sstevel 		/*
146703831d35Sstevel 		 * Found matching slot, return handle.
146803831d35Sstevel 		 */
146903831d35Sstevel 		return (sghsc->sghsc_slot_table[slot].handle);
147003831d35Sstevel 	}
147103831d35Sstevel 
147203831d35Sstevel 	DEBUGF(1, (CE_WARN, "sghsc_find_sloth: slot %d not found for node %d"
147303831d35Sstevel 	" / board %d", slot, node_id, board));
147403831d35Sstevel 	return (NULL);
147503831d35Sstevel }
147603831d35Sstevel 
147703831d35Sstevel /*
147803831d35Sstevel  * sghsc_event_handler()
147903831d35Sstevel  *      Event Handler. This is what for other platforms was an interrupt
148003831d35Sstevel  * Handler servicing events. It accepts an event and signals it to
148103831d35Sstevel  * non-interrupt thread.
148203831d35Sstevel  */
148303831d35Sstevel uint_t
sghsc_event_handler(char * arg)148403831d35Sstevel sghsc_event_handler(char *arg)
148503831d35Sstevel {
148603831d35Sstevel 	sghsc_event_t *rsp_data;
148703831d35Sstevel 	hpc_slot_t sloth;
148803831d35Sstevel 	sghsc_t *enum_state;
148903831d35Sstevel 
149003831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: sghsc_event_handler called"))
149103831d35Sstevel 
149203831d35Sstevel 	rsp_data = (sghsc_event_t *)(((sbbc_msg_t *)arg)->msg_buf);
149303831d35Sstevel 
149403831d35Sstevel 	if (rsp_data == NULL) {
149503831d35Sstevel 		cmn_err(CE_WARN,
149603831d35Sstevel 		    ("sghsc: sghsc_event_handler argument is null\n"));
1497055d7c80Scarlsonj 		return (DDI_INTR_CLAIMED);
149803831d35Sstevel 	}
149903831d35Sstevel 
150003831d35Sstevel 	sloth = sghsc_find_sloth(rsp_data->node_id, rsp_data->board,
150103831d35Sstevel 	    rsp_data->slot);
150203831d35Sstevel 	/*
150303831d35Sstevel 	 * On a board disconnect sghsc soft state may not exist
150403831d35Sstevel 	 * when the interrupt occurs. We should treat these
150503831d35Sstevel 	 * interrupts as noise and but them.
150603831d35Sstevel 	 */
150703831d35Sstevel 	if (sloth == NULL) {
150803831d35Sstevel 		DEBUGF(1, (CE_WARN, "sghsc: slot info not available for"
150903831d35Sstevel 		    " node %d / board %d slot %d. CPCI event rejected",
151003831d35Sstevel 		    rsp_data->node_id, rsp_data->board, rsp_data->slot));
151103831d35Sstevel 		return (DDI_INTR_CLAIMED);
151203831d35Sstevel 	}
151303831d35Sstevel 
151403831d35Sstevel 	enum_state = sghsc_find_softstate(rsp_data->node_id, rsp_data->board,
151503831d35Sstevel 	    rsp_data->slot);
151603831d35Sstevel 	if (enum_state == NULL) {
151703831d35Sstevel 		cmn_err(CE_WARN, "sghsc: soft state not available for"
151803831d35Sstevel 		    " node %d / board %d slot %d", rsp_data->node_id,
151903831d35Sstevel 		    rsp_data->board, rsp_data->slot);
152003831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
152103831d35Sstevel 	}
152203831d35Sstevel 
152303831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: node %d", rsp_data->node_id));
152403831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: board %d", rsp_data->board));
152503831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: slot %d", rsp_data->slot));
152603831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: event info %d", rsp_data->info));
152703831d35Sstevel 
152803831d35Sstevel 	switch (rsp_data->info) {
152903831d35Sstevel 	case SGHSC_EVENT_CARD_INSERT:
153003831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: card inserted node %d / board %d"
153103831d35Sstevel 		    " slot %d", rsp_data->node_id, rsp_data->board,
153203831d35Sstevel 		    rsp_data->slot));
153303831d35Sstevel 		enum_state->sghsc_slot_table[rsp_data->slot].board_type =
153403831d35Sstevel 		    HPC_BOARD_CPCI_HS;
153503831d35Sstevel 		enum_state->sghsc_slot_table[rsp_data->slot].slot_status =
153603831d35Sstevel 		    HPC_SLOT_DISCONNECTED;
153703831d35Sstevel 		break;
153803831d35Sstevel 	case SGHSC_EVENT_CARD_REMOVE:
153903831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: card removed node %d / board %d"
154003831d35Sstevel 		    " slot %d", rsp_data->node_id, rsp_data->board,
154103831d35Sstevel 		    rsp_data->slot));
154203831d35Sstevel 		enum_state->sghsc_slot_table[rsp_data->slot].board_type =
154303831d35Sstevel 		    HPC_BOARD_UNKNOWN;
154403831d35Sstevel 		enum_state->sghsc_slot_table[rsp_data->slot].slot_status =
154503831d35Sstevel 		    HPC_SLOT_EMPTY;
154603831d35Sstevel 		return (DDI_INTR_CLAIMED);
154703831d35Sstevel 	case SGHSC_EVENT_POWER_ON:
154803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: power on node %d / board %d"
154903831d35Sstevel 		    " slot %d", rsp_data->node_id, rsp_data->board,
155003831d35Sstevel 		    rsp_data->slot));
155103831d35Sstevel 		return (DDI_INTR_CLAIMED);
155203831d35Sstevel 	case SGHSC_EVENT_POWER_OFF:
155303831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: power off node %d / board %d"
155403831d35Sstevel 		    " slot %d", rsp_data->node_id, rsp_data->board,
155503831d35Sstevel 		    rsp_data->slot));
155603831d35Sstevel 		return (DDI_INTR_CLAIMED);
155703831d35Sstevel 	case SGHSC_EVENT_HEALTHY_LOST:
155803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: healthy lost node %d / board %d"
155903831d35Sstevel 		    " slot %d", rsp_data->node_id, rsp_data->board,
156003831d35Sstevel 		    rsp_data->slot));
156103831d35Sstevel 		return (DDI_INTR_CLAIMED);
156203831d35Sstevel 	case SGHSC_EVENT_LEVER_ACTION:
156303831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: ENUM generated for node %d /"
156403831d35Sstevel 		    "board %d slot %d", rsp_data->node_id, rsp_data->board,
156503831d35Sstevel 		    rsp_data->slot));
156603831d35Sstevel 		break;
156703831d35Sstevel 	default:
156803831d35Sstevel 		DEBUGF(1, (CE_NOTE, "sghsc: unrecognized event info for"
156903831d35Sstevel 		    " node %d / board %d slot %d", rsp_data->node_id,
157003831d35Sstevel 		    rsp_data->board, rsp_data->slot));
157103831d35Sstevel 		return (DDI_INTR_CLAIMED);
157203831d35Sstevel 	}
157303831d35Sstevel 
157403831d35Sstevel 	/*
157503831d35Sstevel 	 * Signal the ENUM event to the non-interrupt thread as the Hot
157603831d35Sstevel 	 * Plug Framework will eventually call sghsc_control() but all
157703831d35Sstevel 	 * the mailbox messages are not allowed from interrupt context.
157803831d35Sstevel 	 */
157903831d35Sstevel 
158003831d35Sstevel 	if (sghsc_rb_put(&sghsc_rb_header, rsp_data) != DDI_SUCCESS) {
158103831d35Sstevel 		cmn_err(CE_WARN, "sghsc: no space to store #ENUM info");
158203831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
158303831d35Sstevel 	}
158403831d35Sstevel 
158503831d35Sstevel 	cv_signal(&sghsc_event_thread_cv);
158603831d35Sstevel 
158703831d35Sstevel 	return (DDI_INTR_CLAIMED);
158803831d35Sstevel }
158903831d35Sstevel 
159003831d35Sstevel /*
159103831d35Sstevel  * sghsc_event_thread_code()
159203831d35Sstevel  *      Event Thread. This is non-interrupt thread servicing #ENUM, Insert,
159303831d35Sstevel  *      Remove, Power on/off, Healthy lost events.
159403831d35Sstevel  */
159503831d35Sstevel static void
sghsc_event_thread_code(void)159603831d35Sstevel sghsc_event_thread_code(void)
159703831d35Sstevel {
159803831d35Sstevel 	int	rc;
159903831d35Sstevel 	int	result;
160003831d35Sstevel 	hpc_slot_t sloth;
160103831d35Sstevel 	sghsc_t *sghsc;
160203831d35Sstevel 	sghsc_event_t rsp_data;
160303831d35Sstevel 
160403831d35Sstevel 	mutex_enter(&sghsc_event_thread_mutex);
160503831d35Sstevel 
160603831d35Sstevel 	for (;;) {
160703831d35Sstevel 		/*
160803831d35Sstevel 		 * Wait for Event handler to signal event or self destruction.
160903831d35Sstevel 		 * Assuming the mutex will be automatically reaccuired.
161003831d35Sstevel 		 */
161103831d35Sstevel 		cv_wait(&sghsc_event_thread_cv, &sghsc_event_thread_mutex);
161203831d35Sstevel 
161303831d35Sstevel 		if (sghsc_event_thread_exit)
161403831d35Sstevel 			break;
161503831d35Sstevel 
161603831d35Sstevel 		/*
161703831d35Sstevel 		 * Pick up all the relevant events from the ring buffer.
161803831d35Sstevel 		 */
161903831d35Sstevel 		while (sghsc_rb_get(&sghsc_rb_header, &rsp_data) ==
162003831d35Sstevel 		    DDI_SUCCESS) {
162103831d35Sstevel 
162203831d35Sstevel 			sghsc = sghsc_find_softstate(rsp_data.node_id,
162303831d35Sstevel 			    rsp_data.board, rsp_data.slot);
162403831d35Sstevel 			if (sghsc == NULL)
162503831d35Sstevel 				continue;
162603831d35Sstevel 			sloth = sghsc_find_sloth(rsp_data.node_id,
162703831d35Sstevel 			    rsp_data.board, rsp_data.slot);
162803831d35Sstevel 			if (sloth == NULL)
162903831d35Sstevel 				continue;
163003831d35Sstevel 
163103831d35Sstevel 			if (!(sghsc->sghsc_slot_table[rsp_data.slot].flags &
163203831d35Sstevel 			    SGHSC_SLOT_AUTO_CFG_EN))
163303831d35Sstevel 				continue;
163403831d35Sstevel 			/*
163503831d35Sstevel 			 * Insert event leads only to the electrical
163603831d35Sstevel 			 * connection.
163703831d35Sstevel 			 */
163803831d35Sstevel 			if (rsp_data.info == SGHSC_EVENT_CARD_INSERT) {
163903831d35Sstevel 				rc = sghsc_connect((caddr_t)sghsc, sloth,
164003831d35Sstevel 				    NULL, 0);
164103831d35Sstevel 				if (rc != HPC_SUCCESS)
164203831d35Sstevel 					cmn_err(CE_WARN, "sghsc:"
164303831d35Sstevel 					    " could not connect inserted card,"
164403831d35Sstevel 					    " node %d / board %d slot %d",
164503831d35Sstevel 					    rsp_data.node_id, rsp_data.board,
164603831d35Sstevel 					    rsp_data.slot);
164703831d35Sstevel 				continue;
164803831d35Sstevel 			}
164903831d35Sstevel 
165003831d35Sstevel 			/*
165103831d35Sstevel 			 * ENUM event received.
165203831d35Sstevel 			 * Reset ENUM and notify SC to poll for the next one.
165303831d35Sstevel 			 */
165403831d35Sstevel 			rc = hpc_slot_event_notify(sloth, HPC_EVENT_CLEAR_ENUM,
165503831d35Sstevel 			    HPC_EVENT_SYNCHRONOUS);
165603831d35Sstevel 
165703831d35Sstevel 			if (rc == HPC_EVENT_UNCLAIMED) {
165803831d35Sstevel 				DEBUGF(1, (CE_WARN,
165903831d35Sstevel 				    "sghsc: unable to clear ENUM"));
166003831d35Sstevel 				continue;
166103831d35Sstevel 			}
166203831d35Sstevel 
166303831d35Sstevel 			rc = sghsc_scctl(SGHSC_SET_ENUM_CLEARED,
166403831d35Sstevel 			    rsp_data.node_id, rsp_data.board,
166503831d35Sstevel 			    rsp_data.slot, &result);
166603831d35Sstevel 			if (rc) {
166703831d35Sstevel 				DEBUGF(1, (CE_WARN,
166803831d35Sstevel 				    "sghsc: unable to ACK cleared ENUM"));
166903831d35Sstevel 				continue;
167003831d35Sstevel 			}
167103831d35Sstevel 
167203831d35Sstevel 			/*
167303831d35Sstevel 			 * process the ENUM.
167403831d35Sstevel 			 */
167503831d35Sstevel 			rc = hpc_slot_event_notify(sloth,
167603831d35Sstevel 			    HPC_EVENT_PROCESS_ENUM, HPC_EVENT_SYNCHRONOUS);
167703831d35Sstevel 
167803831d35Sstevel 			if (rc == HPC_EVENT_UNCLAIMED) {
167903831d35Sstevel 				DEBUGF(1, (CE_WARN,
168003831d35Sstevel 				    "sghsc: could not process ENUM"));
168103831d35Sstevel 			}
168203831d35Sstevel 		}
168303831d35Sstevel 	}
168403831d35Sstevel 
168503831d35Sstevel 	DEBUGF(1, (CE_NOTE, "sghsc: thread_exit"));
168603831d35Sstevel 	cv_signal(&sghsc_event_thread_cv);
168703831d35Sstevel 	mutex_exit(&sghsc_event_thread_mutex);
168803831d35Sstevel 	thread_exit();
168903831d35Sstevel }
169003831d35Sstevel 
169103831d35Sstevel /*
169203831d35Sstevel  * sghsc_find_softstate()
169303831d35Sstevel  *      Find softstate by node id and board number. Slot number is used for
169403831d35Sstevel  *      verification.
169503831d35Sstevel  * Returns board's softstate or 0 if not found.
169603831d35Sstevel  */
169703831d35Sstevel static sghsc_t *
sghsc_find_softstate(int node_id,int board,int slot)169803831d35Sstevel sghsc_find_softstate(int node_id, int board, int slot)
169903831d35Sstevel {
170003831d35Sstevel 	int instance;
170103831d35Sstevel 	sghsc_t *sghsc;
170203831d35Sstevel 
170303831d35Sstevel 	for (instance = 0; instance < sghsc_maxinst; instance++) {
170403831d35Sstevel 		sghsc = (sghsc_t *)ddi_get_soft_state(sghsc_state, instance);
170503831d35Sstevel 
170603831d35Sstevel 		if (sghsc == NULL || sghsc->sghsc_node_id != node_id ||
170703831d35Sstevel 		    sghsc->sghsc_board != board)
170803831d35Sstevel 			continue;
170903831d35Sstevel 
171003831d35Sstevel 		if (sghsc->sghsc_num_slots < (slot + 1)) {
171103831d35Sstevel 			cmn_err(CE_WARN, "sghsc%d: "
171203831d35Sstevel 			    "slot data corruption", instance);
171303831d35Sstevel 			return (NULL);
171403831d35Sstevel 		}
171503831d35Sstevel 
171603831d35Sstevel 		if (sghsc->sghsc_valid == 0)
171703831d35Sstevel 			return (NULL);
171803831d35Sstevel 
171903831d35Sstevel 		/*
172003831d35Sstevel 		 * Found matching data, return soft state.
172103831d35Sstevel 		 */
172203831d35Sstevel 		return (sghsc);
172303831d35Sstevel 	}
172403831d35Sstevel 
172503831d35Sstevel 	cmn_err(CE_WARN, "sghsc: soft state not found");
172603831d35Sstevel 	return (NULL);
172703831d35Sstevel }
172803831d35Sstevel 
172903831d35Sstevel /*
173003831d35Sstevel  * sghsc_rb_setup()
173103831d35Sstevel  *      Initialize the event ring buffer with a fixed size. It may require
173203831d35Sstevel  *      a more elaborate scheme with buffer extension
173303831d35Sstevel  */
173403831d35Sstevel static void
sghsc_rb_setup(sghsc_rb_head_t * rb_head)173503831d35Sstevel sghsc_rb_setup(sghsc_rb_head_t *rb_head)
173603831d35Sstevel {
173703831d35Sstevel 	if (rb_head->buf == NULL) {
173803831d35Sstevel 		rb_head->put_idx = 0;
173903831d35Sstevel 		rb_head->get_idx = 0;
174003831d35Sstevel 		rb_head->size = SGHSC_RING_BUFFER_SZ;
174103831d35Sstevel 		rb_head->state = SGHSC_RB_EMPTY;
174203831d35Sstevel 
174303831d35Sstevel 		/*
174403831d35Sstevel 		 * Allocate space for event ring buffer
174503831d35Sstevel 		 */
174603831d35Sstevel 		rb_head->buf = (sghsc_event_t *)kmem_zalloc(
174703831d35Sstevel 		    sizeof (sghsc_event_t) * rb_head->size, KM_SLEEP);
174803831d35Sstevel 	}
174903831d35Sstevel }
175003831d35Sstevel 
175103831d35Sstevel /*
175203831d35Sstevel  * sghsc_rb_teardown()
175303831d35Sstevel  *      Free event ring buffer resources.
175403831d35Sstevel  */
175503831d35Sstevel static void
sghsc_rb_teardown(sghsc_rb_head_t * rb_head)175603831d35Sstevel sghsc_rb_teardown(sghsc_rb_head_t *rb_head)
175703831d35Sstevel {
175803831d35Sstevel 	if (rb_head->buf != NULL) {
175903831d35Sstevel 		/*
176003831d35Sstevel 		 * Deallocate space for event ring buffer
176103831d35Sstevel 		 */
176203831d35Sstevel 		kmem_free(rb_head->buf,
176303831d35Sstevel 		    (size_t)(sizeof (sghsc_event_t) * rb_head->size));
176403831d35Sstevel 
176503831d35Sstevel 		rb_head->buf = NULL;
176603831d35Sstevel 		rb_head->put_idx = 0;
176703831d35Sstevel 		rb_head->get_idx = 0;
176803831d35Sstevel 		rb_head->size = 0;
176903831d35Sstevel 		rb_head->state = SGHSC_RB_EMPTY;
177003831d35Sstevel 	}
177103831d35Sstevel }
177203831d35Sstevel 
177303831d35Sstevel /*
177403831d35Sstevel  * sghsc_rb_put()
177503831d35Sstevel  *      Insert an event info into the event ring buffer.
177603831d35Sstevel  * Returns DDI_FAILURE if the buffer is full, DDI_SUCCESS otherwise
177703831d35Sstevel  */
177803831d35Sstevel static int
sghsc_rb_put(sghsc_rb_head_t * rb_head,sghsc_event_t * event)177903831d35Sstevel sghsc_rb_put(sghsc_rb_head_t *rb_head, sghsc_event_t *event)
178003831d35Sstevel {
178103831d35Sstevel 	if (rb_head->state == SGHSC_RB_FULL)
178203831d35Sstevel 		return (DDI_FAILURE);
178303831d35Sstevel 
178403831d35Sstevel 	rb_head->buf[rb_head->put_idx] = *event;
178503831d35Sstevel 
1786*2bc98732SRichard Lowe 	rb_head->put_idx = (rb_head->put_idx + 1) & (rb_head->size - 1);
178703831d35Sstevel 
178803831d35Sstevel 	if (rb_head->put_idx == rb_head->get_idx)
178903831d35Sstevel 		rb_head->state = SGHSC_RB_FULL;
179003831d35Sstevel 	else
179103831d35Sstevel 		rb_head->state = SGHSC_RB_FLOAT;
179203831d35Sstevel 
179303831d35Sstevel 	return (DDI_SUCCESS);
179403831d35Sstevel }
179503831d35Sstevel /*
179603831d35Sstevel  * sghsc_rb_get()
179703831d35Sstevel  *      Remove an event info from the event  ring buffer.
179803831d35Sstevel  * Returns DDI_FAILURE if the buffer is empty, DDI_SUCCESS otherwise.
179903831d35Sstevel  */
180003831d35Sstevel static int
sghsc_rb_get(sghsc_rb_head_t * rb_head,sghsc_event_t * event)180103831d35Sstevel sghsc_rb_get(sghsc_rb_head_t *rb_head, sghsc_event_t *event)
180203831d35Sstevel {
180303831d35Sstevel 
180403831d35Sstevel 	if (rb_head->state == SGHSC_RB_EMPTY)
180503831d35Sstevel 		return (DDI_FAILURE);
180603831d35Sstevel 
180703831d35Sstevel 	*event = rb_head->buf[rb_head->get_idx];
180803831d35Sstevel 
1809*2bc98732SRichard Lowe 	rb_head->get_idx = (rb_head->get_idx + 1) & (rb_head->size - 1);
181003831d35Sstevel 
181103831d35Sstevel 	if (rb_head->get_idx == rb_head->put_idx)
181203831d35Sstevel 		rb_head->state = SGHSC_RB_EMPTY;
181303831d35Sstevel 	else
181403831d35Sstevel 		rb_head->state = SGHSC_RB_FLOAT;
181503831d35Sstevel 
181603831d35Sstevel 	return (DDI_SUCCESS);
181703831d35Sstevel }
1818