Mercurial > illumos > illumos-gate
view usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c @ 3911:94df594513c7
6536641 Hotplug Controller should create physical-slot# property for PCI-X IO Box
author | pjha |
---|---|
date | Wed, 28 Mar 2007 12:00:22 -0700 |
parents | fb3f21cb6650 |
children | f8b6a07acfd6 |
line wrap: on
line source
/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * PCISHPC - The Standard PCI HotPlug Controller driver module. This driver * can be used with PCI HotPlug controllers that are compatible * with the PCI SHPC specification 1.x. */ #include <sys/note.h> #include <sys/conf.h> #include <sys/kmem.h> #include <sys/kstat.h> #include <sys/debug.h> #include <sys/vtrace.h> #include <sys/modctl.h> #include <sys/autoconf.h> #include <sys/varargs.h> #include <sys/hwconf.h> #include <sys/ddi_impldefs.h> #include <sys/pci.h> #include <sys/callb.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunndi.h> #include <sys/ndi_impldefs.h> #include <sys/hotplug/pci/pcishpc.h> #include <sys/hotplug/pci/pcishpc_regs.h> #include <sys/hotplug/hpcsvc.h> /* General Register bit weights for the 32-bit SHPC registers */ #define REG_BIT0 0x00000001 #define REG_BIT1 0x00000002 #define REG_BIT2 0x00000004 #define REG_BIT3 0x00000008 #define REG_BIT4 0x00000010 #define REG_BIT5 0x00000020 #define REG_BIT6 0x00000040 #define REG_BIT7 0x00000080 #define REG_BIT8 0x00000100 #define REG_BIT9 0x00000200 #define REG_BIT10 0x00000400 #define REG_BIT11 0x00000800 #define REG_BIT12 0x00001000 #define REG_BIT13 0x00002000 #define REG_BIT14 0x00004000 #define REG_BIT15 0x00008000 #define REG_BIT16 0x00010000 #define REG_BIT17 0x00020000 #define REG_BIT18 0x00040000 #define REG_BIT19 0x00080000 #define REG_BIT20 0x00100000 #define REG_BIT21 0x00200000 #define REG_BIT22 0x00400000 #define REG_BIT23 0x00800000 #define REG_BIT24 0x01000000 #define REG_BIT25 0x02000000 #define REG_BIT26 0x04000000 #define REG_BIT27 0x08000000 #define REG_BIT28 0x10000000 #define REG_BIT29 0x20000000 #define REG_BIT30 0x40000000 #define REG_BIT31 0x80000000 /* Definitions used with the SHPC SHPC_SLOTS_AVAIL_I_REG register */ #define SHPC_AVAIL_33MHZ_CONV_SPEED_SHIFT 0 #define SHPC_AVAIL_66MHZ_PCIX_SPEED_SHIFT 8 #define SHPC_AVAIL_100MHZ_PCIX_SPEED_SHIFT 16 #define SHPC_AVAIL_133MHZ_PCIX_SPEED_SHIFT 24 #define SHPC_AVAIL_SPEED_MASK 0x1F /* Definitions used with the SHPC SHPC_SLOTS_AVAIL_II_REG register */ #define SHPC_AVAIL_66MHZ_CONV_SPEED_SHIFT 0 /* Register bits used with the SHPC SHPC_PROF_IF_SBCR_REG register */ #define SHPC_SBCR_33MHZ_CONV_SPEED 0 #define SHPC_SBCR_66MHZ_CONV_SPEED REG_BIT0 #define SHPC_SBCR_66MHZ_PCIX_SPEED REG_BIT1 #define SHPC_SBCR_100MHZ_PCIX_SPEED (REG_BIT0|REG_BIT1) #define SHPC_SBCR_133MHZ_PCIX_SPEED REG_BIT2 #define SHPC_SBCR_SPEED_MASK (REG_BIT0|REG_BIT1|REG_BIT2) /* Register bits used with the SHPC SHPC_COMMAND_STATUS_REG register */ #define SHPC_COMM_STS_ERR_INVALID_SPEED REG_BIT19 #define SHPC_COMM_STS_ERR_INVALID_COMMAND REG_BIT18 #define SHPC_COMM_STS_ERR_MRL_OPEN REG_BIT17 #define SHPC_COMM_STS_ERR_MASK (REG_BIT17|REG_BIT18|REG_BIT19) #define SHPC_COMM_STS_CTRL_BUSY REG_BIT16 #define SHPC_COMM_STS_SET_SPEED REG_BIT6 /* Register bits used with the SHPC SHPC_CTRL_SERR_INT_REG register */ #define SHPC_SERR_INT_GLOBAL_IRQ_MASK REG_BIT0 #define SHPC_SERR_INT_GLOBAL_SERR_MASK REG_BIT1 #define SHPC_SERR_INT_CMD_COMPLETE_MASK REG_BIT2 #define SHPC_SERR_INT_ARBITER_SERR_MASK REG_BIT3 #define SHPC_SERR_INT_CMD_COMPLETE_IRQ REG_BIT16 #define SHPC_SERR_INT_ARBITER_IRQ REG_BIT17 #define SHPC_SERR_INT_MASK_ALL (REG_BIT0|REG_BIT1|REG_BIT2|REG_BIT3) /* Register bits used with the SHPC SHPC_LOGICAL_SLOT_REGS register */ #define SHPC_SLOT_POWER_ONLY REG_BIT0 #define SHPC_SLOT_ENABLED REG_BIT1 #define SHPC_SLOT_DISABLED (REG_BIT0 | REG_BIT1) #define SHPC_SLOT_STATE_MASK (REG_BIT0 | REG_BIT1) #define SHPC_SLOT_MRL_STATE_MASK REG_BIT8 #define SHPC_SLOT_66MHZ_CONV_CAPABLE REG_BIT9 #define SHPC_SLOT_CARD_EMPTY_MASK (REG_BIT10 | REG_BIT11) #define SHPC_SLOT_66MHZ_PCIX_CAPABLE REG_BIT12 #define SHPC_SLOT_100MHZ_PCIX_CAPABLE REG_BIT13 #define SHPC_SLOT_133MHZ_PCIX_CAPABLE (REG_BIT12 | REG_BIT13) #define SHPC_SLOT_PCIX_CAPABLE_MASK (REG_BIT12 | REG_BIT13) #define SHPC_SLOT_PCIX_CAPABLE_SHIFT 12 #define SHPC_SLOT_PRESENCE_DETECTED REG_BIT16 #define SHPC_SLOT_ISO_PWR_DETECTED REG_BIT17 #define SHPC_SLOT_ATTN_DETECTED REG_BIT18 #define SHPC_SLOT_MRL_DETECTED REG_BIT19 #define SHPC_SLOT_POWER_DETECTED REG_BIT20 #define SHPC_SLOT_PRESENCE_MASK REG_BIT24 #define SHPC_SLOT_ISO_PWR_MASK REG_BIT25 #define SHPC_SLOT_ATTN_MASK REG_BIT26 #define SHPC_SLOT_MRL_MASK REG_BIT27 #define SHPC_SLOT_POWER_MASK REG_BIT28 #define SHPC_SLOT_MRL_SERR_MASK REG_BIT29 #define SHPC_SLOT_POWER_SERR_MASK REG_BIT30 #define SHPC_SLOT_MASK_ALL (REG_BIT24|REG_BIT25|REG_BIT26|\ REG_BIT27|REG_BIT28|REG_BIT30) /* Register bits used with the SHPC SHPC_IRQ_LOCATOR_REG register. */ #define SHPC_IRQ_CMD_COMPLETE REG_BIT0 #define SHPC_IRQ_SLOT_N_PENDING REG_BIT1 /* Register bits used with the SHPC SHPC_SERR_LOCATOR_REG register. */ #define SHPC_IRQ_SERR_ARBITER_PENDING REG_BIT0 #define SHPC_IRQ_SERR_SLOT_N_PENDING REG_BIT1 /* Register bits used with the SHPC SHPC_SLOT_CONFIGURATION_REG register */ #define SHPC_SLOT_CONFIG_MRL_SENSOR REG_BIT30 #define SHPC_SLOT_CONFIG_ATTN_BUTTON REG_BIT31 #define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16 #define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF #define SHPC_SLOT_CONFIG_PHY_SLOT_NUM(reg) (((reg) >> 16) & 0x3FF) /* Max PCISHPC controller slots */ #define MAX_SHPC_SLOTS 31 /* PCISHPC controller command complete delay in microseconds. */ #define SHPC_COMMAND_WAIT_TIME 10000 /* * Power good wait time after issuing a command to change the slot state * to power only state. */ #define SHPC_POWER_GOOD_WAIT_TIME 220000 /* reset delay to 1 sec. */ static int pcishpc_reset_delay = 1000000; /* PCISHPC controller softstate structure */ typedef struct pcishpc_ctrl { dev_info_t *shpc_dip; /* DIP for SHPC Nexus */ ddi_acc_handle_t shpc_config_hdl; /* SHPC DDI cfg handle */ kmutex_t shpc_intr_mutex; /* Interrupt mutex lock */ boolean_t interrupt_installed; /* Interrupt installed */ boolean_t command_complete; /* Got a cmd complete IRQ */ kcondvar_t cmd_comp_cv; boolean_t arbiter_timeout; /* Got a Arb timeout IRQ */ kmutex_t shpc_mutex; /* Mutex for this SHPC */ char nexus_path[MAXNAMELEN]; /* Pathname of Nexus */ uint32_t shpc_bus; /* SHPC bus */ uint32_t shpc_dev; /* SHPC device */ uint32_t shpc_func; /* SHPC function */ uint8_t shpc_dword_select; /* SHPC register offset */ uint8_t shpc_dword_data_reg; /* SHPC data register */ uint32_t shpc_slots_avail1_reg; /* SHPC Slots Available1 Reg */ uint32_t shpc_slots_avail2_reg; /* SHPC Slots Available2 Reg */ uint32_t numSlotsImpl; /* # of HP Slots Implemented */ uint32_t numSlotsConn; /* # of HP Slots Connected */ int currBusSpeed; /* Current Bus Speed */ uint32_t deviceStart; /* 1st PCI Device # */ uint32_t physStart; /* 1st Phys Device # */ uint32_t deviceIncreases; /* Device # Increases */ struct pcishpc *slots[MAX_SHPC_SLOTS]; /* Slot pointers */ boolean_t has_attn; /* Do we have attn btn? */ boolean_t has_mrl; /* Do we have MRL? */ struct pcishpc_ctrl *nextp; /* Linked list pointer */ } pcishpc_ctrl_t; /* PCISHPC slot softstate structure */ typedef struct pcishpc { pcishpc_ctrl_t *ctrl; /* SHPC ctrl for this slot */ hpc_slot_info_t slot_info; /* HPS framework slot info */ hpc_slot_t slot_handle; /* HPS framework handle */ hpc_slot_ops_t *slot_ops; /* HPS framework callbacks */ uint32_t fault_led_state; /* Fault LED state */ uint32_t power_led_state; /* Power LED state */ uint32_t attn_led_state; /* Attn LED state */ uint32_t active_led_state; /* Active LED state */ hpc_slot_state_t slot_state; /* Slot State */ uint32_t deviceNum; /* PCI device num for slot */ uint32_t slotNum; /* SHPC slot number */ uint32_t phy_slot_num; /* physical slot number */ uint32_t slot_events; /* Slot event(s) IRQ */ kcondvar_t attn_btn_cv; /* ATTN button pressed intr */ boolean_t attn_btn_pending; kthread_t *attn_btn_threadp; /* ATTN button event thread */ boolean_t attn_btn_thread_exit; struct pcishpc *nextp; /* Linked list pointer */ } pcishpc_t; /* mutex to protect the shpc_head and shpc_ctrl_head linked lists */ static kmutex_t pcishpc_list_mutex; /* Pointer to a linked list of shpc slot softstate structures */ static pcishpc_t *pcishpc_head = NULL; /* Pointer to a linked list of shpc controller softstate structures */ static pcishpc_ctrl_t *pcishpc_ctrl_head = NULL; /* mutex to protect access to the controller */ static kmutex_t pcishpc_control_mutex; /* SHPC static function prototypes */ static pcishpc_ctrl_t *pcishpc_create_controller(dev_info_t *dip); static int pcishpc_destroy_controller(dev_info_t *dip); static pcishpc_ctrl_t *pcishpc_get_controller(dev_info_t *dip); static pcishpc_t *pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p); static int pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p); static pcishpc_t *pcishpc_hpc_get_slot_state(hpc_slot_t slot); static int pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p); static int pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot); static int pcishpc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags); static int pcishpc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags); static int pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request, caddr_t arg); static int pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led, hpc_led_state_t state); static int pcishpc_set_power_state(pcishpc_t *pcishpc_p, hpc_slot_state_t state); static int pcishpc_set_bus_speed(pcishpc_t *pcishpc_p); static int pcishpc_probe_controller(pcishpc_ctrl_t *pcishpc_p); static int pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p); static void pcishpc_get_slot_state(pcishpc_t *pcishpc_p); static int pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p); static int pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p); static int pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p); static void pcishpc_set_soft_int(pcishpc_ctrl_t *ctrl_p); static int pcishpc_wait_busy(pcishpc_ctrl_t *ctrl_p); static int pcishpc_issue_command(pcishpc_ctrl_t *ctrl_p, uint32_t cmd_code); static int pcishpc_led_shpc_to_hpc(int state); static int pcishpc_led_hpc_to_shpc(int state); static int pcishpc_slot_shpc_to_hpc(int state); static int pcishpc_slot_hpc_to_shpc(int state); static char *pcishpc_textledstate(hpc_led_state_t state); static char *pcishpc_textslotstate(hpc_slot_state_t state); static char *pcishpc_textrequest(int request); static int pcishpc_set_slot_state(pcishpc_t *pcishpc_p); static void pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p); static void pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg, uint32_t data); static uint32_t pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg); static void pcishpc_debug(char *fmt, ...); static void pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p); static void pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot); static int pcishpc_debug_enabled = 0; /* Module operations information for the kernel */ extern struct mod_ops mod_miscops; static struct modlmisc modlmisc = { &mod_miscops, "PCI SHPC hotplug module v%I%", }; /* Module linkage information for the kernel */ static struct modlinkage modlinkage = { MODREV_1, &modlmisc, NULL }; int _init(void) { int rc; if ((rc = mod_install(&modlinkage)) != 0) { pcishpc_debug("pcishpc: install error=%d", rc); return (rc); } /* Init the shpc driver list mutex. */ mutex_init(&pcishpc_list_mutex, NULL, MUTEX_DRIVER, NULL); /* Init the shpc control mutex. */ mutex_init(&pcishpc_control_mutex, NULL, MUTEX_DRIVER, NULL); pcishpc_debug("pcishpc: installed"); return (rc); } int _fini(void) { pcishpc_debug("pcishpc: _fini called()"); /* XXX - to be fixed later */ return (EBUSY); } int _info(struct modinfo *modinfop) { pcishpc_debug("pcishpc: _info called()"); return (mod_info(&modlinkage, modinfop)); } /* * pcishpc_create_controller() * * This function allocates and creates an SHPC controller state structure * and adds it to the linked list of controllers. */ static pcishpc_ctrl_t * pcishpc_create_controller(dev_info_t *dip) { pcishpc_ctrl_t *ctrl_p; pcishpc_debug("pcishpc: create controller for %s#%d", ddi_driver_name(dip), ddi_get_instance(dip)); ctrl_p = kmem_zalloc(sizeof (pcishpc_ctrl_t), KM_SLEEP); ctrl_p->interrupt_installed = B_FALSE; ctrl_p->shpc_dip = dip; (void) ddi_pathname(dip, ctrl_p->nexus_path); /* Get the PCI BUS,DEVICE,FUNCTION for this SHPC controller. */ if (pcishpc_get_pci_info(ctrl_p) != DDI_SUCCESS) { pcishpc_debug("pcishpc_create_controller() " "Error: pcishpc_get_pci_info() failed"); kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); return (NULL); } if (pci_config_setup(dip, &ctrl_p->shpc_config_hdl) != DDI_SUCCESS) { pcishpc_debug("pcishpc_create_controller() " "Error: Unable to map SHPC PCI Config registers"); kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); return (NULL); } /* Make sure the SHPC is listed in the PCI capibilities list. */ if (pcishpc_probe_controller(ctrl_p) != DDI_SUCCESS) { pcishpc_debug("pcishpc_create_controller() " "Error: Unable to find SHPC controller"); pci_config_teardown(&ctrl_p->shpc_config_hdl); kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); return (NULL); } /* Init the interrupt mutex */ mutex_init(&ctrl_p->shpc_intr_mutex, NULL, MUTEX_DRIVER, (void *)PCISHPC_INTR_PRI); /* Interrupts are now enabled. */ ctrl_p->interrupt_installed = B_TRUE; /* Init the shpc controller's mutex. */ mutex_init(&ctrl_p->shpc_mutex, NULL, MUTEX_DRIVER, NULL); mutex_enter(&pcishpc_list_mutex); /* Insert new softstate into linked list of current soft states. */ ctrl_p->nextp = pcishpc_ctrl_head; pcishpc_ctrl_head = ctrl_p; mutex_exit(&pcishpc_list_mutex); pcishpc_debug("pcishpc_create_controller() success"); return (ctrl_p); } /* * pcishpc_probe_controller() * * This function probes to make sure there is indeed an SHPC controller. */ static int pcishpc_probe_controller(pcishpc_ctrl_t *ctrl_p) { uint8_t cap_ptr; uint8_t cap_id; uint16_t status; status = pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_STAT); if (!(status & PCI_STAT_CAP)) { return (DDI_FAILURE); } /* Get a pointer to the PCI capabilities list. */ cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl, PCI_BCNF_CAP_PTR); cap_ptr &= 0xFC; /* Walk PCI capabilities list searching for the SHPC capability. */ while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { cap_id = pci_config_get8(ctrl_p->shpc_config_hdl, cap_ptr); pcishpc_debug("pcishpc_probe_controller() capability @ " "pointer=%02x (id=%02x)", cap_ptr, cap_id); if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) { /* Save the SHPC register offset. */ ctrl_p->shpc_dword_select = cap_ptr+2; /* Save the SHPC data register. */ ctrl_p->shpc_dword_data_reg = cap_ptr+4; break; } /* Get the pointer to the next capability. */ cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl, cap_ptr+1); cap_ptr &= 0xFC; } if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) { return (DDI_FAILURE); } pcishpc_debug("pcishpc_probe_controller() Found SHPC capibility"); return (DDI_SUCCESS); } /* * pcishpc_destroy_controller() * * This function deallocates all of the SHPC controller resources. */ static int pcishpc_destroy_controller(dev_info_t *dip) { pcishpc_ctrl_t *ctrl_p; pcishpc_ctrl_t **ctrl_pp; pcishpc_debug("pcishpc_destroy_controller() called(dip=%p)", dip); mutex_enter(&pcishpc_list_mutex); ctrl_pp = &pcishpc_ctrl_head; /* Walk the linked list of softstates. */ while ((ctrl_p = *ctrl_pp) != NULL) { if (ctrl_p->shpc_dip == dip) { /* * Deallocate the slot state structures for * this controller. */ (void) pcishpc_destroy_slots(ctrl_p); *ctrl_pp = ctrl_p->nextp; pci_config_teardown(&ctrl_p->shpc_config_hdl); cv_destroy(&ctrl_p->cmd_comp_cv); mutex_destroy(&ctrl_p->shpc_mutex); mutex_destroy(&ctrl_p->shpc_intr_mutex); kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); mutex_exit(&pcishpc_list_mutex); pcishpc_debug("pcishpc_destroy_controller() success"); return (DDI_SUCCESS); } ctrl_pp = &(ctrl_p->nextp); } mutex_exit(&pcishpc_list_mutex); pcishpc_debug("pcishpc_destroy_controller() not found"); return (DDI_FAILURE); } /* * pcishpc_intr() * * This is the SHPC controller interrupt handler. */ int pcishpc_intr(dev_info_t *dip) { pcishpc_ctrl_t *ctrl_p = pcishpc_get_controller(dip); int slot; uint32_t irq_locator, irq_serr_locator, reg; boolean_t slot_event = B_FALSE; pcishpc_debug("pcishpc_intr() called"); if (ctrl_p->interrupt_installed == B_TRUE) { mutex_enter(&ctrl_p->shpc_intr_mutex); pcishpc_debug("pcishpc_intr() interrupt received"); reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); if (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) { pcishpc_debug("pcishpc_intr() " "SHPC_SERR_INT_CMD_COMPLETE_IRQ detected"); ctrl_p->command_complete = B_TRUE; cv_signal(&ctrl_p->cmd_comp_cv); } if (reg & SHPC_SERR_INT_ARBITER_IRQ) { pcishpc_debug("pcishpc_intr() SHPC_SERR_INT_ARBITER_IRQ" " detected"); ctrl_p->arbiter_timeout = B_TRUE; } /* Write back the SERR INT register to acknowledge the IRQs. */ pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg); irq_locator = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG); irq_serr_locator = pcishpc_read_reg(ctrl_p, SHPC_SERR_LOCATOR_REG); /* Check for slot events that might have occured. */ for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) { if ((irq_locator & (SHPC_IRQ_SLOT_N_PENDING<<slot)) || (irq_serr_locator & (SHPC_IRQ_SERR_SLOT_N_PENDING<<slot))) { pcishpc_debug("pcishpc_intr() slot %d and " "pending IRQ", slot+1); /* * Note that we will need to generate a * slot event interrupt. */ slot_event = B_TRUE; reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); /* Record any pending slot interrupts/events. */ ctrl_p->slots[slot]->slot_events |= reg; /* Acknoledge any slot interrupts */ pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg); } } if (slot_event == B_TRUE) { pcishpc_debug("pcishpc_intr() slot(s) have event(s)"); (void) pcishpc_process_intr(ctrl_p); } else { pcishpc_debug("pcishpc_intr() No slot event(s)"); } mutex_exit(&ctrl_p->shpc_intr_mutex); pcishpc_debug("pcishpc_intr() claimed"); return (DDI_INTR_CLAIMED); } pcishpc_debug("pcishpc_intr() unclaimed"); return (DDI_INTR_UNCLAIMED); } /* * pcishpc_process_intr() * * This is the SHPC soft interrupt handler. */ static int pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p) { int slot; mutex_enter(&ctrl_p->shpc_mutex); pcishpc_debug("pcishpc_process_intr() called"); /* XXX - add event handling code here */ for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) { if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_PRESENCE_DETECTED) pcishpc_debug("slot %d: SHPC_SLOT_PRESENCE_DETECTED", slot+1); if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_ISO_PWR_DETECTED) pcishpc_debug("slot %d: SHPC_SLOT_ISO_PWR_DETECTED", slot+1); if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_ATTN_DETECTED) { pcishpc_debug("slot %d: SHPC_SLOT_ATTN_DETECTED", slot+1); /* * if ATTN button event is still pending * then cancel it */ if (ctrl_p->slots[slot]->attn_btn_pending == B_TRUE) ctrl_p->slots[slot]->attn_btn_pending = B_FALSE; /* wake up the ATTN event handler */ cv_signal(&ctrl_p->slots[slot]->attn_btn_cv); } if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_MRL_DETECTED) pcishpc_debug("slot %d: SHPC_SLOT_MRL_DETECTED", slot+1); if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_POWER_DETECTED) pcishpc_debug("slot %d: SHPC_SLOT_POWER_DETECTED", slot+1); /* Clear the events now that we've processed all of them. */ ctrl_p->slots[slot]->slot_events = 0; } mutex_exit(&ctrl_p->shpc_mutex); return (DDI_INTR_CLAIMED); } /* * pcishpc_get_controller() * * This function retrieves the hot plug SHPC controller soft state. */ static pcishpc_ctrl_t * pcishpc_get_controller(dev_info_t *dip) { pcishpc_ctrl_t *ctrl_p; pcishpc_debug("pcishpc_get_controller() called (dip=%p)", dip); mutex_enter(&pcishpc_list_mutex); ctrl_p = pcishpc_ctrl_head; while (ctrl_p) { if (ctrl_p->shpc_dip == dip) break; ctrl_p = ctrl_p->nextp; } mutex_exit(&pcishpc_list_mutex); pcishpc_debug("pcishpc_get_controller() (ctrl_p=%llx)", ctrl_p); return (ctrl_p); } /* * pcishpc_hpc_get_slot_state() * * This function retrieves the hot plug SHPC soft state from the * the HPS framework slot handle. */ static pcishpc_t * pcishpc_hpc_get_slot_state(hpc_slot_t slot) { pcishpc_t *pcishpc_p; pcishpc_debug("pcishpc_hpc_get_slot_state() called (hpc_slot=%x)", slot); mutex_enter(&pcishpc_list_mutex); pcishpc_p = pcishpc_head; while (pcishpc_p) { if (pcishpc_p->slot_handle == slot) { pcishpc_debug("pcishpc_hpc_get_slot_state() found " "(pcishpc=%x)", pcishpc_p); mutex_exit(&pcishpc_list_mutex); return (pcishpc_p); } pcishpc_p = pcishpc_p->nextp; } mutex_exit(&pcishpc_list_mutex); pcishpc_debug("pcishpc_hpc_get_slot_state() failed (slot=%x)", slot); return (NULL); } /* * pcishpc_get_pci_info() * * Read the PCI Bus, PCI Device, and PCI function for the SHPC controller. */ static int pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p) { pci_regspec_t *regspec; int reglen; pcishpc_debug("pcishpc_get_pci_info() called"); if (ddi_getlongprop(DDI_DEV_T_NONE, pcishpc_p->shpc_dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®spec, ®len) != DDI_SUCCESS) { pcishpc_debug("pcishpc_get_pci_info() failed to get regspec."); return (DDI_FAILURE); } pcishpc_p->shpc_bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi); pcishpc_p->shpc_dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi); pcishpc_p->shpc_func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi); kmem_free(regspec, reglen); pcishpc_debug("pcishpc_get_pci_info() %s%d: bus=%d, dev=%d, func=%d", ddi_driver_name(pcishpc_p->shpc_dip), ddi_get_instance(pcishpc_p->shpc_dip), pcishpc_p->shpc_bus, pcishpc_p->shpc_dev, pcishpc_p->shpc_func); return (DDI_SUCCESS); } /* * pcishpc_init() * * Install and configure an SHPC controller and register the HotPlug slots * with the Solaris HotPlug framework. This function is usually called by * a PCI bridge Nexus driver that has a built in SHPC controller. */ int pcishpc_init(dev_info_t *dip) { pcishpc_ctrl_t *ctrl_p; int i; pcishpc_debug("pcishpc_init() called from %s#%d", ddi_driver_name(dip), ddi_get_instance(dip)); mutex_enter(&pcishpc_control_mutex); if (pcishpc_get_controller(dip) != NULL) { pcishpc_debug("pcishpc_init() shpc instance already " "initialized!"); mutex_exit(&pcishpc_control_mutex); return (DDI_SUCCESS); } /* Initialize soft state structure for the SHPC instance. */ ctrl_p = pcishpc_create_controller(dip); if (ctrl_p == NULL) { pcishpc_debug("pcishpc_init() failed to create shpc softstate"); mutex_exit(&pcishpc_control_mutex); return (DDI_FAILURE); } if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) { pcishpc_debug("pcishpc_init() failed to setup controller"); (void) pcishpc_destroy_controller(dip); mutex_exit(&pcishpc_control_mutex); return (DDI_FAILURE); } #if 0 pcishpc_debug("%s%d: P2P bridge register dump:", ddi_driver_name(dip), ddi_get_instance(dip)); for (i = 0; i < 0x100; i += 4) { pcishpc_debug("SHPC Cfg reg 0x%02x: %08x", i, pci_config_get32(ctrl_p->shpc_config_hdl, i)); } #endif /* Setup each HotPlug slot on this SHPC controller. */ for (i = 0; i < ctrl_p->numSlotsImpl; i++) { if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) { pcishpc_debug("pcishpc_init() failed to register " "slot %d", i); (void) pcishpc_destroy_controller(dip); mutex_exit(&pcishpc_control_mutex); return (DDI_FAILURE); } } (void) pcishpc_enable_irqs(ctrl_p); if (pcishpc_debug_enabled) { /* Dump out the SHPC registers. */ pcishpc_dump_regs(ctrl_p); } mutex_exit(&pcishpc_control_mutex); pcishpc_debug("pcishpc_init() success(dip=%p)", dip); return (DDI_SUCCESS); } /* * pcishpc_enable_irqs() * * Enable/unmask the different IRQ's we support from the SHPC controller. */ static int pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p) { uint32_t reg; int slot; reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); /* Enable all interrupts. */ reg &= ~SHPC_SERR_INT_MASK_ALL; pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg); /* Unmask the interrupts for each slot. */ for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) { ctrl_p->slots[slot]->slot_events = 0; reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); if ((reg & SHPC_SLOT_STATE_MASK) == SHPC_SLOT_ENABLED) { reg &= ~(SHPC_SLOT_MASK_ALL | SHPC_SLOT_MRL_SERR_MASK); ctrl_p->numSlotsConn++; if (ctrl_p->currBusSpeed == -1) ctrl_p->currBusSpeed = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG) & SHPC_SBCR_SPEED_MASK; } else { reg &= ~(SHPC_SLOT_MASK_ALL); } /* Enable/Unmask all slot interrupts. */ pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg); } pcishpc_debug("pcishpc_enable_irqs: ctrl_p 0x%p, " "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p, ctrl_p->currBusSpeed, ctrl_p->numSlotsConn); return (DDI_SUCCESS); } /* * pcishpc_disable_irqs() * * Disable/Mask the different IRQ's we support from the SHPC controller. */ static int pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p) { uint32_t reg; int slot; reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); /* Mask all interrupts. */ reg |= SHPC_SERR_INT_MASK_ALL; pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg); /* Unmask the interrupts for each slot. */ for (slot = 0; slot < ctrl_p->numSlotsImpl; slot++) { reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); /* Disable/Mask all slot interrupts. */ reg |= SHPC_SLOT_MASK_ALL; pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg); } pcishpc_debug("pcishpc_disable_irqs: ctrl_p 0x%p, " "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p, ctrl_p->currBusSpeed, ctrl_p->numSlotsConn); return (DDI_SUCCESS); } /* * pcishpc_register_slot() * * Create and register a slot with the Solaris HotPlug framework. */ static int pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot) { pcishpc_t *pcishpc_p; pcishpc_p = pcishpc_create_slot(ctrl_p); ctrl_p->slots[slot] = pcishpc_p; pcishpc_p->slot_ops = hpc_alloc_slot_ops(KM_SLEEP); pcishpc_p->slot_ops->hpc_version = HPC_SLOT_OPS_VERSION; pcishpc_p->slotNum = slot; /* Setup the PCI device # for this SHPC slot. */ if (ctrl_p->deviceIncreases) pcishpc_p->deviceNum = ctrl_p->deviceStart + pcishpc_p->slotNum; else pcishpc_p->deviceNum = ctrl_p->deviceStart - pcishpc_p->slotNum; /* Setup the HPS framework slot ops callbacks for the SHPC driver. */ pcishpc_p->slot_ops->hpc_op_connect = pcishpc_connect; pcishpc_p->slot_ops->hpc_op_disconnect = pcishpc_disconnect; pcishpc_p->slot_ops->hpc_op_control = pcishpc_pci_control; /* PCI HPC drivers do not support the insert/remove callbacks. */ pcishpc_p->slot_ops->hpc_op_insert = NULL; pcishpc_p->slot_ops->hpc_op_remove = NULL; /* Setup the HPS framework slot information. */ pcishpc_p->slot_info.version = HPC_SLOT_OPS_VERSION; pcishpc_p->slot_info.slot_type = HPC_SLOT_TYPE_PCI; /* Do not auto enable the deivce in this slot. */ pcishpc_p->slot_info.slot_flags = HPC_SLOT_NO_AUTO_ENABLE | HPC_SLOT_CREATE_DEVLINK; pcishpc_p->slot_info.slot.pci.device_number = pcishpc_p->deviceNum; pcishpc_p->slot_info.slot.pci.slot_capabilities = HPC_SLOT_64BITS; /* setup thread for handling ATTN button events */ if (ctrl_p->has_attn) { pcishpc_debug("pcishpc_register_slot: " "setting up ATTN button event " "handler thread for slot %d\n", slot); cv_init(&pcishpc_p->attn_btn_cv, NULL, CV_DRIVER, NULL); pcishpc_p->attn_btn_pending = B_FALSE; pcishpc_p->attn_btn_threadp = thread_create(NULL, 0, pcishpc_attn_btn_handler, (void *)pcishpc_p, 0, &p0, TS_RUN, minclsyspri); pcishpc_p->attn_btn_thread_exit = B_FALSE; } /* setup the slot name (used for ap-id) */ pcishpc_set_slot_name(ctrl_p, slot); pcishpc_get_slot_state(pcishpc_p); /* Register this SHPC slot with the HPS framework. */ if (hpc_slot_register(ctrl_p->shpc_dip, ctrl_p->nexus_path, &pcishpc_p->slot_info, &pcishpc_p->slot_handle, pcishpc_p->slot_ops, (caddr_t)pcishpc_p, 0) != 0) { pcishpc_debug("pcishpc_register_slot() failed to Register " "slot"); hpc_free_slot_ops(pcishpc_p->slot_ops); pcishpc_p->slot_ops = NULL; return (DDI_FAILURE); } pcishpc_debug("pcishpc_register_slot() success for slot %d", slot); return (DDI_SUCCESS); } /* * pcishpc_create_slot() * * Allocate and add a new HotPlug slot state structure to the linked list. */ static pcishpc_t * pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p) { pcishpc_t *pcishpc_p; pcishpc_debug("pcishpc_create_slot() called(ctrl_p=%x)", ctrl_p); /* Allocate a new slot structure. */ pcishpc_p = kmem_zalloc(sizeof (pcishpc_t), KM_SLEEP); pcishpc_p->ctrl = ctrl_p; mutex_enter(&pcishpc_list_mutex); /* Insert new slot into linked list of current slots. */ pcishpc_p->nextp = pcishpc_head; pcishpc_head = pcishpc_p; mutex_exit(&pcishpc_list_mutex); pcishpc_debug("pcishpc_create_slot() success"); return (pcishpc_p); } /* * pcishpc_setup_controller() * * Get the number of HotPlug Slots, and the PCI device information * for this HotPlug controller. */ static int pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p) { uint32_t config; dev_info_t *ppdip; config = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG); /* Get the number of HotPlug slots implemented */ ctrl_p->numSlotsImpl = ((config)&31); /* * Initilize the current bus speed and number of hotplug slots * currently connected. */ ctrl_p->currBusSpeed = -1; ctrl_p->numSlotsConn = 0; /* Save the value of Slots Available 1 and 2 registers */ ctrl_p->shpc_slots_avail1_reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_I_REG); ctrl_p->shpc_slots_avail2_reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_II_REG); /* Get the first PCI device Number used. */ /* * PCI-X I/O boat workaround. * The register doesn't set up the correct value. */ ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->shpc_dip)); if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS, "vendor-id", -1) == 0x108e) && (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS, "device-id", -1) == 0x9010)) ctrl_p->deviceStart = 4; else ctrl_p->deviceStart = ((config>>8)&31); /* Get the first Physical device number. */ ctrl_p->physStart = ((config>>16)&0x7ff); /* Check if the device numbers increase or decrease. */ ctrl_p->deviceIncreases = ((config>>29)&0x1); ctrl_p->has_attn = (config & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE; ctrl_p->has_mrl = (config & SHPC_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE; cv_init(&ctrl_p->cmd_comp_cv, NULL, CV_DRIVER, NULL); ctrl_p->command_complete = B_FALSE; ctrl_p->arbiter_timeout = B_FALSE; if (ctrl_p->numSlotsImpl > MAX_SHPC_SLOTS) { pcishpc_debug("pcishpc_setup_controller() too many SHPC " "slots error"); return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * pcishpc_uninit() * Unload the HogPlug controller driver and deallocate all resources. */ int pcishpc_uninit(dev_info_t *dip) { pcishpc_ctrl_t *ctrl_p; pcishpc_debug("pcishpc_uninit() called(dip=%p)", dip); mutex_enter(&pcishpc_control_mutex); ctrl_p = pcishpc_get_controller(dip); if (!ctrl_p) { pcishpc_debug("pcishpc_uninit() Unable to find softstate"); mutex_exit(&pcishpc_control_mutex); return (DDI_FAILURE); } (void) pcishpc_disable_irqs(ctrl_p); ctrl_p->interrupt_installed = B_FALSE; (void) pcishpc_destroy_controller(dip); mutex_exit(&pcishpc_control_mutex); pcishpc_debug("pcishpc_uninit() success(dip=%p)", dip); return (DDI_SUCCESS); } /* * pcishpc_destroy_slots() * * Free up all of the slot resources for this controller. */ static int pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p) { pcishpc_t *pcishpc_p; pcishpc_t **pcishpc_pp; pcishpc_debug("pcishpc_destroy_slots() called(ctrl_p=%p)", ctrl_p); pcishpc_pp = &pcishpc_head; while ((pcishpc_p = *pcishpc_pp) != NULL) { if (pcishpc_p->ctrl == ctrl_p) { if (pcishpc_p->attn_btn_threadp != NULL) { mutex_enter(&ctrl_p->shpc_mutex); pcishpc_p->attn_btn_thread_exit = B_TRUE; cv_signal(&pcishpc_p->attn_btn_cv); pcishpc_debug("pcishpc_destroy_slots: " "waiting for ATTN thread exit\n"); cv_wait(&pcishpc_p->attn_btn_cv, &ctrl_p->shpc_mutex); pcishpc_debug("pcishpc_destroy_slots: " "ATTN thread exit\n"); cv_destroy(&pcishpc_p->attn_btn_cv); pcishpc_p->attn_btn_threadp = NULL; mutex_exit(&ctrl_p->shpc_mutex); } *pcishpc_pp = pcishpc_p->nextp; pcishpc_debug("pcishpc_destroy_slots() (shpc_p=%p) " "destroyed", pcishpc_p); if (pcishpc_p->slot_ops) if (hpc_slot_unregister( &pcishpc_p->slot_handle) != 0) { pcishpc_debug("pcishpc_destroy_slots() " "failed to unregister slot"); return (DDI_FAILURE); } else { hpc_free_slot_ops(pcishpc_p->slot_ops); pcishpc_p->slot_ops = NULL; } kmem_free(pcishpc_p, sizeof (pcishpc_t)); } else pcishpc_pp = &(pcishpc_p->nextp); } return (DDI_SUCCESS); } /* * pcishpc_connect() * * Called by Hot Plug Services to connect a slot on the bus. */ /*ARGSUSED*/ static int pcishpc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags) { pcishpc_t *pcishpc_p; uint32_t status; pcishpc_debug("pcishpc_connect called()"); pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl); if (!pcishpc_p) { pcishpc_debug("pcishpc_connect() " "Failed to find soft state for slot_hdl %x", slot_hdl); return (HPC_ERR_FAILED); } mutex_enter(&pcishpc_p->ctrl->shpc_mutex); /* make sure the MRL sensor is closed */ status = pcishpc_read_reg(pcishpc_p->ctrl, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); if (status & SHPC_SLOT_MRL_STATE_MASK) { pcishpc_debug("pcishpc_connect() failed: MRL open"); goto cleanup; } if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_CONNECTED) != DDI_SUCCESS) { pcishpc_debug("pcishpc_connect() failed: set power state"); goto cleanup; } mutex_exit(&pcishpc_p->ctrl->shpc_mutex); pcishpc_debug("pcishpc_connect() success!"); return (HPC_SUCCESS); cleanup: mutex_exit(&pcishpc_p->ctrl->shpc_mutex); return (HPC_ERR_FAILED); } /* * pcishpc_set_power_state() * * Changed a slot's power state. */ static int pcishpc_set_power_state(pcishpc_t *pcishpc_p, hpc_slot_state_t state) { pcishpc_get_slot_state(pcishpc_p); /* Check to see if the slot is already in this state. */ if (pcishpc_p->slot_state == state) { pcishpc_debug("pcishpc_set_power_state() slot already in " "this state"); return (DDI_SUCCESS); } if ((pcishpc_p->slot_state == HPC_SLOT_EMPTY) && ((state == HPC_SLOT_CONNECTED) || (state == HPC_SLOT_DISCONNECTED))) { pcishpc_debug("pcishpc_set_power_state() slot in " "empty state"); return (DDI_FAILURE); } /* Set the Power LED to blink. */ (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_BLINK); /* Turn all other LEDS off. */ (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF); (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF); (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF); /* Set the slot state to the new slot state. */ pcishpc_p->slot_state = state; /* Set the bus speed only if the bus segment is not running */ if (state == HPC_SLOT_CONNECTED) { if (pcishpc_set_bus_speed(pcishpc_p) != DDI_SUCCESS) return (DDI_FAILURE); pcishpc_p->ctrl->numSlotsConn++; } else { if (--pcishpc_p->ctrl->numSlotsConn == 0) pcishpc_p->ctrl->currBusSpeed = -1; } pcishpc_debug("pcishpc_set_power_state(): ctrl_p 0x%p, " "pcishpc_p 0x%p, slot state 0x%x, current bus speed 0x%x, " "slots connected 0x%x\n", pcishpc_p->ctrl, pcishpc_p, state, pcishpc_p->ctrl->currBusSpeed, pcishpc_p->ctrl->numSlotsConn); /* Mask or Unmask MRL Sensor SEER bit based on new slot state */ if (pcishpc_p->ctrl->has_mrl == B_TRUE) { uint32_t reg; reg = pcishpc_read_reg(pcishpc_p->ctrl, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); reg = (state == HPC_SLOT_CONNECTED) ? (reg & ~SHPC_SLOT_MRL_SERR_MASK) : (reg | SHPC_SLOT_MRL_SERR_MASK); pcishpc_write_reg(pcishpc_p->ctrl, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum, reg); } /* Update the hardweare slot state. */ if (pcishpc_set_slot_state(pcishpc_p) != DDI_SUCCESS) { pcishpc_debug("pcishpc_set_power_state() failed"); (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF); pcishpc_get_slot_state(pcishpc_p); return (DDI_FAILURE); } /* Turn the Power LED ON for a connected slot. */ if (state == HPC_SLOT_CONNECTED) { (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_ON); } /* Turn the Power LED OFF for a disconnected slot. */ if (state == HPC_SLOT_DISCONNECTED) { (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF); } /* Turn all other LEDS off. */ (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF); (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF); (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF); pcishpc_debug("pcishpc_set_power_state() success!"); pcishpc_get_slot_state(pcishpc_p); /* delay after powerON to let the device initialize itself */ delay(drv_usectohz(pcishpc_reset_delay)); return (DDI_SUCCESS); } /* * pcishpc_set_bus_speed() * * Set the bus speed and mode. */ static int pcishpc_set_bus_speed(pcishpc_t *pcishpc_p) { pcishpc_ctrl_t *ctrl_p = pcishpc_p->ctrl; int curr_speed = ctrl_p->currBusSpeed; int speed = -1; int avail_slots; uint32_t status; /* Make sure that the slot is in a correct state */ status = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); /* Return failure if the slot is empty */ if ((status & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK) { pcishpc_debug("pcishpc_set_bus_speed() failed: " "the slot is empty."); return (DDI_FAILURE); } /* Return failure if the slot is not in disabled state */ if ((status & SHPC_SLOT_STATE_MASK) != SHPC_SLOT_DISABLED) { pcishpc_debug("pcishpc_set_bus_speed() failed: " "incorrect slot state."); return (DDI_FAILURE); } /* Set the "power-only" mode for the slot */ if (pcishpc_issue_command(ctrl_p, ((1+pcishpc_p->slotNum)<<8) | SHPC_SLOT_POWER_ONLY) != DDI_SUCCESS) { pcishpc_debug("pcishpc_set_bus_speed() failed to set " "the slot %d in the power-only mode", pcishpc_p->slotNum); return (DDI_FAILURE); } /* Wait for power good */ delay(drv_usectohz(SHPC_POWER_GOOD_WAIT_TIME)); /* Make sure that the slot is in "power-only" state */ status = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); if ((status & SHPC_SLOT_STATE_MASK) != SHPC_SLOT_POWER_ONLY) { pcishpc_debug("pcishpc_set_bus_speed() " "power-only failed: incorrect slot state."); return (DDI_FAILURE); } /* * Check if SHPC has available slots and select the highest * available bus speed for the slot. * * The bus speed codes are: * 100 - 133Mhz; <--+ * 011 - 100Mhz; <--+ PCI-X * 010 - 66Mhz; <--+ * * 001 - 66Mhz; <--+ * 000 - 33Mhz <--+ Conv PCI */ switch (status & SHPC_SLOT_PCIX_CAPABLE_MASK) { case SHPC_SLOT_133MHZ_PCIX_CAPABLE: avail_slots = (ctrl_p->shpc_slots_avail1_reg >> SHPC_AVAIL_133MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK; if (((curr_speed == -1) && avail_slots) || (curr_speed == SHPC_SBCR_133MHZ_PCIX_SPEED)) { speed = SHPC_SBCR_133MHZ_PCIX_SPEED; break; } /* FALLTHROUGH */ case SHPC_SLOT_100MHZ_PCIX_CAPABLE: avail_slots = (ctrl_p->shpc_slots_avail1_reg >> SHPC_AVAIL_100MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK; if (((curr_speed == -1) && avail_slots) || (curr_speed == SHPC_SBCR_100MHZ_PCIX_SPEED)) { speed = SHPC_SBCR_100MHZ_PCIX_SPEED; break; } /* FALLTHROUGH */ case SHPC_SLOT_66MHZ_PCIX_CAPABLE: avail_slots = (ctrl_p->shpc_slots_avail1_reg >> SHPC_AVAIL_66MHZ_PCIX_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK; if (((curr_speed == -1) && avail_slots) || (curr_speed == SHPC_SBCR_66MHZ_PCIX_SPEED)) { speed = SHPC_SBCR_66MHZ_PCIX_SPEED; break; } /* FALLTHROUGH */ default: avail_slots = (ctrl_p->shpc_slots_avail2_reg >> SHPC_AVAIL_66MHZ_CONV_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK; if ((status & SHPC_SLOT_66MHZ_CONV_CAPABLE) && (((curr_speed == -1) && avail_slots) || (curr_speed == SHPC_SBCR_66MHZ_CONV_SPEED))) { speed = SHPC_SBCR_66MHZ_CONV_SPEED; } else { avail_slots = (ctrl_p->shpc_slots_avail1_reg >> SHPC_AVAIL_33MHZ_CONV_SPEED_SHIFT) & SHPC_AVAIL_SPEED_MASK; if (((curr_speed == -1) && (avail_slots)) || (curr_speed == SHPC_SBCR_33MHZ_CONV_SPEED)) { speed = SHPC_SBCR_33MHZ_CONV_SPEED; } else { pcishpc_debug("pcishpc_set_bus_speed() " " failed to set the bus speed, slot# %d", pcishpc_p->slotNum); return (DDI_FAILURE); } } break; } /* * If the bus segment is already running, check to see the card * in the slot can support the current bus speed. */ if (curr_speed == speed) { /* * Check to see there is any slot available for the current * bus speed. Otherwise, we need fail the current slot connect * request. */ return ((avail_slots <= ctrl_p->numSlotsConn) ? DDI_FAILURE : DDI_SUCCESS); } /* Set the bus speed */ if (pcishpc_issue_command(ctrl_p, SHPC_COMM_STS_SET_SPEED | speed) == DDI_FAILURE) { pcishpc_debug("pcishpc_set_bus_speed() failed " "to set bus %d speed", pcishpc_p->slotNum); return (DDI_FAILURE); } /* Check the current bus speed */ status = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG) & SHPC_SBCR_SPEED_MASK; if ((status & SHPC_SBCR_SPEED_MASK) != speed) { pcishpc_debug("pcishpc_set_bus_speed() an incorrect " "bus speed, slot = 0x%x, speed = 0x%x", pcishpc_p->slotNum, status & SHPC_SBCR_SPEED_MASK); return (DDI_FAILURE); } /* Save the current bus speed */ ctrl_p->currBusSpeed = speed; return (DDI_SUCCESS); } /* * pcishpc_disconnect() * * Called by Hot Plug Services to disconnect a slot on the bus. */ /*ARGSUSED*/ static int pcishpc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags) { pcishpc_t *pcishpc_p; pcishpc_debug("pcishpc_disconnect called()"); pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl); if (!pcishpc_p) { pcishpc_debug("pcishpc_disconnect() " "Failed to find soft state for slot_hdl %x", slot_hdl); return (HPC_ERR_FAILED); } mutex_enter(&pcishpc_p->ctrl->shpc_mutex); if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_DISCONNECTED) != DDI_SUCCESS) { pcishpc_debug("pcishpc_disconnect() failed"); goto cleanup; } mutex_exit(&pcishpc_p->ctrl->shpc_mutex); pcishpc_debug("pcishpc_disconnect() success!"); return (HPC_SUCCESS); cleanup: mutex_exit(&pcishpc_p->ctrl->shpc_mutex); return (HPC_ERR_FAILED); } /* * pcishpc_pci_control() * * Called by Hot Plug Services to perform a attachment point specific * operation on a Hot Pluggable Standard PCI Slot. */ /*ARGSUSED*/ static int pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request, caddr_t arg) { hpc_slot_state_t *hpc_slot_state; hpc_board_type_t *hpc_board_type; hpc_led_info_t *hpc_led_info; pcishpc_t *pcishpc_p; int ret = HPC_SUCCESS; pcishpc_debug("pcishpc_pci_control called(Request %s)", pcishpc_textrequest(request)); pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl); if (!pcishpc_p) { pcishpc_debug("pcishpc_pci_control() Error: " "Failed to find soft state for slot_hdl %x", slot_hdl); return (HPC_ERR_FAILED); } mutex_enter(&pcishpc_p->ctrl->shpc_mutex); switch (request) { case HPC_CTRL_GET_SLOT_STATE: hpc_slot_state = (hpc_slot_state_t *)arg; pcishpc_get_slot_state(pcishpc_p); *hpc_slot_state = pcishpc_p->slot_state; pcishpc_debug("pcishpc_pci_control() - " "HPC_CTRL_GET_SLOT_STATE (state=%s)", pcishpc_textslotstate(pcishpc_p->slot_state)); break; case HPC_CTRL_GET_BOARD_TYPE: hpc_board_type = (hpc_board_type_t *)arg; pcishpc_debug("pcishpc_pci_control() - " "HPC_CTRL_GET_BOARD_TYPE"); pcishpc_get_slot_state(pcishpc_p); /* * The HPS framework does not know what board * type is plugged in. */ if (pcishpc_p->slot_state == HPC_SLOT_EMPTY) *hpc_board_type = HPC_BOARD_UNKNOWN; else *hpc_board_type = HPC_BOARD_PCI_HOTPLUG; break; case HPC_CTRL_GET_LED_STATE: hpc_led_info = (hpc_led_info_t *)arg; pcishpc_get_slot_state(pcishpc_p); switch (hpc_led_info->led) { case HPC_FAULT_LED: hpc_led_info->state = pcishpc_p->fault_led_state; pcishpc_debug("pcishpc_pci_control() - " "GET_LED FAULT (state=%s)", pcishpc_textledstate( hpc_led_info->state)); break; case HPC_POWER_LED: hpc_led_info->state = pcishpc_p->power_led_state; pcishpc_debug("pcishpc_pci_control() - " "GET_LED POWER (state=%s)", pcishpc_textledstate( hpc_led_info->state)); break; case HPC_ATTN_LED: hpc_led_info->state = pcishpc_p->attn_led_state; pcishpc_debug("pcishpc_pci_control() - " "GET_LED ATTN(state = %s)", pcishpc_textledstate( hpc_led_info->state)); break; case HPC_ACTIVE_LED: hpc_led_info->state = pcishpc_p->active_led_state; pcishpc_debug("pcishpc_pci_control() - " "GET_LED ACTIVE(state = %s)", pcishpc_textledstate( hpc_led_info->state)); break; default: pcishpc_debug("pcishpc_pci_control() " "Error: GET_LED - " "Invalid LED %x", hpc_led_info->led); ret = HPC_ERR_NOTSUPPORTED; break; } break; case HPC_CTRL_SET_LED_STATE: hpc_led_info = (hpc_led_info_t *)arg; switch (hpc_led_info->led) { case HPC_ATTN_LED: (void) pcishpc_setled(pcishpc_p, hpc_led_info->led, hpc_led_info->state); break; case HPC_POWER_LED: pcishpc_debug("pcishpc_pci_control() " "Error: SET_LED - power LED"); ret = HPC_ERR_NOTSUPPORTED; break; case HPC_FAULT_LED: case HPC_ACTIVE_LED: break; default: pcishpc_debug("pcishpc_pci_control() " "Error: SET_LED - Unknown LED %x", hpc_led_info->led); ret = HPC_ERR_NOTSUPPORTED; break; } break; case HPC_CTRL_DEV_UNCONFIG_FAILURE: case HPC_CTRL_DEV_CONFIG_FAILURE: pcishpc_debug("pcishpc_pci_control() Config/Unconfig " "failed."); (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_BLINK); break; case HPC_CTRL_ENABLE_AUTOCFG: case HPC_CTRL_DISABLE_AUTOCFG: case HPC_CTRL_DISABLE_SLOT: case HPC_CTRL_DEV_UNCONFIGURED: case HPC_CTRL_ENABLE_SLOT: case HPC_CTRL_DISABLE_ENUM: case HPC_CTRL_DEV_UNCONFIG_START: case HPC_CTRL_DEV_CONFIG_START: case HPC_CTRL_DEV_CONFIGURED: pcishpc_debug("pcishpc_pci_control() - %s", pcishpc_textrequest(request)); break; case HPC_CTRL_ENABLE_ENUM: default: pcishpc_debug("pcishpc_pci_control() - Error: " "request (%d) NOT SUPPORTED", request); ret = HPC_ERR_NOTSUPPORTED; break; } mutex_exit(&pcishpc_p->ctrl->shpc_mutex); return (ret); } /* * pcishpc_setled() * * Change the state of a slot's LED. */ static int pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led, hpc_led_state_t state) { switch (led) { case HPC_FAULT_LED: pcishpc_debug("pcishpc_setled() - HPC_FAULT_LED " "(set %s)", pcishpc_textledstate(state)); pcishpc_p->fault_led_state = state; break; case HPC_POWER_LED: pcishpc_debug("pcishpc_setled() - HPC_POWER_LED " "(set %s)", pcishpc_textledstate(state)); pcishpc_p->power_led_state = state; break; case HPC_ATTN_LED: pcishpc_debug("pcishpc_setled() - HPC_ATTN_LED " "(set %s)", pcishpc_textledstate(state)); pcishpc_p->attn_led_state = state; break; case HPC_ACTIVE_LED: pcishpc_debug("pcishpc_setled() - HPC_ACTIVE_LED " "(set %s)", pcishpc_textledstate(state)); pcishpc_p->active_led_state = state; break; } return (pcishpc_set_slot_state(pcishpc_p)); } /* * pcishpc_set_slot_state() * * Updates the slot's state and leds. */ static int pcishpc_set_slot_state(pcishpc_t *pcishpc_p) { uint32_t reg; uint32_t cmd_code; hpc_slot_state_t slot_state; reg = pcishpc_read_reg(pcishpc_p->ctrl, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); /* Default all states to unchanged. */ cmd_code = ((1+pcishpc_p->slotNum)<<8); /* Has the slot state changed? */ if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK) slot_state = HPC_SLOT_EMPTY; else slot_state = pcishpc_slot_shpc_to_hpc(reg & 3); if (pcishpc_p->slot_state != slot_state) { pcishpc_debug("pcishpc_set_slot_state() Slot State changed"); /* Set the new slot state in the Slot operation command. */ cmd_code |= pcishpc_slot_hpc_to_shpc(pcishpc_p->slot_state); } /* Has the Power LED state changed? */ if (pcishpc_p->power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) { pcishpc_debug("pcishpc_set_slot_state() Power LED State " "changed"); /* Set the new power led state in the Slot operation command. */ cmd_code |= (pcishpc_led_hpc_to_shpc(pcishpc_p->power_led_state) << 2); } /* Has the Attn LED state changed? */ if (pcishpc_p->attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) { pcishpc_debug("pcishpc_set_slot_state() Attn LED State " "changed"); /* Set the new attn led state in the Slot operation command. */ cmd_code |= (pcishpc_led_hpc_to_shpc(pcishpc_p->attn_led_state) << 4); } return (pcishpc_issue_command(pcishpc_p->ctrl, cmd_code)); } /* * pcishpc_wait_busy() * * Wait until the SHPC controller is not busy. */ static int pcishpc_wait_busy(pcishpc_ctrl_t *ctrl_p) { uint32_t status; /* Wait until SHPC controller is NOT busy */ /*CONSTCOND*/ while (1) { status = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG); /* Is there an MRL Sensor error? */ if ((status & SHPC_COMM_STS_ERR_MASK) == SHPC_COMM_STS_ERR_MRL_OPEN) { pcishpc_debug("pcishpc_wait_busy() ERROR: MRL Sensor " "error"); break; } /* Is there an Invalid command error? */ if ((status & SHPC_COMM_STS_ERR_MASK) == SHPC_COMM_STS_ERR_INVALID_COMMAND) { pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid " "command error"); break; } /* Is there an Invalid Speed/Mode error? */ if ((status & SHPC_COMM_STS_ERR_MASK) == SHPC_COMM_STS_ERR_INVALID_SPEED) { pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid " "Speed/Mode error"); break; } /* Is the SHPC controller not BUSY? */ if (!(status & SHPC_COMM_STS_CTRL_BUSY)) { /* Return Success. */ return (DDI_SUCCESS); } pcishpc_debug("pcishpc_wait_busy() SHPC controller busy. " "Waiting"); /* Wait before polling the status register again. */ delay(drv_usectohz(SHPC_COMMAND_WAIT_TIME)); } return (DDI_FAILURE); } /* * pcishpc_issue_command() * * Sends a command to the SHPC controller. */ static int pcishpc_issue_command(pcishpc_ctrl_t *ctrl_p, uint32_t cmd_code) { int retCode; pcishpc_debug("pcishpc_issue_command() cmd_code=%02x", cmd_code); mutex_enter(&ctrl_p->shpc_intr_mutex); ctrl_p->command_complete = B_FALSE; /* Write the command to the SHPC controller. */ pcishpc_write_reg(ctrl_p, SHPC_COMMAND_STATUS_REG, cmd_code); while (ctrl_p->command_complete == B_FALSE) cv_wait(&ctrl_p->cmd_comp_cv, &ctrl_p->shpc_intr_mutex); /* Wait until the SHPC controller processes the command. */ retCode = pcishpc_wait_busy(ctrl_p); /* Make sure the command completed. */ if (retCode == DDI_SUCCESS) { /* Did the command fail to generate the command complete IRQ? */ if (ctrl_p->command_complete != B_TRUE) { pcishpc_debug("pcishpc_issue_command() Failed on " "generate cmd complete IRQ"); retCode = DDI_FAILURE; } } mutex_exit(&ctrl_p->shpc_intr_mutex); if (retCode == DDI_FAILURE) pcishpc_debug("pcishpc_issue_command() Failed on cmd_code=%02x", cmd_code); else pcishpc_debug("pcishpc_issue_command() Success on " "cmd_code=%02x", cmd_code); return (retCode); } /* * pcishpc_led_shpc_to_hpc() * * Convert from SHPC indicator status to HPC indicator status. */ static int pcishpc_led_shpc_to_hpc(int state) { switch (state) { case 1: /* SHPC On bits b01 */ return (HPC_LED_ON); case 2: /* SHPC Blink bits b10 */ return (HPC_LED_BLINK); case 3: /* SHPC Off bits b11 */ return (HPC_LED_OFF); } return (HPC_LED_OFF); } /* * pcishpc_led_hpc_to_shpc() * * Convert from HPC indicator status to SHPC indicator status. */ static int pcishpc_led_hpc_to_shpc(int state) { switch (state) { case HPC_LED_ON: return (1); /* SHPC On bits b01 */ case HPC_LED_BLINK: return (2); /* SHPC Blink bits b10 */ case HPC_LED_OFF: return (3); /* SHPC Off bits b11 */ } return (3); /* SHPC Off bits b11 */ } /* * pcishpc_slot_shpc_to_hpc() * * Convert from SHPC slot state to HPC slot state. */ static int pcishpc_slot_shpc_to_hpc(int state) { switch (state) { case 0: /* SHPC Reserved */ return (HPC_SLOT_EMPTY); case 1: /* SHPC Powered Only */ return (HPC_SLOT_UNKNOWN); case 2: /* SHPC Enabled */ return (HPC_SLOT_CONNECTED); case 3: /* SHPC Disabled */ return (HPC_SLOT_DISCONNECTED); } /* Unknown slot state. */ return (HPC_SLOT_UNKNOWN); } /* * pcishpc_slot_hpc_to_shpc() * * Convert from HPC slot state to SHPC slot state. */ static int pcishpc_slot_hpc_to_shpc(int state) { switch (state) { case HPC_SLOT_EMPTY: return (0); /* SHPC Reserved */ case HPC_SLOT_UNKNOWN: return (1); /* SHPC Powered Only */ case HPC_SLOT_CONNECTED: return (2); /* SHPC Enabled */ case HPC_SLOT_DISCONNECTED: return (3); /* SHPC Disabled */ } /* Known slot state is reserved. */ return (0); } /* * pcishpc_get_slot_state() * * Get the state of the slot. */ static void pcishpc_get_slot_state(pcishpc_t *pcishpc_p) { uint32_t reg; /* Read the logical slot register for this Slot. */ reg = pcishpc_read_reg(pcishpc_p->ctrl, SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); /* Convert from the SHPC slot state to the HPC slot state. */ if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK) pcishpc_p->slot_state = HPC_SLOT_EMPTY; else pcishpc_p->slot_state = pcishpc_slot_shpc_to_hpc(reg & 3); /* Convert from the SHPC Power LED state to the HPC Power LED state. */ pcishpc_p->power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3); /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */ pcishpc_p->attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3); /* We don't have a fault LED so just default it to OFF. */ pcishpc_p->fault_led_state = HPC_LED_OFF; /* We don't have an active LED so just default it to OFF. */ pcishpc_p->active_led_state = HPC_LED_OFF; } /* * pcishpc_textledstate() * * Convert the led state into a text message. */ static char * pcishpc_textledstate(hpc_led_state_t state) { /* Convert an HPC led state into a textual string. */ switch (state) { case HPC_LED_OFF: return ("off"); case HPC_LED_ON: return ("on"); case HPC_LED_BLINK: return ("blink"); } return ("unknown"); } /* * pcishpc_textrequest() * * Convert the request into a text message. */ static char * pcishpc_textrequest(int request) { /* Convert an HPC request into a textual string. */ switch (request) { case HPC_CTRL_GET_LED_STATE: return ("HPC_CTRL_GET_LED_STATE"); case HPC_CTRL_SET_LED_STATE: return ("HPC_CTRL_SET_LED_STATE"); case HPC_CTRL_GET_SLOT_STATE: return ("HPC_CTRL_GET_SLOT_STATE"); case HPC_CTRL_DEV_CONFIGURED: return ("HPC_CTRL_DEV_CONFIGURED"); case HPC_CTRL_DEV_UNCONFIGURED: return ("HPC_CTRL_DEV_UNCONFIGURED"); case HPC_CTRL_GET_BOARD_TYPE: return ("HPC_CTRL_GET_BOARD_TYPE"); case HPC_CTRL_DISABLE_AUTOCFG: return ("HPC_CTRL_DISABLE_AUTOCFG"); case HPC_CTRL_ENABLE_AUTOCFG: return ("HPC_CTRL_ENABLE_AUTOCFG"); case HPC_CTRL_DISABLE_SLOT: return ("HPC_CTRL_DISABLE_SLOT"); case HPC_CTRL_ENABLE_SLOT: return ("HPC_CTRL_ENABLE_SLOT"); case HPC_CTRL_DISABLE_ENUM: return ("HPC_CTRL_DISABLE_ENUM"); case HPC_CTRL_ENABLE_ENUM: return ("HPC_CTRL_ENABLE_ENUM"); case HPC_CTRL_DEV_CONFIG_FAILURE: return ("HPC_CTRL_DEV_CONFIG_FAILURE"); case HPC_CTRL_DEV_UNCONFIG_FAILURE: return ("HPC_CTRL_DEV_UNCONFIG_FAILURE"); case HPC_CTRL_DEV_CONFIG_START: return ("HPC_CTRL_DEV_CONFIG_START"); case HPC_CTRL_DEV_UNCONFIG_START: return ("HPC_CTRL_DEV_UNCONFIG_START"); } return ("Unknown"); } /* * pcishpc_textslotstate() * * Convert the request into a text message. */ static char * pcishpc_textslotstate(hpc_slot_state_t state) { /* Convert an HPC slot state into a textual string. */ switch (state) { case HPC_SLOT_EMPTY: return ("HPC_SLOT_EMPTY"); case HPC_SLOT_DISCONNECTED: return ("HPC_SLOT_DISCONNECTED"); case HPC_SLOT_CONNECTED: return ("HPC_SLOT_CONNECTED"); case HPC_SLOT_UNKNOWN: return ("HPC_SLOT_UNKNOWN"); } return ("Unknown"); } /* * pcishpc_write_reg() * * Write to a SHPC controller register. */ static void pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg, uint32_t data) { /* Setup the SHPC dword select register. */ pci_config_put8(ctrl_p->shpc_config_hdl, ctrl_p->shpc_dword_select, (uint8_t)reg); /* Read back the SHPC dword select register and verify. */ if (pci_config_get8(ctrl_p->shpc_config_hdl, ctrl_p->shpc_dword_select) != (uint8_t)reg) { pcishpc_debug("pcishpc_write_reg() - Failed writing " "DWORD select reg"); return; } /* Write to the SHPC dword data register. */ pci_config_put32(ctrl_p->shpc_config_hdl, ctrl_p->shpc_dword_data_reg, data); /* * Issue a read of the VendorID/DeviceID just to force the previous * write to complete. This is probably not necessary, but it does * help enforce ordering if there is an issue. */ (void) pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_VENID); } /* * pcishpc_read_reg() * * Read from a SHPC controller register. */ static uint32_t pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg) { /* Setup the SHPC dword select register. */ pci_config_put8(ctrl_p->shpc_config_hdl, ctrl_p->shpc_dword_select, (uint8_t)reg); /* Read back the SHPC dword select register and verify. */ if (pci_config_get8(ctrl_p->shpc_config_hdl, ctrl_p->shpc_dword_select) != (uint8_t)reg) { pcishpc_debug("pcishpc_read_reg() - Failed writing DWORD " "select reg"); return (0xFFFFFFFF); } /* Read from the SHPC dword data register. */ return (pci_config_get32(ctrl_p->shpc_config_hdl, ctrl_p->shpc_dword_data_reg)); } /* * pcishpc_debug() * * Controls debug output if enabled. */ static void pcishpc_debug(char *fmt, ...) { va_list ap; if (pcishpc_debug_enabled) { va_start(ap, fmt); vcmn_err(CE_WARN, fmt, ap); va_end(ap); } } /* * pcishpc_dump_regs() * * Dumps all of the SHPC controller registers. */ static void pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p) { int slot, numSlots; uint32_t reg; char *state; cmn_err(CE_WARN, "pcishpc_dump_regs() called:"); cmn_err(CE_WARN, "================================================" "=========="); cmn_err(CE_WARN, "SHPC Base Offset " ": 0x%08x", pcishpc_read_reg(ctrl_p, SHPC_BASE_OFFSET_REG)); reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_I_REG); cmn_err(CE_WARN, "Number of PCIX slots avail (33 Mhz) : %d", (reg & 31)); cmn_err(CE_WARN, "Number of PCIX slots avail (66 Mhz) : %d", ((reg>>8) & 31)); cmn_err(CE_WARN, "Number of PCIX slots avail (100 Mhz) : %d", ((reg>>16) & 31)); cmn_err(CE_WARN, "Number of PCIX slots avail (133 Mhz) : %d", ((reg>>24) & 31)); reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_II_REG); cmn_err(CE_WARN, "Number of conventional PCI slots (66 Mhz) : %d", (reg & 31)); reg = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG); numSlots = (reg & 31); cmn_err(CE_WARN, "Number of Slots connected to this port : %d", numSlots); cmn_err(CE_WARN, "PCI Device # for First HotPlug Slot : %d", ((reg>>8) & 31)); cmn_err(CE_WARN, "Physical Slot # for First PCI Device # : %d", ((reg>>16) & 0x7ff)); cmn_err(CE_WARN, "Physical Slot Number Up/Down " ": %d", ((reg>>29) & 0x1)); cmn_err(CE_WARN, "MRL Sensor Implemented " ": %s", (reg & SHPC_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : "No"); cmn_err(CE_WARN, "Attention Button Implemented " ": %s", (reg & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : "No"); reg = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG); switch (reg & 7) { case 0: state = "33Mhz Conventional PCI"; break; case 1: state = "66Mhz Conventional PCI"; break; case 2: state = "66Mhz PCI-X"; break; case 3: state = "100Mhz PCI-X"; break; case 4: state = "133Mhz PCI-X"; break; default: state = "Reserved (Error)"; break; } cmn_err(CE_WARN, "Current Port Operation Mode " ": %s", state); cmn_err(CE_WARN, "SHPC Interrupt Message Number " ": %d", ((reg>>16) &31)); cmn_err(CE_WARN, "SHPC Programming Interface " ": %d", ((reg>>24) & 0xff)); reg = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG); cmn_err(CE_WARN, "SHPC Command Code " ": %d", (reg & 0xff)); cmn_err(CE_WARN, "SHPC Target Slot " ": %d", ((reg>>8) & 31)); cmn_err(CE_WARN, "SHPC Controller Busy " ": %s", ((reg>>16) & 1) ? "Yes" : "No"); cmn_err(CE_WARN, "SHPC Controller Err: MRL Sensor " ": %s", ((reg>>17) & 1) ? "Yes" : "No"); cmn_err(CE_WARN, "SHPC Controller Err: Invalid Command : %s", ((reg>>18) & 1) ? "Yes" : "No"); cmn_err(CE_WARN, "SHPC Controller Err: Invalid Speed/Mode : %s", ((reg>>19) & 1) ? "Yes" : "No"); reg = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG); cmn_err(CE_WARN, "Command Completion Interrupt Pending : %s", (reg & SHPC_IRQ_CMD_COMPLETE) ? "Yes" : "No"); for (slot = 0; slot < numSlots; slot++) { cmn_err(CE_WARN, "Slot %d Interrupt Pending " ": %s", slot+1, (reg & (SHPC_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No"); } reg = pcishpc_read_reg(ctrl_p, SHPC_SERR_LOCATOR_REG); cmn_err(CE_WARN, "Arbiter SERR Pending " ": %s", (reg & SHPC_IRQ_SERR_ARBITER_PENDING) ? "Yes" : "No"); for (slot = 0; slot < numSlots; slot++) { cmn_err(CE_WARN, "Slot %d SERR Pending " ": %s", slot+1, (reg & (SHPC_IRQ_SERR_SLOT_N_PENDING<<slot)) ? "Yes" : "No"); } reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); cmn_err(CE_WARN, "Global Interrupt Mask " ": %s", (reg & SHPC_SERR_INT_GLOBAL_IRQ_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Global SERR Mask " ": %s", (reg & SHPC_SERR_INT_GLOBAL_SERR_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Command Completion Interrupt Mask " ": %s", (reg & SHPC_SERR_INT_CMD_COMPLETE_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Arbiter SERR Mask " ": %s", (reg & SHPC_SERR_INT_ARBITER_SERR_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Command Completion Detected " ": %s", (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) ? "Yes" : "No"); cmn_err(CE_WARN, "Arbiter Timeout Detected " ": %s", (reg & SHPC_SERR_INT_ARBITER_IRQ) ? "Yes" : "No"); for (slot = 0; slot < numSlots; slot++) { cmn_err(CE_WARN, "Logical Slot %d Registers:", slot+1); cmn_err(CE_WARN, "------------------------------------"); reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); cmn_err(CE_WARN, "Slot %d state " ": %s", slot+1, pcishpc_textslotstate(pcishpc_slot_shpc_to_hpc( (reg & 3)))); cmn_err(CE_WARN, "Slot %d Power Indicator State " ": %s", slot+1, pcishpc_textledstate(pcishpc_led_shpc_to_hpc( (reg>>2) &3))); cmn_err(CE_WARN, "Slot %d Attention Indicator State " ": %s", slot+1, pcishpc_textledstate(pcishpc_led_shpc_to_hpc( (reg>>4)&3))); cmn_err(CE_WARN, "Slot %d Power Fault " ": %s", slot+1, ((reg>>6)&1) ? "Fault Detected" : "No Fault"); cmn_err(CE_WARN, "Slot %d Attention Button " ": %s", slot+1, ((reg>>7)&1) ? "Depressed" : "Not Depressed"); cmn_err(CE_WARN, "Slot %d MRL Sensor " ": %s", slot+1, ((reg>>8)&1) ? "Not Closed" : "Closed"); cmn_err(CE_WARN, "Slot %d 66mhz Capable " ": %s", slot+1, ((reg>>9)&1) ? "66mhz" : "33mgz"); switch ((reg>>10)&3) { case 0: state = "Card Present 7.5W"; break; case 1: state = "Card Present 15W"; break; case 2: state = "Card Present 25W"; break; case 3: state = "Slot Empty"; break; } cmn_err(CE_WARN, "Slot %d PRSNT1#/PRSNT2# " ": %s", slot+1, state); switch ((reg>>12)&3) { case 0: state = "Non PCI-X"; break; case 1: state = "66mhz PCI-X"; break; case 2: state = "Reserved"; break; case 3: state = "133mhz PCI-X"; break; } cmn_err(CE_WARN, "Slot %d Card Presence Change Detected : %s", slot+1, (reg & SHPC_SLOT_PRESENCE_DETECTED) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Isolated Power Fault Detected : %s", slot+1, (reg & SHPC_SLOT_ISO_PWR_DETECTED) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Attention Button Press Detected" ": %s", slot+1, (reg & SHPC_SLOT_ATTN_DETECTED) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d MRL Sensor Change Detected " ": %s", slot+1, (reg & SHPC_SLOT_MRL_DETECTED) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Connected Power Fault Detected" ": %s", slot+1, (reg & SHPC_SLOT_POWER_DETECTED) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Card Presence IRQ Masked " ": %s", slot+1, (reg & SHPC_SLOT_PRESENCE_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Isolated Power Fault IRQ Masked" ": %s", slot+1, (reg & SHPC_SLOT_ISO_PWR_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Attention Button IRQ Masked " ": %s", slot+1, (reg & SHPC_SLOT_ATTN_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d MRL Sensor IRQ Masked " ": %s", slot+1, (reg & SHPC_SLOT_MRL_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Connected Power Fault IRQ Masked" " : %s", slot+1, (reg & SHPC_SLOT_POWER_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d MRL Sensor SERR Masked " ": %s", slot+1, (reg & SHPC_SLOT_MRL_SERR_MASK) ? "Yes" : "No"); cmn_err(CE_WARN, "Slot %d Connected Power Fault SERR Masked :" "%s", slot+1, (reg & SHPC_SLOT_POWER_SERR_MASK) ? "Yes" : "No"); } } static void pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p) { hpc_led_state_t power_led_state; callb_cpr_t cprinfo; pcishpc_debug("pcishpc_attn_btn_handler: thread started\n"); CALLB_CPR_INIT(&cprinfo, &pcishpc_p->ctrl->shpc_mutex, callb_generic_cpr, "pcishpc_attn_btn_handler"); mutex_enter(&pcishpc_p->ctrl->shpc_mutex); /* wait for ATTN button event */ cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex); while (pcishpc_p->attn_btn_thread_exit == B_FALSE) { if (pcishpc_p->attn_btn_pending == B_TRUE) { /* get the current state of power LED */ power_led_state = pcishpc_p->power_led_state; /* Blink the Power LED while we wait for 5 seconds */ (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_BLINK); /* wait for 5 seconds before taking any action */ if (cv_timedwait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex, ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) { /* * It is a time out; * make sure the ATTN pending flag is * still ON before sending the event * to HPS framework. */ if (pcishpc_p->attn_btn_pending == B_TRUE) { /* * send the ATTN button event * to HPS framework */ pcishpc_p->attn_btn_pending = B_FALSE; (void) hpc_slot_event_notify( pcishpc_p->slot_handle, HPC_EVENT_SLOT_ATTN, HPC_EVENT_NORMAL); } } /* restore the power LED state ??? XXX */ (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, power_led_state); continue; } /* wait for another ATTN button event */ cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex); } pcishpc_debug("pcishpc_attn_btn_handler: thread exit\n"); cv_signal(&pcishpc_p->attn_btn_cv); CALLB_CPR_EXIT(&cprinfo); thread_exit(); } /* * setup slot name/slot-number info. */ static void pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot) { pcishpc_t *p = ctrl_p->slots[slot]; uchar_t *slotname_data; int *slotnum; uint_t count; int len; uchar_t *s; uint32_t bit_mask; int pci_id_cnt, pci_id_bit; int slots_before, found; int invalid_slotnum = 0; if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->shpc_dip, DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) == DDI_PROP_SUCCESS) { p->phy_slot_num = slotnum[0]; ddi_prop_free(slotnum); } else { if (ctrl_p->deviceIncreases) p->phy_slot_num = ctrl_p->physStart + slot; else p->phy_slot_num = ctrl_p->physStart - slot; if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->shpc_dip, "physical-slot#", p->phy_slot_num)) != DDI_SUCCESS) { pcishpc_debug("pcishpc_set_slot_name(): failed to " "create phyical-slot#%d", p->phy_slot_num); } } if (!p->phy_slot_num) { /* platform may not have initialized it */ p->phy_slot_num = pci_config_get8(ctrl_p->shpc_config_hdl, PCI_BCNF_SECBUS); invalid_slotnum = 1; } /* * construct the slot_name: * if "slot-names" property exists then use that name * else if valid slot number exists then it is "pci<slot-num>". * else it will be "pci<sec-bus-number>dev<dev-number>" */ if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->shpc_dip, DDI_PROP_DONTPASS, "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) { bit_mask = slotname_data[3] | (slotname_data[2] << 8) | (slotname_data[1] << 16) | (slotname_data[0] << 24); pci_id_bit = 1; pci_id_cnt = slots_before = found = 0; /* * Walk the bit mask until we find the bit that corresponds * to our slots device number. We count how many bits * we find before we find our slot's bit. */ while (!found && (pci_id_cnt < 32)) { while (p->deviceNum != pci_id_cnt) { /* * Find the next bit set. */ while (!(bit_mask & pci_id_bit) && (pci_id_cnt < 32)) { pci_id_bit = pci_id_bit << 1; pci_id_cnt++; } if (p->deviceNum != pci_id_cnt) slots_before++; else found = 1; } } if (pci_id_cnt < 32) { /* * Set ptr to first string. */ s = slotname_data + 4; /* * Increment past all the strings for the slots * before ours. */ while (slots_before) { while (*s != NULL) s++; s++; slots_before--; } (void) sprintf(p->slot_info.pci_slot_name, (char *)s); kmem_free(slotname_data, len); return; } /* slot-names entry not found */ pcishpc_debug("pcishpc_set_slot_name(): " "No slot-names entry found for slot #%d", p->phy_slot_num); kmem_free(slotname_data, len); } if (invalid_slotnum) (void) sprintf(p->slot_info.pci_slot_name, "pci%d", p->deviceNum); else (void) sprintf(p->slot_info.pci_slot_name, "pci%d", p->phy_slot_num); }