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