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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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