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