xref: /titanic_44/usr/src/uts/sun4u/montecarlo/io/hsc.c (revision 07d06da50d310a325b457d6330165aebab1e0064)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
5*07d06da5SSurya Prakki  * Common Development and Distribution License (the "License").
6*07d06da5SSurya Prakki  * 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  */
21*07d06da5SSurya Prakki 
2203831d35Sstevel /*
23*07d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * MonteCarlo HotSwap Controller functionality
2903831d35Sstevel  */
3003831d35Sstevel 
3103831d35Sstevel #include	<sys/types.h>
3203831d35Sstevel #include	<sys/stropts.h>
3303831d35Sstevel #include	<sys/stream.h>
3403831d35Sstevel #include	<sys/strsun.h>
3503831d35Sstevel #include	<sys/kmem.h>
3603831d35Sstevel #include	<sys/cmn_err.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/promif.h>
4603831d35Sstevel #include	<sys/hotplug/hpcsvc.h>
4703831d35Sstevel 
4803831d35Sstevel #include	<sys/hscimpl.h>
4903831d35Sstevel #include	<sys/hsc.h>
5003831d35Sstevel 
5103831d35Sstevel #include	<sys/mct_topology.h>
5203831d35Sstevel #include	<sys/scsbioctl.h>
5303831d35Sstevel #include	<sys/scsb.h>
5403831d35Sstevel 
5503831d35Sstevel #define	HOTSWAP_MODE_PROP	"hotswap-mode"
5603831d35Sstevel #define	ALARM_CARD_ON_SLOT	1
5703831d35Sstevel #define	SCSB_HSC_FORCE_REMOVE	1	/* force remove enum intr handler */
5803831d35Sstevel 
5903831d35Sstevel /* TUNABLE PARAMETERS. Some are Debug Only. Please take care when using. */
6003831d35Sstevel 
6103831d35Sstevel /*
6203831d35Sstevel  * Set this flag to 1, to enable full hotswap mode at boot time.
6303831d35Sstevel  * Since HPS is threaded, it is not recommended that we set this flag
6403831d35Sstevel  * to 1 because enabling full hotswap interrupt can invoke the ENUM
6503831d35Sstevel  * event handler accessing the slot data structure which may have not
6603831d35Sstevel  * been initialized in the hotplug framework since the HPS may not yet
6703831d35Sstevel  * have called the slot registration function with the bus nexus.
6803831d35Sstevel  */
6903831d35Sstevel static int	scsb_hsc_enable_fhs = 0;
7003831d35Sstevel 
7103831d35Sstevel /*
7203831d35Sstevel  * Every time  a slot is registered with the hotswap framework, the
7303831d35Sstevel  * framework calls back. This variable keeps a count on how many
7403831d35Sstevel  * callbacks are done.
7503831d35Sstevel  */
7603831d35Sstevel static int scsb_hsc_numReg = 0;
7703831d35Sstevel /*
7803831d35Sstevel  * When this flag is set, the board is taken offline (put in reset) after
7903831d35Sstevel  * a unconfigure operation, in Basic Hotswap mode.
8003831d35Sstevel  */
8103831d35Sstevel static int	scsb_hsc_bhs_slot_reset = 1;
8203831d35Sstevel /*
8303831d35Sstevel  * When this flag is set, we take the board to reset after unconfigure
8403831d35Sstevel  * operation when operating in full hotswap mode.
8503831d35Sstevel  */
8603831d35Sstevel static int	scsb_hsc_fhs_slot_reset = 1;
8703831d35Sstevel /*
8803831d35Sstevel  * Implementation of this counter will work only on Montecarlo since
8903831d35Sstevel  * the ENUM# Interrupt line is not shared with other interrupts.
9003831d35Sstevel  * When the hardware routing changes, then there may be need to remove
9103831d35Sstevel  * or change this functionality.
9203831d35Sstevel  * This functionality is provided so that a bad or non friendly full hotswap
9303831d35Sstevel  * board does not hang the system in full hotswap mode. Atleast the
9403831d35Sstevel  * intent is that! Eventually Solaris kernel will provide similar support
9503831d35Sstevel  * for recovering from a stuck interrupt line. Till then, lets do this.
9603831d35Sstevel  */
9703831d35Sstevel static int	scsb_hsc_max_intr_count = 8;
9803831d35Sstevel /*
9903831d35Sstevel  * Since the hardware does not support enabling/disabling ENUM#, the
10003831d35Sstevel  * following flag can be used for imitating that behaviour.
10103831d35Sstevel  * Currently we can set this flag and use the remove op to remove the
10203831d35Sstevel  * interrupt handler from the system. Care must be taken when using this
10303831d35Sstevel  * function since trying to remove the interrupt handler when the interrupts
10403831d35Sstevel  * are pending may hang the system permanently.
10503831d35Sstevel  * Since the hardware does not support this functionality, we adopt this
10603831d35Sstevel  * approach for debugs.
10703831d35Sstevel  */
10803831d35Sstevel static int	scsb_hsc_enum_switch = 0;
10903831d35Sstevel 
11003831d35Sstevel /*
11103831d35Sstevel  * When the board loses Healthy# at runtime (with the board being configured),
11203831d35Sstevel  * cPCI specs states that a Reset has to be asserted immediately.
11303831d35Sstevel  * We dont do this currently, until satellite processor support is given
11403831d35Sstevel  * and the implications of such a act is fully understood.
11503831d35Sstevel  * To adopt the cPCI specs recommendation, set this flag to 1.
11603831d35Sstevel  */
11703831d35Sstevel static	int	scsb_hsc_healthy_reset = 0;
11803831d35Sstevel 
11903831d35Sstevel /*
12003831d35Sstevel  * According to PCI 2.2 specification, once a board comes out of PCI_RST#,
12103831d35Sstevel  * it may take upto 2^25 clock cycles to respond to config cycles. For
12203831d35Sstevel  * montecarlo using a 33MHz cPCI bus, it's around 1.024 s. The variable
12303831d35Sstevel  * will specify the time in ms to wait before attempting config access.
12403831d35Sstevel  */
12503831d35Sstevel static	int scsb_connect_delay = 1025;
12603831d35Sstevel 
12703831d35Sstevel /*
12803831d35Sstevel  * slot map property for MC should be
12903831d35Sstevel  *
13003831d35Sstevel  *	hsc-slot-map="/pci@1f,0/pci@1/pci@1","15","2",
13103831d35Sstevel  *               "/pci@1f,0/pci@1/pci@1","14","3",
13203831d35Sstevel  *               "/pci@1f,0/pci@1/pci@1","13","4",
13303831d35Sstevel  *               "/pci@1f,0/pci@1/pci@1","12","5"
13403831d35Sstevel  *               "/pci@1f,0/pci@1/pci@1","11","6"
13503831d35Sstevel  *               "/pci@1f,0/pci@1/pci@1","10","7"
13603831d35Sstevel  *               "/pci@1f,0/pci@1/pci@1","8","8";
13703831d35Sstevel  *
13803831d35Sstevel  * slot map property for Tonga should be
13903831d35Sstevel  *	hsc-slot-map="/pci@1f,0/pci@1/pci@1","8","1"
14003831d35Sstevel  *		"/pci@1f,0/pci@1/pci@1", "15", "2"
14103831d35Sstevel  *		"/pci@1f,0/pci@1/pci@1", "14", "4"
14203831d35Sstevel  *		"/pci@1f,0/pci@1/pci@1", "13", "5"
14303831d35Sstevel  *
14403831d35Sstevel  * Please note that the CPU slot number is 3 for Tonga.
14503831d35Sstevel  */
14603831d35Sstevel 
14703831d35Sstevel /*
14803831d35Sstevel  * Services we require from the SCSB
14903831d35Sstevel  */
15003831d35Sstevel extern int	scsb_get_slot_state(void *, int, int *);
15103831d35Sstevel extern int	scsb_read_bhealthy(scsb_state_t *scsb);
15203831d35Sstevel extern int	scsb_read_slot_health(scsb_state_t *scsb, int pslotnum);
15303831d35Sstevel extern int	scsb_connect_slot(void *, int, int);
15403831d35Sstevel extern int	scsb_disconnect_slot(void *, int, int);
15503831d35Sstevel 
15603831d35Sstevel static void	*hsc_state;
15703831d35Sstevel 
15803831d35Sstevel static uint_t	hsc_enum_intr(char *);
15903831d35Sstevel static hsc_slot_t *hsc_get_slot_info(hsc_state_t *, int);
16003831d35Sstevel static int	scsb_enable_enum(hsc_state_t *);
16103831d35Sstevel static int	scsb_disable_enum(hsc_state_t *, int);
16203831d35Sstevel static int	atoi(const char *);
16303831d35Sstevel static int	isdigit(int);
16403831d35Sstevel static hsc_slot_t *hsc_find_slot(int);
16503831d35Sstevel static void	hsc_led_op(hsc_slot_t *, int, hpc_led_t, hpc_led_state_t);
16603831d35Sstevel static int	hsc_led_state(hsc_slot_t *, int, hpc_led_info_t *);
16703831d35Sstevel static int	scsb_hsc_disable_slot(hsc_slot_t *);
16803831d35Sstevel static int	scsb_hsc_enable_slot(hsc_slot_t *);
16903831d35Sstevel #ifndef	lint
17003831d35Sstevel static int hsc_clear_all_enum(hsc_state_t *);
17103831d35Sstevel #endif
17203831d35Sstevel static int	hsc_slot_register(hsc_state_t *, char *, uint16_t, uint_t,
17303831d35Sstevel 					boolean_t);
17403831d35Sstevel static int	hsc_slot_unregister(int);
17503831d35Sstevel static int	scsb_hsc_init_slot_state(hsc_state_t *, hsc_slot_t *);
17603831d35Sstevel static int	hsc_slot_autoconnect(hsc_slot_t *);
17703831d35Sstevel 
17803831d35Sstevel static hpc_slot_ops_t	*hsc_slotops;
17903831d35Sstevel static hsc_slot_t	*hsc_slot_list;		/* linked list of slots */
18003831d35Sstevel 
18103831d35Sstevel /*
18203831d35Sstevel  * This mutex protects the following variables:
18303831d35Sstevel  *	hsc_slot_list
18403831d35Sstevel  */
18503831d35Sstevel static kmutex_t		hsc_mutex;
18603831d35Sstevel 
18703831d35Sstevel 
18803831d35Sstevel /* ARGSUSED */
18903831d35Sstevel static int
hsc_connect(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)19003831d35Sstevel hsc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
19103831d35Sstevel {
19203831d35Sstevel 	hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
19303831d35Sstevel 	int rc, rstate;
19403831d35Sstevel 	hsc_state_t	*hsc;
19503831d35Sstevel 
19603831d35Sstevel 	DEBUG2("hsc_connect: slot %d, healthy %d", hsp->hs_slot_number,
19703831d35Sstevel 						hsp->hs_board_healthy);
19803831d35Sstevel 
19903831d35Sstevel 	if (!(hsp->hs_flags & (HSC_ENABLED|HSC_SLOT_ENABLED)))
20003831d35Sstevel 		return (HPC_ERR_FAILED);
20103831d35Sstevel 	/* if SCB hotswapped, do not allow connect operations */
20203831d35Sstevel 	if (hsp->hs_flags & HSC_SCB_HOTSWAPPED)
20303831d35Sstevel 		return (HPC_ERR_FAILED);
20403831d35Sstevel 	/*
20503831d35Sstevel 	 * if previous occupant stayed configured, do not allow another
20603831d35Sstevel 	 * occupant to be connected.
20703831d35Sstevel 	 * This behaviour is an indication that the slot state
20803831d35Sstevel 	 * is not clean.
20903831d35Sstevel 	 */
21003831d35Sstevel 	if (hsp->hs_flags & HSC_SLOT_BAD_STATE) {
21103831d35Sstevel 		/*
21203831d35Sstevel 		 * In the current implementation, we turn both fault
21303831d35Sstevel 		 * and active LEDs to ON state in this situation.
21403831d35Sstevel 		 */
21503831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
21603831d35Sstevel 							HPC_LED_ON);
21703831d35Sstevel 		return (HPC_ERR_FAILED);
21803831d35Sstevel 	}
21903831d35Sstevel 	/*
22003831d35Sstevel 	 * Get the actual status from the i2c bus
22103831d35Sstevel 	 */
22203831d35Sstevel 	rc = scsb_get_slot_state(hsp->hs_hpchandle, hsp->hs_slot_number,
22303831d35Sstevel 								&rstate);
22403831d35Sstevel 	if (rc != DDI_SUCCESS)
22503831d35Sstevel 		return (HPC_ERR_FAILED);
22603831d35Sstevel 
22703831d35Sstevel 	hsp->hs_slot_state = rstate;
22803831d35Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
22903831d35Sstevel #ifdef DEBUG
23003831d35Sstevel 		cmn_err(CE_CONT,
23103831d35Sstevel 			"?hsc_connect: slot %d is empty\n",
23203831d35Sstevel 			hsp->hs_slot_number);
23303831d35Sstevel #endif
23403831d35Sstevel 		return (HPC_ERR_FAILED);
23503831d35Sstevel 	}
23603831d35Sstevel 
23703831d35Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_CONNECTED)
23803831d35Sstevel 		return (HPC_SUCCESS);
23903831d35Sstevel 
24003831d35Sstevel 	rc = HPC_SUCCESS;
24103831d35Sstevel 	/*
24203831d35Sstevel 	 * call scsb to connect the slot. This also makes sure board is healthy
24303831d35Sstevel 	 */
24403831d35Sstevel 	if (scsb_connect_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
24503831d35Sstevel 				hsp->hs_board_healthy) != DDI_SUCCESS) {
24603831d35Sstevel 		DEBUG1("hsc_connect: slot %d connection failed",
24703831d35Sstevel 				hsp->hs_slot_number);
24803831d35Sstevel 		rc = HPC_ERR_FAILED;
24903831d35Sstevel 	} else {
25003831d35Sstevel 		if (hsp->hs_slot_state != HPC_SLOT_CONNECTED) {
25103831d35Sstevel 			if (hsp->hs_board_healthy == B_FALSE) {
25203831d35Sstevel 				cmn_err(CE_NOTE, "HEALTHY# not asserted on "
25303831d35Sstevel 					" slot %d", hsp->hs_slot_number);
25403831d35Sstevel 				return (HPC_ERR_FAILED);
25503831d35Sstevel 			}
25603831d35Sstevel 			hsc = hsp->hsc;
25703831d35Sstevel 			hsc->hsp_last = hsp;
25803831d35Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
25903831d35Sstevel 				hsp->hs_slot_number, SCSB_UNRESET_SLOT) != 0) {
26003831d35Sstevel 
26103831d35Sstevel 				return (HPC_ERR_FAILED);
26203831d35Sstevel 			}
26303831d35Sstevel 			/*
26403831d35Sstevel 			 * Unresetting a board may have caused an interrupt
26503831d35Sstevel 			 * burst in case of non friendly boards. So it is
26603831d35Sstevel 			 * important to make sure that the ISR has not
26703831d35Sstevel 			 * put this board back to disconnect state.
26803831d35Sstevel 			 */
26903831d35Sstevel 			delay(1);
27003831d35Sstevel 			if (hsp->hs_flags & HSC_ENUM_FAILED) {
27103831d35Sstevel 				hsp->hs_flags &= ~HSC_ENUM_FAILED;
27203831d35Sstevel 				return (HPC_ERR_FAILED);
27303831d35Sstevel 			}
27403831d35Sstevel 			DEBUG1("hsc_connect: slot %d connected",
27503831d35Sstevel 						hsp->hs_slot_number);
27603831d35Sstevel 			rc = HPC_SUCCESS;
27703831d35Sstevel 			hsp->hs_slot_state = HPC_SLOT_CONNECTED;
27803831d35Sstevel 			(void) hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
27903831d35Sstevel 				HPC_FAULT_LED, HPC_LED_OFF);
28003831d35Sstevel 		}
28103831d35Sstevel 	}
28203831d35Sstevel 
28303831d35Sstevel 	/*
28403831d35Sstevel 	 * PCI 2.2 specs recommend that the probe software wait
28503831d35Sstevel 	 * for upto 2^25 PCI clock cycles after deassertion of
28603831d35Sstevel 	 * PCI_RST# before the board is able to respond to config
28703831d35Sstevel 	 * cycles. So, before we return, we wait for ~1 sec.
28803831d35Sstevel 	 */
28903831d35Sstevel 	delay(drv_usectohz(scsb_connect_delay * 1000));
29003831d35Sstevel 	return (rc);
29103831d35Sstevel }
29203831d35Sstevel 
29303831d35Sstevel 
29403831d35Sstevel /* ARGSUSED */
29503831d35Sstevel static int
hsc_disconnect(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)29603831d35Sstevel hsc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
29703831d35Sstevel {
29803831d35Sstevel 	hsc_slot_t		*hsp = (hsc_slot_t *)ops_arg;
29903831d35Sstevel 	hsc_state_t		*hsc;
30003831d35Sstevel #ifdef	DEBUG
30103831d35Sstevel 	static const char	func[] = "hsc_disconnect";
30203831d35Sstevel #endif
30303831d35Sstevel 
30403831d35Sstevel 	DEBUG1("hsc_disconnect: slot %d", hsp->hs_slot_number);
30503831d35Sstevel 
30603831d35Sstevel 	if (hsp->hs_board_configured) {
30703831d35Sstevel #ifdef	DEBUG
30803831d35Sstevel 		cmn_err(CE_NOTE,
30903831d35Sstevel 			"%s: cannot disconnect configured board in slot %d",
31003831d35Sstevel 			func, hsp->hs_slot_number);
31103831d35Sstevel #endif
31203831d35Sstevel 		return (HPC_ERR_FAILED);
31303831d35Sstevel 	}
31403831d35Sstevel 
31503831d35Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
31603831d35Sstevel #ifdef	DEBUG
31703831d35Sstevel 		cmn_err(CE_NOTE, "%s: slot %d is empty",
31803831d35Sstevel 			func, hsp->hs_slot_number);
31903831d35Sstevel #endif
32003831d35Sstevel 		return (HPC_SUCCESS);
32103831d35Sstevel 	}
32203831d35Sstevel 
32303831d35Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
32403831d35Sstevel 		/*
32503831d35Sstevel 		 * if already disconnected, just return success
32603831d35Sstevel 		 * Duplicate disconnect messages should not be failed!
32703831d35Sstevel 		 */
32803831d35Sstevel 		return (HPC_SUCCESS);
32903831d35Sstevel 	}
33003831d35Sstevel 	/* if SCB hotswapped, do not allow disconnect operations */
33103831d35Sstevel 	if (hsp->hs_flags & HSC_SCB_HOTSWAPPED)
33203831d35Sstevel 		return (HPC_ERR_FAILED);
33303831d35Sstevel 
33403831d35Sstevel 	/* call scsb to disconnect the slot */
33503831d35Sstevel 	if (scsb_disconnect_slot(hsp->hs_hpchandle, B_TRUE, hsp->hs_slot_number)
33603831d35Sstevel 			!= DDI_SUCCESS)
33703831d35Sstevel 		return (HPC_ERR_FAILED);
33803831d35Sstevel 	hsc = hsp->hsc;
33903831d35Sstevel 	if (hsc->hsp_last == hsp)
34003831d35Sstevel 		hsc->hsp_last = NULL;
34103831d35Sstevel 
34203831d35Sstevel 	return (HPC_SUCCESS);
34303831d35Sstevel }
34403831d35Sstevel 
34503831d35Sstevel 
34603831d35Sstevel /*
34703831d35Sstevel  * In the cPCI world, this operation is not applicable.
34803831d35Sstevel  * However, we use this function to enable full hotswap mode in debug mode.
34903831d35Sstevel  */
35003831d35Sstevel /* ARGSUSED */
35103831d35Sstevel static int
hsc_insert(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)35203831d35Sstevel hsc_insert(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
35303831d35Sstevel {
35403831d35Sstevel 	hsc_slot_t		*hsp = (hsc_slot_t *)ops_arg;
35503831d35Sstevel 
35603831d35Sstevel 	if (scsb_hsc_enum_switch &&
35703831d35Sstevel 			(scsb_enable_enum(hsp->hsc) == DDI_SUCCESS)) {
35803831d35Sstevel 		return (HPC_SUCCESS);
35903831d35Sstevel 	}
36003831d35Sstevel 	return (HPC_ERR_NOTSUPPORTED);
36103831d35Sstevel }
36203831d35Sstevel 
36303831d35Sstevel 
36403831d35Sstevel /*
36503831d35Sstevel  * In the cPCI world, this operation is not applicable.
36603831d35Sstevel  * However, we use this function to disable full hotswap mode in debug mode.
36703831d35Sstevel  */
36803831d35Sstevel /* ARGSUSED */
36903831d35Sstevel static int
hsc_remove(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)37003831d35Sstevel hsc_remove(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
37103831d35Sstevel {
37203831d35Sstevel 	hsc_slot_t		*hsp = (hsc_slot_t *)ops_arg;
37303831d35Sstevel 
37403831d35Sstevel 	if (scsb_hsc_enum_switch &&
37503831d35Sstevel 			(scsb_disable_enum(hsp->hsc, SCSB_HSC_FORCE_REMOVE)
37603831d35Sstevel 					== DDI_SUCCESS)) {
37703831d35Sstevel 		hsp->hs_flags &= ~HSC_ENUM_FAILED;
37803831d35Sstevel 		return (HPC_SUCCESS);
37903831d35Sstevel 	}
38003831d35Sstevel 	return (HPC_ERR_NOTSUPPORTED);
38103831d35Sstevel }
38203831d35Sstevel 
38303831d35Sstevel static void
hsc_led_op(hsc_slot_t * hsp,int cmd,hpc_led_t led,hpc_led_state_t led_state)38403831d35Sstevel hsc_led_op(hsc_slot_t *hsp, int cmd, hpc_led_t led, hpc_led_state_t led_state)
38503831d35Sstevel {
38603831d35Sstevel 	hpc_led_info_t	ledinfo;
38703831d35Sstevel 
38803831d35Sstevel 	ledinfo.led = led;
38903831d35Sstevel 	ledinfo.state = led_state;
39003831d35Sstevel 	(void) hsc_led_state(hsp, cmd, &ledinfo);
39103831d35Sstevel }
39203831d35Sstevel 
39303831d35Sstevel static int
hsc_led_state(hsc_slot_t * hsp,int cmd,hpc_led_info_t * hlip)39403831d35Sstevel hsc_led_state(hsc_slot_t *hsp, int cmd, hpc_led_info_t *hlip)
39503831d35Sstevel {
39603831d35Sstevel 	hpc_led_state_t	*hlsp;
39703831d35Sstevel 	scsb_uinfo_t	sunit;
39803831d35Sstevel 	int		res;
39903831d35Sstevel 
40003831d35Sstevel 	DEBUG3("hsc_led_state: slot %d, led %x, state %x",
40103831d35Sstevel 		hsp->hs_slot_number, hlip->led, hlip->state);
40203831d35Sstevel 
40303831d35Sstevel 	sunit.unit_type = SLOT;
40403831d35Sstevel 	sunit.unit_number = hsp->hs_slot_number;
40503831d35Sstevel 	/*
40603831d35Sstevel 	 * We ignore operations on LEDs that we don't support
40703831d35Sstevel 	 */
40803831d35Sstevel 	switch (hlip->led) {
40903831d35Sstevel 		case HPC_FAULT_LED:
41003831d35Sstevel 			sunit.led_type = NOK;
41103831d35Sstevel 			hlsp = &hsp->hs_fault_led_state;
41203831d35Sstevel 			break;
41303831d35Sstevel 		case HPC_ACTIVE_LED:
41403831d35Sstevel 			sunit.led_type = OK;
41503831d35Sstevel 			hlsp = &hsp->hs_active_led_state;
41603831d35Sstevel 			break;
41703831d35Sstevel 		default:
41803831d35Sstevel 			return (HPC_ERR_NOTSUPPORTED);
41903831d35Sstevel 	}
42003831d35Sstevel 
42103831d35Sstevel 	switch (hlip->state) {
42203831d35Sstevel 		case HPC_LED_BLINK:
42303831d35Sstevel 			sunit.unit_state = BLINK;
42403831d35Sstevel 			if (hlip->led != HPC_ACTIVE_LED)
42503831d35Sstevel 				return (HPC_ERR_NOTSUPPORTED);
42603831d35Sstevel 			break;
42703831d35Sstevel 		case HPC_LED_ON:
42803831d35Sstevel 			sunit.unit_state = ON;
42903831d35Sstevel 			break;
43003831d35Sstevel 		case HPC_LED_OFF:
43103831d35Sstevel 			sunit.unit_state = OFF;
43203831d35Sstevel 			break;
43303831d35Sstevel 		default:
43403831d35Sstevel 			break;
43503831d35Sstevel 	}
43603831d35Sstevel 
43703831d35Sstevel 	switch (cmd) {
43803831d35Sstevel 	case HPC_CTRL_SET_LED_STATE:
43903831d35Sstevel 		res = scsb_led_set(hsp->hs_hpchandle, &sunit, sunit.led_type);
44003831d35Sstevel 		if (res != 0)
44103831d35Sstevel 			return (HPC_ERR_FAILED);
44203831d35Sstevel 		*hlsp = (hpc_led_state_t)sunit.unit_state;
44303831d35Sstevel 		break;
44403831d35Sstevel 
44503831d35Sstevel 	case HPC_CTRL_GET_LED_STATE:
44603831d35Sstevel 		res = scsb_led_get(hsp->hs_hpchandle, &sunit, sunit.led_type);
44703831d35Sstevel 		if (res)
44803831d35Sstevel 			return (HPC_ERR_FAILED);
44903831d35Sstevel 		/* hlip->state = sunit.unit_state; */
45003831d35Sstevel 		break;
45103831d35Sstevel 
45203831d35Sstevel 	default:
45303831d35Sstevel 		return (HPC_ERR_INVALID);
45403831d35Sstevel 	}
45503831d35Sstevel 
45603831d35Sstevel 	return (HPC_SUCCESS);
45703831d35Sstevel 
45803831d35Sstevel }
45903831d35Sstevel 
46003831d35Sstevel 
46103831d35Sstevel static int
hsc_get_slot_state(hsc_slot_t * hsp,hpc_slot_state_t * hssp)46203831d35Sstevel hsc_get_slot_state(hsc_slot_t *hsp, hpc_slot_state_t *hssp)
46303831d35Sstevel {
46403831d35Sstevel 	int rstate = 0;
46503831d35Sstevel 	int rc;
46603831d35Sstevel #ifdef	DEBUG
46703831d35Sstevel 	int orstate;	/* original rstate */
46803831d35Sstevel #endif
46903831d35Sstevel 
47003831d35Sstevel 	DEBUG1("hsc_get_slot_state: slot %d", hsp->hs_slot_number);
47103831d35Sstevel 	rc = scsb_get_slot_state(hsp->hs_hpchandle, hsp->hs_slot_number,
47203831d35Sstevel 								&rstate);
47303831d35Sstevel 	if (rc != DDI_SUCCESS)
47403831d35Sstevel 		return (HPC_ERR_FAILED);
47503831d35Sstevel #ifdef	DEBUG
47603831d35Sstevel 	orstate = hsp->hs_slot_state;
47703831d35Sstevel #endif
47803831d35Sstevel 	hsp->hs_slot_state = rstate;
47903831d35Sstevel 	switch (hsp->hs_slot_state) {
48003831d35Sstevel 	case HPC_SLOT_EMPTY:
48103831d35Sstevel 		DEBUG0("empty");
48203831d35Sstevel 		break;
48303831d35Sstevel 	case HPC_SLOT_CONNECTED:
48403831d35Sstevel 		DEBUG0("connected");
48503831d35Sstevel 		break;
48603831d35Sstevel 	case HPC_SLOT_DISCONNECTED:
48703831d35Sstevel 		DEBUG0("disconnected");
48803831d35Sstevel 		break;
48903831d35Sstevel 	}
49003831d35Sstevel 
49103831d35Sstevel 	*hssp = hsp->hs_slot_state;
49203831d35Sstevel 
49303831d35Sstevel 	/* doing get-state above may have caused a freeze operation */
49403831d35Sstevel 	if ((hsp->hs_flags & HSC_SCB_HOTSWAPPED) &&
49503831d35Sstevel 			(rstate == HPC_SLOT_DISCONNECTED)) {
49603831d35Sstevel 		/* freeze puts disconnected boards to connected state */
49703831d35Sstevel 		*hssp = HPC_SLOT_CONNECTED;
49803831d35Sstevel #if 0
49903831d35Sstevel 		/* in FHS, deassertion of reset may have configured the board */
50003831d35Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
50103831d35Sstevel 			hsp->hs_slot_state = *hssp;
50203831d35Sstevel 		}
50303831d35Sstevel #endif
50403831d35Sstevel 	}
50503831d35Sstevel #ifdef	DEBUG
50603831d35Sstevel 	/* a SCB hotswap may have forced a state change on the receptacle */
50703831d35Sstevel 	if (orstate != *hssp) {
50803831d35Sstevel 		cmn_err(CE_NOTE, "hsc_get_state: slot%d state change due"
50903831d35Sstevel 			" to SCB hotswap!", hsp->hs_slot_number);
51003831d35Sstevel 	}
51103831d35Sstevel #endif
51203831d35Sstevel 	return (HPC_SUCCESS);
51303831d35Sstevel }
51403831d35Sstevel 
51503831d35Sstevel 
51603831d35Sstevel static int
hsc_set_config_state(hsc_slot_t * hsp,int cmd)51703831d35Sstevel hsc_set_config_state(hsc_slot_t *hsp, int cmd)
51803831d35Sstevel {
51903831d35Sstevel 	hsc_state_t	*hsc = hsp->hsc;
52003831d35Sstevel 
52103831d35Sstevel 	DEBUG1("hsc_set_config_state: slot %d", hsp->hs_slot_number);
52203831d35Sstevel 
52303831d35Sstevel 	switch (cmd) {
52403831d35Sstevel 	case HPC_CTRL_DEV_CONFIGURED:
52503831d35Sstevel 		/*
52603831d35Sstevel 		 * Closing of the Ejector switch in configured/busy state can
52703831d35Sstevel 		 * cause duplicate CONFIGURED messages to come down.
52803831d35Sstevel 		 * Make sure our LED states are fine.
52903831d35Sstevel 		 */
53003831d35Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
53103831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
53203831d35Sstevel 								HPC_LED_ON);
53303831d35Sstevel 			break;
53403831d35Sstevel 		}
53503831d35Sstevel 		hsp->hs_board_configured = B_TRUE;
53603831d35Sstevel 		hsp->hs_board_configuring = B_FALSE;
53703831d35Sstevel 		if ((hsc->state & HSC_ATTACHED) == HSC_ATTACHED &&
53803831d35Sstevel 			hsp->hs_flags & HSC_ALARM_CARD_PRES)
539*07d06da5SSurya Prakki 			(void) scsb_hsc_ac_op(hsp->hs_hpchandle,
54003831d35Sstevel 				hsp->hs_slot_number, SCSB_HSC_AC_CONFIGURED);
54103831d35Sstevel 		/* LED must be OFF on the occupant. */
54203831d35Sstevel 		(void) hpc_slot_event_notify(hsp->hs_slot_handle,
54303831d35Sstevel 					HPC_EVENT_SLOT_BLUE_LED_OFF, 0);
54403831d35Sstevel 		if (hsp->hs_flags & HSC_AUTOCFG)
54503831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
54603831d35Sstevel 					HPC_EVENT_ENABLE_ENUM, 0);
54703831d35Sstevel 		else
54803831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
54903831d35Sstevel 					HPC_EVENT_DISABLE_ENUM, 0);
55003831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
55103831d35Sstevel 								HPC_LED_ON);
55203831d35Sstevel 		if (hsc->hsp_last == hsp)
55303831d35Sstevel 			hsc->hsp_last = NULL;
55403831d35Sstevel 		break;
55503831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
55603831d35Sstevel 		hsp->hs_board_configured = B_FALSE;
55703831d35Sstevel 		hsp->hs_board_unconfiguring = B_FALSE;
55803831d35Sstevel 		hsp->hs_flags &= ~HSC_SLOT_BAD_STATE;
55903831d35Sstevel 		if (hsp->hs_flags & HSC_ALARM_CARD_PRES)
560*07d06da5SSurya Prakki 			(void) scsb_hsc_ac_op(hsp->hs_hpchandle,
56103831d35Sstevel 				hsp->hs_slot_number, SCSB_HSC_AC_UNCONFIGURED);
56203831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
56303831d35Sstevel 							HPC_LED_BLINK);
56403831d35Sstevel 		if (((hsc->state & HSC_ENUM_ENABLED) &&
56503831d35Sstevel 			scsb_hsc_fhs_slot_reset) ||
56603831d35Sstevel 		(((hsc->state & HSC_ENUM_ENABLED) != HSC_ENUM_ENABLED) &&
56703831d35Sstevel 				scsb_hsc_bhs_slot_reset) ||
56803831d35Sstevel 				((hsp->hs_flags & HSC_AUTOCFG) !=
56903831d35Sstevel 					HSC_AUTOCFG)) {
57003831d35Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
57103831d35Sstevel 				hsp->hs_slot_number, SCSB_RESET_SLOT) == 0) {
57203831d35Sstevel 
57303831d35Sstevel 				hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
57403831d35Sstevel 				hsp->hs_board_healthy = B_FALSE;
57503831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
57603831d35Sstevel 					HPC_FAULT_LED, HPC_LED_ON);
57703831d35Sstevel 			}
57803831d35Sstevel 		}
57903831d35Sstevel 		break;
58003831d35Sstevel 	case HPC_CTRL_DEV_CONFIG_FAILURE:
58103831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
58203831d35Sstevel 							HPC_LED_BLINK);
58303831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
58403831d35Sstevel 				HPC_FAULT_LED, HPC_LED_ON);
58503831d35Sstevel 		break;
58603831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIG_FAILURE:
58703831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
58803831d35Sstevel 							HPC_LED_ON);
58903831d35Sstevel 		break;
59003831d35Sstevel 	case HPC_CTRL_DEV_CONFIG_START:
59103831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIG_START:
59203831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
59303831d35Sstevel 					HPC_LED_OFF);
59403831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
59503831d35Sstevel 					HPC_LED_BLINK);
59603831d35Sstevel 		break;
59703831d35Sstevel 	default:
59803831d35Sstevel 		return (HPC_ERR_INVALID);
59903831d35Sstevel 	}
60003831d35Sstevel 
60103831d35Sstevel 	if (cmd != HPC_CTRL_DEV_CONFIG_START &&
60203831d35Sstevel 		cmd != HPC_CTRL_DEV_UNCONFIG_START &&
60303831d35Sstevel 		hsc->regDone == B_FALSE &&
60403831d35Sstevel 			scsb_hsc_numReg < hsc->n_registered_occupants) {
60503831d35Sstevel 		scsb_hsc_numReg++;
60603831d35Sstevel 
60703831d35Sstevel 		/*
60803831d35Sstevel 		 * If the callback is invoked for all registered slots,
60903831d35Sstevel 		 * enable ENUM.
61003831d35Sstevel 		 */
61103831d35Sstevel 		if (((hsc->state & HSC_ATTACHED) == HSC_ATTACHED) &&
61203831d35Sstevel 			(scsb_hsc_numReg == hsc->n_registered_occupants)) {
61303831d35Sstevel 			hsc->regDone = B_TRUE;
61403831d35Sstevel 			if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL) {
61503831d35Sstevel #ifdef DEBUG
61603831d35Sstevel 				cmn_err(CE_CONT, "%s%d: Enabling full hotswap"
61703831d35Sstevel 					":%d non-empty slots\n",
61803831d35Sstevel 					ddi_driver_name(hsc->dip),
61903831d35Sstevel 					ddi_get_instance(hsc->dip),
62003831d35Sstevel 					hsc->n_registered_occupants);
62103831d35Sstevel #endif
62203831d35Sstevel 				if (scsb_enable_enum(hsc) != DDI_SUCCESS) {
62303831d35Sstevel 					cmn_err(CE_WARN, "%s#%d: Cannot enable "
62403831d35Sstevel 						"Full Hotswap",
62503831d35Sstevel 						ddi_driver_name(hsc->dip),
62603831d35Sstevel 						ddi_get_instance(hsc->dip));
62703831d35Sstevel 
62803831d35Sstevel 					return (HPC_ERR_FAILED);
62903831d35Sstevel 				}
63003831d35Sstevel 			}
63103831d35Sstevel 		}
63203831d35Sstevel 	}
63303831d35Sstevel 
63403831d35Sstevel 	return (HPC_SUCCESS);
63503831d35Sstevel }
63603831d35Sstevel 
63703831d35Sstevel 
63803831d35Sstevel /*ARGSUSED*/
63903831d35Sstevel static int
hsc_get_board_type(hsc_slot_t * hsp,hpc_board_type_t * hbtp)64003831d35Sstevel hsc_get_board_type(hsc_slot_t *hsp, hpc_board_type_t *hbtp)
64103831d35Sstevel {
64203831d35Sstevel 	*hbtp = hsp->hs_board_type;
64303831d35Sstevel 	return (HPC_SUCCESS);
64403831d35Sstevel }
64503831d35Sstevel 
64603831d35Sstevel 
64703831d35Sstevel /* ARGSUSED */
64803831d35Sstevel static int
hsc_autoconfig(hsc_slot_t * hsp,int cmd)64903831d35Sstevel hsc_autoconfig(hsc_slot_t *hsp, int cmd)
65003831d35Sstevel {
65103831d35Sstevel 	int res = HPC_SUCCESS, enum_disable = B_TRUE, i;
65203831d35Sstevel 	char slotautocfg_prop[18];
65303831d35Sstevel 	hsc_state_t *hsc;
65403831d35Sstevel 
65503831d35Sstevel 	DEBUG1("hsc_autoconfig: slot %d", hsp->hs_slot_number);
656*07d06da5SSurya Prakki 	(void) sprintf(slotautocfg_prop, "slot%d-autoconfig",
657*07d06da5SSurya Prakki 	    hsp->hs_slot_number);
65803831d35Sstevel 
65903831d35Sstevel 	if (cmd == HPC_CTRL_ENABLE_AUTOCFG) {
66003831d35Sstevel 		hsp->hs_flags |= HSC_AUTOCFG;
661*07d06da5SSurya Prakki 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
66203831d35Sstevel 				slotautocfg_prop, "enabled");
66303831d35Sstevel 		if ((res = scsb_enable_enum(hsp->hsc)) == DDI_SUCCESS) {
66403831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
66503831d35Sstevel 					HPC_EVENT_ENABLE_ENUM, 0);
66603831d35Sstevel 		}
66703831d35Sstevel 	} else {
668*07d06da5SSurya Prakki 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
66903831d35Sstevel 		    slotautocfg_prop, "disabled");
67003831d35Sstevel 		hsp->hs_flags &= ~HSC_AUTOCFG;
67103831d35Sstevel 		hsc = hsp->hsc;
67203831d35Sstevel 		if (hsc->state & HSC_ATTACHED) {
67303831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
67403831d35Sstevel 						HPC_EVENT_DISABLE_ENUM, 0);
67503831d35Sstevel 			for (i = 0; i < hsc->slot_table_size; i++) {
67603831d35Sstevel 				hsc_slot_t	*thsp;
67703831d35Sstevel 				int slotnum;
67803831d35Sstevel 
67903831d35Sstevel 				slotnum = hsc->slot_table_prop[i].pslotnum;
68003831d35Sstevel 				thsp = hsc_find_slot(slotnum);
68103831d35Sstevel 				if (thsp == NULL) {
68203831d35Sstevel 					cmn_err(CE_WARN, "%s#%d: hsc_autocfg:"
68303831d35Sstevel 						"No Slot Info for slot %d",
68403831d35Sstevel 						ddi_driver_name(hsc->dip),
68503831d35Sstevel 						ddi_get_instance(hsc->dip),
68603831d35Sstevel 						slotnum);
68703831d35Sstevel 					continue;
68803831d35Sstevel 				}
68903831d35Sstevel 				if (thsp->hs_flags & HSC_AUTOCFG) {
69003831d35Sstevel 					enum_disable = B_FALSE;
69103831d35Sstevel 					break;
69203831d35Sstevel 				}
69303831d35Sstevel 			}
69403831d35Sstevel 			if (enum_disable == B_TRUE)
695*07d06da5SSurya Prakki 				(void) scsb_disable_enum(hsc,
696*07d06da5SSurya Prakki 				    SCSB_HSC_FORCE_REMOVE);
69703831d35Sstevel 		}
69803831d35Sstevel 	}
69903831d35Sstevel 	return (res);
70003831d35Sstevel }
70103831d35Sstevel 
70203831d35Sstevel 
70303831d35Sstevel /*
70403831d35Sstevel  * This function is invoked to enable/disable a slot
70503831d35Sstevel  */
70603831d35Sstevel /* ARGSUSED */
70703831d35Sstevel #ifndef	lint
70803831d35Sstevel static int
hsc_slot_enable(hsc_slot_t * hsp,boolean_t enabled)70903831d35Sstevel hsc_slot_enable(hsc_slot_t *hsp, boolean_t enabled)
71003831d35Sstevel {
71103831d35Sstevel 	scsb_uinfo_t	sunit;
71203831d35Sstevel 	int		res;
71303831d35Sstevel 
71403831d35Sstevel 	DEBUG1("hsc_slot_enable: slot %d", hsp->hs_slot_number);
71503831d35Sstevel 
71603831d35Sstevel 	sunit.unit_type = SLOT;
71703831d35Sstevel 	sunit.unit_number = hsp->hs_slot_number;
71803831d35Sstevel 	if (enabled)
71903831d35Sstevel 		sunit.unit_state = ON;
72003831d35Sstevel 	else
72103831d35Sstevel 		sunit.unit_state = OFF;
72203831d35Sstevel 
72303831d35Sstevel 	res = scsb_reset_unit(hsp->hs_hpchandle, &sunit);
72403831d35Sstevel 	if (res == 0)
72503831d35Sstevel 		return (HPC_SUCCESS);
72603831d35Sstevel 	else if (res == EINVAL)
72703831d35Sstevel 		return (HPC_ERR_INVALID);
72803831d35Sstevel 	else
72903831d35Sstevel 		return (HPC_ERR_FAILED);
73003831d35Sstevel }
73103831d35Sstevel #endif
73203831d35Sstevel 
73303831d35Sstevel 
73403831d35Sstevel /*ARGSUSED*/
73503831d35Sstevel static int
hsc_control(caddr_t ops_arg,hpc_slot_t slot_hdl,int request,caddr_t arg)73603831d35Sstevel hsc_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request, caddr_t arg)
73703831d35Sstevel {
73803831d35Sstevel 	hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
73903831d35Sstevel 	int rc = HPC_SUCCESS;
74003831d35Sstevel 
74103831d35Sstevel 	DEBUG2("hsc_control: slot %d, op=%x\n", hsp->hs_slot_number, request);
74203831d35Sstevel 
74303831d35Sstevel 	switch (request) {
74403831d35Sstevel 	case HPC_CTRL_GET_LED_STATE:
74503831d35Sstevel 		return (hsc_led_state(hsp,
74603831d35Sstevel 			HPC_CTRL_GET_LED_STATE, (hpc_led_info_t *)arg));
74703831d35Sstevel 
74803831d35Sstevel 	case HPC_CTRL_SET_LED_STATE:
74903831d35Sstevel 		return (hsc_led_state(hsp,
75003831d35Sstevel 			HPC_CTRL_SET_LED_STATE, (hpc_led_info_t *)arg));
75103831d35Sstevel 
75203831d35Sstevel 	case HPC_CTRL_GET_SLOT_STATE:
75303831d35Sstevel 		return (hsc_get_slot_state(hsp, (hpc_slot_state_t *)arg));
75403831d35Sstevel 
75503831d35Sstevel 	case HPC_CTRL_DEV_CONFIGURED:
75603831d35Sstevel 		return (hsc_set_config_state(hsp, HPC_CTRL_DEV_CONFIGURED));
75703831d35Sstevel 
75803831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
75903831d35Sstevel 		return (hsc_set_config_state(hsp, HPC_CTRL_DEV_UNCONFIGURED));
76003831d35Sstevel 
76103831d35Sstevel 	case HPC_CTRL_DEV_CONFIG_FAILURE:
76203831d35Sstevel 		return (hsc_set_config_state(hsp, HPC_CTRL_DEV_CONFIG_FAILURE));
76303831d35Sstevel 
76403831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIG_FAILURE:
76503831d35Sstevel 		return (hsc_set_config_state(hsp,
76603831d35Sstevel 				HPC_CTRL_DEV_UNCONFIG_FAILURE));
76703831d35Sstevel 
76803831d35Sstevel 	case HPC_CTRL_DEV_CONFIG_START:
76903831d35Sstevel 	case HPC_CTRL_DEV_UNCONFIG_START:
77003831d35Sstevel 		return (hsc_set_config_state(hsp, request));
77103831d35Sstevel 
77203831d35Sstevel 	case HPC_CTRL_GET_BOARD_TYPE:
77303831d35Sstevel 		return (hsc_get_board_type(hsp, (hpc_board_type_t *)arg));
77403831d35Sstevel 
77503831d35Sstevel 	case HPC_CTRL_DISABLE_AUTOCFG:
77603831d35Sstevel 		return (hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG));
77703831d35Sstevel 
77803831d35Sstevel 	case HPC_CTRL_ENABLE_AUTOCFG:
77903831d35Sstevel 		return (hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG));
78003831d35Sstevel 
78103831d35Sstevel 	case HPC_CTRL_DISABLE_SLOT:
78203831d35Sstevel 		/*
78303831d35Sstevel 		 * No hardware support for disabling the slot.
78403831d35Sstevel 		 * Just imitate a disable_autoconfig operation for now
78503831d35Sstevel 		 */
78603831d35Sstevel 		if (hsp->hs_board_configured == B_TRUE)
78703831d35Sstevel 			return (HPC_ERR_FAILED);
78803831d35Sstevel 		if (scsb_hsc_disable_slot(hsp) != DDI_SUCCESS)
78903831d35Sstevel 			rc = HPC_ERR_FAILED;
79003831d35Sstevel 		return (rc);
79103831d35Sstevel 
79203831d35Sstevel 	case HPC_CTRL_ENABLE_SLOT:
79303831d35Sstevel 		if (scsb_hsc_enable_slot(hsp) != DDI_SUCCESS)
79403831d35Sstevel 			rc = HPC_ERR_FAILED;
79503831d35Sstevel 		return (rc);
79603831d35Sstevel 
79703831d35Sstevel 	case HPC_CTRL_ENABLE_ENUM:
79803831d35Sstevel 		return (scsb_enable_enum(hsp->hsc));
79903831d35Sstevel 
80003831d35Sstevel 	case HPC_CTRL_DISABLE_ENUM:
80103831d35Sstevel 		return (scsb_disable_enum(hsp->hsc, 0));
80203831d35Sstevel 
80303831d35Sstevel 	default:
80403831d35Sstevel 		return (HPC_ERR_INVALID);
80503831d35Sstevel 	}
80603831d35Sstevel }
80703831d35Sstevel 
80803831d35Sstevel static int
scsb_hsc_disable_slot(hsc_slot_t * hsp)80903831d35Sstevel scsb_hsc_disable_slot(hsc_slot_t *hsp)
81003831d35Sstevel {
81103831d35Sstevel 	int rc;
81203831d35Sstevel 	char slot_disable_prop[18];
81303831d35Sstevel 
81403831d35Sstevel 	DEBUG1("hsc_disable_slot: slot %d", hsp->hs_slot_number);
815*07d06da5SSurya Prakki 	(void) sprintf(slot_disable_prop, "slot%d-status", hsp->hs_slot_number);
81603831d35Sstevel 
81703831d35Sstevel 	rc = scsb_reset_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
81803831d35Sstevel 					SCSB_RESET_SLOT);
81903831d35Sstevel 	if (rc == DDI_SUCCESS) {
820*07d06da5SSurya Prakki 		(void) hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG);
82103831d35Sstevel 		hsp->hs_flags &= ~HSC_SLOT_ENABLED;
822*07d06da5SSurya Prakki 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
82303831d35Sstevel 		    slot_disable_prop, "disabled");
82403831d35Sstevel 	} else
82503831d35Sstevel 		rc = DDI_FAILURE;
82603831d35Sstevel 	return (rc);
82703831d35Sstevel }
82803831d35Sstevel 
82903831d35Sstevel static int
scsb_hsc_enable_slot(hsc_slot_t * hsp)83003831d35Sstevel scsb_hsc_enable_slot(hsc_slot_t *hsp)
83103831d35Sstevel {
83203831d35Sstevel 	int rc;
83303831d35Sstevel 	char slot_disable_prop[18];
83403831d35Sstevel 
83503831d35Sstevel 	DEBUG1("hsc_disable_slot: slot %d", hsp->hs_slot_number);
836*07d06da5SSurya Prakki 	(void) sprintf(slot_disable_prop, "slot%d-status", hsp->hs_slot_number);
83703831d35Sstevel 
83803831d35Sstevel 	rc = scsb_reset_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
83903831d35Sstevel 					SCSB_UNRESET_SLOT);
84003831d35Sstevel 	if (rc == DDI_SUCCESS) {
841*07d06da5SSurya Prakki 		(void) hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG);
84203831d35Sstevel 		hsp->hs_flags |= HSC_SLOT_ENABLED;
843*07d06da5SSurya Prakki 		(void) ddi_prop_remove(DDI_DEV_T_NONE, hsp->hsc->dip,
84403831d35Sstevel 		    slot_disable_prop);
84503831d35Sstevel 	} else
84603831d35Sstevel 		rc = HPC_ERR_FAILED;
84703831d35Sstevel 	return (rc);
84803831d35Sstevel }
84903831d35Sstevel 
85003831d35Sstevel #define	NEW(type)	(type *) kmem_zalloc(sizeof (type), KM_SLEEP)
85103831d35Sstevel 
85203831d35Sstevel static hsc_slot_t *
hsc_alloc_slot(uint16_t device_number,int slot_number,boolean_t board_in_slot)85303831d35Sstevel hsc_alloc_slot(
85403831d35Sstevel 		uint16_t	device_number,
85503831d35Sstevel 		int		slot_number,
85603831d35Sstevel 		boolean_t	board_in_slot)
85703831d35Sstevel {
85803831d35Sstevel 	hpc_slot_info_t	*hsip;
85903831d35Sstevel 	hsc_slot_t	*hsp = NEW(hsc_slot_t);
86003831d35Sstevel 
86103831d35Sstevel 	DEBUG2("hsc_alloc_slot: slot %d %s", slot_number,
86203831d35Sstevel 		board_in_slot ? "occupied" : "empty");
86303831d35Sstevel 
86403831d35Sstevel 	if (hsp == NULL) {
86503831d35Sstevel 		cmn_err(CE_NOTE,
86603831d35Sstevel 			"hsc_alloc_slot: allocation failed for slot %d",
86703831d35Sstevel 			slot_number);
86803831d35Sstevel 		return (NULL);
86903831d35Sstevel 	}
87003831d35Sstevel 
87103831d35Sstevel 	hsip = &hsp->hs_info;
87203831d35Sstevel 
87303831d35Sstevel 	hsip->version			= HPC_SLOT_INFO_VERSION;
87403831d35Sstevel 	hsip->slot_type			= HPC_SLOT_TYPE_CPCI;
87503831d35Sstevel 	hsip->pci_dev_num		= device_number;
87603831d35Sstevel 	hsip->pci_slot_capabilities	= 0;
87703831d35Sstevel 	hsip->slot_flags		= HPC_SLOT_CREATE_DEVLINK;
87803831d35Sstevel 	/*
87903831d35Sstevel 	 * Note: the name *must* be 'pci' so that the correct cfgadm plug-in
88003831d35Sstevel 	 *	 library is selected
88103831d35Sstevel 	 */
882*07d06da5SSurya Prakki 	(void) sprintf(hsip->pci_slot_name, "cpci_slot%d", slot_number);
88303831d35Sstevel 
88403831d35Sstevel 	/*
88503831d35Sstevel 	 * We assume that the following LED settings reflect
88603831d35Sstevel 	 * the hardware state.
88703831d35Sstevel 	 * After we register the slot, we will be invoked by the nexus
88803831d35Sstevel 	 * if the slot is occupied, and we will turn on the LED then.
88903831d35Sstevel 	 */
89003831d35Sstevel 	hsp->hs_active_led_state	= HPC_LED_OFF;
89103831d35Sstevel 	hsp->hs_fault_led_state		= HPC_LED_OFF;
89203831d35Sstevel 
89303831d35Sstevel 	hsp->hs_board_configured	= B_FALSE;
89403831d35Sstevel 	hsp->hs_board_healthy		= B_FALSE;
89503831d35Sstevel 	hsp->hs_board_type		= HPC_BOARD_UNKNOWN;
89603831d35Sstevel 
89703831d35Sstevel 	hsp->hs_flags			= HSC_ENABLED | HSC_SLOT_ENABLED;
89803831d35Sstevel 	hsp->hs_slot_number		= slot_number;
89903831d35Sstevel 
90003831d35Sstevel 	/*
90103831d35Sstevel 	 * we should just set this to connected,
90203831d35Sstevel 	 * as MC slots are always connected.
90303831d35Sstevel 	 */
90403831d35Sstevel 	if (board_in_slot)
90503831d35Sstevel 		hsp->hs_slot_state = HPC_SLOT_CONNECTED;
90603831d35Sstevel 	else
90703831d35Sstevel 		hsp->hs_slot_state = HPC_SLOT_EMPTY;
90803831d35Sstevel 
90903831d35Sstevel 	return (hsp);
91003831d35Sstevel }
91103831d35Sstevel 
91203831d35Sstevel 
91303831d35Sstevel static void
hsc_free_slot(hsc_slot_t * hsp)91403831d35Sstevel hsc_free_slot(hsc_slot_t *hsp)
91503831d35Sstevel {
91603831d35Sstevel 	DEBUG0("hsc_free_slot");
91703831d35Sstevel 
91803831d35Sstevel 	kmem_free(hsp, sizeof (*hsp));
91903831d35Sstevel }
92003831d35Sstevel 
92103831d35Sstevel 
92203831d35Sstevel /*
92303831d35Sstevel  * This function is invoked to register a slot
92403831d35Sstevel  */
92503831d35Sstevel static int
hsc_slot_register(hsc_state_t * hsc,char * bus_path,uint16_t device_number,uint_t slot_number,boolean_t board_in_slot)92603831d35Sstevel hsc_slot_register(
92703831d35Sstevel 	hsc_state_t	*hsc,
92803831d35Sstevel 	char		*bus_path,	/* PCI nexus pathname */
92903831d35Sstevel 	uint16_t	device_number,	/* PCI device number */
93003831d35Sstevel 	uint_t		slot_number,	/* physical slot number */
93103831d35Sstevel 	boolean_t	board_in_slot)	/* receptacle status */
93203831d35Sstevel {
93303831d35Sstevel 	int		rc = HPC_SUCCESS;
93403831d35Sstevel 	hsc_slot_t	*hsp;
93503831d35Sstevel 
93603831d35Sstevel 	DEBUG2("hsc_slot_register: slot number %d, device number %d",
93703831d35Sstevel 		slot_number, device_number);
93803831d35Sstevel 
93903831d35Sstevel 	hsp = hsc_alloc_slot(device_number, slot_number,
94003831d35Sstevel 			board_in_slot);
94103831d35Sstevel 
94203831d35Sstevel 	if (hsp == NULL) {
94303831d35Sstevel #ifdef	DEBUG
94403831d35Sstevel 		cmn_err(CE_NOTE, "hsc_slot_register: hsc_alloc_slot failed");
94503831d35Sstevel #endif
94603831d35Sstevel 		return (HPC_ERR_FAILED);
94703831d35Sstevel 	}
94803831d35Sstevel 
94903831d35Sstevel 	hsp->hs_hpchandle = hsc->scsb_handle; /* handle for call backs */
95003831d35Sstevel 	hsp->hsc = hsc;
95103831d35Sstevel 
95203831d35Sstevel 	rc = scsb_hsc_init_slot_state(hsc, hsp);
95303831d35Sstevel 	if (rc != DDI_SUCCESS)
95403831d35Sstevel 		return (HPC_ERR_FAILED);
95503831d35Sstevel 
95603831d35Sstevel 	/* slot autoconfiguration by default. */
95703831d35Sstevel 	if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL)
95803831d35Sstevel 		(void) hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG);
95903831d35Sstevel 	else
96003831d35Sstevel 		(void) hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG);
96103831d35Sstevel 
96203831d35Sstevel 	/*
96303831d35Sstevel 	 * Append to our list
96403831d35Sstevel 	 */
96503831d35Sstevel 	mutex_enter(&hsc_mutex);
96603831d35Sstevel 	hsp->hs_next = hsc_slot_list;
96703831d35Sstevel 	hsc_slot_list = hsp;
96803831d35Sstevel 	mutex_exit(&hsc_mutex);
96903831d35Sstevel 
97003831d35Sstevel 	rc = hpc_slot_register(hsc->dip,
97103831d35Sstevel 			bus_path,
97203831d35Sstevel 			&hsp->hs_info,
97303831d35Sstevel 			&hsp->hs_slot_handle,	/* return value */
97403831d35Sstevel 			hsc_slotops,
97503831d35Sstevel 			(caddr_t)hsp,
97603831d35Sstevel 			0);
97703831d35Sstevel 
97803831d35Sstevel 	if (rc != HPC_SUCCESS) {
97903831d35Sstevel 		cmn_err(CE_WARN, "%s#%d: failed to register slot %s:%d",
98003831d35Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip),
98103831d35Sstevel 			bus_path, device_number);
98203831d35Sstevel 		hsc_free_slot(hsp);
98303831d35Sstevel 		return (rc);
98403831d35Sstevel 	}
98503831d35Sstevel 
98603831d35Sstevel 	DEBUG0("hsc_slot_register: hpc_slot_register successful");
98703831d35Sstevel 
98803831d35Sstevel 	return (rc);
98903831d35Sstevel }
99003831d35Sstevel 
99103831d35Sstevel 
99203831d35Sstevel static int
hsc_slot_unregister(int slot_number)99303831d35Sstevel hsc_slot_unregister(int slot_number)
99403831d35Sstevel {
99503831d35Sstevel 	hsc_slot_t	*hsp, *prev;
99603831d35Sstevel 
99703831d35Sstevel 	DEBUG1("hsc_slot_unregister: slot number %d", slot_number);
99803831d35Sstevel 
99903831d35Sstevel 	mutex_enter(&hsc_mutex);
100003831d35Sstevel 	hsp = prev = NULL;
100103831d35Sstevel 	for (hsp = hsc_slot_list; hsp != NULL; hsp = hsp->hs_next) {
100203831d35Sstevel 		if (hsp->hs_slot_number == slot_number) {
100303831d35Sstevel 			if (prev == NULL) /* first entry */
100403831d35Sstevel 				hsc_slot_list = hsc_slot_list->hs_next;
100503831d35Sstevel 			else
100603831d35Sstevel 				prev->hs_next = hsp->hs_next;
100703831d35Sstevel 			hsp->hs_next = NULL;
100803831d35Sstevel 			break;
100903831d35Sstevel 		}
101003831d35Sstevel 		prev = hsp;
101103831d35Sstevel 	}
101203831d35Sstevel 	mutex_exit(&hsc_mutex);
101303831d35Sstevel 
101403831d35Sstevel 	if (hsp != NULL) {
1015*07d06da5SSurya Prakki 		(void) hpc_slot_unregister(&hsp->hs_slot_handle);
101603831d35Sstevel 		if ((hsp->hsc->state & HSC_ATTACHED) != HSC_ATTACHED &&
101703831d35Sstevel 				hsp->hs_slot_state != HPC_SLOT_EMPTY) {
101803831d35Sstevel 			hsp->hsc->n_registered_occupants--;
101903831d35Sstevel 		}
102003831d35Sstevel 		hsc_free_slot(hsp);
102103831d35Sstevel 		return (0);
102203831d35Sstevel 	}
102303831d35Sstevel 	return (1);
102403831d35Sstevel }
102503831d35Sstevel 
102603831d35Sstevel static int
scsb_hsc_init_slot_state(hsc_state_t * hsc,hsc_slot_t * hsp)102703831d35Sstevel scsb_hsc_init_slot_state(hsc_state_t *hsc, hsc_slot_t *hsp)
102803831d35Sstevel {
102903831d35Sstevel 	int rc, rstate;
103003831d35Sstevel 	int slot_number = hsp->hs_slot_number;
103103831d35Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)hsc->scsb_handle;
103203831d35Sstevel 
103303831d35Sstevel 	rc = scsb_get_slot_state(hsc->scsb_handle, slot_number, &rstate);
103403831d35Sstevel 	if (rc != DDI_SUCCESS)
103503831d35Sstevel 		return (DDI_FAILURE);
103603831d35Sstevel 
103703831d35Sstevel 	/*
103803831d35Sstevel 	 * Set the healthy status for this slot
103903831d35Sstevel 	 */
104003831d35Sstevel 	hsp->hs_board_healthy = scsb_read_slot_health(scsb, slot_number);
104103831d35Sstevel 	hsp->hs_slot_state = rstate;
104203831d35Sstevel 	switch (rstate) {
104303831d35Sstevel 		case HPC_SLOT_EMPTY:
104403831d35Sstevel 			/*
104503831d35Sstevel 			 * this will clear any state differences between
104603831d35Sstevel 			 * SCB Freeze operations.
104703831d35Sstevel 			 */
104803831d35Sstevel 			hsp->hs_slot_state = HPC_SLOT_EMPTY;
104903831d35Sstevel 			/* slot empty. */
1050*07d06da5SSurya Prakki 			(void) scsb_reset_slot(hsc->scsb_handle, slot_number,
105103831d35Sstevel 			    SCSB_RESET_SLOT);
105203831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
105303831d35Sstevel 			    HPC_LED_OFF);
105403831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
105503831d35Sstevel 			    HPC_LED_OFF);
105603831d35Sstevel 			break;
105703831d35Sstevel 		case HPC_SLOT_DISCONNECTED:
105803831d35Sstevel 			/*
105903831d35Sstevel 			 * this will clear any state differences between
106003831d35Sstevel 			 * SCB Freeze operations.
106103831d35Sstevel 			 */
106203831d35Sstevel 			hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
106303831d35Sstevel 			/* check recovery from SCB freeze */
106403831d35Sstevel 			if (hsp->hs_board_configured != B_TRUE) {
106503831d35Sstevel 				/*
106603831d35Sstevel 				 * Force a disconnect just in case there are
106703831d35Sstevel 				 * differences between healthy and reset states.
106803831d35Sstevel 				 */
1069*07d06da5SSurya Prakki 				(void) scsb_reset_slot(hsc->scsb_handle,
1070*07d06da5SSurya Prakki 				    slot_number, SCSB_RESET_SLOT);
107103831d35Sstevel 				/*
107203831d35Sstevel 				 * Slot in reset. OBP has not probed this
107303831d35Sstevel 				 * device. Hence it is ok to remove this board.
107403831d35Sstevel 				 */
107503831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
107603831d35Sstevel 						HPC_ACTIVE_LED, HPC_LED_BLINK);
107703831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
107803831d35Sstevel 						HPC_FAULT_LED, HPC_LED_ON);
107903831d35Sstevel 				break;
108003831d35Sstevel 			}
108103831d35Sstevel 			/*FALLTHROUGH*/
108203831d35Sstevel 		case HPC_SLOT_CONNECTED:
108303831d35Sstevel 			/*
108403831d35Sstevel 			 * this will clear any state differences between
108503831d35Sstevel 			 * SCB Freeze operations.
108603831d35Sstevel 			 */
108703831d35Sstevel 			hsp->hs_slot_state = HPC_SLOT_CONNECTED;
108803831d35Sstevel 			/*
108903831d35Sstevel 			 * OBP should have probed this device, unless
109003831d35Sstevel 			 * it was plugged in during the boot operation
109103831d35Sstevel 			 * before the driver was loaded. In any case,
109203831d35Sstevel 			 * no assumption is made and hence we take
109303831d35Sstevel 			 * the conservative approach by keeping fault
109403831d35Sstevel 			 * led off so board removal is not allowed.
109503831d35Sstevel 			 */
109603831d35Sstevel 			if (hsp->hs_board_configured == B_TRUE)
109703831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
109803831d35Sstevel 					HPC_ACTIVE_LED, HPC_LED_ON);
109903831d35Sstevel 			else
110003831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
110103831d35Sstevel 					HPC_ACTIVE_LED, HPC_LED_BLINK);
110203831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
110303831d35Sstevel 							HPC_LED_OFF);
110403831d35Sstevel 			/*
110503831d35Sstevel 			 * Netra ct alarm card hotswap support
110603831d35Sstevel 			 */
110703831d35Sstevel 			if (slot_number == scsb->ac_slotnum &&
110803831d35Sstevel 				scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES) {
110903831d35Sstevel 				hsp->hs_flags |= HSC_ALARM_CARD_PRES;
111003831d35Sstevel 				DEBUG0("Xscsb_hsc_init_slot_state: "
111103831d35Sstevel 						"set HSC_ALARM_CARD_PRES");
111203831d35Sstevel 			}
111303831d35Sstevel 			break;
111403831d35Sstevel 		default:
111503831d35Sstevel 			break;
111603831d35Sstevel 	}
111703831d35Sstevel 	return (rc);
111803831d35Sstevel }
111903831d35Sstevel 
112003831d35Sstevel static hsc_slot_t *
hsc_get_slot_info(hsc_state_t * hsc,int pci_devno)112103831d35Sstevel hsc_get_slot_info(hsc_state_t *hsc, int pci_devno)
112203831d35Sstevel {
112303831d35Sstevel 	int i;
112403831d35Sstevel 
112503831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
112603831d35Sstevel 
112703831d35Sstevel 		if (hsc->slot_table_prop[i].pci_devno == pci_devno)
112803831d35Sstevel 			return ((hsc_slot_t *)hsc_find_slot(
112903831d35Sstevel 				hsc->slot_table_prop[i].pslotnum));
113003831d35Sstevel 	}
113103831d35Sstevel 	return (NULL);
113203831d35Sstevel }
113303831d35Sstevel 
113403831d35Sstevel static hsc_slot_t *
hsc_find_slot(int slot_number)113503831d35Sstevel hsc_find_slot(int slot_number)
113603831d35Sstevel {
113703831d35Sstevel 	hsc_slot_t	*hsp;
113803831d35Sstevel 
113903831d35Sstevel 	mutex_enter(&hsc_mutex);
114003831d35Sstevel 	for (hsp = hsc_slot_list; hsp != NULL; hsp = hsp->hs_next) {
114103831d35Sstevel 		if (hsp->hs_slot_number == slot_number)
114203831d35Sstevel 			break;
114303831d35Sstevel 	}
114403831d35Sstevel 	mutex_exit(&hsc_mutex);
114503831d35Sstevel 	return (hsp);
114603831d35Sstevel }
114703831d35Sstevel 
114803831d35Sstevel 
114903831d35Sstevel /*
115003831d35Sstevel  * This function is invoked by the SCSB when an interrupt
115103831d35Sstevel  * happens to indicate that a board has been inserted-in/removed-from
115203831d35Sstevel  * the specified slot.
115303831d35Sstevel  */
115403831d35Sstevel int
hsc_slot_occupancy(int slot_number,boolean_t occupied,int flags,int healthy)115503831d35Sstevel hsc_slot_occupancy(int slot_number, boolean_t occupied, int flags, int healthy)
115603831d35Sstevel {
115703831d35Sstevel 	static const char	func[]	= "hsc_slot_occupancy";
115803831d35Sstevel 	hsc_slot_t		*hsp;
115903831d35Sstevel 	int			rc = DDI_SUCCESS;
116003831d35Sstevel 
116103831d35Sstevel 	DEBUG4("hsc_slot_occupancy: slot %d %s, ac=%d, healthy=%d",
116203831d35Sstevel 			slot_number, occupied ? "occupied" : "not occupied",
116303831d35Sstevel 			(flags == ALARM_CARD_ON_SLOT) ? 1:0, healthy);
116403831d35Sstevel 
116503831d35Sstevel 	hsp = hsc_find_slot(slot_number);
116603831d35Sstevel 
116703831d35Sstevel 	if (hsp == NULL) {
116803831d35Sstevel 		cmn_err(CE_NOTE,
116903831d35Sstevel 			"%s: cannot map slot number %d to a hsc_slot_t",
117003831d35Sstevel 			func, slot_number);
117103831d35Sstevel 		return (DDI_FAILURE);
117203831d35Sstevel 	}
117303831d35Sstevel 
117403831d35Sstevel 	hsp->hs_board_healthy = healthy;
117503831d35Sstevel 	if (occupied) {
117603831d35Sstevel 		/*
117703831d35Sstevel 		 * A board was just inserted. We are disconnected at this point.
117803831d35Sstevel 		 */
117903831d35Sstevel 		if (hsp->hs_slot_state == HPC_SLOT_EMPTY)
118003831d35Sstevel 			hsp->hs_board_type = HPC_BOARD_CPCI_HS;
118103831d35Sstevel 		hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
118203831d35Sstevel 		if (flags == ALARM_CARD_ON_SLOT) {
118303831d35Sstevel 			hsp->hs_flags |= HSC_ALARM_CARD_PRES;
118403831d35Sstevel 			DEBUG0("Xhsc_slot_occupancy: set HSC_ALARM_CARD_PRES");
118503831d35Sstevel 		}
118603831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
118703831d35Sstevel 						HPC_LED_ON);
118803831d35Sstevel 		/*
118903831d35Sstevel 		 * if previous occupant stayed configured, do not allow another
119003831d35Sstevel 		 * occupant to be connected.
119103831d35Sstevel 		 * So as soon as the board is plugged in, we turn both LEDs On.
119203831d35Sstevel 		 * This behaviour is an indication that the slot state
119303831d35Sstevel 		 * is not clean.
119403831d35Sstevel 		 */
119503831d35Sstevel 		if (hsp->hs_flags & HSC_SLOT_BAD_STATE) {
119603831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
119703831d35Sstevel 								HPC_LED_ON);
119803831d35Sstevel 			return (DDI_SUCCESS);
119903831d35Sstevel 		}
120003831d35Sstevel 
120103831d35Sstevel 		/* Do not allow connect if slot is disabled */
120203831d35Sstevel 		if ((hsp->hs_flags & HSC_SLOT_ENABLED) != HSC_SLOT_ENABLED)
120303831d35Sstevel 			return (DDI_SUCCESS);
120403831d35Sstevel 		/* if no healthy, we stay disconnected. */
120503831d35Sstevel 		if (healthy == B_FALSE) {
120603831d35Sstevel 			return (DDI_SUCCESS);
120703831d35Sstevel 		}
120803831d35Sstevel 		rc = hsc_slot_autoconnect(hsp);
120903831d35Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
121003831d35Sstevel 								HPC_LED_BLINK);
121103831d35Sstevel 	} else {
121203831d35Sstevel 		/*
121303831d35Sstevel 		 * A board was just removed
121403831d35Sstevel 		 */
121503831d35Sstevel 		hsp->hs_slot_state = HPC_SLOT_EMPTY;
121603831d35Sstevel 		hsp->hs_board_type = HPC_BOARD_UNKNOWN;
121703831d35Sstevel 		hsp->hs_flags &= ~HSC_ENUM_FAILED;
121803831d35Sstevel 		if (hsp->hs_flags & HSC_ALARM_CARD_PRES) {
121903831d35Sstevel 			hsp->hs_flags &= ~HSC_ALARM_CARD_PRES;
122003831d35Sstevel 			DEBUG0("Xhsc_slot_occupancy:clear HSC_ALARM_CARD_PRES");
122103831d35Sstevel 		}
122203831d35Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
122303831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
122403831d35Sstevel 					HPC_EVENT_SLOT_NOT_HEALTHY, 0);
122503831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: ALERT! Surprise Removal "
122603831d35Sstevel 				" on Slot %d, Occupant Online!!",
122703831d35Sstevel 					ddi_driver_name(hsp->hsc->dip),
122803831d35Sstevel 					ddi_get_instance(hsp->hsc->dip),
122903831d35Sstevel 					slot_number);
123003831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: ALERT! System now in "
123103831d35Sstevel 				" Inconsistent State! Slot disabled. Halt!",
123203831d35Sstevel 					ddi_driver_name(hsp->hsc->dip),
123303831d35Sstevel 					ddi_get_instance(hsp->hsc->dip));
123403831d35Sstevel 			/* Slot in reset and disabled */
1235*07d06da5SSurya Prakki 			(void) scsb_hsc_disable_slot(hsp);
123603831d35Sstevel 			hsp->hs_flags |= HSC_SLOT_BAD_STATE;
123703831d35Sstevel 			/* the following works for P1.0 only. */
123803831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
123903831d35Sstevel 						HPC_LED_ON);
124003831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
124103831d35Sstevel 								HPC_LED_ON);
124203831d35Sstevel 		} else {
124303831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
124403831d35Sstevel 						HPC_LED_OFF);
124503831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
124603831d35Sstevel 								HPC_LED_OFF);
124703831d35Sstevel 		}
124803831d35Sstevel 	}
124903831d35Sstevel 	return (rc);
125003831d35Sstevel }
125103831d35Sstevel 
125203831d35Sstevel 
125303831d35Sstevel /*
125403831d35Sstevel  * This function is invoked by the SCSB when the health status of
125503831d35Sstevel  * a board changes.
125603831d35Sstevel  */
125703831d35Sstevel /*ARGSUSED*/
125803831d35Sstevel int
scsb_hsc_board_healthy(int slot_number,boolean_t healthy)125903831d35Sstevel scsb_hsc_board_healthy(int slot_number, boolean_t healthy)
126003831d35Sstevel {
126103831d35Sstevel 	hsc_slot_t		*hsp;
126203831d35Sstevel 	hsc_state_t		*hsc;
126303831d35Sstevel 
126403831d35Sstevel 	DEBUG2("hsc_board_healthy: slot %d = %d\n", slot_number, healthy);
126503831d35Sstevel 
126603831d35Sstevel 	hsp = hsc_find_slot(slot_number);
126703831d35Sstevel 	if (hsp == NULL) {
126803831d35Sstevel 		cmn_err(CE_NOTE, "hsc_board_healthy: No Slot Info.");
126903831d35Sstevel 		return (DDI_FAILURE);
127003831d35Sstevel 	}
127103831d35Sstevel 
127203831d35Sstevel 	hsc = hsp->hsc;
127303831d35Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
127403831d35Sstevel #ifdef	DEBUG
127503831d35Sstevel 		cmn_err(CE_NOTE, "%s#%d: Healthy# %s on "
127603831d35Sstevel 			"empty slot %d", ddi_driver_name(hsc->dip),
127703831d35Sstevel 			ddi_get_instance(hsc->dip),
127803831d35Sstevel 			healthy == B_TRUE ? "On" : "Off", slot_number);
127903831d35Sstevel #endif
128003831d35Sstevel 		return (DDI_FAILURE);
128103831d35Sstevel 	}
128203831d35Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
128303831d35Sstevel 		DEBUG2("healthy %s on disconnected slot %d\n",
128403831d35Sstevel 			healthy == B_TRUE ? "On":"Off", slot_number);
128503831d35Sstevel 		/*
128603831d35Sstevel 		 * Connect the slot if board healthy and in autoconfig mode.
128703831d35Sstevel 		 */
128803831d35Sstevel 		hsp->hs_board_healthy = healthy;
128903831d35Sstevel 		if (healthy == B_TRUE)
129003831d35Sstevel 			return (hsc_slot_autoconnect(hsp));
129103831d35Sstevel 	}
129203831d35Sstevel 
129303831d35Sstevel 	/*
129403831d35Sstevel 	 * the board is connected. The result could be seviour depending
129503831d35Sstevel 	 * on the occupant state.
129603831d35Sstevel 	 */
129703831d35Sstevel 	if (healthy == B_TRUE) {
129803831d35Sstevel 		if (hsp->hs_board_healthy != B_TRUE) {
129903831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
130003831d35Sstevel 					HPC_LED_OFF);
130103831d35Sstevel 			/* Regained HEALTHY# at Run Time...!!! */
130203831d35Sstevel 			cmn_err(CE_NOTE, "%s#%d: slot %d Occupant "
130303831d35Sstevel 				"%s, Regained HEALTHY#!",
130403831d35Sstevel 				ddi_driver_name(hsc->dip),
130503831d35Sstevel 				ddi_get_instance(hsc->dip), slot_number,
130603831d35Sstevel 				hsp->hs_board_configured == B_TRUE ?
130703831d35Sstevel 						"configured" : "Unconfigured");
130803831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
130903831d35Sstevel 				HPC_EVENT_SLOT_HEALTHY_OK, 0);
131003831d35Sstevel 		}
131103831d35Sstevel 	} else {
131203831d35Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
131303831d35Sstevel 			/* Lost HEALTHY# at Run Time...Serious Condition. */
131403831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: ALERT! Lost HEALTHY#"
131503831d35Sstevel 				" on Slot %d, Occupant %s",
131603831d35Sstevel 				ddi_driver_name(hsc->dip),
131703831d35Sstevel 				ddi_get_instance(hsc->dip), slot_number,
131803831d35Sstevel 					hsp->hs_board_configured == B_TRUE ?
131903831d35Sstevel 						"Online!!!" : "Offline");
132003831d35Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
132103831d35Sstevel 					HPC_EVENT_SLOT_NOT_HEALTHY, 0);
132203831d35Sstevel 		}
132303831d35Sstevel 		if ((hsp->hs_board_configured != B_TRUE) ||
132403831d35Sstevel 						scsb_hsc_healthy_reset) {
132503831d35Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
132603831d35Sstevel 					slot_number, SCSB_RESET_SLOT) == 0) {
132703831d35Sstevel 				/* signal Ok to remove board. */
132803831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
132903831d35Sstevel 					HPC_FAULT_LED, HPC_LED_ON);
133003831d35Sstevel 				cmn_err(CE_WARN, "%s#%d: Slot %d "
133103831d35Sstevel 					"successfully taken offline",
133203831d35Sstevel 					ddi_driver_name(hsc->dip),
133303831d35Sstevel 					ddi_get_instance(hsc->dip),
133403831d35Sstevel 					slot_number);
133503831d35Sstevel 			}
133603831d35Sstevel 		}
133703831d35Sstevel 	}
133803831d35Sstevel 	hsp->hs_board_healthy = healthy;
133903831d35Sstevel 	return (DDI_SUCCESS);
134003831d35Sstevel }
134103831d35Sstevel 
134203831d35Sstevel static int
hsc_slot_autoconnect(hsc_slot_t * hsp)134303831d35Sstevel hsc_slot_autoconnect(hsc_slot_t *hsp)
134403831d35Sstevel {
134503831d35Sstevel 	hsc_state_t *hsc = hsp->hsc;
134603831d35Sstevel 	int rc = DDI_SUCCESS;
134703831d35Sstevel 	/*
134803831d35Sstevel 	 * Keep slot in reset unless autoconfiguration is enabled
134903831d35Sstevel 	 * Ie. for Basic Hotswap mode, we stay disconnected at
135003831d35Sstevel 	 * insertion. For full hotswap mode, we automatically
135103831d35Sstevel 	 * go into connected state at insertion, so that occupant
135203831d35Sstevel 	 * autoconfiguration is possible.
135303831d35Sstevel 	 */
135403831d35Sstevel 	if (((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) &&
135503831d35Sstevel 			(hsp->hs_flags & HSC_AUTOCFG)) {
135603831d35Sstevel 		/* this statement must be here before unreset. */
135703831d35Sstevel 		hsc->hsp_last = hsp;
135803831d35Sstevel 		if ((rc = scsb_reset_slot(hsp->hs_hpchandle,
135903831d35Sstevel 			hsp->hs_slot_number, SCSB_UNRESET_SLOT)) == 0) {
136003831d35Sstevel 
136103831d35Sstevel 			hsp->hs_slot_state = HPC_SLOT_CONNECTED;
136203831d35Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
136303831d35Sstevel 					HPC_FAULT_LED, HPC_LED_OFF);
136403831d35Sstevel 		} else {
136503831d35Sstevel 			hsc->hsp_last = NULL;
136603831d35Sstevel 			rc = DDI_FAILURE;
136703831d35Sstevel 		}
136803831d35Sstevel 	}
136903831d35Sstevel 	return (rc);
137003831d35Sstevel }
137103831d35Sstevel 
137203831d35Sstevel /*
137303831d35Sstevel  * The SCSB code should invoke this function from its _init() function.
137403831d35Sstevel  */
137503831d35Sstevel int
hsc_init()137603831d35Sstevel hsc_init()
137703831d35Sstevel {
137803831d35Sstevel 	int rc;
137903831d35Sstevel 
138003831d35Sstevel 	rc = ddi_soft_state_init(&hsc_state, sizeof (hsc_state_t), 1);
138103831d35Sstevel 	if (rc != 0)
138203831d35Sstevel 		return (rc);
138303831d35Sstevel 
138403831d35Sstevel 	hsc_slotops = hpc_alloc_slot_ops(KM_SLEEP);
138503831d35Sstevel 
138603831d35Sstevel 	hsc_slotops->hpc_version	= HPC_SLOT_OPS_VERSION;
138703831d35Sstevel 	hsc_slotops->hpc_op_connect	= hsc_connect;
138803831d35Sstevel 	hsc_slotops->hpc_op_disconnect	= hsc_disconnect;
138903831d35Sstevel 	hsc_slotops->hpc_op_insert	= hsc_insert;
139003831d35Sstevel 	hsc_slotops->hpc_op_remove	= hsc_remove;
139103831d35Sstevel 	hsc_slotops->hpc_op_control	= hsc_control;
139203831d35Sstevel 
139303831d35Sstevel 	return (DDI_SUCCESS);
139403831d35Sstevel }
139503831d35Sstevel 
139603831d35Sstevel 
139703831d35Sstevel /*
139803831d35Sstevel  * The SCSB code should invoke this function from its _fini() function.
139903831d35Sstevel  */
140003831d35Sstevel int
hsc_fini()140103831d35Sstevel hsc_fini()
140203831d35Sstevel {
140303831d35Sstevel 	if (hsc_slotops != NULL) {
140403831d35Sstevel 		hpc_free_slot_ops(hsc_slotops);
140503831d35Sstevel 		hsc_slotops = NULL;
140603831d35Sstevel 	}
140703831d35Sstevel 	ddi_soft_state_fini(&hsc_state);
140803831d35Sstevel 	return (DDI_SUCCESS);
140903831d35Sstevel }
141003831d35Sstevel 
141103831d35Sstevel static int
scsb_enable_enum(hsc_state_t * hsc)141203831d35Sstevel scsb_enable_enum(hsc_state_t *hsc)
141303831d35Sstevel {
141403831d35Sstevel 	DEBUG0("hsc: Enable ENUM#\n");
141503831d35Sstevel 
141603831d35Sstevel 	if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED)
141703831d35Sstevel 		return (DDI_SUCCESS);
141803831d35Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
141903831d35Sstevel 		return (DDI_FAILURE);
142003831d35Sstevel 
142103831d35Sstevel 	if (ddi_add_intr(hsc->dip, 1, NULL, NULL,
142203831d35Sstevel 			hsc_enum_intr, (caddr_t)hsc) != DDI_SUCCESS) {
142303831d35Sstevel 		cmn_err(CE_WARN, "%s#%d: failed ENUM# interrupt registration",
142403831d35Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
142503831d35Sstevel 		return (DDI_FAILURE);
142603831d35Sstevel 	}
142703831d35Sstevel 	cmn_err(CE_CONT, "?%s%d: Successfully Upgraded to "
142803831d35Sstevel 			"Full Hotswap Mode\n", ddi_driver_name(hsc->dip),
142903831d35Sstevel 			ddi_get_instance(hsc->dip));
143003831d35Sstevel 	hsc->state |= HSC_ENUM_ENABLED;
1431*07d06da5SSurya Prakki 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
1432*07d06da5SSurya Prakki 	    HOTSWAP_MODE_PROP, "full");
143303831d35Sstevel 	return (DDI_SUCCESS);
143403831d35Sstevel 
143503831d35Sstevel }
143603831d35Sstevel 
143703831d35Sstevel /*ARGSUSED*/
143803831d35Sstevel static int
scsb_disable_enum(hsc_state_t * hsc,int op)143903831d35Sstevel scsb_disable_enum(hsc_state_t *hsc, int op)
144003831d35Sstevel {
144103831d35Sstevel 
144203831d35Sstevel 	DEBUG0("hsc: Disable ENUM#\n");
144303831d35Sstevel 	if (op == SCSB_HSC_FORCE_REMOVE) {
144403831d35Sstevel 		/*
144503831d35Sstevel 		 * Clear all pending interrupts before unregistering
144603831d35Sstevel 		 * the interrupt. Otherwise the system will hang.
144703831d35Sstevel 		 *
144803831d35Sstevel 		 * Due to the hang problem, we'll not turn off or disable
144903831d35Sstevel 		 * interrupts because if there's a non-friendly full hotswap
145003831d35Sstevel 		 * device out there, the ENUM# will be kept asserted and
145103831d35Sstevel 		 * hence hsc_clear_all_enum() can never deassert ENUM#.
145203831d35Sstevel 		 * So the system will hang.
145303831d35Sstevel 		 */
145403831d35Sstevel 		if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) {
145503831d35Sstevel 			/* hsc_clear_all_enum(hsc); */
145603831d35Sstevel 			ddi_remove_intr(hsc->dip, 1, NULL);
145703831d35Sstevel 			hsc->state &= ~HSC_ENUM_ENABLED;
145803831d35Sstevel 			cmn_err(CE_CONT, "?%s%d: Successfully Downgraded to "
145903831d35Sstevel 				"Basic Hotswap Mode\n",
146003831d35Sstevel 				ddi_driver_name(hsc->dip),
146103831d35Sstevel 				ddi_get_instance(hsc->dip));
146203831d35Sstevel 		}
1463*07d06da5SSurya Prakki 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
146403831d35Sstevel 		    HOTSWAP_MODE_PROP, "basic");
146503831d35Sstevel 		return (DDI_SUCCESS);
146603831d35Sstevel 	} else
146703831d35Sstevel 		/* No programming interface for disabling ENUM# on MC/Tonga */
146803831d35Sstevel 		return (HPC_ERR_NOTSUPPORTED);
146903831d35Sstevel }
147003831d35Sstevel 
147103831d35Sstevel #ifndef	lint
147203831d35Sstevel static int
hsc_clear_all_enum(hsc_state_t * hsc)147303831d35Sstevel hsc_clear_all_enum(hsc_state_t *hsc)
147403831d35Sstevel {
147503831d35Sstevel 	int i, rc;
147603831d35Sstevel 	hsc_slot_t *hsp;
147703831d35Sstevel 
147803831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
147903831d35Sstevel 
148003831d35Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
148103831d35Sstevel 		if (hsp == NULL)
148203831d35Sstevel 			continue;
148303831d35Sstevel 		rc = hpc_slot_event_notify(hsp->hs_slot_handle,
148403831d35Sstevel 					HPC_EVENT_CLEAR_ENUM,
148503831d35Sstevel 						HPC_EVENT_SYNCHRONOUS);
148603831d35Sstevel 		if (rc == HPC_EVENT_UNCLAIMED)
148703831d35Sstevel 			break;	/* no pending interrupts across the bus */
148803831d35Sstevel 		DEBUG1("Pending Intr on slot %d\n",
148903831d35Sstevel 			hsc->slot_table_prop[i].pslotnum);
149003831d35Sstevel 	}
149103831d35Sstevel 	return (0);
149203831d35Sstevel }
149303831d35Sstevel #endif
149403831d35Sstevel 
149503831d35Sstevel int
scsb_hsc_attach(dev_info_t * dip,void * scsb_handle,int instance)149603831d35Sstevel scsb_hsc_attach(dev_info_t *dip, void *scsb_handle, int instance)
149703831d35Sstevel {
149803831d35Sstevel 	int i, n, prop_len;
149903831d35Sstevel 	int prom_prop = 0;	/* default: OS property gives slot-table */
150003831d35Sstevel 	int rc;
150103831d35Sstevel 	char *hotswap_model;
150203831d35Sstevel 	hsc_state_t	*hsc;
150303831d35Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)scsb_handle;
150403831d35Sstevel 	caddr_t hpc_slot_table_data, s;
150503831d35Sstevel 	int hpc_slot_table_size;
150603831d35Sstevel 	hsc_prom_slot_table_t	*hpstp;
150703831d35Sstevel 	int rstate;
150803831d35Sstevel 
150903831d35Sstevel 	DEBUG0("hsc_attach: enter\n");
151003831d35Sstevel 	/*
151103831d35Sstevel 	 * To get the slot information,
151203831d35Sstevel 	 * The OBP defines the 'slot-table' property. But the OS
151303831d35Sstevel 	 * can override it with 'hsc-slot-map' property
151403831d35Sstevel 	 * through the .conf file.
151503831d35Sstevel 	 * Since the formats are different, 2 different property names
151603831d35Sstevel 	 * are chosen.
151703831d35Sstevel 	 * The OBP property format is
151803831d35Sstevel 	 * <phandle>,<pci-devno>,<phys-slotno>,<ga-bits>
151903831d35Sstevel 	 * The OS property format is (ga-bits is not used however)
152003831d35Sstevel 	 * <busnexus-path>,<pci-devno>,<phys-slotno>,<ga-bits>
152103831d35Sstevel 	 */
152203831d35Sstevel 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
152303831d35Sstevel 		"hsc-slot-map", (caddr_t)&hpc_slot_table_data,
152403831d35Sstevel 		&hpc_slot_table_size);
152503831d35Sstevel 	if (rc != DDI_PROP_SUCCESS)  {
152603831d35Sstevel 		prom_prop = 1;
152703831d35Sstevel 		rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
152803831d35Sstevel 			"slot-table", (caddr_t)&hpc_slot_table_data,
152903831d35Sstevel 			&hpc_slot_table_size);
153003831d35Sstevel 		if (rc != DDI_PROP_SUCCESS) {
153103831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: 'slot-table' property "
153203831d35Sstevel 				"missing!", ddi_driver_name(dip),
153303831d35Sstevel 						ddi_get_instance(dip));
153403831d35Sstevel 			return (DDI_FAILURE);
153503831d35Sstevel 		}
153603831d35Sstevel 	}
153703831d35Sstevel 	rc = ddi_soft_state_zalloc(hsc_state, instance);
153803831d35Sstevel 	if (rc != DDI_SUCCESS)
153903831d35Sstevel 		return (DDI_FAILURE);
154003831d35Sstevel 
154103831d35Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
154203831d35Sstevel 	hsc->scsb_handle = scsb_handle;
154303831d35Sstevel 	hsc->dip = dip;
154403831d35Sstevel 	hsc->instance = instance;
154503831d35Sstevel 	hsc->n_registered_occupants = 0;
154603831d35Sstevel 	hsc->regDone = B_FALSE;
154703831d35Sstevel 	/* hsc->slot_info = hsc_slot_list; */
154803831d35Sstevel 
154903831d35Sstevel 	/*
155003831d35Sstevel 	 * Check whether the system should be in basic or full
155103831d35Sstevel 	 * hotswap mode. The PROM property always says full, so
155203831d35Sstevel 	 * look at the .conf file property whether this is "full"
155303831d35Sstevel 	 */
155403831d35Sstevel 	if (scsb_hsc_enable_fhs) {
155503831d35Sstevel 		hsc->hotswap_mode = HSC_HOTSWAP_MODE_FULL;
155603831d35Sstevel 	} else {
155703831d35Sstevel 		hsc->hotswap_mode = HSC_HOTSWAP_MODE_BASIC;
155803831d35Sstevel 	}
155903831d35Sstevel 
156003831d35Sstevel 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
156103831d35Sstevel 		"default-hotswap-mode", (caddr_t)&hotswap_model, &prop_len);
156203831d35Sstevel 
156303831d35Sstevel 	if (rc == DDI_PROP_SUCCESS) {
156403831d35Sstevel 		if (strcmp(hotswap_model, "full") == 0) {
156503831d35Sstevel 			hsc->hotswap_mode = HSC_HOTSWAP_MODE_FULL;
156603831d35Sstevel 		} else if (strcmp(hotswap_model, "basic") == 0) {
156703831d35Sstevel 			hsc->hotswap_mode = HSC_HOTSWAP_MODE_BASIC;
156803831d35Sstevel 		}
156903831d35Sstevel 
157003831d35Sstevel 		kmem_free(hotswap_model, prop_len);
157103831d35Sstevel 	}
157203831d35Sstevel 
157303831d35Sstevel 	/*
157403831d35Sstevel 	 * Determine the size of the slot table from the property and
157503831d35Sstevel 	 * allocate the slot table arrary..Decoding is different for
157603831d35Sstevel 	 * OS and PROM property.
157703831d35Sstevel 	 */
157803831d35Sstevel 	if (!prom_prop) {	/* OS .conf property */
157903831d35Sstevel 		for (i = 0, n = 0; i < hpc_slot_table_size; i++) {
158003831d35Sstevel 			if (hpc_slot_table_data[i] == 0) {
158103831d35Sstevel 				n++;
158203831d35Sstevel 			}
158303831d35Sstevel 		}
158403831d35Sstevel 
158503831d35Sstevel 		/* There should be four elements per entry */
158603831d35Sstevel 		if (n % 4) {
158703831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: bad format for "
158803831d35Sstevel 				"slot-table(%d)", ddi_driver_name(dip),
158903831d35Sstevel 						ddi_get_instance(dip), n);
159003831d35Sstevel 			kmem_free(hpc_slot_table_data, hpc_slot_table_size);
159103831d35Sstevel 			ddi_soft_state_free(hsc_state, instance);
159203831d35Sstevel 			return (DDI_FAILURE);
159303831d35Sstevel 		}
159403831d35Sstevel 
159503831d35Sstevel 		hsc->slot_table_size = n / 4;
159603831d35Sstevel 	} else {
159703831d35Sstevel 		hsc->slot_table_size = hpc_slot_table_size /
159803831d35Sstevel 						sizeof (hsc_prom_slot_table_t);
159903831d35Sstevel 		n = hpc_slot_table_size % sizeof (hsc_prom_slot_table_t);
160003831d35Sstevel 		if (n) {
160103831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: bad format for "
160203831d35Sstevel 				"slot-table(%d)", ddi_driver_name(dip),
160303831d35Sstevel 				ddi_get_instance(dip), hpc_slot_table_size);
160403831d35Sstevel 			kmem_free(hpc_slot_table_data, hpc_slot_table_size);
160503831d35Sstevel 			ddi_soft_state_free(hsc_state, instance);
160603831d35Sstevel 			return (DDI_FAILURE);
160703831d35Sstevel 		}
160803831d35Sstevel 	}
160903831d35Sstevel 
161003831d35Sstevel 	/*
161103831d35Sstevel 	 * Netract800 FTC (formerly known as CFTM) workaround.
161203831d35Sstevel 	 * Leave Slot 2 out of the HS table if FTC is present in Slot 2
161303831d35Sstevel 	 */
161403831d35Sstevel 	if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES) {
161503831d35Sstevel 		hsc->slot_table_size -= 1;
161603831d35Sstevel 	}
161703831d35Sstevel 	DEBUG1("hsc_attach: %d hotplug slots on bus\n", hsc->slot_table_size);
161803831d35Sstevel 	/*
161903831d35Sstevel 	 * Create enough space for each slot table entry
162003831d35Sstevel 	 * based on how many entries in the property
162103831d35Sstevel 	 */
162203831d35Sstevel 	hsc->slot_table_prop = (hsc_slot_table_t *)
162303831d35Sstevel 		kmem_zalloc(hsc->slot_table_size *
162403831d35Sstevel 			sizeof (hsc_slot_table_t), KM_SLEEP);
162503831d35Sstevel 
162603831d35Sstevel 	if (!prom_prop) {
162703831d35Sstevel 		s = hpc_slot_table_data;
162803831d35Sstevel 		for (i = 0; i < hsc->slot_table_size; i++) {
162903831d35Sstevel 
163003831d35Sstevel 			char *nexus, *pcidev, *phys_slotname, *ga;
163103831d35Sstevel 
163203831d35Sstevel 			/* Pick off pointer to nexus path or PROM handle */
163303831d35Sstevel 			nexus = s;
163403831d35Sstevel 			while (*s != NULL)
163503831d35Sstevel 				s++;
163603831d35Sstevel 			s++;
163703831d35Sstevel 
163803831d35Sstevel 			/* Pick off pointer to the pci device number */
163903831d35Sstevel 			pcidev = s;
164003831d35Sstevel 			while (*s != NULL)
164103831d35Sstevel 				s++;
164203831d35Sstevel 			s++;
164303831d35Sstevel 
164403831d35Sstevel 			/* Pick off physical slot no */
164503831d35Sstevel 			phys_slotname = s;
164603831d35Sstevel 			while (*s != NULL)
164703831d35Sstevel 				s++;
164803831d35Sstevel 			s++;
164903831d35Sstevel 
165003831d35Sstevel 			/* Pick off GA bits which we dont use for now. */
165103831d35Sstevel 			ga = s;
165203831d35Sstevel 			while (*s != NULL)
165303831d35Sstevel 				s++;
165403831d35Sstevel 			s++;
165503831d35Sstevel 
165603831d35Sstevel 			if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
165703831d35Sstevel 					atoi(phys_slotname) == SC_MC_CTC_SLOT) {
165803831d35Sstevel 				--i;
165903831d35Sstevel 				continue;
166003831d35Sstevel 			}
166103831d35Sstevel 			hsc->slot_table_prop[i].pslotnum = atoi(phys_slotname);
166203831d35Sstevel 			hsc->slot_table_prop[i].ga = atoi(ga);
166303831d35Sstevel 			hsc->slot_table_prop[i].pci_devno = atoi(pcidev);
1664*07d06da5SSurya Prakki 			(void) strcpy(hsc->slot_table_prop[i].nexus, nexus);
166503831d35Sstevel 		}
166603831d35Sstevel 	} else {
166703831d35Sstevel 		hpstp = (hsc_prom_slot_table_t *)hpc_slot_table_data;
166803831d35Sstevel 		for (i = 0; i < hsc->slot_table_size; i++, hpstp++) {
166903831d35Sstevel 			if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
167003831d35Sstevel 					hpstp->pslotnum == SC_MC_CTC_SLOT) {
167103831d35Sstevel 				--i;
167203831d35Sstevel 				continue;
167303831d35Sstevel 			}
167403831d35Sstevel 			hsc->slot_table_prop[i].pslotnum = hpstp->pslotnum;
167503831d35Sstevel 			hsc->slot_table_prop[i].ga = hpstp->ga;
167603831d35Sstevel 			hsc->slot_table_prop[i].pci_devno = hpstp->pci_devno;
167703831d35Sstevel 
167803831d35Sstevel 			if (prom_phandle_to_path((uint_t)hpstp->phandle,
167903831d35Sstevel 				hsc->slot_table_prop[i].nexus,
168003831d35Sstevel 				sizeof (hsc->slot_table_prop[i].nexus))
168103831d35Sstevel 						== -1) {
168203831d35Sstevel 				cmn_err(CE_WARN, "%s#%d: Cannot get phandle "
168303831d35Sstevel 					"to nexus path", ddi_driver_name(dip),
168403831d35Sstevel 					ddi_get_instance(dip));
168503831d35Sstevel 				kmem_free(hsc->slot_table_prop,
168603831d35Sstevel 					(hsc->slot_table_size *
168703831d35Sstevel 						sizeof (hsc_slot_table_t)));
168803831d35Sstevel 				kmem_free(hpc_slot_table_data,
168903831d35Sstevel 						hpc_slot_table_size);
169003831d35Sstevel 				ddi_soft_state_free(hsc_state, instance);
169103831d35Sstevel 				return (DDI_FAILURE);
169203831d35Sstevel 			}
169303831d35Sstevel 		}
169403831d35Sstevel 	}
169503831d35Sstevel 
169603831d35Sstevel 	/* keep healthy register cache uptodate before reading slot state */
169703831d35Sstevel 	if (scsb_read_bhealthy(scsb_handle) != 0) {
169803831d35Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_attach: Cannot read "
169903831d35Sstevel 			"Healthy Registers", ddi_driver_name(dip),
170003831d35Sstevel 				ddi_get_instance(dip));
170103831d35Sstevel 		kmem_free(hsc->slot_table_prop,
170203831d35Sstevel 			(hsc->slot_table_size *
170303831d35Sstevel 				sizeof (hsc_slot_table_t)));
170403831d35Sstevel 		kmem_free(hpc_slot_table_data,
170503831d35Sstevel 				hpc_slot_table_size);
170603831d35Sstevel 		ddi_soft_state_free(hsc_state, instance);
170703831d35Sstevel 		return (DDI_FAILURE);
170803831d35Sstevel 	}
170903831d35Sstevel 
171003831d35Sstevel 	/*
171103831d35Sstevel 	 * Before we start registering the slots, calculate how many
171203831d35Sstevel 	 * slots are occupied.
171303831d35Sstevel 	 */
171403831d35Sstevel 
171503831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
171603831d35Sstevel 		if (scsb_get_slot_state(scsb_handle,
171703831d35Sstevel 				hsc->slot_table_prop[i].pslotnum, &rstate) !=
171803831d35Sstevel 				DDI_SUCCESS)
171903831d35Sstevel 				return (rc);
172003831d35Sstevel 		if (rstate != HPC_SLOT_EMPTY)
172103831d35Sstevel 			hsc->n_registered_occupants++;
172203831d35Sstevel 	}
172303831d35Sstevel 
172403831d35Sstevel 	mutex_init(&hsc->hsc_mutex, NULL, MUTEX_DRIVER, NULL);
172503831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
172603831d35Sstevel 
172703831d35Sstevel 		DEBUG2("Registering on nexus [%s] cPCI device [%d]\n",
172803831d35Sstevel 			hsc->slot_table_prop[i].nexus,
172903831d35Sstevel 			hsc->slot_table_prop[i].pci_devno);
173003831d35Sstevel 
173103831d35Sstevel 		if (hsc_slot_register(hsc, hsc->slot_table_prop[i].nexus,
173203831d35Sstevel 			hsc->slot_table_prop[i].pci_devno,
173303831d35Sstevel 			hsc->slot_table_prop[i].pslotnum, B_FALSE) !=
173403831d35Sstevel 								HPC_SUCCESS) {
173503831d35Sstevel 
173603831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: Slot Registration Failure",
173703831d35Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
173803831d35Sstevel 			while (i) {
173903831d35Sstevel 				i--;
174003831d35Sstevel 				n = hsc->slot_table_prop[i].pslotnum;
174103831d35Sstevel 				if (hsc_slot_unregister(n) != 0) {
174203831d35Sstevel 					cmn_err(CE_WARN,
174303831d35Sstevel 						"%s#%d: failed to unregister"
174403831d35Sstevel 						" slot %d",
174503831d35Sstevel 						ddi_driver_name(dip),
174603831d35Sstevel 						ddi_get_instance(dip), n);
174703831d35Sstevel 
174803831d35Sstevel 				}
174903831d35Sstevel 			}
175003831d35Sstevel 			mutex_destroy(&hsc->hsc_mutex);
175103831d35Sstevel 			kmem_free(hsc->slot_table_prop, (hsc->slot_table_size *
175203831d35Sstevel 					sizeof (hsc_slot_table_t)));
175303831d35Sstevel 			kmem_free(hpc_slot_table_data, hpc_slot_table_size);
175403831d35Sstevel 			ddi_soft_state_free(hsc_state, instance);
175503831d35Sstevel 			return (DDI_FAILURE);
175603831d35Sstevel 		}
175703831d35Sstevel 	}
175803831d35Sstevel 
175903831d35Sstevel 	hsc->hsp_last = NULL;
176003831d35Sstevel 	hsc->hsc_intr_counter = 0;
176103831d35Sstevel 	kmem_free(hpc_slot_table_data, hpc_slot_table_size);
1762*07d06da5SSurya Prakki 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
1763*07d06da5SSurya Prakki 	    HOTSWAP_MODE_PROP, "basic");
176403831d35Sstevel 	hsc->state |= (HSC_ATTACHED|HSC_SCB_CONNECTED);
176503831d35Sstevel 
176603831d35Sstevel 	/*
176703831d35Sstevel 	 * We enable full hotswap right here if all the slots are empty.
176803831d35Sstevel 	 */
176903831d35Sstevel 	if ((hsc->regDone == B_FALSE && hsc->n_registered_occupants == 0) ||
177003831d35Sstevel 			scsb_hsc_numReg == hsc->n_registered_occupants) {
177103831d35Sstevel 		hsc->regDone = B_TRUE;
177203831d35Sstevel 		if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL) {
177303831d35Sstevel 			if (scsb_enable_enum(hsc) != DDI_SUCCESS) {
177403831d35Sstevel 				cmn_err(CE_WARN, "%s#%d: Cannot enable "
177503831d35Sstevel 					"Full Hotswap", ddi_driver_name(dip),
177603831d35Sstevel 					ddi_get_instance(dip));
177703831d35Sstevel 			}
177803831d35Sstevel 		}
177903831d35Sstevel 	}
178003831d35Sstevel 	return (DDI_SUCCESS);
178103831d35Sstevel }
178203831d35Sstevel 
178303831d35Sstevel /*ARGSUSED*/
178403831d35Sstevel int
scsb_hsc_detach(dev_info_t * dip,void * scsb_handle,int instance)178503831d35Sstevel scsb_hsc_detach(dev_info_t *dip, void *scsb_handle, int instance)
178603831d35Sstevel {
178703831d35Sstevel 	int i = 0;
178803831d35Sstevel 	hsc_state_t	*hsc;
178903831d35Sstevel 	char slotautocfg_prop[18];
179003831d35Sstevel 
179103831d35Sstevel 	DEBUG0("hsc_detach: enter\n");
179203831d35Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
179303831d35Sstevel 	if (hsc == NULL) {
179403831d35Sstevel 		DEBUG2("%s#%d: hsc_detach: Soft state NULL",
179503831d35Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
179603831d35Sstevel 		return (DDI_FAILURE);
179703831d35Sstevel 	}
179803831d35Sstevel 
179903831d35Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
180003831d35Sstevel 		return (DDI_FAILURE);
180103831d35Sstevel 	/*
180203831d35Sstevel 	 * let's unregister the hotpluggable slots with hotplug service.
180303831d35Sstevel 	 */
180403831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
180503831d35Sstevel 
180603831d35Sstevel 		hsc_slot_t	*hsp;
180703831d35Sstevel 
180803831d35Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
180903831d35Sstevel 		if (hsp == NULL) {
181003831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: hsc_detach: No Slot Info",
181103831d35Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
181203831d35Sstevel 		} else {
181303831d35Sstevel 			hpc_led_info_t	aledinfo;	/* active led info. */
181403831d35Sstevel 			hpc_led_info_t	fledinfo;	/* fault led info. */
181503831d35Sstevel 
181603831d35Sstevel 			aledinfo.led = HPC_ACTIVE_LED;
181703831d35Sstevel 			aledinfo.state = HPC_LED_BLINK;
181803831d35Sstevel 			fledinfo.led = HPC_FAULT_LED;
181903831d35Sstevel 			fledinfo.state = HPC_LED_OFF;
182003831d35Sstevel 			(void) hsc_led_state(hsp, HPC_CTRL_SET_LED_STATE,
182103831d35Sstevel 							&aledinfo);
182203831d35Sstevel 			(void) hsc_led_state(hsp, HPC_CTRL_SET_LED_STATE,
182303831d35Sstevel 							&fledinfo);
182403831d35Sstevel 		}
1825*07d06da5SSurya Prakki 		(void) sprintf(slotautocfg_prop, "slot%d-autoconfig",
182603831d35Sstevel 		    hsp->hs_slot_number);
1827*07d06da5SSurya Prakki 		(void) ddi_prop_remove(DDI_DEV_T_NONE, hsc->dip,
1828*07d06da5SSurya Prakki 		    slotautocfg_prop);
182903831d35Sstevel 		if (hsc_slot_unregister(hsc->slot_table_prop[i].pslotnum)
183003831d35Sstevel 						!= 0) {
183103831d35Sstevel 			cmn_err(CE_NOTE, "%s#%d: failed to unregister"
183203831d35Sstevel 				" slot %d\n", ddi_driver_name(dip),
183303831d35Sstevel 				ddi_get_instance(dip),
183403831d35Sstevel 				hsc->slot_table_prop[i].pslotnum);
183503831d35Sstevel 			return (DDI_FAILURE);
183603831d35Sstevel 		}
183703831d35Sstevel 	}
183803831d35Sstevel 	kmem_free(hsc->slot_table_prop, (hsc->slot_table_size *
183903831d35Sstevel 					sizeof (hsc_slot_table_t)));
184003831d35Sstevel 	if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) {
184103831d35Sstevel 		ddi_remove_intr(hsc->dip, 1, hsc->enum_iblock);
184203831d35Sstevel 		hsc->state &= ~HSC_ENUM_ENABLED;
184303831d35Sstevel 	}
184403831d35Sstevel 	mutex_destroy(&hsc->hsc_mutex);
1845*07d06da5SSurya Prakki 	(void) ddi_prop_remove(DDI_DEV_T_NONE, hsc->dip, HOTSWAP_MODE_PROP);
184603831d35Sstevel 	hsc->state &= ~(HSC_ATTACHED|HSC_SCB_CONNECTED);
184703831d35Sstevel 	ddi_soft_state_free(hsc_state, instance);
184803831d35Sstevel 	return (DDI_SUCCESS);
184903831d35Sstevel }
185003831d35Sstevel 
185103831d35Sstevel /*
185203831d35Sstevel  * The following function is called when the SCSB is hot extracted from
185303831d35Sstevel  * the system.
185403831d35Sstevel  */
185503831d35Sstevel int
scsb_hsc_freeze(dev_info_t * dip)185603831d35Sstevel scsb_hsc_freeze(dev_info_t *dip)
185703831d35Sstevel {
185803831d35Sstevel 	hsc_state_t	*hsc;
185903831d35Sstevel 	int instance = ddi_get_instance(dip);
186003831d35Sstevel 	int i;
186103831d35Sstevel 	hsc_slot_t	*hsp;
186203831d35Sstevel 
186303831d35Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
186403831d35Sstevel 	if (hsc == NULL) {
186503831d35Sstevel 		DEBUG2("%s#%d: Soft state NULL",
186603831d35Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
186703831d35Sstevel 		return (DDI_SUCCESS);
186803831d35Sstevel 	}
186903831d35Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
187003831d35Sstevel 		return (DDI_SUCCESS);
187103831d35Sstevel 	hsc->state &= ~HSC_SCB_CONNECTED;
187203831d35Sstevel 
187303831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
187403831d35Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
187503831d35Sstevel 
187603831d35Sstevel 		if (hsp == NULL) {
187703831d35Sstevel 			cmn_err(CE_NOTE, "hsc_freeze: "
187803831d35Sstevel 				" Cannot map slot number %d to a hsc_slot_t",
187903831d35Sstevel 					hsc->slot_table_prop[i].pslotnum);
188003831d35Sstevel 			continue;
188103831d35Sstevel 		}
188203831d35Sstevel 		/*
188303831d35Sstevel 		 * Since reset lines are pulled low, lets mark these
188403831d35Sstevel 		 * slots and not allow a connect operation.
188503831d35Sstevel 		 * Note that we still keep the slot as slot disconnected,
188603831d35Sstevel 		 * although it is connected from the hardware standpoint.
188703831d35Sstevel 		 * As soon as the SCB is plugged back in, we check these
188803831d35Sstevel 		 * states and put the hardware state back to its original
188903831d35Sstevel 		 * state.
189003831d35Sstevel 		 */
189103831d35Sstevel 		if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
189203831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: Slot %d Now out of Reset!",
189303831d35Sstevel 				ddi_driver_name(hsc->dip),
189403831d35Sstevel 				ddi_get_instance(hsc->dip),
189503831d35Sstevel 				hsp->hs_slot_number);
189603831d35Sstevel 		}
189703831d35Sstevel 		hsp->hs_flags |= HSC_SCB_HOTSWAPPED;
189803831d35Sstevel 	}
189903831d35Sstevel 
190003831d35Sstevel 	return (DDI_SUCCESS);
190103831d35Sstevel }
190203831d35Sstevel 
190303831d35Sstevel /*
190403831d35Sstevel  * The following function is called when the SCSB is hot inserted from
190503831d35Sstevel  * the system. We must update the LED status and set the RST# registers
190603831d35Sstevel  * again.
190703831d35Sstevel  */
190803831d35Sstevel int
scsb_hsc_restore(dev_info_t * dip)190903831d35Sstevel scsb_hsc_restore(dev_info_t *dip)
191003831d35Sstevel {
191103831d35Sstevel 	int i;
191203831d35Sstevel 	hsc_state_t	*hsc;
191303831d35Sstevel 	hsc_slot_t	*hsp;
191403831d35Sstevel 	int instance = ddi_get_instance(dip);
191503831d35Sstevel 
191603831d35Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
191703831d35Sstevel 	if (hsc == NULL) {
191803831d35Sstevel 		DEBUG2("%s#%d: Soft state NULL",
191903831d35Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
192003831d35Sstevel 		return (DDI_SUCCESS);
192103831d35Sstevel 	}
192203831d35Sstevel 
192303831d35Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
192403831d35Sstevel 		return (DDI_SUCCESS);
192503831d35Sstevel 	hsc->state |= HSC_SCB_CONNECTED;
192603831d35Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
192703831d35Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
192803831d35Sstevel 
192903831d35Sstevel 		if (hsp == NULL) {
193003831d35Sstevel 			cmn_err(CE_NOTE, "%s#%d: hsc_restore: "
193103831d35Sstevel 				" Cannot map slot number %d to a hsc_slot_t",
193203831d35Sstevel 					ddi_driver_name(hsc->dip),
193303831d35Sstevel 					ddi_get_instance(hsc->dip),
193403831d35Sstevel 					hsc->slot_table_prop[i].pslotnum);
193503831d35Sstevel 			continue;
193603831d35Sstevel 		}
193703831d35Sstevel 		if ((hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) &&
193803831d35Sstevel 				(hsp->hs_board_configured == B_FALSE)) {
193903831d35Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
194003831d35Sstevel 					hsp->hs_slot_number,
194103831d35Sstevel 					SCSB_RESET_SLOT) != 0) {
194203831d35Sstevel 				cmn_err(CE_WARN, "%s#%d: hsc_restore: "
194303831d35Sstevel 					" Cannot reset disconnected slot %d",
194403831d35Sstevel 						ddi_driver_name(hsc->dip),
194503831d35Sstevel 						ddi_get_instance(hsc->dip),
194603831d35Sstevel 						hsp->hs_slot_number);
194703831d35Sstevel 			}
194803831d35Sstevel 		}
194903831d35Sstevel 
195003831d35Sstevel 		if (scsb_hsc_init_slot_state(hsc, hsp) != DDI_SUCCESS) {
195103831d35Sstevel 
195203831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: hsc_freeze: Cannot init"
195303831d35Sstevel 				" slot%d state",
195403831d35Sstevel 				ddi_driver_name(hsc->dip),
195503831d35Sstevel 				ddi_get_instance(hsc->dip),
195603831d35Sstevel 				hsp->hs_slot_number);
195703831d35Sstevel 		}
195803831d35Sstevel 		hsp->hs_flags &= ~HSC_SCB_HOTSWAPPED;
195903831d35Sstevel 	}
196003831d35Sstevel 	return (DDI_SUCCESS);
196103831d35Sstevel }
196203831d35Sstevel 
196303831d35Sstevel #ifndef	lint
196403831d35Sstevel int
scsb_hsc_freeze_check(dev_info_t * dip)196503831d35Sstevel scsb_hsc_freeze_check(dev_info_t *dip)
196603831d35Sstevel {
196703831d35Sstevel 	hsc_state_t	*hsc;
196803831d35Sstevel 	int instance = ddi_get_instance(dip);
196903831d35Sstevel 
197003831d35Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
197103831d35Sstevel 	if (hsc == NULL) {
197203831d35Sstevel 		DEBUG2("%s#%d: Soft state NULL",
197303831d35Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
197403831d35Sstevel 		return (DDI_SUCCESS);
197503831d35Sstevel 	}
197603831d35Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
197703831d35Sstevel 		return (DDI_SUCCESS);
197803831d35Sstevel 	return (DDI_SUCCESS);
197903831d35Sstevel }
198003831d35Sstevel #endif
198103831d35Sstevel 
198203831d35Sstevel /*
198303831d35Sstevel  * update info about Alarm Card insert/remove mechanism.
198403831d35Sstevel  */
198503831d35Sstevel void
hsc_ac_op(int instance,int pslotnum,int op,void * arg)198603831d35Sstevel hsc_ac_op(int instance, int pslotnum, int op, void *arg)
198703831d35Sstevel {
198803831d35Sstevel 	hsc_slot_t *hsp;
198903831d35Sstevel 	hsc_state_t	*hsc;
199003831d35Sstevel 
199103831d35Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
199203831d35Sstevel 	if (hsc == NULL) {
199303831d35Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_ac_op: No Soft State Info",
199403831d35Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
199503831d35Sstevel 		return;
199603831d35Sstevel 	}
199703831d35Sstevel 
199803831d35Sstevel 	hsp = hsc_find_slot(pslotnum);
199903831d35Sstevel 	if (hsp == NULL) {
200003831d35Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_ac_op: No Slot Info",
200103831d35Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
200203831d35Sstevel 		return;
200303831d35Sstevel 	}
200403831d35Sstevel 
200503831d35Sstevel 	switch (op) {
200603831d35Sstevel 		case SCSB_HSC_AC_UNCONFIGURE :
200703831d35Sstevel 			/*
200803831d35Sstevel 			 * If ENUM# is enabled, then action is pending on
200903831d35Sstevel 			 * this slot, just send a event.
201003831d35Sstevel 			 */
201103831d35Sstevel 			if (hsc->state & HSC_ENUM_ENABLED)
2012*07d06da5SSurya Prakki 				(void) hpc_slot_event_notify(
2013*07d06da5SSurya Prakki 				    hsp->hs_slot_handle,
201403831d35Sstevel 				    HPC_EVENT_PROCESS_ENUM, 0);
201503831d35Sstevel 			break;
201603831d35Sstevel 		case SCSB_HSC_AC_GET_SLOT_INFO :
201703831d35Sstevel 			*(hsc_slot_t **)arg = hsp;
201803831d35Sstevel 			break;
201903831d35Sstevel 		default :
202003831d35Sstevel 			break;
202103831d35Sstevel 	}
202203831d35Sstevel }
202303831d35Sstevel 
202403831d35Sstevel static uint_t
hsc_enum_intr(caddr_t iarg)202503831d35Sstevel hsc_enum_intr(caddr_t iarg)
202603831d35Sstevel {
202703831d35Sstevel 	int rc;
202803831d35Sstevel 	hsc_state_t *hsc = (hsc_state_t *)iarg;
202903831d35Sstevel 	hsc_slot_t *hsp;
203003831d35Sstevel 
203103831d35Sstevel 	DEBUG0("!E!");
203203831d35Sstevel 	if ((hsc->state & HSC_ATTACHED) == 0)
203303831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
203403831d35Sstevel 
203503831d35Sstevel 	hsp = hsc_find_slot(hsc->slot_table_prop[0].pslotnum);
203603831d35Sstevel 	if (hsp == NULL)	/* No slots registered */
203703831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
203803831d35Sstevel 
203903831d35Sstevel 	/*
204003831d35Sstevel 	 * The following must be done to clear interrupt (synchronous event).
204103831d35Sstevel 	 * To process the interrupt, we send an asynchronous event.
204203831d35Sstevel 	 */
204303831d35Sstevel 	rc = hpc_slot_event_notify(hsp->hs_slot_handle,
204403831d35Sstevel 					HPC_EVENT_CLEAR_ENUM,
204503831d35Sstevel 						HPC_EVENT_SYNCHRONOUS);
204603831d35Sstevel 	if (rc == HPC_EVENT_UNCLAIMED) {
204703831d35Sstevel 		/*
204803831d35Sstevel 		 * possible support for handling insertion of non friendly
204903831d35Sstevel 		 * full hotswap boards, otherwise the system hangs due
205003831d35Sstevel 		 * to uncleared interrupt bursts.
205103831d35Sstevel 		 */
205203831d35Sstevel 		DEBUG2("!E>counter %d, last op@slot %lx\n",
205303831d35Sstevel 				hsc->hsc_intr_counter, hsc->hsp_last);
205403831d35Sstevel 		hsc->hsc_intr_counter ++;
205503831d35Sstevel 		if (hsc->hsc_intr_counter == scsb_hsc_max_intr_count) {
205603831d35Sstevel 			if (!hsc->hsp_last) {
205703831d35Sstevel 				cmn_err(CE_WARN, "%s#%d: hsc_enum_intr: "
205803831d35Sstevel 					" No Last Board Insertion Info.",
205903831d35Sstevel 					ddi_driver_name(hsc->dip),
206003831d35Sstevel 					ddi_get_instance(hsc->dip));
206103831d35Sstevel 				hsc->hsc_intr_counter = 0;
206203831d35Sstevel 				return (DDI_INTR_UNCLAIMED);
206303831d35Sstevel 			}
206403831d35Sstevel 			hsp = hsc->hsp_last;
206503831d35Sstevel 			cmn_err(CE_WARN, "%s#%d: Bad (non friendly ?) Board "
206603831d35Sstevel 				"in Slot %d ? Taking it Offline.",
206703831d35Sstevel 				ddi_driver_name(hsc->dip),
206803831d35Sstevel 				ddi_get_instance(hsc->dip),
206903831d35Sstevel 				hsp->hs_slot_number);
207003831d35Sstevel 			/*
207103831d35Sstevel 			 * this should put just inserted board back in
207203831d35Sstevel 			 * reset, thus deasserting the ENUM# and the
207303831d35Sstevel 			 * system hang.
207403831d35Sstevel 			 */
207503831d35Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
207603831d35Sstevel 					hsp->hs_slot_number,
207703831d35Sstevel 					SCSB_RESET_SLOT) == 0) {
207803831d35Sstevel 				/* Enumeration failed on this board */
207903831d35Sstevel 				hsp->hs_flags |= HSC_ENUM_FAILED;
208003831d35Sstevel 				if (hsp->hs_board_configured == B_TRUE)
208103831d35Sstevel 					cmn_err(CE_WARN, "%s#%d: ALERT! System"
208203831d35Sstevel 						" now in Inconsistent State."
208303831d35Sstevel 						" Halt!",
208403831d35Sstevel 					    ddi_driver_name(hsc->dip),
208503831d35Sstevel 					    ddi_get_instance(hsc->dip));
208603831d35Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
208703831d35Sstevel 						HPC_FAULT_LED, HPC_LED_ON);
208803831d35Sstevel 			}
208903831d35Sstevel 			hsc->hsc_intr_counter = 0;
209003831d35Sstevel 		}
209103831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
209203831d35Sstevel 	}
209303831d35Sstevel 	hsc->hsc_intr_counter = 0;
209403831d35Sstevel 	/*
209503831d35Sstevel 	 * if interrupt success, rc denotes the PCI device number which
209603831d35Sstevel 	 * generated the ENUM# interrupt.
209703831d35Sstevel 	 */
209803831d35Sstevel 	hsp = hsc_get_slot_info(hsc, rc);
209903831d35Sstevel 	if (hsp == NULL) {
210003831d35Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_enum_intr: no slot info for "
210103831d35Sstevel 			"dev %x", ddi_driver_name(hsc->dip),
210203831d35Sstevel 			ddi_get_instance(hsc->dip), rc);
210303831d35Sstevel 		return (DDI_INTR_CLAIMED);	/* interrupt already cleared */
210403831d35Sstevel 	}
210503831d35Sstevel 	/* if this is Alarm Card and if it is busy, dont process event */
210603831d35Sstevel 	if (hsp->hs_flags & HSC_ALARM_CARD_PRES) {
210703831d35Sstevel 		if (scsb_hsc_ac_op(hsp->hs_hpchandle, hsp->hs_slot_number,
210803831d35Sstevel 						SCSB_HSC_AC_BUSY) == B_TRUE) {
210903831d35Sstevel 			/*
211003831d35Sstevel 			 * Busy means we need to inform (envmond)alarmcard.so
211103831d35Sstevel 			 * that it should save the AC configuration, stop the
211203831d35Sstevel 			 * heartbeat, and shutdown the RSC link.
211303831d35Sstevel 			 */
211403831d35Sstevel 			(void) scsb_hsc_ac_op(hsp->hs_hpchandle,
211503831d35Sstevel 					hsp->hs_slot_number,
211603831d35Sstevel 					SCSB_HSC_AC_REMOVAL_ALERT);
211703831d35Sstevel 			return (DDI_INTR_CLAIMED);
211803831d35Sstevel 		}
211903831d35Sstevel 	}
212003831d35Sstevel 	/*
212103831d35Sstevel 	 * If SCB was swapped out, dont process ENUM#. We put this slot
212203831d35Sstevel 	 * back in reset after SCB is inserted.
212303831d35Sstevel 	 */
212403831d35Sstevel 	if ((hsp->hs_flags & HSC_SCB_HOTSWAPPED) &&
212503831d35Sstevel 			(hsp->hs_slot_state == HPC_SLOT_DISCONNECTED))
212603831d35Sstevel 		return (DDI_INTR_CLAIMED);
212703831d35Sstevel 
2128*07d06da5SSurya Prakki 	(void) hpc_slot_event_notify(hsp->hs_slot_handle,
2129*07d06da5SSurya Prakki 	    HPC_EVENT_PROCESS_ENUM, 0);
213003831d35Sstevel 	return (DDI_INTR_CLAIMED);
213103831d35Sstevel }
213203831d35Sstevel /*
213303831d35Sstevel  * A routine to convert a number (represented as a string) to
213403831d35Sstevel  * the integer value it represents.
213503831d35Sstevel  */
213603831d35Sstevel 
213703831d35Sstevel static int
isdigit(int ch)213803831d35Sstevel isdigit(int ch)
213903831d35Sstevel {
214003831d35Sstevel 	return (ch >= '0' && ch <= '9');
214103831d35Sstevel }
214203831d35Sstevel 
214303831d35Sstevel #define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
214403831d35Sstevel #define	bad(val)	(val == NULL || !isdigit(*val))
214503831d35Sstevel 
214603831d35Sstevel static int
atoi(const char * p)214703831d35Sstevel atoi(const char *p)
214803831d35Sstevel {
214903831d35Sstevel 	int n;
215003831d35Sstevel 	int c, neg = 0;
215103831d35Sstevel 
215203831d35Sstevel 	if (!isdigit(c = *p)) {
215303831d35Sstevel 		while (isspace(c))
215403831d35Sstevel 			c = *++p;
215503831d35Sstevel 		switch (c) {
215603831d35Sstevel 			case '-':
215703831d35Sstevel 				neg++;
215803831d35Sstevel 				/* FALLTHROUGH */
215903831d35Sstevel 			case '+':
216003831d35Sstevel 			c = *++p;
216103831d35Sstevel 		}
216203831d35Sstevel 		if (!isdigit(c))
216303831d35Sstevel 			return (0);
216403831d35Sstevel 	}
216503831d35Sstevel 	for (n = '0' - c; isdigit(c = *++p); ) {
216603831d35Sstevel 		n *= 10; /* two steps to avoid unnecessary overflow */
216703831d35Sstevel 		n += '0' - c; /* accum neg to avoid surprises at MAX */
216803831d35Sstevel 	}
216903831d35Sstevel 	return (neg ? n : -n);
217003831d35Sstevel }
2171