xref: /onnv-gate/usr/src/uts/sun4u/montecarlo/io/hsc.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
5*11311SSurya.Prakki@Sun.COM  * Common Development and Distribution License (the "License").
6*11311SSurya.Prakki@Sun.COM  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
21*11311SSurya.Prakki@Sun.COM 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel /*
281708Sstevel  * MonteCarlo HotSwap Controller functionality
291708Sstevel  */
301708Sstevel 
311708Sstevel #include	<sys/types.h>
321708Sstevel #include	<sys/stropts.h>
331708Sstevel #include	<sys/stream.h>
341708Sstevel #include	<sys/strsun.h>
351708Sstevel #include	<sys/kmem.h>
361708Sstevel #include	<sys/cmn_err.h>
371708Sstevel #include	<sys/errno.h>
381708Sstevel #include	<sys/cpuvar.h>
391708Sstevel #include	<sys/open.h>
401708Sstevel #include	<sys/stat.h>
411708Sstevel #include	<sys/conf.h>
421708Sstevel #include	<sys/ddi.h>
431708Sstevel #include	<sys/sunddi.h>
441708Sstevel #include	<sys/modctl.h>
451708Sstevel #include	<sys/promif.h>
461708Sstevel #include	<sys/hotplug/hpcsvc.h>
471708Sstevel 
481708Sstevel #include	<sys/hscimpl.h>
491708Sstevel #include	<sys/hsc.h>
501708Sstevel 
511708Sstevel #include	<sys/mct_topology.h>
521708Sstevel #include	<sys/scsbioctl.h>
531708Sstevel #include	<sys/scsb.h>
541708Sstevel 
551708Sstevel #define	HOTSWAP_MODE_PROP	"hotswap-mode"
561708Sstevel #define	ALARM_CARD_ON_SLOT	1
571708Sstevel #define	SCSB_HSC_FORCE_REMOVE	1	/* force remove enum intr handler */
581708Sstevel 
591708Sstevel /* TUNABLE PARAMETERS. Some are Debug Only. Please take care when using. */
601708Sstevel 
611708Sstevel /*
621708Sstevel  * Set this flag to 1, to enable full hotswap mode at boot time.
631708Sstevel  * Since HPS is threaded, it is not recommended that we set this flag
641708Sstevel  * to 1 because enabling full hotswap interrupt can invoke the ENUM
651708Sstevel  * event handler accessing the slot data structure which may have not
661708Sstevel  * been initialized in the hotplug framework since the HPS may not yet
671708Sstevel  * have called the slot registration function with the bus nexus.
681708Sstevel  */
691708Sstevel static int	scsb_hsc_enable_fhs = 0;
701708Sstevel 
711708Sstevel /*
721708Sstevel  * Every time  a slot is registered with the hotswap framework, the
731708Sstevel  * framework calls back. This variable keeps a count on how many
741708Sstevel  * callbacks are done.
751708Sstevel  */
761708Sstevel static int scsb_hsc_numReg = 0;
771708Sstevel /*
781708Sstevel  * When this flag is set, the board is taken offline (put in reset) after
791708Sstevel  * a unconfigure operation, in Basic Hotswap mode.
801708Sstevel  */
811708Sstevel static int	scsb_hsc_bhs_slot_reset = 1;
821708Sstevel /*
831708Sstevel  * When this flag is set, we take the board to reset after unconfigure
841708Sstevel  * operation when operating in full hotswap mode.
851708Sstevel  */
861708Sstevel static int	scsb_hsc_fhs_slot_reset = 1;
871708Sstevel /*
881708Sstevel  * Implementation of this counter will work only on Montecarlo since
891708Sstevel  * the ENUM# Interrupt line is not shared with other interrupts.
901708Sstevel  * When the hardware routing changes, then there may be need to remove
911708Sstevel  * or change this functionality.
921708Sstevel  * This functionality is provided so that a bad or non friendly full hotswap
931708Sstevel  * board does not hang the system in full hotswap mode. Atleast the
941708Sstevel  * intent is that! Eventually Solaris kernel will provide similar support
951708Sstevel  * for recovering from a stuck interrupt line. Till then, lets do this.
961708Sstevel  */
971708Sstevel static int	scsb_hsc_max_intr_count = 8;
981708Sstevel /*
991708Sstevel  * Since the hardware does not support enabling/disabling ENUM#, the
1001708Sstevel  * following flag can be used for imitating that behaviour.
1011708Sstevel  * Currently we can set this flag and use the remove op to remove the
1021708Sstevel  * interrupt handler from the system. Care must be taken when using this
1031708Sstevel  * function since trying to remove the interrupt handler when the interrupts
1041708Sstevel  * are pending may hang the system permanently.
1051708Sstevel  * Since the hardware does not support this functionality, we adopt this
1061708Sstevel  * approach for debugs.
1071708Sstevel  */
1081708Sstevel static int	scsb_hsc_enum_switch = 0;
1091708Sstevel 
1101708Sstevel /*
1111708Sstevel  * When the board loses Healthy# at runtime (with the board being configured),
1121708Sstevel  * cPCI specs states that a Reset has to be asserted immediately.
1131708Sstevel  * We dont do this currently, until satellite processor support is given
1141708Sstevel  * and the implications of such a act is fully understood.
1151708Sstevel  * To adopt the cPCI specs recommendation, set this flag to 1.
1161708Sstevel  */
1171708Sstevel static	int	scsb_hsc_healthy_reset = 0;
1181708Sstevel 
1191708Sstevel /*
1201708Sstevel  * According to PCI 2.2 specification, once a board comes out of PCI_RST#,
1211708Sstevel  * it may take upto 2^25 clock cycles to respond to config cycles. For
1221708Sstevel  * montecarlo using a 33MHz cPCI bus, it's around 1.024 s. The variable
1231708Sstevel  * will specify the time in ms to wait before attempting config access.
1241708Sstevel  */
1251708Sstevel static	int scsb_connect_delay = 1025;
1261708Sstevel 
1271708Sstevel /*
1281708Sstevel  * slot map property for MC should be
1291708Sstevel  *
1301708Sstevel  *	hsc-slot-map="/pci@1f,0/pci@1/pci@1","15","2",
1311708Sstevel  *               "/pci@1f,0/pci@1/pci@1","14","3",
1321708Sstevel  *               "/pci@1f,0/pci@1/pci@1","13","4",
1331708Sstevel  *               "/pci@1f,0/pci@1/pci@1","12","5"
1341708Sstevel  *               "/pci@1f,0/pci@1/pci@1","11","6"
1351708Sstevel  *               "/pci@1f,0/pci@1/pci@1","10","7"
1361708Sstevel  *               "/pci@1f,0/pci@1/pci@1","8","8";
1371708Sstevel  *
1381708Sstevel  * slot map property for Tonga should be
1391708Sstevel  *	hsc-slot-map="/pci@1f,0/pci@1/pci@1","8","1"
1401708Sstevel  *		"/pci@1f,0/pci@1/pci@1", "15", "2"
1411708Sstevel  *		"/pci@1f,0/pci@1/pci@1", "14", "4"
1421708Sstevel  *		"/pci@1f,0/pci@1/pci@1", "13", "5"
1431708Sstevel  *
1441708Sstevel  * Please note that the CPU slot number is 3 for Tonga.
1451708Sstevel  */
1461708Sstevel 
1471708Sstevel /*
1481708Sstevel  * Services we require from the SCSB
1491708Sstevel  */
1501708Sstevel extern int	scsb_get_slot_state(void *, int, int *);
1511708Sstevel extern int	scsb_read_bhealthy(scsb_state_t *scsb);
1521708Sstevel extern int	scsb_read_slot_health(scsb_state_t *scsb, int pslotnum);
1531708Sstevel extern int	scsb_connect_slot(void *, int, int);
1541708Sstevel extern int	scsb_disconnect_slot(void *, int, int);
1551708Sstevel 
1561708Sstevel static void	*hsc_state;
1571708Sstevel 
1581708Sstevel static uint_t	hsc_enum_intr(char *);
1591708Sstevel static hsc_slot_t *hsc_get_slot_info(hsc_state_t *, int);
1601708Sstevel static int	scsb_enable_enum(hsc_state_t *);
1611708Sstevel static int	scsb_disable_enum(hsc_state_t *, int);
1621708Sstevel static int	atoi(const char *);
1631708Sstevel static int	isdigit(int);
1641708Sstevel static hsc_slot_t *hsc_find_slot(int);
1651708Sstevel static void	hsc_led_op(hsc_slot_t *, int, hpc_led_t, hpc_led_state_t);
1661708Sstevel static int	hsc_led_state(hsc_slot_t *, int, hpc_led_info_t *);
1671708Sstevel static int	scsb_hsc_disable_slot(hsc_slot_t *);
1681708Sstevel static int	scsb_hsc_enable_slot(hsc_slot_t *);
1691708Sstevel #ifndef	lint
1701708Sstevel static int hsc_clear_all_enum(hsc_state_t *);
1711708Sstevel #endif
1721708Sstevel static int	hsc_slot_register(hsc_state_t *, char *, uint16_t, uint_t,
1731708Sstevel 					boolean_t);
1741708Sstevel static int	hsc_slot_unregister(int);
1751708Sstevel static int	scsb_hsc_init_slot_state(hsc_state_t *, hsc_slot_t *);
1761708Sstevel static int	hsc_slot_autoconnect(hsc_slot_t *);
1771708Sstevel 
1781708Sstevel static hpc_slot_ops_t	*hsc_slotops;
1791708Sstevel static hsc_slot_t	*hsc_slot_list;		/* linked list of slots */
1801708Sstevel 
1811708Sstevel /*
1821708Sstevel  * This mutex protects the following variables:
1831708Sstevel  *	hsc_slot_list
1841708Sstevel  */
1851708Sstevel static kmutex_t		hsc_mutex;
1861708Sstevel 
1871708Sstevel 
1881708Sstevel /* ARGSUSED */
1891708Sstevel static int
hsc_connect(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)1901708Sstevel hsc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
1911708Sstevel {
1921708Sstevel 	hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
1931708Sstevel 	int rc, rstate;
1941708Sstevel 	hsc_state_t	*hsc;
1951708Sstevel 
1961708Sstevel 	DEBUG2("hsc_connect: slot %d, healthy %d", hsp->hs_slot_number,
1971708Sstevel 						hsp->hs_board_healthy);
1981708Sstevel 
1991708Sstevel 	if (!(hsp->hs_flags & (HSC_ENABLED|HSC_SLOT_ENABLED)))
2001708Sstevel 		return (HPC_ERR_FAILED);
2011708Sstevel 	/* if SCB hotswapped, do not allow connect operations */
2021708Sstevel 	if (hsp->hs_flags & HSC_SCB_HOTSWAPPED)
2031708Sstevel 		return (HPC_ERR_FAILED);
2041708Sstevel 	/*
2051708Sstevel 	 * if previous occupant stayed configured, do not allow another
2061708Sstevel 	 * occupant to be connected.
2071708Sstevel 	 * This behaviour is an indication that the slot state
2081708Sstevel 	 * is not clean.
2091708Sstevel 	 */
2101708Sstevel 	if (hsp->hs_flags & HSC_SLOT_BAD_STATE) {
2111708Sstevel 		/*
2121708Sstevel 		 * In the current implementation, we turn both fault
2131708Sstevel 		 * and active LEDs to ON state in this situation.
2141708Sstevel 		 */
2151708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
2161708Sstevel 							HPC_LED_ON);
2171708Sstevel 		return (HPC_ERR_FAILED);
2181708Sstevel 	}
2191708Sstevel 	/*
2201708Sstevel 	 * Get the actual status from the i2c bus
2211708Sstevel 	 */
2221708Sstevel 	rc = scsb_get_slot_state(hsp->hs_hpchandle, hsp->hs_slot_number,
2231708Sstevel 								&rstate);
2241708Sstevel 	if (rc != DDI_SUCCESS)
2251708Sstevel 		return (HPC_ERR_FAILED);
2261708Sstevel 
2271708Sstevel 	hsp->hs_slot_state = rstate;
2281708Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
2291708Sstevel #ifdef DEBUG
2301708Sstevel 		cmn_err(CE_CONT,
2311708Sstevel 			"?hsc_connect: slot %d is empty\n",
2321708Sstevel 			hsp->hs_slot_number);
2331708Sstevel #endif
2341708Sstevel 		return (HPC_ERR_FAILED);
2351708Sstevel 	}
2361708Sstevel 
2371708Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_CONNECTED)
2381708Sstevel 		return (HPC_SUCCESS);
2391708Sstevel 
2401708Sstevel 	rc = HPC_SUCCESS;
2411708Sstevel 	/*
2421708Sstevel 	 * call scsb to connect the slot. This also makes sure board is healthy
2431708Sstevel 	 */
2441708Sstevel 	if (scsb_connect_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
2451708Sstevel 				hsp->hs_board_healthy) != DDI_SUCCESS) {
2461708Sstevel 		DEBUG1("hsc_connect: slot %d connection failed",
2471708Sstevel 				hsp->hs_slot_number);
2481708Sstevel 		rc = HPC_ERR_FAILED;
2491708Sstevel 	} else {
2501708Sstevel 		if (hsp->hs_slot_state != HPC_SLOT_CONNECTED) {
2511708Sstevel 			if (hsp->hs_board_healthy == B_FALSE) {
2521708Sstevel 				cmn_err(CE_NOTE, "HEALTHY# not asserted on "
2531708Sstevel 					" slot %d", hsp->hs_slot_number);
2541708Sstevel 				return (HPC_ERR_FAILED);
2551708Sstevel 			}
2561708Sstevel 			hsc = hsp->hsc;
2571708Sstevel 			hsc->hsp_last = hsp;
2581708Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
2591708Sstevel 				hsp->hs_slot_number, SCSB_UNRESET_SLOT) != 0) {
2601708Sstevel 
2611708Sstevel 				return (HPC_ERR_FAILED);
2621708Sstevel 			}
2631708Sstevel 			/*
2641708Sstevel 			 * Unresetting a board may have caused an interrupt
2651708Sstevel 			 * burst in case of non friendly boards. So it is
2661708Sstevel 			 * important to make sure that the ISR has not
2671708Sstevel 			 * put this board back to disconnect state.
2681708Sstevel 			 */
2691708Sstevel 			delay(1);
2701708Sstevel 			if (hsp->hs_flags & HSC_ENUM_FAILED) {
2711708Sstevel 				hsp->hs_flags &= ~HSC_ENUM_FAILED;
2721708Sstevel 				return (HPC_ERR_FAILED);
2731708Sstevel 			}
2741708Sstevel 			DEBUG1("hsc_connect: slot %d connected",
2751708Sstevel 						hsp->hs_slot_number);
2761708Sstevel 			rc = HPC_SUCCESS;
2771708Sstevel 			hsp->hs_slot_state = HPC_SLOT_CONNECTED;
2781708Sstevel 			(void) hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
2791708Sstevel 				HPC_FAULT_LED, HPC_LED_OFF);
2801708Sstevel 		}
2811708Sstevel 	}
2821708Sstevel 
2831708Sstevel 	/*
2841708Sstevel 	 * PCI 2.2 specs recommend that the probe software wait
2851708Sstevel 	 * for upto 2^25 PCI clock cycles after deassertion of
2861708Sstevel 	 * PCI_RST# before the board is able to respond to config
2871708Sstevel 	 * cycles. So, before we return, we wait for ~1 sec.
2881708Sstevel 	 */
2891708Sstevel 	delay(drv_usectohz(scsb_connect_delay * 1000));
2901708Sstevel 	return (rc);
2911708Sstevel }
2921708Sstevel 
2931708Sstevel 
2941708Sstevel /* ARGSUSED */
2951708Sstevel static int
hsc_disconnect(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)2961708Sstevel hsc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
2971708Sstevel {
2981708Sstevel 	hsc_slot_t		*hsp = (hsc_slot_t *)ops_arg;
2991708Sstevel 	hsc_state_t		*hsc;
3001708Sstevel #ifdef	DEBUG
3011708Sstevel 	static const char	func[] = "hsc_disconnect";
3021708Sstevel #endif
3031708Sstevel 
3041708Sstevel 	DEBUG1("hsc_disconnect: slot %d", hsp->hs_slot_number);
3051708Sstevel 
3061708Sstevel 	if (hsp->hs_board_configured) {
3071708Sstevel #ifdef	DEBUG
3081708Sstevel 		cmn_err(CE_NOTE,
3091708Sstevel 			"%s: cannot disconnect configured board in slot %d",
3101708Sstevel 			func, hsp->hs_slot_number);
3111708Sstevel #endif
3121708Sstevel 		return (HPC_ERR_FAILED);
3131708Sstevel 	}
3141708Sstevel 
3151708Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
3161708Sstevel #ifdef	DEBUG
3171708Sstevel 		cmn_err(CE_NOTE, "%s: slot %d is empty",
3181708Sstevel 			func, hsp->hs_slot_number);
3191708Sstevel #endif
3201708Sstevel 		return (HPC_SUCCESS);
3211708Sstevel 	}
3221708Sstevel 
3231708Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
3241708Sstevel 		/*
3251708Sstevel 		 * if already disconnected, just return success
3261708Sstevel 		 * Duplicate disconnect messages should not be failed!
3271708Sstevel 		 */
3281708Sstevel 		return (HPC_SUCCESS);
3291708Sstevel 	}
3301708Sstevel 	/* if SCB hotswapped, do not allow disconnect operations */
3311708Sstevel 	if (hsp->hs_flags & HSC_SCB_HOTSWAPPED)
3321708Sstevel 		return (HPC_ERR_FAILED);
3331708Sstevel 
3341708Sstevel 	/* call scsb to disconnect the slot */
3351708Sstevel 	if (scsb_disconnect_slot(hsp->hs_hpchandle, B_TRUE, hsp->hs_slot_number)
3361708Sstevel 			!= DDI_SUCCESS)
3371708Sstevel 		return (HPC_ERR_FAILED);
3381708Sstevel 	hsc = hsp->hsc;
3391708Sstevel 	if (hsc->hsp_last == hsp)
3401708Sstevel 		hsc->hsp_last = NULL;
3411708Sstevel 
3421708Sstevel 	return (HPC_SUCCESS);
3431708Sstevel }
3441708Sstevel 
3451708Sstevel 
3461708Sstevel /*
3471708Sstevel  * In the cPCI world, this operation is not applicable.
3481708Sstevel  * However, we use this function to enable full hotswap mode in debug mode.
3491708Sstevel  */
3501708Sstevel /* ARGSUSED */
3511708Sstevel static int
hsc_insert(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)3521708Sstevel hsc_insert(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
3531708Sstevel {
3541708Sstevel 	hsc_slot_t		*hsp = (hsc_slot_t *)ops_arg;
3551708Sstevel 
3561708Sstevel 	if (scsb_hsc_enum_switch &&
3571708Sstevel 			(scsb_enable_enum(hsp->hsc) == DDI_SUCCESS)) {
3581708Sstevel 		return (HPC_SUCCESS);
3591708Sstevel 	}
3601708Sstevel 	return (HPC_ERR_NOTSUPPORTED);
3611708Sstevel }
3621708Sstevel 
3631708Sstevel 
3641708Sstevel /*
3651708Sstevel  * In the cPCI world, this operation is not applicable.
3661708Sstevel  * However, we use this function to disable full hotswap mode in debug mode.
3671708Sstevel  */
3681708Sstevel /* ARGSUSED */
3691708Sstevel static int
hsc_remove(caddr_t ops_arg,hpc_slot_t slot_hdl,void * data,uint_t flags)3701708Sstevel hsc_remove(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags)
3711708Sstevel {
3721708Sstevel 	hsc_slot_t		*hsp = (hsc_slot_t *)ops_arg;
3731708Sstevel 
3741708Sstevel 	if (scsb_hsc_enum_switch &&
3751708Sstevel 			(scsb_disable_enum(hsp->hsc, SCSB_HSC_FORCE_REMOVE)
3761708Sstevel 					== DDI_SUCCESS)) {
3771708Sstevel 		hsp->hs_flags &= ~HSC_ENUM_FAILED;
3781708Sstevel 		return (HPC_SUCCESS);
3791708Sstevel 	}
3801708Sstevel 	return (HPC_ERR_NOTSUPPORTED);
3811708Sstevel }
3821708Sstevel 
3831708Sstevel static void
hsc_led_op(hsc_slot_t * hsp,int cmd,hpc_led_t led,hpc_led_state_t led_state)3841708Sstevel hsc_led_op(hsc_slot_t *hsp, int cmd, hpc_led_t led, hpc_led_state_t led_state)
3851708Sstevel {
3861708Sstevel 	hpc_led_info_t	ledinfo;
3871708Sstevel 
3881708Sstevel 	ledinfo.led = led;
3891708Sstevel 	ledinfo.state = led_state;
3901708Sstevel 	(void) hsc_led_state(hsp, cmd, &ledinfo);
3911708Sstevel }
3921708Sstevel 
3931708Sstevel static int
hsc_led_state(hsc_slot_t * hsp,int cmd,hpc_led_info_t * hlip)3941708Sstevel hsc_led_state(hsc_slot_t *hsp, int cmd, hpc_led_info_t *hlip)
3951708Sstevel {
3961708Sstevel 	hpc_led_state_t	*hlsp;
3971708Sstevel 	scsb_uinfo_t	sunit;
3981708Sstevel 	int		res;
3991708Sstevel 
4001708Sstevel 	DEBUG3("hsc_led_state: slot %d, led %x, state %x",
4011708Sstevel 		hsp->hs_slot_number, hlip->led, hlip->state);
4021708Sstevel 
4031708Sstevel 	sunit.unit_type = SLOT;
4041708Sstevel 	sunit.unit_number = hsp->hs_slot_number;
4051708Sstevel 	/*
4061708Sstevel 	 * We ignore operations on LEDs that we don't support
4071708Sstevel 	 */
4081708Sstevel 	switch (hlip->led) {
4091708Sstevel 		case HPC_FAULT_LED:
4101708Sstevel 			sunit.led_type = NOK;
4111708Sstevel 			hlsp = &hsp->hs_fault_led_state;
4121708Sstevel 			break;
4131708Sstevel 		case HPC_ACTIVE_LED:
4141708Sstevel 			sunit.led_type = OK;
4151708Sstevel 			hlsp = &hsp->hs_active_led_state;
4161708Sstevel 			break;
4171708Sstevel 		default:
4181708Sstevel 			return (HPC_ERR_NOTSUPPORTED);
4191708Sstevel 	}
4201708Sstevel 
4211708Sstevel 	switch (hlip->state) {
4221708Sstevel 		case HPC_LED_BLINK:
4231708Sstevel 			sunit.unit_state = BLINK;
4241708Sstevel 			if (hlip->led != HPC_ACTIVE_LED)
4251708Sstevel 				return (HPC_ERR_NOTSUPPORTED);
4261708Sstevel 			break;
4271708Sstevel 		case HPC_LED_ON:
4281708Sstevel 			sunit.unit_state = ON;
4291708Sstevel 			break;
4301708Sstevel 		case HPC_LED_OFF:
4311708Sstevel 			sunit.unit_state = OFF;
4321708Sstevel 			break;
4331708Sstevel 		default:
4341708Sstevel 			break;
4351708Sstevel 	}
4361708Sstevel 
4371708Sstevel 	switch (cmd) {
4381708Sstevel 	case HPC_CTRL_SET_LED_STATE:
4391708Sstevel 		res = scsb_led_set(hsp->hs_hpchandle, &sunit, sunit.led_type);
4401708Sstevel 		if (res != 0)
4411708Sstevel 			return (HPC_ERR_FAILED);
4421708Sstevel 		*hlsp = (hpc_led_state_t)sunit.unit_state;
4431708Sstevel 		break;
4441708Sstevel 
4451708Sstevel 	case HPC_CTRL_GET_LED_STATE:
4461708Sstevel 		res = scsb_led_get(hsp->hs_hpchandle, &sunit, sunit.led_type);
4471708Sstevel 		if (res)
4481708Sstevel 			return (HPC_ERR_FAILED);
4491708Sstevel 		/* hlip->state = sunit.unit_state; */
4501708Sstevel 		break;
4511708Sstevel 
4521708Sstevel 	default:
4531708Sstevel 		return (HPC_ERR_INVALID);
4541708Sstevel 	}
4551708Sstevel 
4561708Sstevel 	return (HPC_SUCCESS);
4571708Sstevel 
4581708Sstevel }
4591708Sstevel 
4601708Sstevel 
4611708Sstevel static int
hsc_get_slot_state(hsc_slot_t * hsp,hpc_slot_state_t * hssp)4621708Sstevel hsc_get_slot_state(hsc_slot_t *hsp, hpc_slot_state_t *hssp)
4631708Sstevel {
4641708Sstevel 	int rstate = 0;
4651708Sstevel 	int rc;
4661708Sstevel #ifdef	DEBUG
4671708Sstevel 	int orstate;	/* original rstate */
4681708Sstevel #endif
4691708Sstevel 
4701708Sstevel 	DEBUG1("hsc_get_slot_state: slot %d", hsp->hs_slot_number);
4711708Sstevel 	rc = scsb_get_slot_state(hsp->hs_hpchandle, hsp->hs_slot_number,
4721708Sstevel 								&rstate);
4731708Sstevel 	if (rc != DDI_SUCCESS)
4741708Sstevel 		return (HPC_ERR_FAILED);
4751708Sstevel #ifdef	DEBUG
4761708Sstevel 	orstate = hsp->hs_slot_state;
4771708Sstevel #endif
4781708Sstevel 	hsp->hs_slot_state = rstate;
4791708Sstevel 	switch (hsp->hs_slot_state) {
4801708Sstevel 	case HPC_SLOT_EMPTY:
4811708Sstevel 		DEBUG0("empty");
4821708Sstevel 		break;
4831708Sstevel 	case HPC_SLOT_CONNECTED:
4841708Sstevel 		DEBUG0("connected");
4851708Sstevel 		break;
4861708Sstevel 	case HPC_SLOT_DISCONNECTED:
4871708Sstevel 		DEBUG0("disconnected");
4881708Sstevel 		break;
4891708Sstevel 	}
4901708Sstevel 
4911708Sstevel 	*hssp = hsp->hs_slot_state;
4921708Sstevel 
4931708Sstevel 	/* doing get-state above may have caused a freeze operation */
4941708Sstevel 	if ((hsp->hs_flags & HSC_SCB_HOTSWAPPED) &&
4951708Sstevel 			(rstate == HPC_SLOT_DISCONNECTED)) {
4961708Sstevel 		/* freeze puts disconnected boards to connected state */
4971708Sstevel 		*hssp = HPC_SLOT_CONNECTED;
4981708Sstevel #if 0
4991708Sstevel 		/* in FHS, deassertion of reset may have configured the board */
5001708Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
5011708Sstevel 			hsp->hs_slot_state = *hssp;
5021708Sstevel 		}
5031708Sstevel #endif
5041708Sstevel 	}
5051708Sstevel #ifdef	DEBUG
5061708Sstevel 	/* a SCB hotswap may have forced a state change on the receptacle */
5071708Sstevel 	if (orstate != *hssp) {
5081708Sstevel 		cmn_err(CE_NOTE, "hsc_get_state: slot%d state change due"
5091708Sstevel 			" to SCB hotswap!", hsp->hs_slot_number);
5101708Sstevel 	}
5111708Sstevel #endif
5121708Sstevel 	return (HPC_SUCCESS);
5131708Sstevel }
5141708Sstevel 
5151708Sstevel 
5161708Sstevel static int
hsc_set_config_state(hsc_slot_t * hsp,int cmd)5171708Sstevel hsc_set_config_state(hsc_slot_t *hsp, int cmd)
5181708Sstevel {
5191708Sstevel 	hsc_state_t	*hsc = hsp->hsc;
5201708Sstevel 
5211708Sstevel 	DEBUG1("hsc_set_config_state: slot %d", hsp->hs_slot_number);
5221708Sstevel 
5231708Sstevel 	switch (cmd) {
5241708Sstevel 	case HPC_CTRL_DEV_CONFIGURED:
5251708Sstevel 		/*
5261708Sstevel 		 * Closing of the Ejector switch in configured/busy state can
5271708Sstevel 		 * cause duplicate CONFIGURED messages to come down.
5281708Sstevel 		 * Make sure our LED states are fine.
5291708Sstevel 		 */
5301708Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
5311708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
5321708Sstevel 								HPC_LED_ON);
5331708Sstevel 			break;
5341708Sstevel 		}
5351708Sstevel 		hsp->hs_board_configured = B_TRUE;
5361708Sstevel 		hsp->hs_board_configuring = B_FALSE;
5371708Sstevel 		if ((hsc->state & HSC_ATTACHED) == HSC_ATTACHED &&
5381708Sstevel 			hsp->hs_flags & HSC_ALARM_CARD_PRES)
539*11311SSurya.Prakki@Sun.COM 			(void) scsb_hsc_ac_op(hsp->hs_hpchandle,
5401708Sstevel 				hsp->hs_slot_number, SCSB_HSC_AC_CONFIGURED);
5411708Sstevel 		/* LED must be OFF on the occupant. */
5421708Sstevel 		(void) hpc_slot_event_notify(hsp->hs_slot_handle,
5431708Sstevel 					HPC_EVENT_SLOT_BLUE_LED_OFF, 0);
5441708Sstevel 		if (hsp->hs_flags & HSC_AUTOCFG)
5451708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
5461708Sstevel 					HPC_EVENT_ENABLE_ENUM, 0);
5471708Sstevel 		else
5481708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
5491708Sstevel 					HPC_EVENT_DISABLE_ENUM, 0);
5501708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
5511708Sstevel 								HPC_LED_ON);
5521708Sstevel 		if (hsc->hsp_last == hsp)
5531708Sstevel 			hsc->hsp_last = NULL;
5541708Sstevel 		break;
5551708Sstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
5561708Sstevel 		hsp->hs_board_configured = B_FALSE;
5571708Sstevel 		hsp->hs_board_unconfiguring = B_FALSE;
5581708Sstevel 		hsp->hs_flags &= ~HSC_SLOT_BAD_STATE;
5591708Sstevel 		if (hsp->hs_flags & HSC_ALARM_CARD_PRES)
560*11311SSurya.Prakki@Sun.COM 			(void) scsb_hsc_ac_op(hsp->hs_hpchandle,
5611708Sstevel 				hsp->hs_slot_number, SCSB_HSC_AC_UNCONFIGURED);
5621708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
5631708Sstevel 							HPC_LED_BLINK);
5641708Sstevel 		if (((hsc->state & HSC_ENUM_ENABLED) &&
5651708Sstevel 			scsb_hsc_fhs_slot_reset) ||
5661708Sstevel 		(((hsc->state & HSC_ENUM_ENABLED) != HSC_ENUM_ENABLED) &&
5671708Sstevel 				scsb_hsc_bhs_slot_reset) ||
5681708Sstevel 				((hsp->hs_flags & HSC_AUTOCFG) !=
5691708Sstevel 					HSC_AUTOCFG)) {
5701708Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
5711708Sstevel 				hsp->hs_slot_number, SCSB_RESET_SLOT) == 0) {
5721708Sstevel 
5731708Sstevel 				hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
5741708Sstevel 				hsp->hs_board_healthy = B_FALSE;
5751708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
5761708Sstevel 					HPC_FAULT_LED, HPC_LED_ON);
5771708Sstevel 			}
5781708Sstevel 		}
5791708Sstevel 		break;
5801708Sstevel 	case HPC_CTRL_DEV_CONFIG_FAILURE:
5811708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
5821708Sstevel 							HPC_LED_BLINK);
5831708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
5841708Sstevel 				HPC_FAULT_LED, HPC_LED_ON);
5851708Sstevel 		break;
5861708Sstevel 	case HPC_CTRL_DEV_UNCONFIG_FAILURE:
5871708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
5881708Sstevel 							HPC_LED_ON);
5891708Sstevel 		break;
5901708Sstevel 	case HPC_CTRL_DEV_CONFIG_START:
5911708Sstevel 	case HPC_CTRL_DEV_UNCONFIG_START:
5921708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
5931708Sstevel 					HPC_LED_OFF);
5941708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
5951708Sstevel 					HPC_LED_BLINK);
5961708Sstevel 		break;
5971708Sstevel 	default:
5981708Sstevel 		return (HPC_ERR_INVALID);
5991708Sstevel 	}
6001708Sstevel 
6011708Sstevel 	if (cmd != HPC_CTRL_DEV_CONFIG_START &&
6021708Sstevel 		cmd != HPC_CTRL_DEV_UNCONFIG_START &&
6031708Sstevel 		hsc->regDone == B_FALSE &&
6041708Sstevel 			scsb_hsc_numReg < hsc->n_registered_occupants) {
6051708Sstevel 		scsb_hsc_numReg++;
6061708Sstevel 
6071708Sstevel 		/*
6081708Sstevel 		 * If the callback is invoked for all registered slots,
6091708Sstevel 		 * enable ENUM.
6101708Sstevel 		 */
6111708Sstevel 		if (((hsc->state & HSC_ATTACHED) == HSC_ATTACHED) &&
6121708Sstevel 			(scsb_hsc_numReg == hsc->n_registered_occupants)) {
6131708Sstevel 			hsc->regDone = B_TRUE;
6141708Sstevel 			if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL) {
6151708Sstevel #ifdef DEBUG
6161708Sstevel 				cmn_err(CE_CONT, "%s%d: Enabling full hotswap"
6171708Sstevel 					":%d non-empty slots\n",
6181708Sstevel 					ddi_driver_name(hsc->dip),
6191708Sstevel 					ddi_get_instance(hsc->dip),
6201708Sstevel 					hsc->n_registered_occupants);
6211708Sstevel #endif
6221708Sstevel 				if (scsb_enable_enum(hsc) != DDI_SUCCESS) {
6231708Sstevel 					cmn_err(CE_WARN, "%s#%d: Cannot enable "
6241708Sstevel 						"Full Hotswap",
6251708Sstevel 						ddi_driver_name(hsc->dip),
6261708Sstevel 						ddi_get_instance(hsc->dip));
6271708Sstevel 
6281708Sstevel 					return (HPC_ERR_FAILED);
6291708Sstevel 				}
6301708Sstevel 			}
6311708Sstevel 		}
6321708Sstevel 	}
6331708Sstevel 
6341708Sstevel 	return (HPC_SUCCESS);
6351708Sstevel }
6361708Sstevel 
6371708Sstevel 
6381708Sstevel /*ARGSUSED*/
6391708Sstevel static int
hsc_get_board_type(hsc_slot_t * hsp,hpc_board_type_t * hbtp)6401708Sstevel hsc_get_board_type(hsc_slot_t *hsp, hpc_board_type_t *hbtp)
6411708Sstevel {
6421708Sstevel 	*hbtp = hsp->hs_board_type;
6431708Sstevel 	return (HPC_SUCCESS);
6441708Sstevel }
6451708Sstevel 
6461708Sstevel 
6471708Sstevel /* ARGSUSED */
6481708Sstevel static int
hsc_autoconfig(hsc_slot_t * hsp,int cmd)6491708Sstevel hsc_autoconfig(hsc_slot_t *hsp, int cmd)
6501708Sstevel {
6511708Sstevel 	int res = HPC_SUCCESS, enum_disable = B_TRUE, i;
6521708Sstevel 	char slotautocfg_prop[18];
6531708Sstevel 	hsc_state_t *hsc;
6541708Sstevel 
6551708Sstevel 	DEBUG1("hsc_autoconfig: slot %d", hsp->hs_slot_number);
656*11311SSurya.Prakki@Sun.COM 	(void) sprintf(slotautocfg_prop, "slot%d-autoconfig",
657*11311SSurya.Prakki@Sun.COM 	    hsp->hs_slot_number);
6581708Sstevel 
6591708Sstevel 	if (cmd == HPC_CTRL_ENABLE_AUTOCFG) {
6601708Sstevel 		hsp->hs_flags |= HSC_AUTOCFG;
661*11311SSurya.Prakki@Sun.COM 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
6621708Sstevel 				slotautocfg_prop, "enabled");
6631708Sstevel 		if ((res = scsb_enable_enum(hsp->hsc)) == DDI_SUCCESS) {
6641708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
6651708Sstevel 					HPC_EVENT_ENABLE_ENUM, 0);
6661708Sstevel 		}
6671708Sstevel 	} else {
668*11311SSurya.Prakki@Sun.COM 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
669*11311SSurya.Prakki@Sun.COM 		    slotautocfg_prop, "disabled");
6701708Sstevel 		hsp->hs_flags &= ~HSC_AUTOCFG;
6711708Sstevel 		hsc = hsp->hsc;
6721708Sstevel 		if (hsc->state & HSC_ATTACHED) {
6731708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
6741708Sstevel 						HPC_EVENT_DISABLE_ENUM, 0);
6751708Sstevel 			for (i = 0; i < hsc->slot_table_size; i++) {
6761708Sstevel 				hsc_slot_t	*thsp;
6771708Sstevel 				int slotnum;
6781708Sstevel 
6791708Sstevel 				slotnum = hsc->slot_table_prop[i].pslotnum;
6801708Sstevel 				thsp = hsc_find_slot(slotnum);
6811708Sstevel 				if (thsp == NULL) {
6821708Sstevel 					cmn_err(CE_WARN, "%s#%d: hsc_autocfg:"
6831708Sstevel 						"No Slot Info for slot %d",
6841708Sstevel 						ddi_driver_name(hsc->dip),
6851708Sstevel 						ddi_get_instance(hsc->dip),
6861708Sstevel 						slotnum);
6871708Sstevel 					continue;
6881708Sstevel 				}
6891708Sstevel 				if (thsp->hs_flags & HSC_AUTOCFG) {
6901708Sstevel 					enum_disable = B_FALSE;
6911708Sstevel 					break;
6921708Sstevel 				}
6931708Sstevel 			}
6941708Sstevel 			if (enum_disable == B_TRUE)
695*11311SSurya.Prakki@Sun.COM 				(void) scsb_disable_enum(hsc,
696*11311SSurya.Prakki@Sun.COM 				    SCSB_HSC_FORCE_REMOVE);
6971708Sstevel 		}
6981708Sstevel 	}
6991708Sstevel 	return (res);
7001708Sstevel }
7011708Sstevel 
7021708Sstevel 
7031708Sstevel /*
7041708Sstevel  * This function is invoked to enable/disable a slot
7051708Sstevel  */
7061708Sstevel /* ARGSUSED */
7071708Sstevel #ifndef	lint
7081708Sstevel static int
hsc_slot_enable(hsc_slot_t * hsp,boolean_t enabled)7091708Sstevel hsc_slot_enable(hsc_slot_t *hsp, boolean_t enabled)
7101708Sstevel {
7111708Sstevel 	scsb_uinfo_t	sunit;
7121708Sstevel 	int		res;
7131708Sstevel 
7141708Sstevel 	DEBUG1("hsc_slot_enable: slot %d", hsp->hs_slot_number);
7151708Sstevel 
7161708Sstevel 	sunit.unit_type = SLOT;
7171708Sstevel 	sunit.unit_number = hsp->hs_slot_number;
7181708Sstevel 	if (enabled)
7191708Sstevel 		sunit.unit_state = ON;
7201708Sstevel 	else
7211708Sstevel 		sunit.unit_state = OFF;
7221708Sstevel 
7231708Sstevel 	res = scsb_reset_unit(hsp->hs_hpchandle, &sunit);
7241708Sstevel 	if (res == 0)
7251708Sstevel 		return (HPC_SUCCESS);
7261708Sstevel 	else if (res == EINVAL)
7271708Sstevel 		return (HPC_ERR_INVALID);
7281708Sstevel 	else
7291708Sstevel 		return (HPC_ERR_FAILED);
7301708Sstevel }
7311708Sstevel #endif
7321708Sstevel 
7331708Sstevel 
7341708Sstevel /*ARGSUSED*/
7351708Sstevel static int
hsc_control(caddr_t ops_arg,hpc_slot_t slot_hdl,int request,caddr_t arg)7361708Sstevel hsc_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request, caddr_t arg)
7371708Sstevel {
7381708Sstevel 	hsc_slot_t *hsp = (hsc_slot_t *)ops_arg;
7391708Sstevel 	int rc = HPC_SUCCESS;
7401708Sstevel 
7411708Sstevel 	DEBUG2("hsc_control: slot %d, op=%x\n", hsp->hs_slot_number, request);
7421708Sstevel 
7431708Sstevel 	switch (request) {
7441708Sstevel 	case HPC_CTRL_GET_LED_STATE:
7451708Sstevel 		return (hsc_led_state(hsp,
7461708Sstevel 			HPC_CTRL_GET_LED_STATE, (hpc_led_info_t *)arg));
7471708Sstevel 
7481708Sstevel 	case HPC_CTRL_SET_LED_STATE:
7491708Sstevel 		return (hsc_led_state(hsp,
7501708Sstevel 			HPC_CTRL_SET_LED_STATE, (hpc_led_info_t *)arg));
7511708Sstevel 
7521708Sstevel 	case HPC_CTRL_GET_SLOT_STATE:
7531708Sstevel 		return (hsc_get_slot_state(hsp, (hpc_slot_state_t *)arg));
7541708Sstevel 
7551708Sstevel 	case HPC_CTRL_DEV_CONFIGURED:
7561708Sstevel 		return (hsc_set_config_state(hsp, HPC_CTRL_DEV_CONFIGURED));
7571708Sstevel 
7581708Sstevel 	case HPC_CTRL_DEV_UNCONFIGURED:
7591708Sstevel 		return (hsc_set_config_state(hsp, HPC_CTRL_DEV_UNCONFIGURED));
7601708Sstevel 
7611708Sstevel 	case HPC_CTRL_DEV_CONFIG_FAILURE:
7621708Sstevel 		return (hsc_set_config_state(hsp, HPC_CTRL_DEV_CONFIG_FAILURE));
7631708Sstevel 
7641708Sstevel 	case HPC_CTRL_DEV_UNCONFIG_FAILURE:
7651708Sstevel 		return (hsc_set_config_state(hsp,
7661708Sstevel 				HPC_CTRL_DEV_UNCONFIG_FAILURE));
7671708Sstevel 
7681708Sstevel 	case HPC_CTRL_DEV_CONFIG_START:
7691708Sstevel 	case HPC_CTRL_DEV_UNCONFIG_START:
7701708Sstevel 		return (hsc_set_config_state(hsp, request));
7711708Sstevel 
7721708Sstevel 	case HPC_CTRL_GET_BOARD_TYPE:
7731708Sstevel 		return (hsc_get_board_type(hsp, (hpc_board_type_t *)arg));
7741708Sstevel 
7751708Sstevel 	case HPC_CTRL_DISABLE_AUTOCFG:
7761708Sstevel 		return (hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG));
7771708Sstevel 
7781708Sstevel 	case HPC_CTRL_ENABLE_AUTOCFG:
7791708Sstevel 		return (hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG));
7801708Sstevel 
7811708Sstevel 	case HPC_CTRL_DISABLE_SLOT:
7821708Sstevel 		/*
7831708Sstevel 		 * No hardware support for disabling the slot.
7841708Sstevel 		 * Just imitate a disable_autoconfig operation for now
7851708Sstevel 		 */
7861708Sstevel 		if (hsp->hs_board_configured == B_TRUE)
7871708Sstevel 			return (HPC_ERR_FAILED);
7881708Sstevel 		if (scsb_hsc_disable_slot(hsp) != DDI_SUCCESS)
7891708Sstevel 			rc = HPC_ERR_FAILED;
7901708Sstevel 		return (rc);
7911708Sstevel 
7921708Sstevel 	case HPC_CTRL_ENABLE_SLOT:
7931708Sstevel 		if (scsb_hsc_enable_slot(hsp) != DDI_SUCCESS)
7941708Sstevel 			rc = HPC_ERR_FAILED;
7951708Sstevel 		return (rc);
7961708Sstevel 
7971708Sstevel 	case HPC_CTRL_ENABLE_ENUM:
7981708Sstevel 		return (scsb_enable_enum(hsp->hsc));
7991708Sstevel 
8001708Sstevel 	case HPC_CTRL_DISABLE_ENUM:
8011708Sstevel 		return (scsb_disable_enum(hsp->hsc, 0));
8021708Sstevel 
8031708Sstevel 	default:
8041708Sstevel 		return (HPC_ERR_INVALID);
8051708Sstevel 	}
8061708Sstevel }
8071708Sstevel 
8081708Sstevel static int
scsb_hsc_disable_slot(hsc_slot_t * hsp)8091708Sstevel scsb_hsc_disable_slot(hsc_slot_t *hsp)
8101708Sstevel {
8111708Sstevel 	int rc;
8121708Sstevel 	char slot_disable_prop[18];
8131708Sstevel 
8141708Sstevel 	DEBUG1("hsc_disable_slot: slot %d", hsp->hs_slot_number);
815*11311SSurya.Prakki@Sun.COM 	(void) sprintf(slot_disable_prop, "slot%d-status", hsp->hs_slot_number);
8161708Sstevel 
8171708Sstevel 	rc = scsb_reset_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
8181708Sstevel 					SCSB_RESET_SLOT);
8191708Sstevel 	if (rc == DDI_SUCCESS) {
820*11311SSurya.Prakki@Sun.COM 		(void) hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG);
8211708Sstevel 		hsp->hs_flags &= ~HSC_SLOT_ENABLED;
822*11311SSurya.Prakki@Sun.COM 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsp->hsc->dip,
823*11311SSurya.Prakki@Sun.COM 		    slot_disable_prop, "disabled");
8241708Sstevel 	} else
8251708Sstevel 		rc = DDI_FAILURE;
8261708Sstevel 	return (rc);
8271708Sstevel }
8281708Sstevel 
8291708Sstevel static int
scsb_hsc_enable_slot(hsc_slot_t * hsp)8301708Sstevel scsb_hsc_enable_slot(hsc_slot_t *hsp)
8311708Sstevel {
8321708Sstevel 	int rc;
8331708Sstevel 	char slot_disable_prop[18];
8341708Sstevel 
8351708Sstevel 	DEBUG1("hsc_disable_slot: slot %d", hsp->hs_slot_number);
836*11311SSurya.Prakki@Sun.COM 	(void) sprintf(slot_disable_prop, "slot%d-status", hsp->hs_slot_number);
8371708Sstevel 
8381708Sstevel 	rc = scsb_reset_slot(hsp->hs_hpchandle, hsp->hs_slot_number,
8391708Sstevel 					SCSB_UNRESET_SLOT);
8401708Sstevel 	if (rc == DDI_SUCCESS) {
841*11311SSurya.Prakki@Sun.COM 		(void) hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG);
8421708Sstevel 		hsp->hs_flags |= HSC_SLOT_ENABLED;
843*11311SSurya.Prakki@Sun.COM 		(void) ddi_prop_remove(DDI_DEV_T_NONE, hsp->hsc->dip,
844*11311SSurya.Prakki@Sun.COM 		    slot_disable_prop);
8451708Sstevel 	} else
8461708Sstevel 		rc = HPC_ERR_FAILED;
8471708Sstevel 	return (rc);
8481708Sstevel }
8491708Sstevel 
8501708Sstevel #define	NEW(type)	(type *) kmem_zalloc(sizeof (type), KM_SLEEP)
8511708Sstevel 
8521708Sstevel static hsc_slot_t *
hsc_alloc_slot(uint16_t device_number,int slot_number,boolean_t board_in_slot)8531708Sstevel hsc_alloc_slot(
8541708Sstevel 		uint16_t	device_number,
8551708Sstevel 		int		slot_number,
8561708Sstevel 		boolean_t	board_in_slot)
8571708Sstevel {
8581708Sstevel 	hpc_slot_info_t	*hsip;
8591708Sstevel 	hsc_slot_t	*hsp = NEW(hsc_slot_t);
8601708Sstevel 
8611708Sstevel 	DEBUG2("hsc_alloc_slot: slot %d %s", slot_number,
8621708Sstevel 		board_in_slot ? "occupied" : "empty");
8631708Sstevel 
8641708Sstevel 	if (hsp == NULL) {
8651708Sstevel 		cmn_err(CE_NOTE,
8661708Sstevel 			"hsc_alloc_slot: allocation failed for slot %d",
8671708Sstevel 			slot_number);
8681708Sstevel 		return (NULL);
8691708Sstevel 	}
8701708Sstevel 
8711708Sstevel 	hsip = &hsp->hs_info;
8721708Sstevel 
8731708Sstevel 	hsip->version			= HPC_SLOT_INFO_VERSION;
8741708Sstevel 	hsip->slot_type			= HPC_SLOT_TYPE_CPCI;
8751708Sstevel 	hsip->pci_dev_num		= device_number;
8761708Sstevel 	hsip->pci_slot_capabilities	= 0;
8771708Sstevel 	hsip->slot_flags		= HPC_SLOT_CREATE_DEVLINK;
8781708Sstevel 	/*
8791708Sstevel 	 * Note: the name *must* be 'pci' so that the correct cfgadm plug-in
8801708Sstevel 	 *	 library is selected
8811708Sstevel 	 */
882*11311SSurya.Prakki@Sun.COM 	(void) sprintf(hsip->pci_slot_name, "cpci_slot%d", slot_number);
8831708Sstevel 
8841708Sstevel 	/*
8851708Sstevel 	 * We assume that the following LED settings reflect
8861708Sstevel 	 * the hardware state.
8871708Sstevel 	 * After we register the slot, we will be invoked by the nexus
8881708Sstevel 	 * if the slot is occupied, and we will turn on the LED then.
8891708Sstevel 	 */
8901708Sstevel 	hsp->hs_active_led_state	= HPC_LED_OFF;
8911708Sstevel 	hsp->hs_fault_led_state		= HPC_LED_OFF;
8921708Sstevel 
8931708Sstevel 	hsp->hs_board_configured	= B_FALSE;
8941708Sstevel 	hsp->hs_board_healthy		= B_FALSE;
8951708Sstevel 	hsp->hs_board_type		= HPC_BOARD_UNKNOWN;
8961708Sstevel 
8971708Sstevel 	hsp->hs_flags			= HSC_ENABLED | HSC_SLOT_ENABLED;
8981708Sstevel 	hsp->hs_slot_number		= slot_number;
8991708Sstevel 
9001708Sstevel 	/*
9011708Sstevel 	 * we should just set this to connected,
9021708Sstevel 	 * as MC slots are always connected.
9031708Sstevel 	 */
9041708Sstevel 	if (board_in_slot)
9051708Sstevel 		hsp->hs_slot_state = HPC_SLOT_CONNECTED;
9061708Sstevel 	else
9071708Sstevel 		hsp->hs_slot_state = HPC_SLOT_EMPTY;
9081708Sstevel 
9091708Sstevel 	return (hsp);
9101708Sstevel }
9111708Sstevel 
9121708Sstevel 
9131708Sstevel static void
hsc_free_slot(hsc_slot_t * hsp)9141708Sstevel hsc_free_slot(hsc_slot_t *hsp)
9151708Sstevel {
9161708Sstevel 	DEBUG0("hsc_free_slot");
9171708Sstevel 
9181708Sstevel 	kmem_free(hsp, sizeof (*hsp));
9191708Sstevel }
9201708Sstevel 
9211708Sstevel 
9221708Sstevel /*
9231708Sstevel  * This function is invoked to register a slot
9241708Sstevel  */
9251708Sstevel 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)9261708Sstevel hsc_slot_register(
9271708Sstevel 	hsc_state_t	*hsc,
9281708Sstevel 	char		*bus_path,	/* PCI nexus pathname */
9291708Sstevel 	uint16_t	device_number,	/* PCI device number */
9301708Sstevel 	uint_t		slot_number,	/* physical slot number */
9311708Sstevel 	boolean_t	board_in_slot)	/* receptacle status */
9321708Sstevel {
9331708Sstevel 	int		rc = HPC_SUCCESS;
9341708Sstevel 	hsc_slot_t	*hsp;
9351708Sstevel 
9361708Sstevel 	DEBUG2("hsc_slot_register: slot number %d, device number %d",
9371708Sstevel 		slot_number, device_number);
9381708Sstevel 
9391708Sstevel 	hsp = hsc_alloc_slot(device_number, slot_number,
9401708Sstevel 			board_in_slot);
9411708Sstevel 
9421708Sstevel 	if (hsp == NULL) {
9431708Sstevel #ifdef	DEBUG
9441708Sstevel 		cmn_err(CE_NOTE, "hsc_slot_register: hsc_alloc_slot failed");
9451708Sstevel #endif
9461708Sstevel 		return (HPC_ERR_FAILED);
9471708Sstevel 	}
9481708Sstevel 
9491708Sstevel 	hsp->hs_hpchandle = hsc->scsb_handle; /* handle for call backs */
9501708Sstevel 	hsp->hsc = hsc;
9511708Sstevel 
9521708Sstevel 	rc = scsb_hsc_init_slot_state(hsc, hsp);
9531708Sstevel 	if (rc != DDI_SUCCESS)
9541708Sstevel 		return (HPC_ERR_FAILED);
9551708Sstevel 
9561708Sstevel 	/* slot autoconfiguration by default. */
9571708Sstevel 	if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL)
9581708Sstevel 		(void) hsc_autoconfig(hsp, HPC_CTRL_ENABLE_AUTOCFG);
9591708Sstevel 	else
9601708Sstevel 		(void) hsc_autoconfig(hsp, HPC_CTRL_DISABLE_AUTOCFG);
9611708Sstevel 
9621708Sstevel 	/*
9631708Sstevel 	 * Append to our list
9641708Sstevel 	 */
9651708Sstevel 	mutex_enter(&hsc_mutex);
9661708Sstevel 	hsp->hs_next = hsc_slot_list;
9671708Sstevel 	hsc_slot_list = hsp;
9681708Sstevel 	mutex_exit(&hsc_mutex);
9691708Sstevel 
9701708Sstevel 	rc = hpc_slot_register(hsc->dip,
9711708Sstevel 			bus_path,
9721708Sstevel 			&hsp->hs_info,
9731708Sstevel 			&hsp->hs_slot_handle,	/* return value */
9741708Sstevel 			hsc_slotops,
9751708Sstevel 			(caddr_t)hsp,
9761708Sstevel 			0);
9771708Sstevel 
9781708Sstevel 	if (rc != HPC_SUCCESS) {
9791708Sstevel 		cmn_err(CE_WARN, "%s#%d: failed to register slot %s:%d",
9801708Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip),
9811708Sstevel 			bus_path, device_number);
9821708Sstevel 		hsc_free_slot(hsp);
9831708Sstevel 		return (rc);
9841708Sstevel 	}
9851708Sstevel 
9861708Sstevel 	DEBUG0("hsc_slot_register: hpc_slot_register successful");
9871708Sstevel 
9881708Sstevel 	return (rc);
9891708Sstevel }
9901708Sstevel 
9911708Sstevel 
9921708Sstevel static int
hsc_slot_unregister(int slot_number)9931708Sstevel hsc_slot_unregister(int slot_number)
9941708Sstevel {
9951708Sstevel 	hsc_slot_t	*hsp, *prev;
9961708Sstevel 
9971708Sstevel 	DEBUG1("hsc_slot_unregister: slot number %d", slot_number);
9981708Sstevel 
9991708Sstevel 	mutex_enter(&hsc_mutex);
10001708Sstevel 	hsp = prev = NULL;
10011708Sstevel 	for (hsp = hsc_slot_list; hsp != NULL; hsp = hsp->hs_next) {
10021708Sstevel 		if (hsp->hs_slot_number == slot_number) {
10031708Sstevel 			if (prev == NULL) /* first entry */
10041708Sstevel 				hsc_slot_list = hsc_slot_list->hs_next;
10051708Sstevel 			else
10061708Sstevel 				prev->hs_next = hsp->hs_next;
10071708Sstevel 			hsp->hs_next = NULL;
10081708Sstevel 			break;
10091708Sstevel 		}
10101708Sstevel 		prev = hsp;
10111708Sstevel 	}
10121708Sstevel 	mutex_exit(&hsc_mutex);
10131708Sstevel 
10141708Sstevel 	if (hsp != NULL) {
1015*11311SSurya.Prakki@Sun.COM 		(void) hpc_slot_unregister(&hsp->hs_slot_handle);
10161708Sstevel 		if ((hsp->hsc->state & HSC_ATTACHED) != HSC_ATTACHED &&
10171708Sstevel 				hsp->hs_slot_state != HPC_SLOT_EMPTY) {
10181708Sstevel 			hsp->hsc->n_registered_occupants--;
10191708Sstevel 		}
10201708Sstevel 		hsc_free_slot(hsp);
10211708Sstevel 		return (0);
10221708Sstevel 	}
10231708Sstevel 	return (1);
10241708Sstevel }
10251708Sstevel 
10261708Sstevel static int
scsb_hsc_init_slot_state(hsc_state_t * hsc,hsc_slot_t * hsp)10271708Sstevel scsb_hsc_init_slot_state(hsc_state_t *hsc, hsc_slot_t *hsp)
10281708Sstevel {
10291708Sstevel 	int rc, rstate;
10301708Sstevel 	int slot_number = hsp->hs_slot_number;
10311708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)hsc->scsb_handle;
10321708Sstevel 
10331708Sstevel 	rc = scsb_get_slot_state(hsc->scsb_handle, slot_number, &rstate);
10341708Sstevel 	if (rc != DDI_SUCCESS)
10351708Sstevel 		return (DDI_FAILURE);
10361708Sstevel 
10371708Sstevel 	/*
10381708Sstevel 	 * Set the healthy status for this slot
10391708Sstevel 	 */
10401708Sstevel 	hsp->hs_board_healthy = scsb_read_slot_health(scsb, slot_number);
10411708Sstevel 	hsp->hs_slot_state = rstate;
10421708Sstevel 	switch (rstate) {
10431708Sstevel 		case HPC_SLOT_EMPTY:
10441708Sstevel 			/*
10451708Sstevel 			 * this will clear any state differences between
10461708Sstevel 			 * SCB Freeze operations.
10471708Sstevel 			 */
10481708Sstevel 			hsp->hs_slot_state = HPC_SLOT_EMPTY;
10491708Sstevel 			/* slot empty. */
1050*11311SSurya.Prakki@Sun.COM 			(void) scsb_reset_slot(hsc->scsb_handle, slot_number,
1051*11311SSurya.Prakki@Sun.COM 			    SCSB_RESET_SLOT);
10521708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
1053*11311SSurya.Prakki@Sun.COM 			    HPC_LED_OFF);
10541708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
1055*11311SSurya.Prakki@Sun.COM 			    HPC_LED_OFF);
10561708Sstevel 			break;
10571708Sstevel 		case HPC_SLOT_DISCONNECTED:
10581708Sstevel 			/*
10591708Sstevel 			 * this will clear any state differences between
10601708Sstevel 			 * SCB Freeze operations.
10611708Sstevel 			 */
10621708Sstevel 			hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
10631708Sstevel 			/* check recovery from SCB freeze */
10641708Sstevel 			if (hsp->hs_board_configured != B_TRUE) {
10651708Sstevel 				/*
10661708Sstevel 				 * Force a disconnect just in case there are
10671708Sstevel 				 * differences between healthy and reset states.
10681708Sstevel 				 */
1069*11311SSurya.Prakki@Sun.COM 				(void) scsb_reset_slot(hsc->scsb_handle,
1070*11311SSurya.Prakki@Sun.COM 				    slot_number, SCSB_RESET_SLOT);
10711708Sstevel 				/*
10721708Sstevel 				 * Slot in reset. OBP has not probed this
10731708Sstevel 				 * device. Hence it is ok to remove this board.
10741708Sstevel 				 */
10751708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
10761708Sstevel 						HPC_ACTIVE_LED, HPC_LED_BLINK);
10771708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
10781708Sstevel 						HPC_FAULT_LED, HPC_LED_ON);
10791708Sstevel 				break;
10801708Sstevel 			}
10811708Sstevel 			/*FALLTHROUGH*/
10821708Sstevel 		case HPC_SLOT_CONNECTED:
10831708Sstevel 			/*
10841708Sstevel 			 * this will clear any state differences between
10851708Sstevel 			 * SCB Freeze operations.
10861708Sstevel 			 */
10871708Sstevel 			hsp->hs_slot_state = HPC_SLOT_CONNECTED;
10881708Sstevel 			/*
10891708Sstevel 			 * OBP should have probed this device, unless
10901708Sstevel 			 * it was plugged in during the boot operation
10911708Sstevel 			 * before the driver was loaded. In any case,
10921708Sstevel 			 * no assumption is made and hence we take
10931708Sstevel 			 * the conservative approach by keeping fault
10941708Sstevel 			 * led off so board removal is not allowed.
10951708Sstevel 			 */
10961708Sstevel 			if (hsp->hs_board_configured == B_TRUE)
10971708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
10981708Sstevel 					HPC_ACTIVE_LED, HPC_LED_ON);
10991708Sstevel 			else
11001708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
11011708Sstevel 					HPC_ACTIVE_LED, HPC_LED_BLINK);
11021708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
11031708Sstevel 							HPC_LED_OFF);
11041708Sstevel 			/*
11051708Sstevel 			 * Netra ct alarm card hotswap support
11061708Sstevel 			 */
11071708Sstevel 			if (slot_number == scsb->ac_slotnum &&
11081708Sstevel 				scsb->scsb_hsc_state & SCSB_ALARM_CARD_PRES) {
11091708Sstevel 				hsp->hs_flags |= HSC_ALARM_CARD_PRES;
11101708Sstevel 				DEBUG0("Xscsb_hsc_init_slot_state: "
11111708Sstevel 						"set HSC_ALARM_CARD_PRES");
11121708Sstevel 			}
11131708Sstevel 			break;
11141708Sstevel 		default:
11151708Sstevel 			break;
11161708Sstevel 	}
11171708Sstevel 	return (rc);
11181708Sstevel }
11191708Sstevel 
11201708Sstevel static hsc_slot_t *
hsc_get_slot_info(hsc_state_t * hsc,int pci_devno)11211708Sstevel hsc_get_slot_info(hsc_state_t *hsc, int pci_devno)
11221708Sstevel {
11231708Sstevel 	int i;
11241708Sstevel 
11251708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
11261708Sstevel 
11271708Sstevel 		if (hsc->slot_table_prop[i].pci_devno == pci_devno)
11281708Sstevel 			return ((hsc_slot_t *)hsc_find_slot(
11291708Sstevel 				hsc->slot_table_prop[i].pslotnum));
11301708Sstevel 	}
11311708Sstevel 	return (NULL);
11321708Sstevel }
11331708Sstevel 
11341708Sstevel static hsc_slot_t *
hsc_find_slot(int slot_number)11351708Sstevel hsc_find_slot(int slot_number)
11361708Sstevel {
11371708Sstevel 	hsc_slot_t	*hsp;
11381708Sstevel 
11391708Sstevel 	mutex_enter(&hsc_mutex);
11401708Sstevel 	for (hsp = hsc_slot_list; hsp != NULL; hsp = hsp->hs_next) {
11411708Sstevel 		if (hsp->hs_slot_number == slot_number)
11421708Sstevel 			break;
11431708Sstevel 	}
11441708Sstevel 	mutex_exit(&hsc_mutex);
11451708Sstevel 	return (hsp);
11461708Sstevel }
11471708Sstevel 
11481708Sstevel 
11491708Sstevel /*
11501708Sstevel  * This function is invoked by the SCSB when an interrupt
11511708Sstevel  * happens to indicate that a board has been inserted-in/removed-from
11521708Sstevel  * the specified slot.
11531708Sstevel  */
11541708Sstevel int
hsc_slot_occupancy(int slot_number,boolean_t occupied,int flags,int healthy)11551708Sstevel hsc_slot_occupancy(int slot_number, boolean_t occupied, int flags, int healthy)
11561708Sstevel {
11571708Sstevel 	static const char	func[]	= "hsc_slot_occupancy";
11581708Sstevel 	hsc_slot_t		*hsp;
11591708Sstevel 	int			rc = DDI_SUCCESS;
11601708Sstevel 
11611708Sstevel 	DEBUG4("hsc_slot_occupancy: slot %d %s, ac=%d, healthy=%d",
11621708Sstevel 			slot_number, occupied ? "occupied" : "not occupied",
11631708Sstevel 			(flags == ALARM_CARD_ON_SLOT) ? 1:0, healthy);
11641708Sstevel 
11651708Sstevel 	hsp = hsc_find_slot(slot_number);
11661708Sstevel 
11671708Sstevel 	if (hsp == NULL) {
11681708Sstevel 		cmn_err(CE_NOTE,
11691708Sstevel 			"%s: cannot map slot number %d to a hsc_slot_t",
11701708Sstevel 			func, slot_number);
11711708Sstevel 		return (DDI_FAILURE);
11721708Sstevel 	}
11731708Sstevel 
11741708Sstevel 	hsp->hs_board_healthy = healthy;
11751708Sstevel 	if (occupied) {
11761708Sstevel 		/*
11771708Sstevel 		 * A board was just inserted. We are disconnected at this point.
11781708Sstevel 		 */
11791708Sstevel 		if (hsp->hs_slot_state == HPC_SLOT_EMPTY)
11801708Sstevel 			hsp->hs_board_type = HPC_BOARD_CPCI_HS;
11811708Sstevel 		hsp->hs_slot_state = HPC_SLOT_DISCONNECTED;
11821708Sstevel 		if (flags == ALARM_CARD_ON_SLOT) {
11831708Sstevel 			hsp->hs_flags |= HSC_ALARM_CARD_PRES;
11841708Sstevel 			DEBUG0("Xhsc_slot_occupancy: set HSC_ALARM_CARD_PRES");
11851708Sstevel 		}
11861708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
11871708Sstevel 						HPC_LED_ON);
11881708Sstevel 		/*
11891708Sstevel 		 * if previous occupant stayed configured, do not allow another
11901708Sstevel 		 * occupant to be connected.
11911708Sstevel 		 * So as soon as the board is plugged in, we turn both LEDs On.
11921708Sstevel 		 * This behaviour is an indication that the slot state
11931708Sstevel 		 * is not clean.
11941708Sstevel 		 */
11951708Sstevel 		if (hsp->hs_flags & HSC_SLOT_BAD_STATE) {
11961708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
11971708Sstevel 								HPC_LED_ON);
11981708Sstevel 			return (DDI_SUCCESS);
11991708Sstevel 		}
12001708Sstevel 
12011708Sstevel 		/* Do not allow connect if slot is disabled */
12021708Sstevel 		if ((hsp->hs_flags & HSC_SLOT_ENABLED) != HSC_SLOT_ENABLED)
12031708Sstevel 			return (DDI_SUCCESS);
12041708Sstevel 		/* if no healthy, we stay disconnected. */
12051708Sstevel 		if (healthy == B_FALSE) {
12061708Sstevel 			return (DDI_SUCCESS);
12071708Sstevel 		}
12081708Sstevel 		rc = hsc_slot_autoconnect(hsp);
12091708Sstevel 		hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
12101708Sstevel 								HPC_LED_BLINK);
12111708Sstevel 	} else {
12121708Sstevel 		/*
12131708Sstevel 		 * A board was just removed
12141708Sstevel 		 */
12151708Sstevel 		hsp->hs_slot_state = HPC_SLOT_EMPTY;
12161708Sstevel 		hsp->hs_board_type = HPC_BOARD_UNKNOWN;
12171708Sstevel 		hsp->hs_flags &= ~HSC_ENUM_FAILED;
12181708Sstevel 		if (hsp->hs_flags & HSC_ALARM_CARD_PRES) {
12191708Sstevel 			hsp->hs_flags &= ~HSC_ALARM_CARD_PRES;
12201708Sstevel 			DEBUG0("Xhsc_slot_occupancy:clear HSC_ALARM_CARD_PRES");
12211708Sstevel 		}
12221708Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
12231708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
12241708Sstevel 					HPC_EVENT_SLOT_NOT_HEALTHY, 0);
12251708Sstevel 			cmn_err(CE_WARN, "%s#%d: ALERT! Surprise Removal "
12261708Sstevel 				" on Slot %d, Occupant Online!!",
12271708Sstevel 					ddi_driver_name(hsp->hsc->dip),
12281708Sstevel 					ddi_get_instance(hsp->hsc->dip),
12291708Sstevel 					slot_number);
12301708Sstevel 			cmn_err(CE_WARN, "%s#%d: ALERT! System now in "
12311708Sstevel 				" Inconsistent State! Slot disabled. Halt!",
12321708Sstevel 					ddi_driver_name(hsp->hsc->dip),
12331708Sstevel 					ddi_get_instance(hsp->hsc->dip));
12341708Sstevel 			/* Slot in reset and disabled */
1235*11311SSurya.Prakki@Sun.COM 			(void) scsb_hsc_disable_slot(hsp);
12361708Sstevel 			hsp->hs_flags |= HSC_SLOT_BAD_STATE;
12371708Sstevel 			/* the following works for P1.0 only. */
12381708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
12391708Sstevel 						HPC_LED_ON);
12401708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
12411708Sstevel 								HPC_LED_ON);
12421708Sstevel 		} else {
12431708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
12441708Sstevel 						HPC_LED_OFF);
12451708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_ACTIVE_LED,
12461708Sstevel 								HPC_LED_OFF);
12471708Sstevel 		}
12481708Sstevel 	}
12491708Sstevel 	return (rc);
12501708Sstevel }
12511708Sstevel 
12521708Sstevel 
12531708Sstevel /*
12541708Sstevel  * This function is invoked by the SCSB when the health status of
12551708Sstevel  * a board changes.
12561708Sstevel  */
12571708Sstevel /*ARGSUSED*/
12581708Sstevel int
scsb_hsc_board_healthy(int slot_number,boolean_t healthy)12591708Sstevel scsb_hsc_board_healthy(int slot_number, boolean_t healthy)
12601708Sstevel {
12611708Sstevel 	hsc_slot_t		*hsp;
12621708Sstevel 	hsc_state_t		*hsc;
12631708Sstevel 
12641708Sstevel 	DEBUG2("hsc_board_healthy: slot %d = %d\n", slot_number, healthy);
12651708Sstevel 
12661708Sstevel 	hsp = hsc_find_slot(slot_number);
12671708Sstevel 	if (hsp == NULL) {
12681708Sstevel 		cmn_err(CE_NOTE, "hsc_board_healthy: No Slot Info.");
12691708Sstevel 		return (DDI_FAILURE);
12701708Sstevel 	}
12711708Sstevel 
12721708Sstevel 	hsc = hsp->hsc;
12731708Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_EMPTY) {
12741708Sstevel #ifdef	DEBUG
12751708Sstevel 		cmn_err(CE_NOTE, "%s#%d: Healthy# %s on "
12761708Sstevel 			"empty slot %d", ddi_driver_name(hsc->dip),
12771708Sstevel 			ddi_get_instance(hsc->dip),
12781708Sstevel 			healthy == B_TRUE ? "On" : "Off", slot_number);
12791708Sstevel #endif
12801708Sstevel 		return (DDI_FAILURE);
12811708Sstevel 	}
12821708Sstevel 	if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
12831708Sstevel 		DEBUG2("healthy %s on disconnected slot %d\n",
12841708Sstevel 			healthy == B_TRUE ? "On":"Off", slot_number);
12851708Sstevel 		/*
12861708Sstevel 		 * Connect the slot if board healthy and in autoconfig mode.
12871708Sstevel 		 */
12881708Sstevel 		hsp->hs_board_healthy = healthy;
12891708Sstevel 		if (healthy == B_TRUE)
12901708Sstevel 			return (hsc_slot_autoconnect(hsp));
12911708Sstevel 	}
12921708Sstevel 
12931708Sstevel 	/*
12941708Sstevel 	 * the board is connected. The result could be seviour depending
12951708Sstevel 	 * on the occupant state.
12961708Sstevel 	 */
12971708Sstevel 	if (healthy == B_TRUE) {
12981708Sstevel 		if (hsp->hs_board_healthy != B_TRUE) {
12991708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE, HPC_FAULT_LED,
13001708Sstevel 					HPC_LED_OFF);
13011708Sstevel 			/* Regained HEALTHY# at Run Time...!!! */
13021708Sstevel 			cmn_err(CE_NOTE, "%s#%d: slot %d Occupant "
13031708Sstevel 				"%s, Regained HEALTHY#!",
13041708Sstevel 				ddi_driver_name(hsc->dip),
13051708Sstevel 				ddi_get_instance(hsc->dip), slot_number,
13061708Sstevel 				hsp->hs_board_configured == B_TRUE ?
13071708Sstevel 						"configured" : "Unconfigured");
13081708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
13091708Sstevel 				HPC_EVENT_SLOT_HEALTHY_OK, 0);
13101708Sstevel 		}
13111708Sstevel 	} else {
13121708Sstevel 		if (hsp->hs_board_configured == B_TRUE) {
13131708Sstevel 			/* Lost HEALTHY# at Run Time...Serious Condition. */
13141708Sstevel 			cmn_err(CE_WARN, "%s#%d: ALERT! Lost HEALTHY#"
13151708Sstevel 				" on Slot %d, Occupant %s",
13161708Sstevel 				ddi_driver_name(hsc->dip),
13171708Sstevel 				ddi_get_instance(hsc->dip), slot_number,
13181708Sstevel 					hsp->hs_board_configured == B_TRUE ?
13191708Sstevel 						"Online!!!" : "Offline");
13201708Sstevel 			(void) hpc_slot_event_notify(hsp->hs_slot_handle,
13211708Sstevel 					HPC_EVENT_SLOT_NOT_HEALTHY, 0);
13221708Sstevel 		}
13231708Sstevel 		if ((hsp->hs_board_configured != B_TRUE) ||
13241708Sstevel 						scsb_hsc_healthy_reset) {
13251708Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
13261708Sstevel 					slot_number, SCSB_RESET_SLOT) == 0) {
13271708Sstevel 				/* signal Ok to remove board. */
13281708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
13291708Sstevel 					HPC_FAULT_LED, HPC_LED_ON);
13301708Sstevel 				cmn_err(CE_WARN, "%s#%d: Slot %d "
13311708Sstevel 					"successfully taken offline",
13321708Sstevel 					ddi_driver_name(hsc->dip),
13331708Sstevel 					ddi_get_instance(hsc->dip),
13341708Sstevel 					slot_number);
13351708Sstevel 			}
13361708Sstevel 		}
13371708Sstevel 	}
13381708Sstevel 	hsp->hs_board_healthy = healthy;
13391708Sstevel 	return (DDI_SUCCESS);
13401708Sstevel }
13411708Sstevel 
13421708Sstevel static int
hsc_slot_autoconnect(hsc_slot_t * hsp)13431708Sstevel hsc_slot_autoconnect(hsc_slot_t *hsp)
13441708Sstevel {
13451708Sstevel 	hsc_state_t *hsc = hsp->hsc;
13461708Sstevel 	int rc = DDI_SUCCESS;
13471708Sstevel 	/*
13481708Sstevel 	 * Keep slot in reset unless autoconfiguration is enabled
13491708Sstevel 	 * Ie. for Basic Hotswap mode, we stay disconnected at
13501708Sstevel 	 * insertion. For full hotswap mode, we automatically
13511708Sstevel 	 * go into connected state at insertion, so that occupant
13521708Sstevel 	 * autoconfiguration is possible.
13531708Sstevel 	 */
13541708Sstevel 	if (((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) &&
13551708Sstevel 			(hsp->hs_flags & HSC_AUTOCFG)) {
13561708Sstevel 		/* this statement must be here before unreset. */
13571708Sstevel 		hsc->hsp_last = hsp;
13581708Sstevel 		if ((rc = scsb_reset_slot(hsp->hs_hpchandle,
13591708Sstevel 			hsp->hs_slot_number, SCSB_UNRESET_SLOT)) == 0) {
13601708Sstevel 
13611708Sstevel 			hsp->hs_slot_state = HPC_SLOT_CONNECTED;
13621708Sstevel 			hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
13631708Sstevel 					HPC_FAULT_LED, HPC_LED_OFF);
13641708Sstevel 		} else {
13651708Sstevel 			hsc->hsp_last = NULL;
13661708Sstevel 			rc = DDI_FAILURE;
13671708Sstevel 		}
13681708Sstevel 	}
13691708Sstevel 	return (rc);
13701708Sstevel }
13711708Sstevel 
13721708Sstevel /*
13731708Sstevel  * The SCSB code should invoke this function from its _init() function.
13741708Sstevel  */
13751708Sstevel int
hsc_init()13761708Sstevel hsc_init()
13771708Sstevel {
13781708Sstevel 	int rc;
13791708Sstevel 
13801708Sstevel 	rc = ddi_soft_state_init(&hsc_state, sizeof (hsc_state_t), 1);
13811708Sstevel 	if (rc != 0)
13821708Sstevel 		return (rc);
13831708Sstevel 
13841708Sstevel 	hsc_slotops = hpc_alloc_slot_ops(KM_SLEEP);
13851708Sstevel 
13861708Sstevel 	hsc_slotops->hpc_version	= HPC_SLOT_OPS_VERSION;
13871708Sstevel 	hsc_slotops->hpc_op_connect	= hsc_connect;
13881708Sstevel 	hsc_slotops->hpc_op_disconnect	= hsc_disconnect;
13891708Sstevel 	hsc_slotops->hpc_op_insert	= hsc_insert;
13901708Sstevel 	hsc_slotops->hpc_op_remove	= hsc_remove;
13911708Sstevel 	hsc_slotops->hpc_op_control	= hsc_control;
13921708Sstevel 
13931708Sstevel 	return (DDI_SUCCESS);
13941708Sstevel }
13951708Sstevel 
13961708Sstevel 
13971708Sstevel /*
13981708Sstevel  * The SCSB code should invoke this function from its _fini() function.
13991708Sstevel  */
14001708Sstevel int
hsc_fini()14011708Sstevel hsc_fini()
14021708Sstevel {
14031708Sstevel 	if (hsc_slotops != NULL) {
14041708Sstevel 		hpc_free_slot_ops(hsc_slotops);
14051708Sstevel 		hsc_slotops = NULL;
14061708Sstevel 	}
14071708Sstevel 	ddi_soft_state_fini(&hsc_state);
14081708Sstevel 	return (DDI_SUCCESS);
14091708Sstevel }
14101708Sstevel 
14111708Sstevel static int
scsb_enable_enum(hsc_state_t * hsc)14121708Sstevel scsb_enable_enum(hsc_state_t *hsc)
14131708Sstevel {
14141708Sstevel 	DEBUG0("hsc: Enable ENUM#\n");
14151708Sstevel 
14161708Sstevel 	if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED)
14171708Sstevel 		return (DDI_SUCCESS);
14181708Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
14191708Sstevel 		return (DDI_FAILURE);
14201708Sstevel 
14211708Sstevel 	if (ddi_add_intr(hsc->dip, 1, NULL, NULL,
14221708Sstevel 			hsc_enum_intr, (caddr_t)hsc) != DDI_SUCCESS) {
14231708Sstevel 		cmn_err(CE_WARN, "%s#%d: failed ENUM# interrupt registration",
14241708Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
14251708Sstevel 		return (DDI_FAILURE);
14261708Sstevel 	}
14271708Sstevel 	cmn_err(CE_CONT, "?%s%d: Successfully Upgraded to "
14281708Sstevel 			"Full Hotswap Mode\n", ddi_driver_name(hsc->dip),
14291708Sstevel 			ddi_get_instance(hsc->dip));
14301708Sstevel 	hsc->state |= HSC_ENUM_ENABLED;
1431*11311SSurya.Prakki@Sun.COM 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
1432*11311SSurya.Prakki@Sun.COM 	    HOTSWAP_MODE_PROP, "full");
14331708Sstevel 	return (DDI_SUCCESS);
14341708Sstevel 
14351708Sstevel }
14361708Sstevel 
14371708Sstevel /*ARGSUSED*/
14381708Sstevel static int
scsb_disable_enum(hsc_state_t * hsc,int op)14391708Sstevel scsb_disable_enum(hsc_state_t *hsc, int op)
14401708Sstevel {
14411708Sstevel 
14421708Sstevel 	DEBUG0("hsc: Disable ENUM#\n");
14431708Sstevel 	if (op == SCSB_HSC_FORCE_REMOVE) {
14441708Sstevel 		/*
14451708Sstevel 		 * Clear all pending interrupts before unregistering
14461708Sstevel 		 * the interrupt. Otherwise the system will hang.
14471708Sstevel 		 *
14481708Sstevel 		 * Due to the hang problem, we'll not turn off or disable
14491708Sstevel 		 * interrupts because if there's a non-friendly full hotswap
14501708Sstevel 		 * device out there, the ENUM# will be kept asserted and
14511708Sstevel 		 * hence hsc_clear_all_enum() can never deassert ENUM#.
14521708Sstevel 		 * So the system will hang.
14531708Sstevel 		 */
14541708Sstevel 		if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) {
14551708Sstevel 			/* hsc_clear_all_enum(hsc); */
14561708Sstevel 			ddi_remove_intr(hsc->dip, 1, NULL);
14571708Sstevel 			hsc->state &= ~HSC_ENUM_ENABLED;
14581708Sstevel 			cmn_err(CE_CONT, "?%s%d: Successfully Downgraded to "
14591708Sstevel 				"Basic Hotswap Mode\n",
14601708Sstevel 				ddi_driver_name(hsc->dip),
14611708Sstevel 				ddi_get_instance(hsc->dip));
14621708Sstevel 		}
1463*11311SSurya.Prakki@Sun.COM 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
1464*11311SSurya.Prakki@Sun.COM 		    HOTSWAP_MODE_PROP, "basic");
14651708Sstevel 		return (DDI_SUCCESS);
14661708Sstevel 	} else
14671708Sstevel 		/* No programming interface for disabling ENUM# on MC/Tonga */
14681708Sstevel 		return (HPC_ERR_NOTSUPPORTED);
14691708Sstevel }
14701708Sstevel 
14711708Sstevel #ifndef	lint
14721708Sstevel static int
hsc_clear_all_enum(hsc_state_t * hsc)14731708Sstevel hsc_clear_all_enum(hsc_state_t *hsc)
14741708Sstevel {
14751708Sstevel 	int i, rc;
14761708Sstevel 	hsc_slot_t *hsp;
14771708Sstevel 
14781708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
14791708Sstevel 
14801708Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
14811708Sstevel 		if (hsp == NULL)
14821708Sstevel 			continue;
14831708Sstevel 		rc = hpc_slot_event_notify(hsp->hs_slot_handle,
14841708Sstevel 					HPC_EVENT_CLEAR_ENUM,
14851708Sstevel 						HPC_EVENT_SYNCHRONOUS);
14861708Sstevel 		if (rc == HPC_EVENT_UNCLAIMED)
14871708Sstevel 			break;	/* no pending interrupts across the bus */
14881708Sstevel 		DEBUG1("Pending Intr on slot %d\n",
14891708Sstevel 			hsc->slot_table_prop[i].pslotnum);
14901708Sstevel 	}
14911708Sstevel 	return (0);
14921708Sstevel }
14931708Sstevel #endif
14941708Sstevel 
14951708Sstevel int
scsb_hsc_attach(dev_info_t * dip,void * scsb_handle,int instance)14961708Sstevel scsb_hsc_attach(dev_info_t *dip, void *scsb_handle, int instance)
14971708Sstevel {
14981708Sstevel 	int i, n, prop_len;
14991708Sstevel 	int prom_prop = 0;	/* default: OS property gives slot-table */
15001708Sstevel 	int rc;
15011708Sstevel 	char *hotswap_model;
15021708Sstevel 	hsc_state_t	*hsc;
15031708Sstevel 	scsb_state_t	*scsb = (scsb_state_t *)scsb_handle;
15041708Sstevel 	caddr_t hpc_slot_table_data, s;
15051708Sstevel 	int hpc_slot_table_size;
15061708Sstevel 	hsc_prom_slot_table_t	*hpstp;
15071708Sstevel 	int rstate;
15081708Sstevel 
15091708Sstevel 	DEBUG0("hsc_attach: enter\n");
15101708Sstevel 	/*
15111708Sstevel 	 * To get the slot information,
15121708Sstevel 	 * The OBP defines the 'slot-table' property. But the OS
15131708Sstevel 	 * can override it with 'hsc-slot-map' property
15141708Sstevel 	 * through the .conf file.
15151708Sstevel 	 * Since the formats are different, 2 different property names
15161708Sstevel 	 * are chosen.
15171708Sstevel 	 * The OBP property format is
15181708Sstevel 	 * <phandle>,<pci-devno>,<phys-slotno>,<ga-bits>
15191708Sstevel 	 * The OS property format is (ga-bits is not used however)
15201708Sstevel 	 * <busnexus-path>,<pci-devno>,<phys-slotno>,<ga-bits>
15211708Sstevel 	 */
15221708Sstevel 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15231708Sstevel 		"hsc-slot-map", (caddr_t)&hpc_slot_table_data,
15241708Sstevel 		&hpc_slot_table_size);
15251708Sstevel 	if (rc != DDI_PROP_SUCCESS)  {
15261708Sstevel 		prom_prop = 1;
15271708Sstevel 		rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15281708Sstevel 			"slot-table", (caddr_t)&hpc_slot_table_data,
15291708Sstevel 			&hpc_slot_table_size);
15301708Sstevel 		if (rc != DDI_PROP_SUCCESS) {
15311708Sstevel 			cmn_err(CE_WARN, "%s#%d: 'slot-table' property "
15321708Sstevel 				"missing!", ddi_driver_name(dip),
15331708Sstevel 						ddi_get_instance(dip));
15341708Sstevel 			return (DDI_FAILURE);
15351708Sstevel 		}
15361708Sstevel 	}
15371708Sstevel 	rc = ddi_soft_state_zalloc(hsc_state, instance);
15381708Sstevel 	if (rc != DDI_SUCCESS)
15391708Sstevel 		return (DDI_FAILURE);
15401708Sstevel 
15411708Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
15421708Sstevel 	hsc->scsb_handle = scsb_handle;
15431708Sstevel 	hsc->dip = dip;
15441708Sstevel 	hsc->instance = instance;
15451708Sstevel 	hsc->n_registered_occupants = 0;
15461708Sstevel 	hsc->regDone = B_FALSE;
15471708Sstevel 	/* hsc->slot_info = hsc_slot_list; */
15481708Sstevel 
15491708Sstevel 	/*
15501708Sstevel 	 * Check whether the system should be in basic or full
15511708Sstevel 	 * hotswap mode. The PROM property always says full, so
15521708Sstevel 	 * look at the .conf file property whether this is "full"
15531708Sstevel 	 */
15541708Sstevel 	if (scsb_hsc_enable_fhs) {
15551708Sstevel 		hsc->hotswap_mode = HSC_HOTSWAP_MODE_FULL;
15561708Sstevel 	} else {
15571708Sstevel 		hsc->hotswap_mode = HSC_HOTSWAP_MODE_BASIC;
15581708Sstevel 	}
15591708Sstevel 
15601708Sstevel 	rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15611708Sstevel 		"default-hotswap-mode", (caddr_t)&hotswap_model, &prop_len);
15621708Sstevel 
15631708Sstevel 	if (rc == DDI_PROP_SUCCESS) {
15641708Sstevel 		if (strcmp(hotswap_model, "full") == 0) {
15651708Sstevel 			hsc->hotswap_mode = HSC_HOTSWAP_MODE_FULL;
15661708Sstevel 		} else if (strcmp(hotswap_model, "basic") == 0) {
15671708Sstevel 			hsc->hotswap_mode = HSC_HOTSWAP_MODE_BASIC;
15681708Sstevel 		}
15691708Sstevel 
15701708Sstevel 		kmem_free(hotswap_model, prop_len);
15711708Sstevel 	}
15721708Sstevel 
15731708Sstevel 	/*
15741708Sstevel 	 * Determine the size of the slot table from the property and
15751708Sstevel 	 * allocate the slot table arrary..Decoding is different for
15761708Sstevel 	 * OS and PROM property.
15771708Sstevel 	 */
15781708Sstevel 	if (!prom_prop) {	/* OS .conf property */
15791708Sstevel 		for (i = 0, n = 0; i < hpc_slot_table_size; i++) {
15801708Sstevel 			if (hpc_slot_table_data[i] == 0) {
15811708Sstevel 				n++;
15821708Sstevel 			}
15831708Sstevel 		}
15841708Sstevel 
15851708Sstevel 		/* There should be four elements per entry */
15861708Sstevel 		if (n % 4) {
15871708Sstevel 			cmn_err(CE_WARN, "%s#%d: bad format for "
15881708Sstevel 				"slot-table(%d)", ddi_driver_name(dip),
15891708Sstevel 						ddi_get_instance(dip), n);
15901708Sstevel 			kmem_free(hpc_slot_table_data, hpc_slot_table_size);
15911708Sstevel 			ddi_soft_state_free(hsc_state, instance);
15921708Sstevel 			return (DDI_FAILURE);
15931708Sstevel 		}
15941708Sstevel 
15951708Sstevel 		hsc->slot_table_size = n / 4;
15961708Sstevel 	} else {
15971708Sstevel 		hsc->slot_table_size = hpc_slot_table_size /
15981708Sstevel 						sizeof (hsc_prom_slot_table_t);
15991708Sstevel 		n = hpc_slot_table_size % sizeof (hsc_prom_slot_table_t);
16001708Sstevel 		if (n) {
16011708Sstevel 			cmn_err(CE_WARN, "%s#%d: bad format for "
16021708Sstevel 				"slot-table(%d)", ddi_driver_name(dip),
16031708Sstevel 				ddi_get_instance(dip), hpc_slot_table_size);
16041708Sstevel 			kmem_free(hpc_slot_table_data, hpc_slot_table_size);
16051708Sstevel 			ddi_soft_state_free(hsc_state, instance);
16061708Sstevel 			return (DDI_FAILURE);
16071708Sstevel 		}
16081708Sstevel 	}
16091708Sstevel 
16101708Sstevel 	/*
16111708Sstevel 	 * Netract800 FTC (formerly known as CFTM) workaround.
16121708Sstevel 	 * Leave Slot 2 out of the HS table if FTC is present in Slot 2
16131708Sstevel 	 */
16141708Sstevel 	if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES) {
16151708Sstevel 		hsc->slot_table_size -= 1;
16161708Sstevel 	}
16171708Sstevel 	DEBUG1("hsc_attach: %d hotplug slots on bus\n", hsc->slot_table_size);
16181708Sstevel 	/*
16191708Sstevel 	 * Create enough space for each slot table entry
16201708Sstevel 	 * based on how many entries in the property
16211708Sstevel 	 */
16221708Sstevel 	hsc->slot_table_prop = (hsc_slot_table_t *)
16231708Sstevel 		kmem_zalloc(hsc->slot_table_size *
16241708Sstevel 			sizeof (hsc_slot_table_t), KM_SLEEP);
16251708Sstevel 
16261708Sstevel 	if (!prom_prop) {
16271708Sstevel 		s = hpc_slot_table_data;
16281708Sstevel 		for (i = 0; i < hsc->slot_table_size; i++) {
16291708Sstevel 
16301708Sstevel 			char *nexus, *pcidev, *phys_slotname, *ga;
16311708Sstevel 
16321708Sstevel 			/* Pick off pointer to nexus path or PROM handle */
16331708Sstevel 			nexus = s;
16341708Sstevel 			while (*s != NULL)
16351708Sstevel 				s++;
16361708Sstevel 			s++;
16371708Sstevel 
16381708Sstevel 			/* Pick off pointer to the pci device number */
16391708Sstevel 			pcidev = s;
16401708Sstevel 			while (*s != NULL)
16411708Sstevel 				s++;
16421708Sstevel 			s++;
16431708Sstevel 
16441708Sstevel 			/* Pick off physical slot no */
16451708Sstevel 			phys_slotname = s;
16461708Sstevel 			while (*s != NULL)
16471708Sstevel 				s++;
16481708Sstevel 			s++;
16491708Sstevel 
16501708Sstevel 			/* Pick off GA bits which we dont use for now. */
16511708Sstevel 			ga = s;
16521708Sstevel 			while (*s != NULL)
16531708Sstevel 				s++;
16541708Sstevel 			s++;
16551708Sstevel 
16561708Sstevel 			if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
16571708Sstevel 					atoi(phys_slotname) == SC_MC_CTC_SLOT) {
16581708Sstevel 				--i;
16591708Sstevel 				continue;
16601708Sstevel 			}
16611708Sstevel 			hsc->slot_table_prop[i].pslotnum = atoi(phys_slotname);
16621708Sstevel 			hsc->slot_table_prop[i].ga = atoi(ga);
16631708Sstevel 			hsc->slot_table_prop[i].pci_devno = atoi(pcidev);
1664*11311SSurya.Prakki@Sun.COM 			(void) strcpy(hsc->slot_table_prop[i].nexus, nexus);
16651708Sstevel 		}
16661708Sstevel 	} else {
16671708Sstevel 		hpstp = (hsc_prom_slot_table_t *)hpc_slot_table_data;
16681708Sstevel 		for (i = 0; i < hsc->slot_table_size; i++, hpstp++) {
16691708Sstevel 			if (scsb->scsb_hsc_state & SCSB_HSC_CTC_PRES &&
16701708Sstevel 					hpstp->pslotnum == SC_MC_CTC_SLOT) {
16711708Sstevel 				--i;
16721708Sstevel 				continue;
16731708Sstevel 			}
16741708Sstevel 			hsc->slot_table_prop[i].pslotnum = hpstp->pslotnum;
16751708Sstevel 			hsc->slot_table_prop[i].ga = hpstp->ga;
16761708Sstevel 			hsc->slot_table_prop[i].pci_devno = hpstp->pci_devno;
16771708Sstevel 
16781708Sstevel 			if (prom_phandle_to_path((uint_t)hpstp->phandle,
16791708Sstevel 				hsc->slot_table_prop[i].nexus,
16801708Sstevel 				sizeof (hsc->slot_table_prop[i].nexus))
16811708Sstevel 						== -1) {
16821708Sstevel 				cmn_err(CE_WARN, "%s#%d: Cannot get phandle "
16831708Sstevel 					"to nexus path", ddi_driver_name(dip),
16841708Sstevel 					ddi_get_instance(dip));
16851708Sstevel 				kmem_free(hsc->slot_table_prop,
16861708Sstevel 					(hsc->slot_table_size *
16871708Sstevel 						sizeof (hsc_slot_table_t)));
16881708Sstevel 				kmem_free(hpc_slot_table_data,
16891708Sstevel 						hpc_slot_table_size);
16901708Sstevel 				ddi_soft_state_free(hsc_state, instance);
16911708Sstevel 				return (DDI_FAILURE);
16921708Sstevel 			}
16931708Sstevel 		}
16941708Sstevel 	}
16951708Sstevel 
16961708Sstevel 	/* keep healthy register cache uptodate before reading slot state */
16971708Sstevel 	if (scsb_read_bhealthy(scsb_handle) != 0) {
16981708Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_attach: Cannot read "
16991708Sstevel 			"Healthy Registers", ddi_driver_name(dip),
17001708Sstevel 				ddi_get_instance(dip));
17011708Sstevel 		kmem_free(hsc->slot_table_prop,
17021708Sstevel 			(hsc->slot_table_size *
17031708Sstevel 				sizeof (hsc_slot_table_t)));
17041708Sstevel 		kmem_free(hpc_slot_table_data,
17051708Sstevel 				hpc_slot_table_size);
17061708Sstevel 		ddi_soft_state_free(hsc_state, instance);
17071708Sstevel 		return (DDI_FAILURE);
17081708Sstevel 	}
17091708Sstevel 
17101708Sstevel 	/*
17111708Sstevel 	 * Before we start registering the slots, calculate how many
17121708Sstevel 	 * slots are occupied.
17131708Sstevel 	 */
17141708Sstevel 
17151708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
17161708Sstevel 		if (scsb_get_slot_state(scsb_handle,
17171708Sstevel 				hsc->slot_table_prop[i].pslotnum, &rstate) !=
17181708Sstevel 				DDI_SUCCESS)
17191708Sstevel 				return (rc);
17201708Sstevel 		if (rstate != HPC_SLOT_EMPTY)
17211708Sstevel 			hsc->n_registered_occupants++;
17221708Sstevel 	}
17231708Sstevel 
17241708Sstevel 	mutex_init(&hsc->hsc_mutex, NULL, MUTEX_DRIVER, NULL);
17251708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
17261708Sstevel 
17271708Sstevel 		DEBUG2("Registering on nexus [%s] cPCI device [%d]\n",
17281708Sstevel 			hsc->slot_table_prop[i].nexus,
17291708Sstevel 			hsc->slot_table_prop[i].pci_devno);
17301708Sstevel 
17311708Sstevel 		if (hsc_slot_register(hsc, hsc->slot_table_prop[i].nexus,
17321708Sstevel 			hsc->slot_table_prop[i].pci_devno,
17331708Sstevel 			hsc->slot_table_prop[i].pslotnum, B_FALSE) !=
17341708Sstevel 								HPC_SUCCESS) {
17351708Sstevel 
17361708Sstevel 			cmn_err(CE_WARN, "%s#%d: Slot Registration Failure",
17371708Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
17381708Sstevel 			while (i) {
17391708Sstevel 				i--;
17401708Sstevel 				n = hsc->slot_table_prop[i].pslotnum;
17411708Sstevel 				if (hsc_slot_unregister(n) != 0) {
17421708Sstevel 					cmn_err(CE_WARN,
17431708Sstevel 						"%s#%d: failed to unregister"
17441708Sstevel 						" slot %d",
17451708Sstevel 						ddi_driver_name(dip),
17461708Sstevel 						ddi_get_instance(dip), n);
17471708Sstevel 
17481708Sstevel 				}
17491708Sstevel 			}
17501708Sstevel 			mutex_destroy(&hsc->hsc_mutex);
17511708Sstevel 			kmem_free(hsc->slot_table_prop, (hsc->slot_table_size *
17521708Sstevel 					sizeof (hsc_slot_table_t)));
17531708Sstevel 			kmem_free(hpc_slot_table_data, hpc_slot_table_size);
17541708Sstevel 			ddi_soft_state_free(hsc_state, instance);
17551708Sstevel 			return (DDI_FAILURE);
17561708Sstevel 		}
17571708Sstevel 	}
17581708Sstevel 
17591708Sstevel 	hsc->hsp_last = NULL;
17601708Sstevel 	hsc->hsc_intr_counter = 0;
17611708Sstevel 	kmem_free(hpc_slot_table_data, hpc_slot_table_size);
1762*11311SSurya.Prakki@Sun.COM 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, hsc->dip,
1763*11311SSurya.Prakki@Sun.COM 	    HOTSWAP_MODE_PROP, "basic");
17641708Sstevel 	hsc->state |= (HSC_ATTACHED|HSC_SCB_CONNECTED);
17651708Sstevel 
17661708Sstevel 	/*
17671708Sstevel 	 * We enable full hotswap right here if all the slots are empty.
17681708Sstevel 	 */
17691708Sstevel 	if ((hsc->regDone == B_FALSE && hsc->n_registered_occupants == 0) ||
17701708Sstevel 			scsb_hsc_numReg == hsc->n_registered_occupants) {
17711708Sstevel 		hsc->regDone = B_TRUE;
17721708Sstevel 		if (hsc->hotswap_mode == HSC_HOTSWAP_MODE_FULL) {
17731708Sstevel 			if (scsb_enable_enum(hsc) != DDI_SUCCESS) {
17741708Sstevel 				cmn_err(CE_WARN, "%s#%d: Cannot enable "
17751708Sstevel 					"Full Hotswap", ddi_driver_name(dip),
17761708Sstevel 					ddi_get_instance(dip));
17771708Sstevel 			}
17781708Sstevel 		}
17791708Sstevel 	}
17801708Sstevel 	return (DDI_SUCCESS);
17811708Sstevel }
17821708Sstevel 
17831708Sstevel /*ARGSUSED*/
17841708Sstevel int
scsb_hsc_detach(dev_info_t * dip,void * scsb_handle,int instance)17851708Sstevel scsb_hsc_detach(dev_info_t *dip, void *scsb_handle, int instance)
17861708Sstevel {
17871708Sstevel 	int i = 0;
17881708Sstevel 	hsc_state_t	*hsc;
17891708Sstevel 	char slotautocfg_prop[18];
17901708Sstevel 
17911708Sstevel 	DEBUG0("hsc_detach: enter\n");
17921708Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
17931708Sstevel 	if (hsc == NULL) {
17941708Sstevel 		DEBUG2("%s#%d: hsc_detach: Soft state NULL",
17951708Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
17961708Sstevel 		return (DDI_FAILURE);
17971708Sstevel 	}
17981708Sstevel 
17991708Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
18001708Sstevel 		return (DDI_FAILURE);
18011708Sstevel 	/*
18021708Sstevel 	 * let's unregister the hotpluggable slots with hotplug service.
18031708Sstevel 	 */
18041708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
18051708Sstevel 
18061708Sstevel 		hsc_slot_t	*hsp;
18071708Sstevel 
18081708Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
18091708Sstevel 		if (hsp == NULL) {
18101708Sstevel 			cmn_err(CE_WARN, "%s#%d: hsc_detach: No Slot Info",
18111708Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
18121708Sstevel 		} else {
18131708Sstevel 			hpc_led_info_t	aledinfo;	/* active led info. */
18141708Sstevel 			hpc_led_info_t	fledinfo;	/* fault led info. */
18151708Sstevel 
18161708Sstevel 			aledinfo.led = HPC_ACTIVE_LED;
18171708Sstevel 			aledinfo.state = HPC_LED_BLINK;
18181708Sstevel 			fledinfo.led = HPC_FAULT_LED;
18191708Sstevel 			fledinfo.state = HPC_LED_OFF;
18201708Sstevel 			(void) hsc_led_state(hsp, HPC_CTRL_SET_LED_STATE,
18211708Sstevel 							&aledinfo);
18221708Sstevel 			(void) hsc_led_state(hsp, HPC_CTRL_SET_LED_STATE,
18231708Sstevel 							&fledinfo);
18241708Sstevel 		}
1825*11311SSurya.Prakki@Sun.COM 		(void) sprintf(slotautocfg_prop, "slot%d-autoconfig",
1826*11311SSurya.Prakki@Sun.COM 		    hsp->hs_slot_number);
1827*11311SSurya.Prakki@Sun.COM 		(void) ddi_prop_remove(DDI_DEV_T_NONE, hsc->dip,
1828*11311SSurya.Prakki@Sun.COM 		    slotautocfg_prop);
18291708Sstevel 		if (hsc_slot_unregister(hsc->slot_table_prop[i].pslotnum)
18301708Sstevel 						!= 0) {
18311708Sstevel 			cmn_err(CE_NOTE, "%s#%d: failed to unregister"
18321708Sstevel 				" slot %d\n", ddi_driver_name(dip),
18331708Sstevel 				ddi_get_instance(dip),
18341708Sstevel 				hsc->slot_table_prop[i].pslotnum);
18351708Sstevel 			return (DDI_FAILURE);
18361708Sstevel 		}
18371708Sstevel 	}
18381708Sstevel 	kmem_free(hsc->slot_table_prop, (hsc->slot_table_size *
18391708Sstevel 					sizeof (hsc_slot_table_t)));
18401708Sstevel 	if ((hsc->state & HSC_ENUM_ENABLED) == HSC_ENUM_ENABLED) {
18411708Sstevel 		ddi_remove_intr(hsc->dip, 1, hsc->enum_iblock);
18421708Sstevel 		hsc->state &= ~HSC_ENUM_ENABLED;
18431708Sstevel 	}
18441708Sstevel 	mutex_destroy(&hsc->hsc_mutex);
1845*11311SSurya.Prakki@Sun.COM 	(void) ddi_prop_remove(DDI_DEV_T_NONE, hsc->dip, HOTSWAP_MODE_PROP);
18461708Sstevel 	hsc->state &= ~(HSC_ATTACHED|HSC_SCB_CONNECTED);
18471708Sstevel 	ddi_soft_state_free(hsc_state, instance);
18481708Sstevel 	return (DDI_SUCCESS);
18491708Sstevel }
18501708Sstevel 
18511708Sstevel /*
18521708Sstevel  * The following function is called when the SCSB is hot extracted from
18531708Sstevel  * the system.
18541708Sstevel  */
18551708Sstevel int
scsb_hsc_freeze(dev_info_t * dip)18561708Sstevel scsb_hsc_freeze(dev_info_t *dip)
18571708Sstevel {
18581708Sstevel 	hsc_state_t	*hsc;
18591708Sstevel 	int instance = ddi_get_instance(dip);
18601708Sstevel 	int i;
18611708Sstevel 	hsc_slot_t	*hsp;
18621708Sstevel 
18631708Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
18641708Sstevel 	if (hsc == NULL) {
18651708Sstevel 		DEBUG2("%s#%d: Soft state NULL",
18661708Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
18671708Sstevel 		return (DDI_SUCCESS);
18681708Sstevel 	}
18691708Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
18701708Sstevel 		return (DDI_SUCCESS);
18711708Sstevel 	hsc->state &= ~HSC_SCB_CONNECTED;
18721708Sstevel 
18731708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
18741708Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
18751708Sstevel 
18761708Sstevel 		if (hsp == NULL) {
18771708Sstevel 			cmn_err(CE_NOTE, "hsc_freeze: "
18781708Sstevel 				" Cannot map slot number %d to a hsc_slot_t",
18791708Sstevel 					hsc->slot_table_prop[i].pslotnum);
18801708Sstevel 			continue;
18811708Sstevel 		}
18821708Sstevel 		/*
18831708Sstevel 		 * Since reset lines are pulled low, lets mark these
18841708Sstevel 		 * slots and not allow a connect operation.
18851708Sstevel 		 * Note that we still keep the slot as slot disconnected,
18861708Sstevel 		 * although it is connected from the hardware standpoint.
18871708Sstevel 		 * As soon as the SCB is plugged back in, we check these
18881708Sstevel 		 * states and put the hardware state back to its original
18891708Sstevel 		 * state.
18901708Sstevel 		 */
18911708Sstevel 		if (hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) {
18921708Sstevel 			cmn_err(CE_WARN, "%s#%d: Slot %d Now out of Reset!",
18931708Sstevel 				ddi_driver_name(hsc->dip),
18941708Sstevel 				ddi_get_instance(hsc->dip),
18951708Sstevel 				hsp->hs_slot_number);
18961708Sstevel 		}
18971708Sstevel 		hsp->hs_flags |= HSC_SCB_HOTSWAPPED;
18981708Sstevel 	}
18991708Sstevel 
19001708Sstevel 	return (DDI_SUCCESS);
19011708Sstevel }
19021708Sstevel 
19031708Sstevel /*
19041708Sstevel  * The following function is called when the SCSB is hot inserted from
19051708Sstevel  * the system. We must update the LED status and set the RST# registers
19061708Sstevel  * again.
19071708Sstevel  */
19081708Sstevel int
scsb_hsc_restore(dev_info_t * dip)19091708Sstevel scsb_hsc_restore(dev_info_t *dip)
19101708Sstevel {
19111708Sstevel 	int i;
19121708Sstevel 	hsc_state_t	*hsc;
19131708Sstevel 	hsc_slot_t	*hsp;
19141708Sstevel 	int instance = ddi_get_instance(dip);
19151708Sstevel 
19161708Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
19171708Sstevel 	if (hsc == NULL) {
19181708Sstevel 		DEBUG2("%s#%d: Soft state NULL",
19191708Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
19201708Sstevel 		return (DDI_SUCCESS);
19211708Sstevel 	}
19221708Sstevel 
19231708Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
19241708Sstevel 		return (DDI_SUCCESS);
19251708Sstevel 	hsc->state |= HSC_SCB_CONNECTED;
19261708Sstevel 	for (i = 0; i < hsc->slot_table_size; i++) {
19271708Sstevel 		hsp = hsc_find_slot(hsc->slot_table_prop[i].pslotnum);
19281708Sstevel 
19291708Sstevel 		if (hsp == NULL) {
19301708Sstevel 			cmn_err(CE_NOTE, "%s#%d: hsc_restore: "
19311708Sstevel 				" Cannot map slot number %d to a hsc_slot_t",
19321708Sstevel 					ddi_driver_name(hsc->dip),
19331708Sstevel 					ddi_get_instance(hsc->dip),
19341708Sstevel 					hsc->slot_table_prop[i].pslotnum);
19351708Sstevel 			continue;
19361708Sstevel 		}
19371708Sstevel 		if ((hsp->hs_slot_state == HPC_SLOT_DISCONNECTED) &&
19381708Sstevel 				(hsp->hs_board_configured == B_FALSE)) {
19391708Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
19401708Sstevel 					hsp->hs_slot_number,
19411708Sstevel 					SCSB_RESET_SLOT) != 0) {
19421708Sstevel 				cmn_err(CE_WARN, "%s#%d: hsc_restore: "
19431708Sstevel 					" Cannot reset disconnected slot %d",
19441708Sstevel 						ddi_driver_name(hsc->dip),
19451708Sstevel 						ddi_get_instance(hsc->dip),
19461708Sstevel 						hsp->hs_slot_number);
19471708Sstevel 			}
19481708Sstevel 		}
19491708Sstevel 
19501708Sstevel 		if (scsb_hsc_init_slot_state(hsc, hsp) != DDI_SUCCESS) {
19511708Sstevel 
19521708Sstevel 			cmn_err(CE_WARN, "%s#%d: hsc_freeze: Cannot init"
19531708Sstevel 				" slot%d state",
19541708Sstevel 				ddi_driver_name(hsc->dip),
19551708Sstevel 				ddi_get_instance(hsc->dip),
19561708Sstevel 				hsp->hs_slot_number);
19571708Sstevel 		}
19581708Sstevel 		hsp->hs_flags &= ~HSC_SCB_HOTSWAPPED;
19591708Sstevel 	}
19601708Sstevel 	return (DDI_SUCCESS);
19611708Sstevel }
19621708Sstevel 
19631708Sstevel #ifndef	lint
19641708Sstevel int
scsb_hsc_freeze_check(dev_info_t * dip)19651708Sstevel scsb_hsc_freeze_check(dev_info_t *dip)
19661708Sstevel {
19671708Sstevel 	hsc_state_t	*hsc;
19681708Sstevel 	int instance = ddi_get_instance(dip);
19691708Sstevel 
19701708Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
19711708Sstevel 	if (hsc == NULL) {
19721708Sstevel 		DEBUG2("%s#%d: Soft state NULL",
19731708Sstevel 				ddi_driver_name(dip), ddi_get_instance(dip));
19741708Sstevel 		return (DDI_SUCCESS);
19751708Sstevel 	}
19761708Sstevel 	if ((hsc->state & HSC_ATTACHED) != HSC_ATTACHED)
19771708Sstevel 		return (DDI_SUCCESS);
19781708Sstevel 	return (DDI_SUCCESS);
19791708Sstevel }
19801708Sstevel #endif
19811708Sstevel 
19821708Sstevel /*
19831708Sstevel  * update info about Alarm Card insert/remove mechanism.
19841708Sstevel  */
19851708Sstevel void
hsc_ac_op(int instance,int pslotnum,int op,void * arg)19861708Sstevel hsc_ac_op(int instance, int pslotnum, int op, void *arg)
19871708Sstevel {
19881708Sstevel 	hsc_slot_t *hsp;
19891708Sstevel 	hsc_state_t	*hsc;
19901708Sstevel 
19911708Sstevel 	hsc = (hsc_state_t *)ddi_get_soft_state(hsc_state, instance);
19921708Sstevel 	if (hsc == NULL) {
19931708Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_ac_op: No Soft State Info",
19941708Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
19951708Sstevel 		return;
19961708Sstevel 	}
19971708Sstevel 
19981708Sstevel 	hsp = hsc_find_slot(pslotnum);
19991708Sstevel 	if (hsp == NULL) {
20001708Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_ac_op: No Slot Info",
20011708Sstevel 			ddi_driver_name(hsc->dip), ddi_get_instance(hsc->dip));
20021708Sstevel 		return;
20031708Sstevel 	}
20041708Sstevel 
20051708Sstevel 	switch (op) {
20061708Sstevel 		case SCSB_HSC_AC_UNCONFIGURE :
20071708Sstevel 			/*
20081708Sstevel 			 * If ENUM# is enabled, then action is pending on
20091708Sstevel 			 * this slot, just send a event.
20101708Sstevel 			 */
20111708Sstevel 			if (hsc->state & HSC_ENUM_ENABLED)
2012*11311SSurya.Prakki@Sun.COM 				(void) hpc_slot_event_notify(
2013*11311SSurya.Prakki@Sun.COM 				    hsp->hs_slot_handle,
2014*11311SSurya.Prakki@Sun.COM 				    HPC_EVENT_PROCESS_ENUM, 0);
20151708Sstevel 			break;
20161708Sstevel 		case SCSB_HSC_AC_GET_SLOT_INFO :
20171708Sstevel 			*(hsc_slot_t **)arg = hsp;
20181708Sstevel 			break;
20191708Sstevel 		default :
20201708Sstevel 			break;
20211708Sstevel 	}
20221708Sstevel }
20231708Sstevel 
20241708Sstevel static uint_t
hsc_enum_intr(caddr_t iarg)20251708Sstevel hsc_enum_intr(caddr_t iarg)
20261708Sstevel {
20271708Sstevel 	int rc;
20281708Sstevel 	hsc_state_t *hsc = (hsc_state_t *)iarg;
20291708Sstevel 	hsc_slot_t *hsp;
20301708Sstevel 
20311708Sstevel 	DEBUG0("!E!");
20321708Sstevel 	if ((hsc->state & HSC_ATTACHED) == 0)
20331708Sstevel 		return (DDI_INTR_UNCLAIMED);
20341708Sstevel 
20351708Sstevel 	hsp = hsc_find_slot(hsc->slot_table_prop[0].pslotnum);
20361708Sstevel 	if (hsp == NULL)	/* No slots registered */
20371708Sstevel 		return (DDI_INTR_UNCLAIMED);
20381708Sstevel 
20391708Sstevel 	/*
20401708Sstevel 	 * The following must be done to clear interrupt (synchronous event).
20411708Sstevel 	 * To process the interrupt, we send an asynchronous event.
20421708Sstevel 	 */
20431708Sstevel 	rc = hpc_slot_event_notify(hsp->hs_slot_handle,
20441708Sstevel 					HPC_EVENT_CLEAR_ENUM,
20451708Sstevel 						HPC_EVENT_SYNCHRONOUS);
20461708Sstevel 	if (rc == HPC_EVENT_UNCLAIMED) {
20471708Sstevel 		/*
20481708Sstevel 		 * possible support for handling insertion of non friendly
20491708Sstevel 		 * full hotswap boards, otherwise the system hangs due
20501708Sstevel 		 * to uncleared interrupt bursts.
20511708Sstevel 		 */
20521708Sstevel 		DEBUG2("!E>counter %d, last op@slot %lx\n",
20531708Sstevel 				hsc->hsc_intr_counter, hsc->hsp_last);
20541708Sstevel 		hsc->hsc_intr_counter ++;
20551708Sstevel 		if (hsc->hsc_intr_counter == scsb_hsc_max_intr_count) {
20561708Sstevel 			if (!hsc->hsp_last) {
20571708Sstevel 				cmn_err(CE_WARN, "%s#%d: hsc_enum_intr: "
20581708Sstevel 					" No Last Board Insertion Info.",
20591708Sstevel 					ddi_driver_name(hsc->dip),
20601708Sstevel 					ddi_get_instance(hsc->dip));
20611708Sstevel 				hsc->hsc_intr_counter = 0;
20621708Sstevel 				return (DDI_INTR_UNCLAIMED);
20631708Sstevel 			}
20641708Sstevel 			hsp = hsc->hsp_last;
20651708Sstevel 			cmn_err(CE_WARN, "%s#%d: Bad (non friendly ?) Board "
20661708Sstevel 				"in Slot %d ? Taking it Offline.",
20671708Sstevel 				ddi_driver_name(hsc->dip),
20681708Sstevel 				ddi_get_instance(hsc->dip),
20691708Sstevel 				hsp->hs_slot_number);
20701708Sstevel 			/*
20711708Sstevel 			 * this should put just inserted board back in
20721708Sstevel 			 * reset, thus deasserting the ENUM# and the
20731708Sstevel 			 * system hang.
20741708Sstevel 			 */
20751708Sstevel 			if (scsb_reset_slot(hsp->hs_hpchandle,
20761708Sstevel 					hsp->hs_slot_number,
20771708Sstevel 					SCSB_RESET_SLOT) == 0) {
20781708Sstevel 				/* Enumeration failed on this board */
20791708Sstevel 				hsp->hs_flags |= HSC_ENUM_FAILED;
20801708Sstevel 				if (hsp->hs_board_configured == B_TRUE)
20811708Sstevel 					cmn_err(CE_WARN, "%s#%d: ALERT! System"
20821708Sstevel 						" now in Inconsistent State."
20831708Sstevel 						" Halt!",
20841708Sstevel 					    ddi_driver_name(hsc->dip),
20851708Sstevel 					    ddi_get_instance(hsc->dip));
20861708Sstevel 				hsc_led_op(hsp, HPC_CTRL_SET_LED_STATE,
20871708Sstevel 						HPC_FAULT_LED, HPC_LED_ON);
20881708Sstevel 			}
20891708Sstevel 			hsc->hsc_intr_counter = 0;
20901708Sstevel 		}
20911708Sstevel 		return (DDI_INTR_UNCLAIMED);
20921708Sstevel 	}
20931708Sstevel 	hsc->hsc_intr_counter = 0;
20941708Sstevel 	/*
20951708Sstevel 	 * if interrupt success, rc denotes the PCI device number which
20961708Sstevel 	 * generated the ENUM# interrupt.
20971708Sstevel 	 */
20981708Sstevel 	hsp = hsc_get_slot_info(hsc, rc);
20991708Sstevel 	if (hsp == NULL) {
21001708Sstevel 		cmn_err(CE_WARN, "%s#%d: hsc_enum_intr: no slot info for "
21011708Sstevel 			"dev %x", ddi_driver_name(hsc->dip),
21021708Sstevel 			ddi_get_instance(hsc->dip), rc);
21031708Sstevel 		return (DDI_INTR_CLAIMED);	/* interrupt already cleared */
21041708Sstevel 	}
21051708Sstevel 	/* if this is Alarm Card and if it is busy, dont process event */
21061708Sstevel 	if (hsp->hs_flags & HSC_ALARM_CARD_PRES) {
21071708Sstevel 		if (scsb_hsc_ac_op(hsp->hs_hpchandle, hsp->hs_slot_number,
21081708Sstevel 						SCSB_HSC_AC_BUSY) == B_TRUE) {
21091708Sstevel 			/*
21101708Sstevel 			 * Busy means we need to inform (envmond)alarmcard.so
21111708Sstevel 			 * that it should save the AC configuration, stop the
21121708Sstevel 			 * heartbeat, and shutdown the RSC link.
21131708Sstevel 			 */
21141708Sstevel 			(void) scsb_hsc_ac_op(hsp->hs_hpchandle,
21151708Sstevel 					hsp->hs_slot_number,
21161708Sstevel 					SCSB_HSC_AC_REMOVAL_ALERT);
21171708Sstevel 			return (DDI_INTR_CLAIMED);
21181708Sstevel 		}
21191708Sstevel 	}
21201708Sstevel 	/*
21211708Sstevel 	 * If SCB was swapped out, dont process ENUM#. We put this slot
21221708Sstevel 	 * back in reset after SCB is inserted.
21231708Sstevel 	 */
21241708Sstevel 	if ((hsp->hs_flags & HSC_SCB_HOTSWAPPED) &&
21251708Sstevel 			(hsp->hs_slot_state == HPC_SLOT_DISCONNECTED))
21261708Sstevel 		return (DDI_INTR_CLAIMED);
21271708Sstevel 
2128*11311SSurya.Prakki@Sun.COM 	(void) hpc_slot_event_notify(hsp->hs_slot_handle,
2129*11311SSurya.Prakki@Sun.COM 	    HPC_EVENT_PROCESS_ENUM, 0);
21301708Sstevel 	return (DDI_INTR_CLAIMED);
21311708Sstevel }
21321708Sstevel /*
21331708Sstevel  * A routine to convert a number (represented as a string) to
21341708Sstevel  * the integer value it represents.
21351708Sstevel  */
21361708Sstevel 
21371708Sstevel static int
isdigit(int ch)21381708Sstevel isdigit(int ch)
21391708Sstevel {
21401708Sstevel 	return (ch >= '0' && ch <= '9');
21411708Sstevel }
21421708Sstevel 
21431708Sstevel #define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
21441708Sstevel #define	bad(val)	(val == NULL || !isdigit(*val))
21451708Sstevel 
21461708Sstevel static int
atoi(const char * p)21471708Sstevel atoi(const char *p)
21481708Sstevel {
21491708Sstevel 	int n;
21501708Sstevel 	int c, neg = 0;
21511708Sstevel 
21521708Sstevel 	if (!isdigit(c = *p)) {
21531708Sstevel 		while (isspace(c))
21541708Sstevel 			c = *++p;
21551708Sstevel 		switch (c) {
21561708Sstevel 			case '-':
21571708Sstevel 				neg++;
21581708Sstevel 				/* FALLTHROUGH */
21591708Sstevel 			case '+':
21601708Sstevel 			c = *++p;
21611708Sstevel 		}
21621708Sstevel 		if (!isdigit(c))
21631708Sstevel 			return (0);
21641708Sstevel 	}
21651708Sstevel 	for (n = '0' - c; isdigit(c = *++p); ) {
21661708Sstevel 		n *= 10; /* two steps to avoid unnecessary overflow */
21671708Sstevel 		n += '0' - c; /* accum neg to avoid surprises at MAX */
21681708Sstevel 	}
21691708Sstevel 	return (neg ? n : -n);
21701708Sstevel }
2171