126947304SEvan Yan /* 226947304SEvan Yan * CDDL HEADER START 326947304SEvan Yan * 426947304SEvan Yan * The contents of this file are subject to the terms of the 526947304SEvan Yan * Common Development and Distribution License (the "License"). 626947304SEvan Yan * You may not use this file except in compliance with the License. 726947304SEvan Yan * 826947304SEvan Yan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 926947304SEvan Yan * or http://www.opensolaris.org/os/licensing. 1026947304SEvan Yan * See the License for the specific language governing permissions 1126947304SEvan Yan * and limitations under the License. 1226947304SEvan Yan * 1326947304SEvan Yan * When distributing Covered Code, include this CDDL HEADER in each 1426947304SEvan Yan * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1526947304SEvan Yan * If applicable, add the following below this CDDL HEADER, with the 1626947304SEvan Yan * fields enclosed by brackets "[]" replaced with your own identifying 1726947304SEvan Yan * information: Portions Copyright [yyyy] [name of copyright owner] 1826947304SEvan Yan * 1926947304SEvan Yan * CDDL HEADER END 2026947304SEvan Yan */ 2126947304SEvan Yan /* 2270f83219SEvan Yan * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2326947304SEvan Yan * Use is subject to license terms. 24*b3d69c05SRobert Mustacchi * Copyright 2019 Joyent, Inc. 2526947304SEvan Yan */ 2626947304SEvan Yan 2726947304SEvan Yan /* 2826947304SEvan Yan * This file contains PCI HotPlug functionality that is compatible with the 2926947304SEvan Yan * PCI SHPC specification 1.x. 3026947304SEvan Yan * 3126947304SEvan Yan * NOTE: This file is compiled and delivered through misc/pcie module. 3226947304SEvan Yan */ 3326947304SEvan Yan 3426947304SEvan Yan #include <sys/note.h> 3526947304SEvan Yan #include <sys/conf.h> 3626947304SEvan Yan #include <sys/kmem.h> 3726947304SEvan Yan #include <sys/kstat.h> 3826947304SEvan Yan #include <sys/debug.h> 3926947304SEvan Yan #include <sys/vtrace.h> 4026947304SEvan Yan #include <sys/autoconf.h> 4126947304SEvan Yan #include <sys/varargs.h> 4226947304SEvan Yan #include <sys/hwconf.h> 4326947304SEvan Yan #include <sys/ddi_impldefs.h> 4426947304SEvan Yan #include <sys/callb.h> 4526947304SEvan Yan #include <sys/ddi.h> 4626947304SEvan Yan #include <sys/sunddi.h> 4726947304SEvan Yan #include <sys/sunndi.h> 4826947304SEvan Yan #include <sys/sysevent/dr.h> 4926947304SEvan Yan #include <sys/ndi_impldefs.h> 5026947304SEvan Yan #include <sys/pci_impl.h> 5126947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h> 5226947304SEvan Yan #include <sys/hotplug/pci/pcishpc.h> 5326947304SEvan Yan 5426947304SEvan Yan typedef struct pcishpc_prop { 5526947304SEvan Yan char *prop_name; 5626947304SEvan Yan char *prop_value; 5726947304SEvan Yan } pcishpc_prop_t; 5826947304SEvan Yan 5926947304SEvan Yan static pcishpc_prop_t pcishpc_props[] = { 6026947304SEvan Yan { PCIEHPC_PROP_LED_FAULT, PCIEHPC_PROP_VALUE_LED }, 6126947304SEvan Yan { PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED }, 6226947304SEvan Yan { PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED }, 6326947304SEvan Yan { PCIEHPC_PROP_LED_ACTIVE, PCIEHPC_PROP_VALUE_LED }, 6426947304SEvan Yan { PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE }, 6526947304SEvan Yan { PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE }, 6626947304SEvan Yan { PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE } 6726947304SEvan Yan }; 6826947304SEvan Yan 6926947304SEvan Yan /* reset delay to 1 sec. */ 7026947304SEvan Yan static int pcishpc_reset_delay = 1000000; 7126947304SEvan Yan 7226947304SEvan Yan /* Local function prototype */ 7326947304SEvan Yan static pcie_hp_ctrl_t *pcishpc_create_controller(dev_info_t *dip); 7426947304SEvan Yan static int pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p); 7526947304SEvan Yan static int pcishpc_destroy_controller(dev_info_t *dip); 7626947304SEvan Yan static pcie_hp_slot_t *pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p); 7726947304SEvan Yan static int pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot); 7826947304SEvan Yan static int pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p); 7926947304SEvan Yan static int pcishpc_slot_get_property(pcie_hp_slot_t *slot_p, 8026947304SEvan Yan ddi_hp_property_t *arg, ddi_hp_property_t *rval); 8126947304SEvan Yan static int pcishpc_slot_set_property(pcie_hp_slot_t *slot_p, 8226947304SEvan Yan ddi_hp_property_t *arg, ddi_hp_property_t *rval); 8326947304SEvan Yan static int pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p, 8426947304SEvan Yan uint32_t cmd_code); 8526947304SEvan Yan static int pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p); 8626947304SEvan Yan static void pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p); 8726947304SEvan Yan static void pcishpc_get_slot_state(pcie_hp_slot_t *slot_p); 8826947304SEvan Yan static int pcishpc_set_slot_state(pcie_hp_slot_t *slot_p, 8926947304SEvan Yan ddi_hp_cn_state_t new_slot_state); 9026947304SEvan Yan static void pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot); 9126947304SEvan Yan static int pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p); 9226947304SEvan Yan static int pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led, 9326947304SEvan Yan pcie_hp_led_state_t state); 9426947304SEvan Yan static int pcishpc_led_shpc_to_hpc(int state); 9526947304SEvan Yan static int pcishpc_led_hpc_to_shpc(int state); 9626947304SEvan Yan static int pcishpc_slot_shpc_to_hpc(int shpc_state); 9726947304SEvan Yan static int pcishpc_slot_hpc_to_shpc(int state); 9826947304SEvan Yan static char *pcishpc_slot_textslotstate(ddi_hp_cn_state_t state); 9926947304SEvan Yan static char *pcishpc_slot_textledstate(pcie_hp_led_state_t state); 10026947304SEvan Yan 10126947304SEvan Yan static uint32_t pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg); 10226947304SEvan Yan static void pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg, 10326947304SEvan Yan uint32_t data); 10426947304SEvan Yan 10526947304SEvan Yan static int pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p, 10626947304SEvan Yan ddi_hp_cn_state_t target_state); 10726947304SEvan Yan static int pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p, 10826947304SEvan Yan ddi_hp_cn_state_t target_state); 10926947304SEvan Yan static int pcishpc_change_slot_state(pcie_hp_slot_t *slot_p, 11026947304SEvan Yan ddi_hp_cn_state_t target_state); 11126947304SEvan Yan 11226947304SEvan Yan static int pcishpc_slot_poweron(pcie_hp_slot_t *slot_p, 11326947304SEvan Yan ddi_hp_cn_state_t *result_state); 11426947304SEvan Yan static int pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p, 11526947304SEvan Yan ddi_hp_cn_state_t *result_state); 11626947304SEvan Yan static int pcishpc_slot_probe(pcie_hp_slot_t *slot_p); 11726947304SEvan Yan static int pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p); 11826947304SEvan Yan #ifdef DEBUG 11926947304SEvan Yan static void pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p); 12026947304SEvan Yan #endif /* DEBUG */ 12126947304SEvan Yan 12226947304SEvan Yan 12326947304SEvan Yan /* 12426947304SEvan Yan * Global functions (called by other drivers/modules) 12526947304SEvan Yan */ 12626947304SEvan Yan 12726947304SEvan Yan /* 12826947304SEvan Yan * pcishpc_init() 12926947304SEvan Yan * 13026947304SEvan Yan * Install and configure an SHPC controller and register the HotPlug slots 13126947304SEvan Yan * with the Solaris HotPlug framework. This function is usually called by 13226947304SEvan Yan * a PCI bridge Nexus driver that has a built in SHPC controller. 13326947304SEvan Yan */ 13426947304SEvan Yan int 13526947304SEvan Yan pcishpc_init(dev_info_t *dip) 13626947304SEvan Yan { 13726947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 13826947304SEvan Yan pcie_hp_ctrl_t *ctrl_p; 13926947304SEvan Yan int i; 14026947304SEvan Yan 14126947304SEvan Yan PCIE_DBG("pcishpc_init() called from %s#%d\n", 14226947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip)); 14326947304SEvan Yan 14426947304SEvan Yan if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) { 14526947304SEvan Yan PCIE_DBG("pcishpc_init() shpc instance already " 14626947304SEvan Yan "initialized!\n"); 14726947304SEvan Yan return (DDI_SUCCESS); 14826947304SEvan Yan } 14926947304SEvan Yan 15026947304SEvan Yan /* Initialize soft state structure for the SHPC instance. */ 15126947304SEvan Yan ctrl_p = pcishpc_create_controller(dip); 15226947304SEvan Yan 15326947304SEvan Yan if (ctrl_p == NULL) { 15426947304SEvan Yan PCIE_DBG("pcishpc_init() failed to create shpc softstate\n"); 15526947304SEvan Yan return (DDI_FAILURE); 15626947304SEvan Yan } 15726947304SEvan Yan 15826947304SEvan Yan if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) { 15926947304SEvan Yan PCIE_DBG("pcishpc_init() failed to setup controller\n"); 16026947304SEvan Yan goto cleanup; 16126947304SEvan Yan } 16226947304SEvan Yan 16326947304SEvan Yan /* 16426947304SEvan Yan * Setup resource maps for this bus node. 16526947304SEvan Yan */ 16626947304SEvan Yan (void) pci_resource_setup(dip); 16726947304SEvan Yan 16826947304SEvan Yan #ifdef DEBUG 16926947304SEvan Yan PCIE_DBG("%s%d: P2P bridge register dump:\n", 17026947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip)); 17126947304SEvan Yan 17226947304SEvan Yan for (i = 0; i < 0x100; i += 4) { 17326947304SEvan Yan PCIE_DBG("SHPC Cfg reg 0x%02x: %08x\n", i, 17426947304SEvan Yan pci_config_get32(bus_p->bus_cfg_hdl, i)); 17526947304SEvan Yan } 17626947304SEvan Yan #endif /* DEBUG */ 17726947304SEvan Yan 17826947304SEvan Yan /* Setup each HotPlug slot on this SHPC controller. */ 17926947304SEvan Yan for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) { 18026947304SEvan Yan if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) { 18126947304SEvan Yan PCIE_DBG("pcishpc_init() failed to register " 18226947304SEvan Yan "slot %d\n", i); 18326947304SEvan Yan goto cleanup1; 18426947304SEvan Yan } 18526947304SEvan Yan if (pcie_create_minor_node(ctrl_p, i) != DDI_SUCCESS) { 18626947304SEvan Yan PCIE_DBG("pcishpc_init() failed to create " 18726947304SEvan Yan "minor node for slot %d\n", i); 18826947304SEvan Yan goto cleanup1; 18926947304SEvan Yan } 19026947304SEvan Yan } 19126947304SEvan Yan 19226947304SEvan Yan #ifdef DEBUG 19326947304SEvan Yan /* Dump out the SHPC registers. */ 19426947304SEvan Yan pcishpc_dump_regs(ctrl_p); 19526947304SEvan Yan #endif /* DEBUG */ 19626947304SEvan Yan 19726947304SEvan Yan PCIE_DBG("pcishpc_init() success(dip=%p)\n", dip); 19826947304SEvan Yan return (DDI_SUCCESS); 19926947304SEvan Yan 20026947304SEvan Yan cleanup1: 20126947304SEvan Yan for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) { 20226947304SEvan Yan if (ctrl_p->hc_slots[i] == NULL) 20326947304SEvan Yan continue; 20426947304SEvan Yan 20526947304SEvan Yan pcie_remove_minor_node(ctrl_p, i); 20626947304SEvan Yan } 20726947304SEvan Yan (void) pci_resource_destroy(dip); 20826947304SEvan Yan cleanup: 20926947304SEvan Yan (void) pcishpc_destroy_controller(dip); 21026947304SEvan Yan return (DDI_FAILURE); 21126947304SEvan Yan } 21226947304SEvan Yan 21326947304SEvan Yan /* 21426947304SEvan Yan * pcishpc_uninit() 21526947304SEvan Yan * Unload the HogPlug controller driver and deallocate all resources. 21626947304SEvan Yan */ 21726947304SEvan Yan int 21826947304SEvan Yan pcishpc_uninit(dev_info_t *dip) 21926947304SEvan Yan { 22026947304SEvan Yan pcie_hp_ctrl_t *ctrl_p; 22126947304SEvan Yan int i; 22226947304SEvan Yan 22326947304SEvan Yan PCIE_DBG("pcishpc_uninit() called(dip=%p)\n", dip); 22426947304SEvan Yan 22526947304SEvan Yan ctrl_p = PCIE_GET_HP_CTRL(dip); 22626947304SEvan Yan 22726947304SEvan Yan if (!ctrl_p) { 22826947304SEvan Yan PCIE_DBG("pcishpc_uninit() Unable to find softstate\n"); 22926947304SEvan Yan return (DDI_FAILURE); 23026947304SEvan Yan } 23126947304SEvan Yan 23226947304SEvan Yan for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) { 23326947304SEvan Yan if (ctrl_p->hc_slots[i] == NULL) 23426947304SEvan Yan continue; 23526947304SEvan Yan 23626947304SEvan Yan pcie_remove_minor_node(ctrl_p, i); 23726947304SEvan Yan } 23826947304SEvan Yan 23926947304SEvan Yan ctrl_p->hc_flags = 0; 24026947304SEvan Yan 24126947304SEvan Yan /* 24226947304SEvan Yan * Destroy resource maps for this bus node. 24326947304SEvan Yan */ 24426947304SEvan Yan (void) pci_resource_destroy(dip); 24526947304SEvan Yan 24626947304SEvan Yan (void) pcishpc_destroy_controller(dip); 24726947304SEvan Yan 24826947304SEvan Yan PCIE_DBG("pcishpc_uninit() success(dip=%p)\n", dip); 24926947304SEvan Yan 25026947304SEvan Yan return (DDI_SUCCESS); 25126947304SEvan Yan } 25226947304SEvan Yan 25326947304SEvan Yan /* 25426947304SEvan Yan * pcishpc_intr() 25526947304SEvan Yan * 25626947304SEvan Yan * This is the SHPC controller interrupt handler. 25726947304SEvan Yan */ 25826947304SEvan Yan int 25926947304SEvan Yan pcishpc_intr(dev_info_t *dip) 26026947304SEvan Yan { 26126947304SEvan Yan pcie_hp_ctrl_t *ctrl_p; 26226947304SEvan Yan uint32_t irq_locator, irq_serr_locator, reg; 26326947304SEvan Yan int slot; 26426947304SEvan Yan 26526947304SEvan Yan PCIE_DBG("pcishpc_intr() called\n"); 26626947304SEvan Yan 26726947304SEvan Yan /* get the soft state structure for this dip */ 26826947304SEvan Yan if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) 26926947304SEvan Yan return (DDI_INTR_UNCLAIMED); 27026947304SEvan Yan 27126947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 27226947304SEvan Yan 27326947304SEvan Yan if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) { 27426947304SEvan Yan PCIE_DBG("pcishpc_intr() unclaimed\n"); 27526947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 27626947304SEvan Yan return (DDI_INTR_UNCLAIMED); 27726947304SEvan Yan } 27826947304SEvan Yan 27926947304SEvan Yan PCIE_DBG("pcishpc_intr() interrupt received\n"); 28026947304SEvan Yan 28126947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG); 28226947304SEvan Yan 28326947304SEvan Yan if (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) { 28426947304SEvan Yan PCIE_DBG("pcishpc_intr() " 28526947304SEvan Yan "PCI_HP_SERR_INT_CMD_COMPLETE_IRQ detected\n"); 28626947304SEvan Yan ctrl_p->hc_cmd_pending = B_FALSE; 28726947304SEvan Yan cv_signal(&ctrl_p->hc_cmd_comp_cv); 28826947304SEvan Yan } 28926947304SEvan Yan 29026947304SEvan Yan if (reg & PCI_HP_SERR_INT_ARBITER_IRQ) { 29126947304SEvan Yan PCIE_DBG("pcishpc_intr() PCI_HP_SERR_INT_ARBITER_IRQ " 29226947304SEvan Yan "detected\n"); 29326947304SEvan Yan ctrl_p->hc_arbiter_timeout = B_TRUE; 29426947304SEvan Yan } 29526947304SEvan Yan 29626947304SEvan Yan /* Write back the SERR INT register to acknowledge the IRQs. */ 29726947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg); 29826947304SEvan Yan 29926947304SEvan Yan irq_locator = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG); 30026947304SEvan Yan irq_serr_locator = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG); 30126947304SEvan Yan 30226947304SEvan Yan /* Check for slot events that might have occured. */ 30326947304SEvan Yan for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) { 30426947304SEvan Yan if ((irq_locator & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) || 30526947304SEvan Yan (irq_serr_locator & 30626947304SEvan Yan (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot))) { 30726947304SEvan Yan PCIE_DBG("pcishpc_intr() slot %d and " 30826947304SEvan Yan "pending IRQ\n", slot+1); 30926947304SEvan Yan 31026947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, 31126947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot); 31226947304SEvan Yan 31326947304SEvan Yan if (reg & PCI_HP_SLOT_PRESENCE_DETECTED) 31426947304SEvan Yan PCIE_DBG("slot %d: " 31526947304SEvan Yan "PCI_HP_SLOT_PRESENCE_DETECTED\n", 31626947304SEvan Yan slot+1); 31726947304SEvan Yan 31826947304SEvan Yan if (reg & PCI_HP_SLOT_ISO_PWR_DETECTED) 31926947304SEvan Yan PCIE_DBG("slot %d: " 32026947304SEvan Yan "PCI_HP_SLOT_ISO_PWR_DETECTED\n", 32126947304SEvan Yan slot+1); 32226947304SEvan Yan 32326947304SEvan Yan if (reg & PCI_HP_SLOT_ATTN_DETECTED) { 32426947304SEvan Yan PCIE_DBG("slot %d: " 32526947304SEvan Yan "PCI_HP_SLOT_ATTN_DETECTED\n", slot+1); 32626947304SEvan Yan 32726947304SEvan Yan /* 32826947304SEvan Yan * if ATTN button event is still pending 32926947304SEvan Yan * then cancel it 33026947304SEvan Yan */ 33126947304SEvan Yan if (ctrl_p->hc_slots[slot]-> 33226947304SEvan Yan hs_attn_btn_pending == B_TRUE) 33326947304SEvan Yan ctrl_p->hc_slots[slot]-> 33426947304SEvan Yan hs_attn_btn_pending = B_FALSE; 33526947304SEvan Yan 33626947304SEvan Yan /* wake up the ATTN event handler */ 33726947304SEvan Yan cv_signal(&ctrl_p->hc_slots[slot]-> 33826947304SEvan Yan hs_attn_btn_cv); 33926947304SEvan Yan } 34026947304SEvan Yan 34126947304SEvan Yan if (reg & PCI_HP_SLOT_MRL_DETECTED) 34226947304SEvan Yan PCIE_DBG("slot %d: " 34326947304SEvan Yan "PCI_HP_SLOT_MRL_DETECTED\n", slot+1); 34426947304SEvan Yan 34526947304SEvan Yan if (reg & PCI_HP_SLOT_POWER_DETECTED) 34626947304SEvan Yan PCIE_DBG("slot %d: " 34726947304SEvan Yan "PCI_HP_SLOT_POWER_DETECTED\n", slot+1); 34826947304SEvan Yan 34926947304SEvan Yan /* Acknoledge any slot interrupts */ 35026947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, 35126947304SEvan Yan reg); 35226947304SEvan Yan } 35326947304SEvan Yan } 35426947304SEvan Yan 35526947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 35626947304SEvan Yan 35726947304SEvan Yan PCIE_DBG("pcishpc_intr() claimed\n"); 35826947304SEvan Yan 35926947304SEvan Yan return (DDI_INTR_CLAIMED); 36026947304SEvan Yan } 36126947304SEvan Yan 36226947304SEvan Yan int 36326947304SEvan Yan pcishpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg, 36426947304SEvan Yan ddi_hp_property_t *rval) 36526947304SEvan Yan { 36626947304SEvan Yan ddi_hp_property_t request, result; 36726947304SEvan Yan #ifdef _SYSCALL32_IMPL 36826947304SEvan Yan ddi_hp_property32_t request32, result32; 36926947304SEvan Yan #endif 37026947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 371296f12dcSToomas Soome nvlist_t *prop_list; 37226947304SEvan Yan nvlist_t *prop_rlist; /* nvlist for return values */ 373296f12dcSToomas Soome nvpair_t *prop_pair; 374296f12dcSToomas Soome char *name, *value; 37526947304SEvan Yan int ret = DDI_SUCCESS; 37626947304SEvan Yan int i, n; 37726947304SEvan Yan boolean_t get_all_prop = B_FALSE; 37826947304SEvan Yan 37926947304SEvan Yan if (get_udatamodel() == DATAMODEL_NATIVE) { 38026947304SEvan Yan if (copyin(arg, &request, sizeof (ddi_hp_property_t)) || 38126947304SEvan Yan copyin(rval, &result, sizeof (ddi_hp_property_t))) 38226947304SEvan Yan return (DDI_FAILURE); 38326947304SEvan Yan } 38426947304SEvan Yan #ifdef _SYSCALL32_IMPL 38526947304SEvan Yan else { 38626947304SEvan Yan bzero(&request, sizeof (request)); 38726947304SEvan Yan bzero(&result, sizeof (result)); 38826947304SEvan Yan if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) || 38926947304SEvan Yan copyin(rval, &result32, sizeof (ddi_hp_property32_t))) 39026947304SEvan Yan return (DDI_FAILURE); 39126947304SEvan Yan request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf; 39226947304SEvan Yan request.buf_size = request32.buf_size; 39326947304SEvan Yan result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf; 39426947304SEvan Yan result.buf_size = result32.buf_size; 39526947304SEvan Yan } 39626947304SEvan Yan #endif 39726947304SEvan Yan 39826947304SEvan Yan if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size, 39926947304SEvan Yan &prop_list)) != DDI_SUCCESS) 40026947304SEvan Yan return (ret); 40126947304SEvan Yan 40226947304SEvan Yan if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) { 40326947304SEvan Yan ret = DDI_ENOMEM; 40426947304SEvan Yan goto get_prop_cleanup; 40526947304SEvan Yan } 40626947304SEvan Yan 40726947304SEvan Yan /* check whether the requested property is "all" or "help" */ 40826947304SEvan Yan prop_pair = nvlist_next_nvpair(prop_list, NULL); 40926947304SEvan Yan if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) { 41026947304SEvan Yan name = nvpair_name(prop_pair); 41126947304SEvan Yan n = sizeof (pcishpc_props) / sizeof (pcishpc_prop_t); 41226947304SEvan Yan 41326947304SEvan Yan if (strcmp(name, PCIEHPC_PROP_ALL) == 0) { 41426947304SEvan Yan (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL); 41526947304SEvan Yan 41626947304SEvan Yan /* 41726947304SEvan Yan * Add all properties into the request list, so that we 41826947304SEvan Yan * will get the values in the following for loop. 41926947304SEvan Yan */ 42026947304SEvan Yan for (i = 0; i < n; i++) { 42126947304SEvan Yan if (nvlist_add_string(prop_list, 42226947304SEvan Yan pcishpc_props[i].prop_name, "") != 0) { 42326947304SEvan Yan ret = DDI_FAILURE; 42426947304SEvan Yan goto get_prop_cleanup1; 42526947304SEvan Yan } 42626947304SEvan Yan } 42726947304SEvan Yan get_all_prop = B_TRUE; 42826947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) { 42926947304SEvan Yan /* 43026947304SEvan Yan * Empty the request list, and add help strings into the 43126947304SEvan Yan * return list. We will pass the following for loop. 43226947304SEvan Yan */ 43326947304SEvan Yan (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP); 43426947304SEvan Yan 43526947304SEvan Yan for (i = 0; i < n; i++) { 43626947304SEvan Yan if (nvlist_add_string(prop_rlist, 43726947304SEvan Yan pcishpc_props[i].prop_name, 43826947304SEvan Yan pcishpc_props[i].prop_value) != 0) { 43926947304SEvan Yan ret = DDI_FAILURE; 44026947304SEvan Yan goto get_prop_cleanup1; 44126947304SEvan Yan } 44226947304SEvan Yan } 44326947304SEvan Yan } 44426947304SEvan Yan } 44526947304SEvan Yan 44626947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 44726947304SEvan Yan 44826947304SEvan Yan /* get the current slot state */ 44926947304SEvan Yan pcishpc_get_slot_state(slot_p); 45026947304SEvan Yan 45126947304SEvan Yan /* for each requested property, get the value and add it to nvlist */ 45226947304SEvan Yan prop_pair = NULL; 453*b3d69c05SRobert Mustacchi while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) { 45426947304SEvan Yan name = nvpair_name(prop_pair); 455*b3d69c05SRobert Mustacchi value = NULL; 45626947304SEvan Yan 45726947304SEvan Yan if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) { 45826947304SEvan Yan value = pcie_led_state_text( 45926947304SEvan Yan slot_p->hs_fault_led_state); 46026947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) { 46126947304SEvan Yan value = pcie_led_state_text( 46226947304SEvan Yan slot_p->hs_power_led_state); 46326947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) { 46426947304SEvan Yan value = pcie_led_state_text( 46526947304SEvan Yan slot_p->hs_attn_led_state); 46626947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) { 46726947304SEvan Yan value = pcie_led_state_text( 46826947304SEvan Yan slot_p->hs_active_led_state); 46926947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) { 47026947304SEvan Yan ddi_acc_handle_t handle; 47126947304SEvan Yan dev_info_t *cdip; 47226947304SEvan Yan uint8_t prog_class, base_class, sub_class; 47326947304SEvan Yan int i; 47426947304SEvan Yan 47526947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 47626947304SEvan Yan cdip = pcie_hp_devi_find( 47726947304SEvan Yan ctrl_p->hc_dip, slot_p->hs_device_num, 0); 47826947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 47926947304SEvan Yan 48026947304SEvan Yan if ((slot_p->hs_info.cn_state != 48126947304SEvan Yan DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) { 48226947304SEvan Yan /* 48326947304SEvan Yan * When getting all properties, just ignore the 48426947304SEvan Yan * one that's not available under certain state. 48526947304SEvan Yan */ 48626947304SEvan Yan if (get_all_prop) 48726947304SEvan Yan continue; 48826947304SEvan Yan 48926947304SEvan Yan ret = DDI_ENOTSUP; 49026947304SEvan Yan goto get_prop_cleanup2; 49126947304SEvan Yan } 49226947304SEvan Yan 49326947304SEvan Yan if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) { 49426947304SEvan Yan ret = DDI_FAILURE; 49526947304SEvan Yan goto get_prop_cleanup2; 49626947304SEvan Yan } 49726947304SEvan Yan 49826947304SEvan Yan prog_class = pci_config_get8(handle, 49926947304SEvan Yan PCI_CONF_PROGCLASS); 50026947304SEvan Yan base_class = pci_config_get8(handle, PCI_CONF_BASCLASS); 50126947304SEvan Yan sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS); 50226947304SEvan Yan pci_config_teardown(&handle); 50326947304SEvan Yan 50426947304SEvan Yan for (i = 0; i < class_pci_items; i++) { 50526947304SEvan Yan if ((base_class == class_pci[i].base_class) && 50626947304SEvan Yan (sub_class == class_pci[i].sub_class) && 50726947304SEvan Yan (prog_class == class_pci[i].prog_class)) { 50826947304SEvan Yan value = class_pci[i].short_desc; 50926947304SEvan Yan break; 51026947304SEvan Yan } 51126947304SEvan Yan } 51226947304SEvan Yan if (i == class_pci_items) 51326947304SEvan Yan value = PCIEHPC_PROP_VALUE_UNKNOWN; 51426947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) { 51526947304SEvan Yan if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY) 51626947304SEvan Yan value = PCIEHPC_PROP_VALUE_UNKNOWN; 51726947304SEvan Yan else 51826947304SEvan Yan value = PCIEHPC_PROP_VALUE_PCIHOTPLUG; 51926947304SEvan Yan } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) { 52026947304SEvan Yan value = pcie_slot_condition_text(slot_p->hs_condition); 52126947304SEvan Yan } else { 52226947304SEvan Yan /* unsupported property */ 52324fd5dc4SEvan Yan PCIE_DBG("Unsupported property: %s\n", name); 52426947304SEvan Yan 52526947304SEvan Yan ret = DDI_ENOTSUP; 52626947304SEvan Yan goto get_prop_cleanup2; 52726947304SEvan Yan } 52826947304SEvan Yan if (nvlist_add_string(prop_rlist, name, value) != 0) { 52926947304SEvan Yan ret = DDI_FAILURE; 53026947304SEvan Yan goto get_prop_cleanup2; 53126947304SEvan Yan } 53226947304SEvan Yan } 53326947304SEvan Yan 53426947304SEvan Yan // pack nvlist and copyout 53526947304SEvan Yan if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf, 53626947304SEvan Yan &result.buf_size)) != DDI_SUCCESS) { 53726947304SEvan Yan goto get_prop_cleanup2; 53826947304SEvan Yan } 53926947304SEvan Yan if (get_udatamodel() == DATAMODEL_NATIVE) { 54026947304SEvan Yan if (copyout(&result, rval, sizeof (ddi_hp_property_t))) { 54126947304SEvan Yan ret = DDI_FAILURE; 54226947304SEvan Yan goto get_prop_cleanup2; 54326947304SEvan Yan } 54426947304SEvan Yan } 54526947304SEvan Yan #ifdef _SYSCALL32_IMPL 54626947304SEvan Yan else { 54726947304SEvan Yan if (result.buf_size > UINT32_MAX) { 54826947304SEvan Yan ret = DDI_FAILURE; 54926947304SEvan Yan } else { 55026947304SEvan Yan result32.buf_size = (uint32_t)result.buf_size; 55126947304SEvan Yan if (copyout(&result32, rval, 55226947304SEvan Yan sizeof (ddi_hp_property32_t))) 55326947304SEvan Yan ret = DDI_FAILURE; 55426947304SEvan Yan } 55526947304SEvan Yan } 55626947304SEvan Yan #endif 55726947304SEvan Yan 55826947304SEvan Yan get_prop_cleanup2: 55926947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 56026947304SEvan Yan get_prop_cleanup1: 56126947304SEvan Yan nvlist_free(prop_rlist); 56226947304SEvan Yan get_prop_cleanup: 56326947304SEvan Yan nvlist_free(prop_list); 56426947304SEvan Yan return (ret); 56526947304SEvan Yan } 56626947304SEvan Yan 56726947304SEvan Yan int 56826947304SEvan Yan pcishpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg, 56926947304SEvan Yan ddi_hp_property_t *rval) 57026947304SEvan Yan { 57126947304SEvan Yan ddi_hp_property_t request, result; 57226947304SEvan Yan #ifdef _SYSCALL32_IMPL 57326947304SEvan Yan ddi_hp_property32_t request32, result32; 57426947304SEvan Yan #endif 57526947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 57626947304SEvan Yan nvlist_t *prop_list; 57726947304SEvan Yan nvlist_t *prop_rlist; 57826947304SEvan Yan nvpair_t *prop_pair; 57926947304SEvan Yan char *name, *value; 58026947304SEvan Yan pcie_hp_led_state_t led_state; 58126947304SEvan Yan int ret = DDI_SUCCESS; 58226947304SEvan Yan 58326947304SEvan Yan if (get_udatamodel() == DATAMODEL_NATIVE) { 58426947304SEvan Yan if (copyin(arg, &request, sizeof (ddi_hp_property_t))) 58526947304SEvan Yan return (DDI_FAILURE); 58626947304SEvan Yan if (rval && 58726947304SEvan Yan copyin(rval, &result, sizeof (ddi_hp_property_t))) 58826947304SEvan Yan return (DDI_FAILURE); 58926947304SEvan Yan } 59026947304SEvan Yan #ifdef _SYSCALL32_IMPL 59126947304SEvan Yan else { 59226947304SEvan Yan bzero(&request, sizeof (request)); 59326947304SEvan Yan bzero(&result, sizeof (result)); 59426947304SEvan Yan if (copyin(arg, &request32, sizeof (ddi_hp_property32_t))) 59526947304SEvan Yan return (DDI_FAILURE); 59626947304SEvan Yan if (rval && 59726947304SEvan Yan copyin(rval, &result32, sizeof (ddi_hp_property32_t))) 59826947304SEvan Yan return (DDI_FAILURE); 59926947304SEvan Yan request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf; 60026947304SEvan Yan request.buf_size = request32.buf_size; 60126947304SEvan Yan if (rval) { 60226947304SEvan Yan result.nvlist_buf = 60326947304SEvan Yan (char *)(uintptr_t)result32.nvlist_buf; 60426947304SEvan Yan result.buf_size = result32.buf_size; 60526947304SEvan Yan } 60626947304SEvan Yan } 60726947304SEvan Yan #endif 60826947304SEvan Yan 60926947304SEvan Yan if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size, 61026947304SEvan Yan &prop_list)) != DDI_SUCCESS) 61126947304SEvan Yan return (ret); 61226947304SEvan Yan 61326947304SEvan Yan /* check whether the requested property is "help" */ 61426947304SEvan Yan prop_pair = nvlist_next_nvpair(prop_list, NULL); 61526947304SEvan Yan if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) && 61626947304SEvan Yan (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) { 61726947304SEvan Yan if (!rval) { 61826947304SEvan Yan ret = DDI_ENOTSUP; 61926947304SEvan Yan goto set_prop_cleanup; 62026947304SEvan Yan } 62126947304SEvan Yan 62226947304SEvan Yan if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) { 62326947304SEvan Yan ret = DDI_ENOMEM; 62426947304SEvan Yan goto set_prop_cleanup; 62526947304SEvan Yan } 62626947304SEvan Yan if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN, 62726947304SEvan Yan PCIEHPC_PROP_VALUE_LED) != 0) { 62826947304SEvan Yan ret = DDI_FAILURE; 62926947304SEvan Yan goto set_prop_cleanup1; 63026947304SEvan Yan } 63126947304SEvan Yan 63226947304SEvan Yan if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf, 63326947304SEvan Yan &result.buf_size)) != DDI_SUCCESS) { 63426947304SEvan Yan goto set_prop_cleanup1; 63526947304SEvan Yan } 63626947304SEvan Yan if (get_udatamodel() == DATAMODEL_NATIVE) { 63726947304SEvan Yan if (copyout(&result, rval, 63826947304SEvan Yan sizeof (ddi_hp_property_t))) { 63926947304SEvan Yan ret = DDI_FAILURE; 64026947304SEvan Yan goto set_prop_cleanup1; 64126947304SEvan Yan } 64226947304SEvan Yan } 64326947304SEvan Yan #ifdef _SYSCALL32_IMPL 64426947304SEvan Yan else { 64526947304SEvan Yan if (result.buf_size > UINT32_MAX) { 64626947304SEvan Yan ret = DDI_FAILURE; 64726947304SEvan Yan goto set_prop_cleanup1; 64826947304SEvan Yan } else { 64926947304SEvan Yan result32.buf_size = (uint32_t)result.buf_size; 65026947304SEvan Yan if (copyout(&result32, rval, 65126947304SEvan Yan sizeof (ddi_hp_property32_t))) { 65226947304SEvan Yan ret = DDI_FAILURE; 65326947304SEvan Yan goto set_prop_cleanup1; 65426947304SEvan Yan } 65526947304SEvan Yan } 65626947304SEvan Yan } 65726947304SEvan Yan #endif 65826947304SEvan Yan set_prop_cleanup1: 65926947304SEvan Yan nvlist_free(prop_rlist); 66026947304SEvan Yan nvlist_free(prop_list); 66126947304SEvan Yan return (ret); 66226947304SEvan Yan } 66326947304SEvan Yan 66426947304SEvan Yan /* Validate the request */ 66526947304SEvan Yan prop_pair = NULL; 666*b3d69c05SRobert Mustacchi while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) { 66726947304SEvan Yan name = nvpair_name(prop_pair); 66826947304SEvan Yan if (nvpair_type(prop_pair) != DATA_TYPE_STRING) { 66924fd5dc4SEvan Yan PCIE_DBG("Unexpected data type of setting " 67026947304SEvan Yan "property %s.\n", name); 67126947304SEvan Yan ret = DDI_EINVAL; 67226947304SEvan Yan goto set_prop_cleanup; 67326947304SEvan Yan } 67426947304SEvan Yan if (nvpair_value_string(prop_pair, &value)) { 67524fd5dc4SEvan Yan PCIE_DBG("Get string value failed for property %s.\n", 67624fd5dc4SEvan Yan name); 67726947304SEvan Yan ret = DDI_FAILURE; 67826947304SEvan Yan goto set_prop_cleanup; 67926947304SEvan Yan } 68026947304SEvan Yan 68126947304SEvan Yan if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) { 68226947304SEvan Yan if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) && 68326947304SEvan Yan (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) && 68426947304SEvan Yan (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) { 68524fd5dc4SEvan Yan PCIE_DBG("Unsupported value of setting " 68626947304SEvan Yan "property %s\n", name); 68726947304SEvan Yan ret = DDI_ENOTSUP; 68826947304SEvan Yan goto set_prop_cleanup; 68926947304SEvan Yan } 69026947304SEvan Yan } else { 69124fd5dc4SEvan Yan PCIE_DBG("Unsupported property: %s\n", name); 69226947304SEvan Yan ret = DDI_ENOTSUP; 69326947304SEvan Yan goto set_prop_cleanup; 69426947304SEvan Yan } 69526947304SEvan Yan } 69626947304SEvan Yan 69726947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 69826947304SEvan Yan 69926947304SEvan Yan /* get the current slot state */ 70026947304SEvan Yan pcishpc_get_slot_state(slot_p); 70126947304SEvan Yan 70226947304SEvan Yan // set each property 70326947304SEvan Yan prop_pair = NULL; 704*b3d69c05SRobert Mustacchi while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) { 70526947304SEvan Yan name = nvpair_name(prop_pair); 70626947304SEvan Yan 70726947304SEvan Yan if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) { 70826947304SEvan Yan if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0) 70926947304SEvan Yan led_state = PCIE_HP_LED_ON; 71026947304SEvan Yan else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0) 71126947304SEvan Yan led_state = PCIE_HP_LED_OFF; 71226947304SEvan Yan else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0) 71326947304SEvan Yan led_state = PCIE_HP_LED_BLINK; 714*b3d69c05SRobert Mustacchi else 715*b3d69c05SRobert Mustacchi continue; 71626947304SEvan Yan 71726947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, 71826947304SEvan Yan led_state); 71926947304SEvan Yan } 72026947304SEvan Yan } 72124fd5dc4SEvan Yan if (rval) { 72224fd5dc4SEvan Yan if (get_udatamodel() == DATAMODEL_NATIVE) { 72324fd5dc4SEvan Yan result.buf_size = 0; 72424fd5dc4SEvan Yan if (copyout(&result, rval, sizeof (ddi_hp_property_t))) 72524fd5dc4SEvan Yan ret = DDI_FAILURE; 72624fd5dc4SEvan Yan } 72724fd5dc4SEvan Yan #ifdef _SYSCALL32_IMPL 72824fd5dc4SEvan Yan else { 72924fd5dc4SEvan Yan result32.buf_size = 0; 73024fd5dc4SEvan Yan if (copyout(&result32, rval, 73124fd5dc4SEvan Yan sizeof (ddi_hp_property32_t))) 73224fd5dc4SEvan Yan ret = DDI_FAILURE; 73324fd5dc4SEvan Yan } 73424fd5dc4SEvan Yan #endif 73524fd5dc4SEvan Yan } 73626947304SEvan Yan 73726947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 73826947304SEvan Yan set_prop_cleanup: 73926947304SEvan Yan nvlist_free(prop_list); 74026947304SEvan Yan return (ret); 74126947304SEvan Yan } 74226947304SEvan Yan 74326947304SEvan Yan /* 74426947304SEvan Yan * pcishpc_hp_ops() 74526947304SEvan Yan * 74626947304SEvan Yan * Handle hotplug commands 74726947304SEvan Yan * 74826947304SEvan Yan * Note: This function is called by DDI HP framework at kernel context only 74926947304SEvan Yan */ 75026947304SEvan Yan /* ARGSUSED */ 75126947304SEvan Yan int 75226947304SEvan Yan pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, 75326947304SEvan Yan void *arg, void *result) 75426947304SEvan Yan { 75526947304SEvan Yan pcie_hp_slot_t *slot_p = NULL; 75626947304SEvan Yan pcie_hp_ctrl_t *ctrl_p; 75726947304SEvan Yan int ret = DDI_SUCCESS, i; 75826947304SEvan Yan 75926947304SEvan Yan PCIE_DBG("pcishpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n", 76026947304SEvan Yan dip, cn_name, op, arg); 76126947304SEvan Yan 76226947304SEvan Yan if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) 76326947304SEvan Yan return (DDI_FAILURE); 76426947304SEvan Yan 76526947304SEvan Yan for (i = 0; i < PCIE_HP_MAX_SLOTS && ctrl_p->hc_slots[i]; i++) { 76626947304SEvan Yan if (strcmp(ctrl_p->hc_slots[i]->hs_info.cn_name, cn_name) 76726947304SEvan Yan == 0) { 76826947304SEvan Yan /* Match with a physical slot, found */ 76926947304SEvan Yan slot_p = ctrl_p->hc_slots[i]; 77026947304SEvan Yan break; 77126947304SEvan Yan } 77226947304SEvan Yan } 77326947304SEvan Yan if (!slot_p) { 77426947304SEvan Yan PCIE_DBG("pcishpc_hp_ops: Failed to find the slot under" 77526947304SEvan Yan "dip %p with name: %s; op=%x arg=%p\n", 77626947304SEvan Yan dip, cn_name, op, arg); 77726947304SEvan Yan return (DDI_EINVAL); 77826947304SEvan Yan } 77926947304SEvan Yan switch (op) { 78026947304SEvan Yan case DDI_HPOP_CN_GET_STATE: 78126947304SEvan Yan { 78226947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 78326947304SEvan Yan 78426947304SEvan Yan /* get the current slot state */ 78526947304SEvan Yan pcishpc_get_slot_state(slot_p); 78626947304SEvan Yan 78726947304SEvan Yan *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state; 78826947304SEvan Yan 78926947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 79026947304SEvan Yan break; 79126947304SEvan Yan } 79226947304SEvan Yan case DDI_HPOP_CN_CHANGE_STATE: 79326947304SEvan Yan { 79426947304SEvan Yan ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg; 79526947304SEvan Yan 79626947304SEvan Yan mutex_enter(&slot_p->hs_ctrl->hc_mutex); 79726947304SEvan Yan 79826947304SEvan Yan ret = pcishpc_change_slot_state(slot_p, target_state); 79926947304SEvan Yan *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state; 80026947304SEvan Yan 80126947304SEvan Yan mutex_exit(&slot_p->hs_ctrl->hc_mutex); 80226947304SEvan Yan break; 80326947304SEvan Yan } 80426947304SEvan Yan case DDI_HPOP_CN_PROBE: 80526947304SEvan Yan ret = pcishpc_slot_probe(slot_p); 80626947304SEvan Yan 80726947304SEvan Yan break; 80826947304SEvan Yan case DDI_HPOP_CN_UNPROBE: 80926947304SEvan Yan ret = pcishpc_slot_unprobe(slot_p); 81026947304SEvan Yan 81126947304SEvan Yan break; 81226947304SEvan Yan case DDI_HPOP_CN_GET_PROPERTY: 81326947304SEvan Yan ret = pcishpc_slot_get_property(slot_p, 81426947304SEvan Yan (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result); 81526947304SEvan Yan break; 81626947304SEvan Yan case DDI_HPOP_CN_SET_PROPERTY: 81726947304SEvan Yan ret = pcishpc_slot_set_property(slot_p, 81826947304SEvan Yan (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result); 81926947304SEvan Yan break; 82026947304SEvan Yan default: 82126947304SEvan Yan ret = DDI_ENOTSUP; 82226947304SEvan Yan break; 82326947304SEvan Yan } 82426947304SEvan Yan 82526947304SEvan Yan return (ret); 82626947304SEvan Yan } 82726947304SEvan Yan 82826947304SEvan Yan /* 82926947304SEvan Yan * Local functions (called within this file) 83026947304SEvan Yan */ 83126947304SEvan Yan 83226947304SEvan Yan /* 83326947304SEvan Yan * pcishpc_create_controller() 83426947304SEvan Yan * 83526947304SEvan Yan * This function allocates and creates an SHPC controller state structure 83626947304SEvan Yan * and adds it to the linked list of controllers. 83726947304SEvan Yan */ 83826947304SEvan Yan static pcie_hp_ctrl_t * 83926947304SEvan Yan pcishpc_create_controller(dev_info_t *dip) 84026947304SEvan Yan { 84126947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 84226947304SEvan Yan pcie_hp_ctrl_t *ctrl_p; 84326947304SEvan Yan 84426947304SEvan Yan PCIE_DBG("pcishpc: create controller for %s#%d\n", 84526947304SEvan Yan ddi_driver_name(dip), ddi_get_instance(dip)); 84626947304SEvan Yan 84726947304SEvan Yan ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP); 84826947304SEvan Yan ctrl_p->hc_dip = dip; 84926947304SEvan Yan 85026947304SEvan Yan cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL); 85126947304SEvan Yan 85226947304SEvan Yan /* Init the shpc controller's mutex. */ 85326947304SEvan Yan mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER, NULL); 85426947304SEvan Yan 85526947304SEvan Yan /* HPC initialization is complete now */ 85626947304SEvan Yan ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG; 85726947304SEvan Yan bus_p->bus_hp_curr_mode = PCIE_PCI_HP_MODE; 85826947304SEvan Yan 85926947304SEvan Yan PCIE_SET_HP_CTRL(dip, ctrl_p); 86026947304SEvan Yan 86126947304SEvan Yan PCIE_DBG("pcishpc_create_controller() success\n"); 86226947304SEvan Yan 86326947304SEvan Yan return (ctrl_p); 86426947304SEvan Yan } 86526947304SEvan Yan 86626947304SEvan Yan 86726947304SEvan Yan /* 86826947304SEvan Yan * pcishpc_setup_controller() 86926947304SEvan Yan * 87026947304SEvan Yan * Get the number of HotPlug Slots, and the PCI device information 87126947304SEvan Yan * for this HotPlug controller. 87226947304SEvan Yan */ 87326947304SEvan Yan static int 87426947304SEvan Yan pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p) 87526947304SEvan Yan { 87626947304SEvan Yan uint32_t config; 87726947304SEvan Yan dev_info_t *ppdip; 87826947304SEvan Yan 87926947304SEvan Yan config = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG); 88026947304SEvan Yan 88126947304SEvan Yan /* Get the number of HotPlug slots implemented */ 88226947304SEvan Yan ctrl_p->hc_num_slots_impl = ((config)&31); 88326947304SEvan Yan 88426947304SEvan Yan /* 88526947304SEvan Yan * Initilize the current bus speed and number of hotplug slots 88626947304SEvan Yan * currently connected. 88726947304SEvan Yan */ 88826947304SEvan Yan ctrl_p->hc_curr_bus_speed = -1; 88926947304SEvan Yan ctrl_p->hc_num_slots_connected = 0; 89026947304SEvan Yan 89126947304SEvan Yan /* 89226947304SEvan Yan * Get the first PCI device Number used. 89326947304SEvan Yan * 89426947304SEvan Yan * PCI-X I/O boat workaround. 89526947304SEvan Yan * The register doesn't set up the correct value. 89626947304SEvan Yan */ 89726947304SEvan Yan ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->hc_dip)); 89826947304SEvan Yan if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS, 89926947304SEvan Yan "vendor-id", -1) == 0x108e) && 90026947304SEvan Yan (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS, 90126947304SEvan Yan "device-id", -1) == 0x9010)) 90226947304SEvan Yan ctrl_p->hc_device_start = 4; 90326947304SEvan Yan else 90426947304SEvan Yan ctrl_p->hc_device_start = ((config>>8)&31); 90526947304SEvan Yan 90626947304SEvan Yan /* Get the first Physical device number. */ 90726947304SEvan Yan ctrl_p->hc_phys_start = ((config>>16)&0x7ff); 90826947304SEvan Yan 90926947304SEvan Yan /* Check if the device numbers increase or decrease. */ 91026947304SEvan Yan ctrl_p->hc_device_increases = ((config>>29)&0x1); 91126947304SEvan Yan 91226947304SEvan Yan ctrl_p->hc_has_attn = 91326947304SEvan Yan (config & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE; 91426947304SEvan Yan ctrl_p->hc_has_mrl = 91526947304SEvan Yan (config & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE; 91626947304SEvan Yan 91726947304SEvan Yan ctrl_p->hc_cmd_pending = B_FALSE; 91826947304SEvan Yan ctrl_p->hc_arbiter_timeout = B_FALSE; 91926947304SEvan Yan 92026947304SEvan Yan if (ctrl_p->hc_num_slots_impl > PCIE_HP_MAX_SLOTS) { 92126947304SEvan Yan PCIE_DBG("pcishpc_setup_controller() too many SHPC " 92226947304SEvan Yan "slots error\n"); 92326947304SEvan Yan return (DDI_FAILURE); 92426947304SEvan Yan } 92526947304SEvan Yan 92626947304SEvan Yan return (DDI_SUCCESS); 92726947304SEvan Yan } 92826947304SEvan Yan 92926947304SEvan Yan 93026947304SEvan Yan /* 93126947304SEvan Yan * pcishpc_destroy_controller() 93226947304SEvan Yan * 93326947304SEvan Yan * This function deallocates all of the SHPC controller resources. 93426947304SEvan Yan */ 93526947304SEvan Yan static int 93626947304SEvan Yan pcishpc_destroy_controller(dev_info_t *dip) 93726947304SEvan Yan { 93826947304SEvan Yan pcie_hp_ctrl_t *ctrl_p; 93926947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 94026947304SEvan Yan 94126947304SEvan Yan PCIE_DBG("pcishpc_destroy_controller() called(dip=%p)\n", dip); 94226947304SEvan Yan 94326947304SEvan Yan /* get the soft state structure for this dip */ 94426947304SEvan Yan if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) { 94526947304SEvan Yan PCIE_DBG("pcishpc_destroy_controller() not found\n"); 94626947304SEvan Yan return (DDI_FAILURE); 94726947304SEvan Yan } 94826947304SEvan Yan 94926947304SEvan Yan /* 95026947304SEvan Yan * Deallocate the slot state structures for this controller. 95126947304SEvan Yan */ 952c0da6274SZhi-Jun Robin Fu PCIE_SET_HP_CTRL(dip, NULL); 953c0da6274SZhi-Jun Robin Fu bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE; 954c0da6274SZhi-Jun Robin Fu 95526947304SEvan Yan (void) pcishpc_destroy_slots(ctrl_p); 95626947304SEvan Yan cv_destroy(&ctrl_p->hc_cmd_comp_cv); 95726947304SEvan Yan mutex_destroy(&ctrl_p->hc_mutex); 95826947304SEvan Yan kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t)); 95926947304SEvan Yan 96026947304SEvan Yan PCIE_DBG("pcishpc_destroy_controller() success\n"); 96126947304SEvan Yan return (DDI_SUCCESS); 96226947304SEvan Yan } 96326947304SEvan Yan 96426947304SEvan Yan /* 96526947304SEvan Yan * pcishpc_create_slot() 96626947304SEvan Yan * 96726947304SEvan Yan * Allocate and add a new HotPlug slot state structure to the linked list. 96826947304SEvan Yan */ 96926947304SEvan Yan static pcie_hp_slot_t * 97026947304SEvan Yan pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p) 97126947304SEvan Yan { 97226947304SEvan Yan pcie_hp_slot_t *slot_p; 97326947304SEvan Yan 97426947304SEvan Yan PCIE_DBG("pcishpc_create_slot() called(ctrl_p=%x)\n", ctrl_p); 97526947304SEvan Yan 97626947304SEvan Yan /* Allocate a new slot structure. */ 97726947304SEvan Yan slot_p = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP); 97826947304SEvan Yan slot_p->hs_ctrl = ctrl_p; 97926947304SEvan Yan 98026947304SEvan Yan /* Assign an initial value */ 98126947304SEvan Yan slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY; 98226947304SEvan Yan 98326947304SEvan Yan PCIE_DBG("pcishpc_create_slot() success\n"); 98426947304SEvan Yan return (slot_p); 98526947304SEvan Yan } 98626947304SEvan Yan 98726947304SEvan Yan /* 98826947304SEvan Yan * pcishpc_register_slot() 98926947304SEvan Yan * 99026947304SEvan Yan * Create and register a slot with the Solaris HotPlug framework. 99126947304SEvan Yan */ 99226947304SEvan Yan static int 99326947304SEvan Yan pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot) 99426947304SEvan Yan { 99526947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 99626947304SEvan Yan pcie_hp_slot_t *slot_p; 99726947304SEvan Yan 99826947304SEvan Yan slot_p = pcishpc_create_slot(ctrl_p); 99926947304SEvan Yan ctrl_p->hc_slots[slot] = slot_p; 100026947304SEvan Yan slot_p->hs_num = slot; 100126947304SEvan Yan 100226947304SEvan Yan /* Setup the PCI device # for this SHPC slot. */ 100326947304SEvan Yan if (ctrl_p->hc_device_increases) 100426947304SEvan Yan slot_p->hs_device_num = ctrl_p->hc_device_start + 100526947304SEvan Yan slot_p->hs_num; 100626947304SEvan Yan else 100726947304SEvan Yan slot_p->hs_device_num = ctrl_p->hc_device_start - 100826947304SEvan Yan slot_p->hs_num; 100926947304SEvan Yan 101026947304SEvan Yan /* Setup the DDI HP framework slot information. */ 101126947304SEvan Yan slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCI; 101226947304SEvan Yan slot_p->hs_info.cn_type_str = PCIE_PCI_HP_TYPE; 101326947304SEvan Yan slot_p->hs_info.cn_child = NULL; 101426947304SEvan Yan 101526947304SEvan Yan slot_p->hs_minor = PCI_MINOR_NUM( 101626947304SEvan Yan ddi_get_instance(dip), slot_p->hs_device_num); 101726947304SEvan Yan slot_p->hs_condition = AP_COND_UNKNOWN; 101826947304SEvan Yan 101926947304SEvan Yan /* setup thread for handling ATTN button events */ 102026947304SEvan Yan if (ctrl_p->hc_has_attn) { 102126947304SEvan Yan PCIE_DBG("pcishpc_register_slot: " 102226947304SEvan Yan "setting up ATTN button event " 102326947304SEvan Yan "handler thread for slot %d\n", slot); 102426947304SEvan Yan 102526947304SEvan Yan cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL); 102626947304SEvan Yan slot_p->hs_attn_btn_pending = B_FALSE; 102726947304SEvan Yan slot_p->hs_attn_btn_threadp = thread_create(NULL, 0, 102826947304SEvan Yan pcishpc_attn_btn_handler, 102926947304SEvan Yan (void *)slot_p, 0, &p0, TS_RUN, minclsyspri); 103026947304SEvan Yan slot_p->hs_attn_btn_thread_exit = B_FALSE; 103126947304SEvan Yan } 103226947304SEvan Yan 103326947304SEvan Yan /* setup the slot name (used for ap-id) */ 103426947304SEvan Yan pcishpc_set_slot_name(ctrl_p, slot); 103526947304SEvan Yan 103626947304SEvan Yan pcishpc_get_slot_state(slot_p); 103726947304SEvan Yan if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED) 103826947304SEvan Yan slot_p->hs_condition = AP_COND_OK; 103926947304SEvan Yan 104026947304SEvan Yan /* register the slot with DDI HP framework */ 104126947304SEvan Yan if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) { 104226947304SEvan Yan PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n", 104326947304SEvan Yan slot_p->hs_phy_slot_num); 104426947304SEvan Yan return (DDI_FAILURE); 104526947304SEvan Yan } 104626947304SEvan Yan 104726947304SEvan Yan pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip), 104826947304SEvan Yan slot_p->hs_minor), slot_p->hs_device_num); 104926947304SEvan Yan 105026947304SEvan Yan PCIE_DBG("pcishpc_register_slot() success for slot %d\n", slot); 105126947304SEvan Yan 105226947304SEvan Yan return (DDI_SUCCESS); 105326947304SEvan Yan } 105426947304SEvan Yan 105526947304SEvan Yan /* 105626947304SEvan Yan * pcishpc_destroy_slots() 105726947304SEvan Yan * 105826947304SEvan Yan * Free up all of the slot resources for this controller. 105926947304SEvan Yan */ 106026947304SEvan Yan static int 106126947304SEvan Yan pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p) 106226947304SEvan Yan { 106326947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 106426947304SEvan Yan pcie_hp_slot_t *slot_p; 106526947304SEvan Yan int i; 106626947304SEvan Yan 106726947304SEvan Yan PCIE_DBG("pcishpc_destroy_slots() called(ctrl_p=%p)\n", ctrl_p); 106826947304SEvan Yan 106926947304SEvan Yan for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) { 107026947304SEvan Yan if ((slot_p = ctrl_p->hc_slots[i]) == NULL) 107126947304SEvan Yan continue; 107226947304SEvan Yan 107326947304SEvan Yan if (slot_p->hs_attn_btn_threadp != NULL) { 107426947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 107526947304SEvan Yan slot_p->hs_attn_btn_thread_exit = B_TRUE; 107626947304SEvan Yan cv_signal(&slot_p->hs_attn_btn_cv); 107726947304SEvan Yan PCIE_DBG("pcishpc_destroy_slots: " 107826947304SEvan Yan "waiting for ATTN thread exit\n"); 107926947304SEvan Yan cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex); 108026947304SEvan Yan PCIE_DBG("pcishpc_destroy_slots: " 108126947304SEvan Yan "ATTN thread exit\n"); 108226947304SEvan Yan cv_destroy(&slot_p->hs_attn_btn_cv); 108326947304SEvan Yan slot_p->hs_attn_btn_threadp = NULL; 108426947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 108526947304SEvan Yan } 108626947304SEvan Yan 108726947304SEvan Yan PCIE_DBG("pcishpc_destroy_slots() (shpc_p=%p)\n" 108826947304SEvan Yan "destroyed", slot_p); 108926947304SEvan Yan 109026947304SEvan Yan pcie_hp_delete_occupant_props(dip, 109126947304SEvan Yan makedevice(ddi_driver_major(dip), 109226947304SEvan Yan slot_p->hs_minor)); 109326947304SEvan Yan 109426947304SEvan Yan /* unregister the slot with DDI HP framework */ 109526947304SEvan Yan if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != 109626947304SEvan Yan NDI_SUCCESS) { 109726947304SEvan Yan PCIE_DBG("pcishpc_destroy_slots() " 109826947304SEvan Yan "failed to unregister slot %d\n", 109926947304SEvan Yan slot_p->hs_phy_slot_num); 110026947304SEvan Yan return (DDI_FAILURE); 110126947304SEvan Yan } 110226947304SEvan Yan kmem_free(slot_p->hs_info.cn_name, 110326947304SEvan Yan strlen(slot_p->hs_info.cn_name) + 1); 110426947304SEvan Yan kmem_free(slot_p, sizeof (pcie_hp_slot_t)); 110526947304SEvan Yan } 110626947304SEvan Yan 110726947304SEvan Yan return (DDI_SUCCESS); 110826947304SEvan Yan } 110926947304SEvan Yan 111026947304SEvan Yan /* 111126947304SEvan Yan * pcishpc_enable_irqs() 111226947304SEvan Yan * 111326947304SEvan Yan * Enable/unmask the different IRQ's we support from the SHPC controller. 111426947304SEvan Yan */ 111570f83219SEvan Yan int 111626947304SEvan Yan pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p) 111726947304SEvan Yan { 111826947304SEvan Yan uint32_t reg; 111926947304SEvan Yan int slot; 112026947304SEvan Yan 112126947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG); 112226947304SEvan Yan 112326947304SEvan Yan /* Enable all interrupts. */ 112426947304SEvan Yan reg &= ~PCI_HP_SERR_INT_MASK_ALL; 112526947304SEvan Yan 112626947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg); 112726947304SEvan Yan 112826947304SEvan Yan /* Unmask the interrupts for each slot. */ 112926947304SEvan Yan for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) { 113026947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot); 113126947304SEvan Yan if ((reg & PCI_HP_SLOT_STATE_MASK) == PCI_HP_SLOT_ENABLED) { 113226947304SEvan Yan reg &= ~(PCI_HP_SLOT_MASK_ALL | 113326947304SEvan Yan PCI_HP_SLOT_MRL_SERR_MASK); 113426947304SEvan Yan ctrl_p->hc_num_slots_connected++; 113526947304SEvan Yan if (ctrl_p->hc_curr_bus_speed == -1) 113626947304SEvan Yan ctrl_p->hc_curr_bus_speed = 113726947304SEvan Yan pcishpc_read_reg(ctrl_p, 113826947304SEvan Yan PCI_HP_PROF_IF_SBCR_REG) & 113926947304SEvan Yan PCI_HP_SBCR_SPEED_MASK; 114026947304SEvan Yan } else { 114126947304SEvan Yan reg &= ~(PCI_HP_SLOT_MASK_ALL); 114226947304SEvan Yan } 114326947304SEvan Yan 114426947304SEvan Yan /* Enable/Unmask all slot interrupts. */ 114526947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg); 114626947304SEvan Yan } 114726947304SEvan Yan 114826947304SEvan Yan PCIE_DBG("pcishpc_enable_irqs: ctrl_p 0x%p, " 114926947304SEvan Yan "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p, 115026947304SEvan Yan ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected); 115126947304SEvan Yan 115226947304SEvan Yan return (DDI_SUCCESS); 115326947304SEvan Yan } 115426947304SEvan Yan 115526947304SEvan Yan 115626947304SEvan Yan /* 115726947304SEvan Yan * pcishpc_disable_irqs() 115826947304SEvan Yan * 115926947304SEvan Yan * Disable/Mask the different IRQ's we support from the SHPC controller. 116026947304SEvan Yan */ 116170f83219SEvan Yan int 116226947304SEvan Yan pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p) 116326947304SEvan Yan { 116426947304SEvan Yan uint32_t reg; 116526947304SEvan Yan int slot; 116626947304SEvan Yan 116726947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG); 116826947304SEvan Yan 116926947304SEvan Yan /* Mask all interrupts. */ 117026947304SEvan Yan reg |= PCI_HP_SERR_INT_MASK_ALL; 117126947304SEvan Yan 117226947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg); 117326947304SEvan Yan 117426947304SEvan Yan /* Unmask the interrupts for each slot. */ 117526947304SEvan Yan for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) { 117626947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot); 117726947304SEvan Yan 117826947304SEvan Yan /* Disable/Mask all slot interrupts. */ 117926947304SEvan Yan reg |= PCI_HP_SLOT_MASK_ALL; 118026947304SEvan Yan 118126947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg); 118226947304SEvan Yan } 118326947304SEvan Yan 118426947304SEvan Yan PCIE_DBG("pcishpc_disable_irqs: ctrl_p 0x%p, " 118526947304SEvan Yan "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p, 118626947304SEvan Yan ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected); 118726947304SEvan Yan 118826947304SEvan Yan return (DDI_SUCCESS); 118926947304SEvan Yan } 119026947304SEvan Yan 119126947304SEvan Yan /* 119226947304SEvan Yan * pcishpc_slot_poweron() 119326947304SEvan Yan * 119426947304SEvan Yan * Poweron/Enable the slot. 119526947304SEvan Yan * 119626947304SEvan Yan * Note: This function is called by DDI HP framework at kernel context only 119726947304SEvan Yan */ 119826947304SEvan Yan /*ARGSUSED*/ 119926947304SEvan Yan static int 120026947304SEvan Yan pcishpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state) 120126947304SEvan Yan { 120226947304SEvan Yan uint32_t status; 120326947304SEvan Yan 120426947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron called()\n"); 120526947304SEvan Yan 120626947304SEvan Yan ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex)); 120726947304SEvan Yan 120826947304SEvan Yan /* get the current slot state */ 120926947304SEvan Yan pcishpc_get_slot_state(slot_p); 121026947304SEvan Yan 121126947304SEvan Yan /* check if the slot is already in the 'enabled' state */ 121226947304SEvan Yan if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) { 121326947304SEvan Yan /* slot is already in the 'enabled' state */ 121426947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() slot %d already enabled\n", 121526947304SEvan Yan slot_p->hs_phy_slot_num); 121626947304SEvan Yan 121726947304SEvan Yan *result_state = slot_p->hs_info.cn_state; 121826947304SEvan Yan return (DDI_SUCCESS); 121926947304SEvan Yan } 122026947304SEvan Yan 122126947304SEvan Yan if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_EMPTY) { 122226947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() slot in empty state\n"); 122326947304SEvan Yan goto cleanup; 122426947304SEvan Yan } 122526947304SEvan Yan 122626947304SEvan Yan /* make sure the MRL sensor is closed */ 122726947304SEvan Yan status = pcishpc_read_reg(slot_p->hs_ctrl, 122826947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 122926947304SEvan Yan 123026947304SEvan Yan if (status & PCI_HP_SLOT_MRL_STATE_MASK) { 123126947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() failed: MRL open\n"); 123226947304SEvan Yan goto cleanup; 123326947304SEvan Yan } 123426947304SEvan Yan 123526947304SEvan Yan /* Set the Power LED to blink */ 123626947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK); 123726947304SEvan Yan 123826947304SEvan Yan /* Turn all other LEDS off */ 123926947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF); 124026947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF); 124126947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF); 124226947304SEvan Yan 124326947304SEvan Yan /* Set the bus speed only if the bus segment is not running */ 124426947304SEvan Yan if (pcishpc_set_bus_speed(slot_p) != DDI_SUCCESS) { 124526947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() setting speed failed\n"); 124626947304SEvan Yan goto cleanup; 124726947304SEvan Yan } 124826947304SEvan Yan 124926947304SEvan Yan slot_p->hs_ctrl->hc_num_slots_connected++; 125026947304SEvan Yan 125126947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron(): slot_p 0x%p, slot state 0x%x, " 125226947304SEvan Yan "current bus speed 0x%x, slots connected 0x%x\n", slot_p, 125326947304SEvan Yan slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed, 125426947304SEvan Yan slot_p->hs_ctrl->hc_num_slots_connected); 125526947304SEvan Yan 125626947304SEvan Yan /* Mask or Unmask MRL Sensor SEER bit based on new slot state */ 125726947304SEvan Yan if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) { 125826947304SEvan Yan uint32_t reg; 125926947304SEvan Yan 126026947304SEvan Yan reg = pcishpc_read_reg(slot_p->hs_ctrl, 126126947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 126226947304SEvan Yan 126326947304SEvan Yan pcishpc_write_reg(slot_p->hs_ctrl, 126426947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num, 126526947304SEvan Yan reg & ~PCI_HP_SLOT_MRL_SERR_MASK); 126626947304SEvan Yan } 126726947304SEvan Yan 126826947304SEvan Yan /* Update the hardware slot state. */ 126926947304SEvan Yan if (pcishpc_set_slot_state(slot_p, 127026947304SEvan Yan DDI_HP_CN_STATE_ENABLED) != DDI_SUCCESS) { 127126947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() failed\n"); 127226947304SEvan Yan 127326947304SEvan Yan pcishpc_get_slot_state(slot_p); 127426947304SEvan Yan goto cleanup; 127526947304SEvan Yan } 127626947304SEvan Yan /* Update the current state. It will be used in pcishpc_setled() */ 127726947304SEvan Yan slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED; 127826947304SEvan Yan 127926947304SEvan Yan /* Turn the Power LED ON for a enabled slot. */ 128026947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON); 128126947304SEvan Yan 128226947304SEvan Yan /* Turn all other LEDS off. */ 128326947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF); 128426947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF); 128526947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF); 128626947304SEvan Yan 128726947304SEvan Yan /* delay after powerON to let the device initialize itself */ 128826947304SEvan Yan delay(drv_usectohz(pcishpc_reset_delay)); 128926947304SEvan Yan 129026947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() success!\n"); 129126947304SEvan Yan 129226947304SEvan Yan /* 129326947304SEvan Yan * Want to show up as POWERED state for now. It will be updated to 129426947304SEvan Yan * ENABLED state when user explicitly enable the slot. 129526947304SEvan Yan */ 129626947304SEvan Yan slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED; 129726947304SEvan Yan 129826947304SEvan Yan /* get the current slot state */ 129926947304SEvan Yan pcishpc_get_slot_state(slot_p); 130026947304SEvan Yan /* 130126947304SEvan Yan * It should be poweron'ed now. Have a check here in case any 130226947304SEvan Yan * hardware problems. 130326947304SEvan Yan */ 130426947304SEvan Yan if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 130526947304SEvan Yan PCIE_DBG("pcishpc_slot_poweron() failed after hardware" 130626947304SEvan Yan " registers all programmed.\n"); 130726947304SEvan Yan 130826947304SEvan Yan goto cleanup; 130926947304SEvan Yan } 131026947304SEvan Yan 131126947304SEvan Yan *result_state = slot_p->hs_info.cn_state; 131226947304SEvan Yan 131326947304SEvan Yan return (DDI_SUCCESS); 131426947304SEvan Yan 131526947304SEvan Yan cleanup: 131626947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF); 131726947304SEvan Yan return (DDI_FAILURE); 131826947304SEvan Yan } 131926947304SEvan Yan 132026947304SEvan Yan /*ARGSUSED*/ 132126947304SEvan Yan static int 132226947304SEvan Yan pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state) 132326947304SEvan Yan { 132426947304SEvan Yan PCIE_DBG("pcishpc_slot_poweroff called()\n"); 132526947304SEvan Yan 132626947304SEvan Yan ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex)); 132726947304SEvan Yan 132826947304SEvan Yan /* get the current slot state */ 132926947304SEvan Yan pcishpc_get_slot_state(slot_p); 133026947304SEvan Yan 133126947304SEvan Yan /* check if the slot is not in the "enabled" or "powered" state */ 133226947304SEvan Yan if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 133326947304SEvan Yan /* slot is in the 'disabled' state */ 133426947304SEvan Yan PCIE_DBG("pcishpc_slot_poweroff(): " 133526947304SEvan Yan "slot %d already disabled\n", slot_p->hs_phy_slot_num); 133626947304SEvan Yan 133726947304SEvan Yan *result_state = slot_p->hs_info.cn_state; 133826947304SEvan Yan return (DDI_SUCCESS); 133926947304SEvan Yan } 134026947304SEvan Yan 134126947304SEvan Yan /* Set the Power LED to blink */ 134226947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK); 134326947304SEvan Yan 134426947304SEvan Yan /* Turn all other LEDS off */ 134526947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF); 134626947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF); 134726947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF); 134826947304SEvan Yan 134926947304SEvan Yan if (--slot_p->hs_ctrl->hc_num_slots_connected == 0) 135026947304SEvan Yan slot_p->hs_ctrl->hc_curr_bus_speed = -1; 135126947304SEvan Yan 135226947304SEvan Yan PCIE_DBG("pcishpc_slot_poweroff(): slot_p 0x%p, slot state 0x%x, " 135326947304SEvan Yan "current bus speed 0x%x, slots connected 0x%x\n", slot_p, 135426947304SEvan Yan slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed, 135526947304SEvan Yan slot_p->hs_ctrl->hc_num_slots_connected); 135626947304SEvan Yan 135726947304SEvan Yan /* Mask or Unmask MRL Sensor SEER bit based on new slot state */ 135826947304SEvan Yan if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) { 135926947304SEvan Yan uint32_t reg; 136026947304SEvan Yan 136126947304SEvan Yan reg = pcishpc_read_reg(slot_p->hs_ctrl, 136226947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 136326947304SEvan Yan 136426947304SEvan Yan pcishpc_write_reg(slot_p->hs_ctrl, 136526947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num, 136626947304SEvan Yan reg | PCI_HP_SLOT_MRL_SERR_MASK); 136726947304SEvan Yan } 136826947304SEvan Yan 136926947304SEvan Yan /* Update the hardware slot state. */ 137026947304SEvan Yan if (pcishpc_set_slot_state(slot_p, DDI_HP_CN_STATE_PRESENT) != 137126947304SEvan Yan DDI_SUCCESS) { 137226947304SEvan Yan PCIE_DBG("pcishpc_slot_poweroff() failed\n"); 137326947304SEvan Yan 137426947304SEvan Yan pcishpc_get_slot_state(slot_p); 137526947304SEvan Yan goto cleanup; 137626947304SEvan Yan } 137726947304SEvan Yan 137826947304SEvan Yan /* Update the current state. It will be used in pcishpc_setled() */ 137926947304SEvan Yan slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT; 138026947304SEvan Yan 138126947304SEvan Yan /* Turn the Power LED OFF for a disabled slot. */ 138226947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF); 138326947304SEvan Yan 138426947304SEvan Yan /* Turn all other LEDS off. */ 138526947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF); 138626947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF); 138726947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF); 138826947304SEvan Yan 138926947304SEvan Yan /* delay after powerON to let the device initialize itself */ 139026947304SEvan Yan delay(drv_usectohz(pcishpc_reset_delay)); 139126947304SEvan Yan 139226947304SEvan Yan pcishpc_get_slot_state(slot_p); 139326947304SEvan Yan /* 139426947304SEvan Yan * It should be poweroff'ed now. Have a check here in case any 139526947304SEvan Yan * hardware problems. 139626947304SEvan Yan */ 139726947304SEvan Yan if (slot_p->hs_info.cn_state > DDI_HP_CN_STATE_PRESENT) { 139826947304SEvan Yan PCIE_DBG("pcishpc_slot_poweroff() failed after hardware" 139926947304SEvan Yan " registers all programmed.\n"); 140026947304SEvan Yan 140126947304SEvan Yan goto cleanup; 140226947304SEvan Yan } 140326947304SEvan Yan 140426947304SEvan Yan PCIE_DBG("pcishpc_slot_poweroff() success!\n"); 140526947304SEvan Yan 140626947304SEvan Yan *result_state = slot_p->hs_info.cn_state; 140726947304SEvan Yan return (DDI_SUCCESS); 140826947304SEvan Yan 140926947304SEvan Yan cleanup: 141026947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF); 141126947304SEvan Yan return (DDI_FAILURE); 141226947304SEvan Yan } 141326947304SEvan Yan 141426947304SEvan Yan /* 141526947304SEvan Yan * pcishpc_slot_probe() 141626947304SEvan Yan * 141726947304SEvan Yan * Probe the slot. 141826947304SEvan Yan * 141926947304SEvan Yan * Note: This function is called by DDI HP framework at kernel context only 142026947304SEvan Yan */ 142126947304SEvan Yan /*ARGSUSED*/ 142226947304SEvan Yan static int 142326947304SEvan Yan pcishpc_slot_probe(pcie_hp_slot_t *slot_p) 142426947304SEvan Yan { 142526947304SEvan Yan mutex_enter(&slot_p->hs_ctrl->hc_mutex); 142626947304SEvan Yan 142726947304SEvan Yan PCIE_DBG("pcishpc_slot_probe called()\n"); 142826947304SEvan Yan 142926947304SEvan Yan /* get the current slot state */ 143026947304SEvan Yan pcishpc_get_slot_state(slot_p); 143126947304SEvan Yan 143226947304SEvan Yan /* 143326947304SEvan Yan * Probe a given PCI Hotplug Connection (CN). 143426947304SEvan Yan */ 143526947304SEvan Yan if (pcie_hp_probe(slot_p) != DDI_SUCCESS) { 143626947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, 143726947304SEvan Yan PCIE_HP_LED_BLINK); 143826947304SEvan Yan 143926947304SEvan Yan PCIE_DBG("pcishpc_slot_probe() failed\n"); 144026947304SEvan Yan 144126947304SEvan Yan mutex_exit(&slot_p->hs_ctrl->hc_mutex); 144226947304SEvan Yan return (DDI_FAILURE); 144326947304SEvan Yan } 144426947304SEvan Yan 144526947304SEvan Yan PCIE_DBG("pcishpc_slot_probe() success!\n"); 144626947304SEvan Yan 144726947304SEvan Yan /* get the current slot state */ 144826947304SEvan Yan pcishpc_get_slot_state(slot_p); 144926947304SEvan Yan 145026947304SEvan Yan mutex_exit(&slot_p->hs_ctrl->hc_mutex); 145126947304SEvan Yan return (DDI_SUCCESS); 145226947304SEvan Yan } 145326947304SEvan Yan 145426947304SEvan Yan /* 145526947304SEvan Yan * pcishpc_slot_unprobe() 145626947304SEvan Yan * 145726947304SEvan Yan * Unprobe the slot. 145826947304SEvan Yan * 145926947304SEvan Yan * Note: This function is called by DDI HP framework at kernel context only 146026947304SEvan Yan */ 146126947304SEvan Yan /*ARGSUSED*/ 146226947304SEvan Yan static int 146326947304SEvan Yan pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p) 146426947304SEvan Yan { 146526947304SEvan Yan mutex_enter(&slot_p->hs_ctrl->hc_mutex); 146626947304SEvan Yan 146726947304SEvan Yan PCIE_DBG("pcishpc_slot_unprobe called()\n"); 146826947304SEvan Yan 146926947304SEvan Yan /* get the current slot state */ 147026947304SEvan Yan pcishpc_get_slot_state(slot_p); 147126947304SEvan Yan 147226947304SEvan Yan /* 147326947304SEvan Yan * Unprobe a given PCI Hotplug Connection (CN). 147426947304SEvan Yan */ 147526947304SEvan Yan if (pcie_hp_unprobe(slot_p) != DDI_SUCCESS) { 147626947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, 147726947304SEvan Yan PCIE_HP_LED_BLINK); 147826947304SEvan Yan 147926947304SEvan Yan PCIE_DBG("pcishpc_slot_unprobe() failed\n"); 148026947304SEvan Yan 148126947304SEvan Yan mutex_exit(&slot_p->hs_ctrl->hc_mutex); 148226947304SEvan Yan return (DDI_FAILURE); 148326947304SEvan Yan } 148426947304SEvan Yan 148526947304SEvan Yan PCIE_DBG("pcishpc_slot_unprobe() success!\n"); 148626947304SEvan Yan 148726947304SEvan Yan /* get the current slot state */ 148826947304SEvan Yan pcishpc_get_slot_state(slot_p); 148926947304SEvan Yan 149026947304SEvan Yan mutex_exit(&slot_p->hs_ctrl->hc_mutex); 149126947304SEvan Yan return (DDI_SUCCESS); 149226947304SEvan Yan } 149326947304SEvan Yan 149426947304SEvan Yan static int 149526947304SEvan Yan pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p, 149626947304SEvan Yan ddi_hp_cn_state_t target_state) 149726947304SEvan Yan { 149826947304SEvan Yan ddi_hp_cn_state_t curr_state; 149926947304SEvan Yan int rv = DDI_SUCCESS; 150026947304SEvan Yan 150126947304SEvan Yan if (target_state > DDI_HP_CN_STATE_ENABLED) { 150226947304SEvan Yan return (DDI_EINVAL); 150326947304SEvan Yan } 150426947304SEvan Yan 150526947304SEvan Yan curr_state = slot_p->hs_info.cn_state; 150626947304SEvan Yan while ((curr_state < target_state) && (rv == DDI_SUCCESS)) { 150726947304SEvan Yan 150826947304SEvan Yan switch (curr_state) { 150926947304SEvan Yan case DDI_HP_CN_STATE_EMPTY: 151026947304SEvan Yan /* 151126947304SEvan Yan * From EMPTY to PRESENT, just check the hardware 151226947304SEvan Yan * slot state. 151326947304SEvan Yan */ 151426947304SEvan Yan pcishpc_get_slot_state(slot_p); 151526947304SEvan Yan curr_state = slot_p->hs_info.cn_state; 151626947304SEvan Yan if (curr_state < DDI_HP_CN_STATE_PRESENT) 151726947304SEvan Yan rv = DDI_FAILURE; 151826947304SEvan Yan break; 151926947304SEvan Yan case DDI_HP_CN_STATE_PRESENT: 152026947304SEvan Yan rv = pcishpc_slot_poweron(slot_p, &curr_state); 152126947304SEvan Yan break; 152226947304SEvan Yan case DDI_HP_CN_STATE_POWERED: 152326947304SEvan Yan curr_state = slot_p->hs_info.cn_state = 152426947304SEvan Yan DDI_HP_CN_STATE_ENABLED; 152526947304SEvan Yan break; 152626947304SEvan Yan default: 152726947304SEvan Yan /* should never reach here */ 152826947304SEvan Yan ASSERT("unknown devinfo state"); 152926947304SEvan Yan } 153026947304SEvan Yan } 153126947304SEvan Yan 153226947304SEvan Yan return (rv); 153326947304SEvan Yan } 153426947304SEvan Yan 153526947304SEvan Yan static int 153626947304SEvan Yan pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p, 153726947304SEvan Yan ddi_hp_cn_state_t target_state) 153826947304SEvan Yan { 153926947304SEvan Yan ddi_hp_cn_state_t curr_state; 154026947304SEvan Yan int rv = DDI_SUCCESS; 154126947304SEvan Yan 154226947304SEvan Yan 154326947304SEvan Yan curr_state = slot_p->hs_info.cn_state; 154426947304SEvan Yan while ((curr_state > target_state) && (rv == DDI_SUCCESS)) { 154526947304SEvan Yan 154626947304SEvan Yan switch (curr_state) { 154726947304SEvan Yan case DDI_HP_CN_STATE_PRESENT: 154826947304SEvan Yan /* 154926947304SEvan Yan * From PRESENT to EMPTY, just check hardware 155026947304SEvan Yan * slot state. 155126947304SEvan Yan */ 155226947304SEvan Yan pcishpc_get_slot_state(slot_p); 155326947304SEvan Yan curr_state = slot_p->hs_info.cn_state; 155426947304SEvan Yan if (curr_state >= DDI_HP_CN_STATE_PRESENT) 155526947304SEvan Yan rv = DDI_FAILURE; 155626947304SEvan Yan break; 155726947304SEvan Yan case DDI_HP_CN_STATE_POWERED: 155826947304SEvan Yan rv = pcishpc_slot_poweroff(slot_p, &curr_state); 155926947304SEvan Yan 156026947304SEvan Yan break; 156126947304SEvan Yan case DDI_HP_CN_STATE_ENABLED: 156226947304SEvan Yan curr_state = slot_p->hs_info.cn_state = 156326947304SEvan Yan DDI_HP_CN_STATE_POWERED; 156426947304SEvan Yan 156526947304SEvan Yan break; 156626947304SEvan Yan default: 156726947304SEvan Yan /* should never reach here */ 156826947304SEvan Yan ASSERT("unknown devinfo state"); 156926947304SEvan Yan } 157026947304SEvan Yan } 157126947304SEvan Yan 157226947304SEvan Yan return (rv); 157326947304SEvan Yan } 157426947304SEvan Yan 157526947304SEvan Yan /* Change slot state to a target state */ 157626947304SEvan Yan static int 157726947304SEvan Yan pcishpc_change_slot_state(pcie_hp_slot_t *slot_p, 157826947304SEvan Yan ddi_hp_cn_state_t target_state) 157926947304SEvan Yan { 158026947304SEvan Yan ddi_hp_cn_state_t curr_state; 158126947304SEvan Yan int rv; 158226947304SEvan Yan 158326947304SEvan Yan pcishpc_get_slot_state(slot_p); 158426947304SEvan Yan curr_state = slot_p->hs_info.cn_state; 158526947304SEvan Yan 158626947304SEvan Yan if (curr_state == target_state) { 158726947304SEvan Yan return (DDI_SUCCESS); 158826947304SEvan Yan } 158926947304SEvan Yan if (curr_state < target_state) { 159026947304SEvan Yan 159126947304SEvan Yan rv = pcishpc_upgrade_slot_state(slot_p, target_state); 159226947304SEvan Yan } else { 159326947304SEvan Yan rv = pcishpc_downgrade_slot_state(slot_p, target_state); 159426947304SEvan Yan } 159526947304SEvan Yan 159626947304SEvan Yan return (rv); 159726947304SEvan Yan } 159826947304SEvan Yan 159926947304SEvan Yan /* 160026947304SEvan Yan * pcishpc_issue_command() 160126947304SEvan Yan * 160226947304SEvan Yan * Sends a command to the SHPC controller. 160326947304SEvan Yan */ 160426947304SEvan Yan static int 160526947304SEvan Yan pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p, uint32_t cmd_code) 160626947304SEvan Yan { 160726947304SEvan Yan int retCode; 160826947304SEvan Yan 160926947304SEvan Yan ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 161026947304SEvan Yan 161126947304SEvan Yan PCIE_DBG("pcishpc_issue_command() cmd_code=%02x\n", cmd_code); 161226947304SEvan Yan 161326947304SEvan Yan ctrl_p->hc_cmd_pending = B_TRUE; 161426947304SEvan Yan 161526947304SEvan Yan /* Write the command to the SHPC controller. */ 161626947304SEvan Yan pcishpc_write_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG, cmd_code); 161726947304SEvan Yan 161826947304SEvan Yan while (ctrl_p->hc_cmd_pending == B_TRUE) 161926947304SEvan Yan cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex); 162026947304SEvan Yan 162126947304SEvan Yan /* Wait until the SHPC controller processes the command. */ 162226947304SEvan Yan retCode = pcishpc_wait_busy(ctrl_p); 162326947304SEvan Yan 162426947304SEvan Yan /* Make sure the command completed. */ 162526947304SEvan Yan if (retCode == DDI_SUCCESS) { 162626947304SEvan Yan /* Did the command fail to generate the command complete IRQ? */ 162726947304SEvan Yan if (ctrl_p->hc_cmd_pending != B_FALSE) { 162826947304SEvan Yan PCIE_DBG("pcishpc_issue_command() Failed on " 162926947304SEvan Yan "generate cmd complete IRQ\n"); 163026947304SEvan Yan retCode = DDI_FAILURE; 163126947304SEvan Yan } 163226947304SEvan Yan } 163326947304SEvan Yan 163426947304SEvan Yan if (retCode == DDI_FAILURE) 163526947304SEvan Yan PCIE_DBG("pcishpc_issue_command() Failed on cmd_code=%02x\n", 163626947304SEvan Yan cmd_code); 163726947304SEvan Yan else 163826947304SEvan Yan PCIE_DBG("pcishpc_issue_command() Success on " 163926947304SEvan Yan "cmd_code=%02x\n", cmd_code); 164026947304SEvan Yan 164126947304SEvan Yan return (retCode); 164226947304SEvan Yan } 164326947304SEvan Yan 164426947304SEvan Yan /* 164526947304SEvan Yan * pcishpc_wait_busy() 164626947304SEvan Yan * 164726947304SEvan Yan * Wait until the SHPC controller is not busy. 164826947304SEvan Yan */ 164926947304SEvan Yan static int 165026947304SEvan Yan pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p) 165126947304SEvan Yan { 165226947304SEvan Yan uint32_t status; 165326947304SEvan Yan 165426947304SEvan Yan /* Wait until SHPC controller is NOT busy */ 165526947304SEvan Yan for (;;) { 165626947304SEvan Yan status = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG); 165726947304SEvan Yan 165826947304SEvan Yan /* Is there an MRL Sensor error? */ 165926947304SEvan Yan if ((status & PCI_HP_COMM_STS_ERR_MASK) == 166026947304SEvan Yan PCI_HP_COMM_STS_ERR_MRL_OPEN) { 166126947304SEvan Yan PCIE_DBG("pcishpc_wait_busy() ERROR: " 166226947304SEvan Yan "MRL Sensor error\n"); 166326947304SEvan Yan break; 166426947304SEvan Yan } 166526947304SEvan Yan 166626947304SEvan Yan /* Is there an Invalid command error? */ 166726947304SEvan Yan if ((status & PCI_HP_COMM_STS_ERR_MASK) == 166826947304SEvan Yan PCI_HP_COMM_STS_ERR_INVALID_COMMAND) { 166926947304SEvan Yan PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid " 167026947304SEvan Yan "command error\n"); 167126947304SEvan Yan break; 167226947304SEvan Yan } 167326947304SEvan Yan 167426947304SEvan Yan /* Is there an Invalid Speed/Mode error? */ 167526947304SEvan Yan if ((status & PCI_HP_COMM_STS_ERR_MASK) == 167626947304SEvan Yan PCI_HP_COMM_STS_ERR_INVALID_SPEED) { 167726947304SEvan Yan PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid " 167826947304SEvan Yan "Speed/Mode error\n"); 167926947304SEvan Yan break; 168026947304SEvan Yan } 168126947304SEvan Yan 168226947304SEvan Yan /* Is the SHPC controller not BUSY? */ 168326947304SEvan Yan if (!(status & PCI_HP_COMM_STS_CTRL_BUSY)) { 168426947304SEvan Yan /* Return Success. */ 168526947304SEvan Yan return (DDI_SUCCESS); 168626947304SEvan Yan } 168726947304SEvan Yan 168826947304SEvan Yan PCIE_DBG("pcishpc_wait_busy() SHPC controller busy. Waiting\n"); 168926947304SEvan Yan 169026947304SEvan Yan /* Wait before polling the status register again. */ 169126947304SEvan Yan delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME)); 169226947304SEvan Yan } 169326947304SEvan Yan 169426947304SEvan Yan return (DDI_FAILURE); 169526947304SEvan Yan } 169626947304SEvan Yan 169726947304SEvan Yan static void 169826947304SEvan Yan pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p) 169926947304SEvan Yan { 170026947304SEvan Yan pcie_hp_led_state_t hs_power_led_state; 170126947304SEvan Yan callb_cpr_t cprinfo; 170226947304SEvan Yan 170326947304SEvan Yan PCIE_DBG("pcishpc_attn_btn_handler: thread started\n"); 170426947304SEvan Yan 170526947304SEvan Yan CALLB_CPR_INIT(&cprinfo, &slot_p->hs_ctrl->hc_mutex, 170626947304SEvan Yan callb_generic_cpr, "pcishpc_attn_btn_handler"); 170726947304SEvan Yan 170826947304SEvan Yan mutex_enter(&slot_p->hs_ctrl->hc_mutex); 170926947304SEvan Yan 171026947304SEvan Yan /* wait for ATTN button event */ 171126947304SEvan Yan cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex); 171226947304SEvan Yan 171326947304SEvan Yan while (slot_p->hs_attn_btn_thread_exit == B_FALSE) { 171426947304SEvan Yan if (slot_p->hs_attn_btn_pending == B_TRUE) { 171526947304SEvan Yan /* get the current state of power LED */ 171626947304SEvan Yan hs_power_led_state = slot_p->hs_power_led_state; 171726947304SEvan Yan 171826947304SEvan Yan /* Blink the Power LED while we wait for 5 seconds */ 171926947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, 172026947304SEvan Yan PCIE_HP_LED_BLINK); 172126947304SEvan Yan 172226947304SEvan Yan /* wait for 5 seconds before taking any action */ 1723d3d50737SRafael Vanoni if (cv_reltimedwait(&slot_p->hs_attn_btn_cv, 172426947304SEvan Yan &slot_p->hs_ctrl->hc_mutex, 1725d3d50737SRafael Vanoni SEC_TO_TICK(5), TR_CLOCK_TICK) == -1) { 172626947304SEvan Yan /* 172726947304SEvan Yan * It is a time out; 172826947304SEvan Yan * make sure the ATTN pending flag is 172926947304SEvan Yan * still ON before sending the event 173026947304SEvan Yan * to DDI HP framework. 173126947304SEvan Yan */ 173226947304SEvan Yan if (slot_p->hs_attn_btn_pending == B_TRUE) { 173326947304SEvan Yan int hint; 173426947304SEvan Yan 173526947304SEvan Yan /* restore the power LED state */ 173626947304SEvan Yan (void) pcishpc_setled(slot_p, 173726947304SEvan Yan PCIE_HP_POWER_LED, 173826947304SEvan Yan hs_power_led_state); 173926947304SEvan Yan /* 174026947304SEvan Yan * send the ATTN button event 174126947304SEvan Yan * to DDI HP framework 174226947304SEvan Yan */ 174326947304SEvan Yan slot_p->hs_attn_btn_pending = B_FALSE; 174426947304SEvan Yan 174526947304SEvan Yan pcishpc_get_slot_state(slot_p); 174626947304SEvan Yan 174726947304SEvan Yan if (slot_p->hs_info.cn_state <= 174826947304SEvan Yan DDI_HP_CN_STATE_PRESENT) { 174926947304SEvan Yan /* 175026947304SEvan Yan * Insertion. 175126947304SEvan Yan */ 175226947304SEvan Yan hint = SE_INCOMING_RES; 175326947304SEvan Yan } else { 175426947304SEvan Yan /* 175526947304SEvan Yan * Want to remove; 175626947304SEvan Yan */ 175726947304SEvan Yan hint = SE_OUTGOING_RES; 175826947304SEvan Yan } 175926947304SEvan Yan pcie_hp_gen_sysevent_req( 176026947304SEvan Yan slot_p->hs_info.cn_name, 176126947304SEvan Yan hint, 176226947304SEvan Yan slot_p->hs_ctrl->hc_dip, 176326947304SEvan Yan KM_SLEEP); 176426947304SEvan Yan 176526947304SEvan Yan continue; 176626947304SEvan Yan } 176726947304SEvan Yan } 176826947304SEvan Yan 176926947304SEvan Yan /* restore the power LED state */ 177026947304SEvan Yan (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, 177126947304SEvan Yan hs_power_led_state); 177226947304SEvan Yan continue; 177326947304SEvan Yan } 177426947304SEvan Yan 177526947304SEvan Yan /* wait for another ATTN button event */ 177626947304SEvan Yan cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex); 177726947304SEvan Yan } 177826947304SEvan Yan 177926947304SEvan Yan PCIE_DBG("pcishpc_attn_btn_handler: thread exit\n"); 178026947304SEvan Yan cv_signal(&slot_p->hs_attn_btn_cv); 178126947304SEvan Yan CALLB_CPR_EXIT(&cprinfo); 178226947304SEvan Yan thread_exit(); 178326947304SEvan Yan } 178426947304SEvan Yan 178526947304SEvan Yan /* 178626947304SEvan Yan * pcishpc_get_slot_state() 178726947304SEvan Yan * 178826947304SEvan Yan * Get the state of the slot. 178926947304SEvan Yan * The slot state should have been initialized before this function gets called. 179026947304SEvan Yan */ 179126947304SEvan Yan static void 179226947304SEvan Yan pcishpc_get_slot_state(pcie_hp_slot_t *slot_p) 179326947304SEvan Yan { 179426947304SEvan Yan uint32_t reg; 179526947304SEvan Yan ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state; 179626947304SEvan Yan 179726947304SEvan Yan /* Read the logical slot register for this Slot. */ 179826947304SEvan Yan reg = pcishpc_read_reg(slot_p->hs_ctrl, 179926947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 180026947304SEvan Yan 180126947304SEvan Yan /* Convert from the SHPC slot state to the HPC slot state. */ 180226947304SEvan Yan slot_p->hs_info.cn_state = pcishpc_slot_shpc_to_hpc(reg); 180326947304SEvan Yan if (curr_state == DDI_HP_CN_STATE_POWERED && 180426947304SEvan Yan slot_p->hs_info.cn_state > DDI_HP_CN_STATE_POWERED) { 180526947304SEvan Yan /* 180626947304SEvan Yan * Keep POWERED state if it is currently POWERED state because 180726947304SEvan Yan * this driver does not really implement enable/disable 180826947304SEvan Yan * slot operations. That is, when poweron, it actually enables 180926947304SEvan Yan * the slot also. 181026947304SEvan Yan * So, from hardware view, POWERED == ENABLED. 181126947304SEvan Yan * But, when user explicitly change to POWERED state, it should 181226947304SEvan Yan * be kept until user explicitly change to other states later. 181326947304SEvan Yan */ 181426947304SEvan Yan slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED; 181526947304SEvan Yan } 181626947304SEvan Yan 181726947304SEvan Yan /* Convert from the SHPC Power LED state to the HPC Power LED state. */ 181826947304SEvan Yan slot_p->hs_power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3); 181926947304SEvan Yan 182026947304SEvan Yan /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */ 182126947304SEvan Yan slot_p->hs_attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3); 182226947304SEvan Yan 182326947304SEvan Yan /* We don't have a fault LED so just default it to OFF. */ 182426947304SEvan Yan slot_p->hs_fault_led_state = PCIE_HP_LED_OFF; 182526947304SEvan Yan 182626947304SEvan Yan /* We don't have an active LED so just default it to OFF. */ 182726947304SEvan Yan slot_p->hs_active_led_state = PCIE_HP_LED_OFF; 182826947304SEvan Yan } 182926947304SEvan Yan 183026947304SEvan Yan /* 183126947304SEvan Yan * pcishpc_set_slot_state() 183226947304SEvan Yan * 183326947304SEvan Yan * Updates the slot's state and leds. 183426947304SEvan Yan */ 183526947304SEvan Yan static int 183626947304SEvan Yan pcishpc_set_slot_state(pcie_hp_slot_t *slot_p, 183726947304SEvan Yan ddi_hp_cn_state_t new_slot_state) 183826947304SEvan Yan { 183926947304SEvan Yan uint32_t reg, cmd_code; 184026947304SEvan Yan ddi_hp_cn_state_t curr_state; 184126947304SEvan Yan 184226947304SEvan Yan ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex)); 184326947304SEvan Yan 184426947304SEvan Yan reg = pcishpc_read_reg(slot_p->hs_ctrl, 184526947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 184626947304SEvan Yan 184726947304SEvan Yan /* Default all states to unchanged. */ 184826947304SEvan Yan cmd_code = ((1 + slot_p->hs_num) << 8); 184926947304SEvan Yan 185026947304SEvan Yan /* Has the slot state changed? */ 185126947304SEvan Yan curr_state = pcishpc_slot_shpc_to_hpc(reg); 185226947304SEvan Yan if (curr_state != new_slot_state) { 185326947304SEvan Yan PCIE_DBG("pcishpc_set_slot_state() Slot State changed"); 185426947304SEvan Yan 185526947304SEvan Yan /* Set the new slot state in the Slot operation command. */ 185626947304SEvan Yan cmd_code |= pcishpc_slot_hpc_to_shpc(new_slot_state); 185726947304SEvan Yan } 185826947304SEvan Yan 185926947304SEvan Yan /* Has the Power LED state changed? */ 186026947304SEvan Yan if (slot_p->hs_power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) { 186126947304SEvan Yan PCIE_DBG("pcishpc_set_slot_state() Power LED State changed\n"); 186226947304SEvan Yan 186326947304SEvan Yan /* Set the new power led state in the Slot operation command. */ 186426947304SEvan Yan cmd_code |= 186526947304SEvan Yan (pcishpc_led_hpc_to_shpc(slot_p->hs_power_led_state) << 2); 186626947304SEvan Yan } 186726947304SEvan Yan 186826947304SEvan Yan /* Has the Attn LED state changed? */ 186926947304SEvan Yan if (slot_p->hs_attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) { 187026947304SEvan Yan PCIE_DBG("pcishpc_set_slot_state() Attn LED State changed\n"); 187126947304SEvan Yan 187226947304SEvan Yan /* Set the new attn led state in the Slot operation command. */ 187326947304SEvan Yan cmd_code |= 187426947304SEvan Yan (pcishpc_led_hpc_to_shpc(slot_p->hs_attn_led_state) << 4); 187526947304SEvan Yan } 187626947304SEvan Yan 187726947304SEvan Yan return (pcishpc_issue_command(slot_p->hs_ctrl, cmd_code)); 187826947304SEvan Yan } 187926947304SEvan Yan 188026947304SEvan Yan /* 188126947304SEvan Yan * setup slot name/slot-number info. 188226947304SEvan Yan */ 188326947304SEvan Yan static void 188426947304SEvan Yan pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot) 188526947304SEvan Yan { 188626947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot]; 188726947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 188826947304SEvan Yan uchar_t *slotname_data; 188926947304SEvan Yan int *slotnum; 189026947304SEvan Yan uint_t count; 189126947304SEvan Yan int len; 189226947304SEvan Yan uchar_t *s; 189326947304SEvan Yan uint32_t bit_mask; 189426947304SEvan Yan int pci_id_cnt, pci_id_bit; 189526947304SEvan Yan int slots_before, found; 189626947304SEvan Yan int invalid_slotnum = 0; 189726947304SEvan Yan 189826947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip, 189926947304SEvan Yan DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) == 190026947304SEvan Yan DDI_PROP_SUCCESS) { 190126947304SEvan Yan slot_p->hs_phy_slot_num = slotnum[0]; 190226947304SEvan Yan ddi_prop_free(slotnum); 190326947304SEvan Yan } else { 190426947304SEvan Yan if (ctrl_p->hc_device_increases) 190526947304SEvan Yan slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start + slot; 190626947304SEvan Yan else 190726947304SEvan Yan slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start - slot; 190826947304SEvan Yan 190926947304SEvan Yan if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->hc_dip, 191026947304SEvan Yan "physical-slot#", slot_p->hs_phy_slot_num)) != DDI_SUCCESS) 191126947304SEvan Yan PCIE_DBG("pcishpc_set_slot_name(): failed to " 191226947304SEvan Yan "create phyical-slot#%d\n", 191326947304SEvan Yan slot_p->hs_phy_slot_num); 191426947304SEvan Yan } 191526947304SEvan Yan 191626947304SEvan Yan /* Platform may not have initialized it */ 191726947304SEvan Yan if (!slot_p->hs_phy_slot_num) { 191826947304SEvan Yan slot_p->hs_phy_slot_num = pci_config_get8(bus_p->bus_cfg_hdl, 191926947304SEvan Yan PCI_BCNF_SECBUS); 192026947304SEvan Yan invalid_slotnum = 1; 192126947304SEvan Yan } 192226947304SEvan Yan slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num; 192326947304SEvan Yan slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE; 192426947304SEvan Yan 192526947304SEvan Yan /* 192626947304SEvan Yan * construct the slot_name: 1927296f12dcSToomas Soome * if "slot-names" property exists then use that name 192826947304SEvan Yan * else if valid slot number exists then it is "pci<slot-num>". 192926947304SEvan Yan * else it will be "pci<sec-bus-number>dev<dev-number>" 193026947304SEvan Yan */ 193126947304SEvan Yan if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS, 193226947304SEvan Yan "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) { 193326947304SEvan Yan bit_mask = slotname_data[3] | (slotname_data[2] << 8) | 193426947304SEvan Yan (slotname_data[1] << 16) | (slotname_data[0] << 24); 193526947304SEvan Yan 193626947304SEvan Yan pci_id_bit = 1; 193726947304SEvan Yan pci_id_cnt = slots_before = found = 0; 193826947304SEvan Yan 193926947304SEvan Yan /* 194026947304SEvan Yan * Walk the bit mask until we find the bit that corresponds 194126947304SEvan Yan * to our slots device number. We count how many bits 194226947304SEvan Yan * we find before we find our slot's bit. 194326947304SEvan Yan */ 194426947304SEvan Yan while (!found && (pci_id_cnt < 32)) { 194526947304SEvan Yan while (slot_p->hs_device_num != pci_id_cnt) { 194626947304SEvan Yan 194726947304SEvan Yan /* 194826947304SEvan Yan * Find the next bit set. 194926947304SEvan Yan */ 195026947304SEvan Yan while (!(bit_mask & pci_id_bit) && 195126947304SEvan Yan (pci_id_cnt < 32)) { 195226947304SEvan Yan pci_id_bit = pci_id_bit << 1; 195326947304SEvan Yan pci_id_cnt++; 195426947304SEvan Yan } 195526947304SEvan Yan 195626947304SEvan Yan if (slot_p->hs_device_num != pci_id_cnt) 195726947304SEvan Yan slots_before++; 195826947304SEvan Yan else 195926947304SEvan Yan found = 1; 196026947304SEvan Yan } 196126947304SEvan Yan } 196226947304SEvan Yan 196326947304SEvan Yan if (pci_id_cnt < 32) { 196426947304SEvan Yan 196526947304SEvan Yan /* 196626947304SEvan Yan * Set ptr to first string. 196726947304SEvan Yan */ 196826947304SEvan Yan s = slotname_data + 4; 196926947304SEvan Yan 197026947304SEvan Yan /* 197126947304SEvan Yan * Increment past all the strings for the slots 197226947304SEvan Yan * before ours. 197326947304SEvan Yan */ 197426947304SEvan Yan while (slots_before) { 1975296f12dcSToomas Soome while (*s != '\0') 197626947304SEvan Yan s++; 197726947304SEvan Yan s++; 197826947304SEvan Yan slots_before--; 197926947304SEvan Yan } 198026947304SEvan Yan 198126947304SEvan Yan slot_p->hs_info.cn_name = i_ddi_strdup((char *)s, 198226947304SEvan Yan KM_SLEEP); 198326947304SEvan Yan kmem_free(slotname_data, len); 198426947304SEvan Yan return; 198526947304SEvan Yan } 198626947304SEvan Yan 198726947304SEvan Yan /* slot-names entry not found */ 198826947304SEvan Yan PCIE_DBG("pcishpc_set_slot_name(): " 198926947304SEvan Yan "No slot-names entry found for slot #%d\n", 199026947304SEvan Yan slot_p->hs_phy_slot_num); 199126947304SEvan Yan kmem_free(slotname_data, len); 199226947304SEvan Yan } 199326947304SEvan Yan 199426947304SEvan Yan if (invalid_slotnum) { 199526947304SEvan Yan char tmp_name[256]; 199626947304SEvan Yan 199726947304SEvan Yan (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d", 199826947304SEvan Yan slot_p->hs_device_num); 199926947304SEvan Yan slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP); 200026947304SEvan Yan } else { 200126947304SEvan Yan char tmp_name[256]; 200226947304SEvan Yan 200326947304SEvan Yan (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d", 200426947304SEvan Yan slot_p->hs_phy_slot_num); 200526947304SEvan Yan slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP); 200626947304SEvan Yan } 200726947304SEvan Yan } 200826947304SEvan Yan 200926947304SEvan Yan /* 201026947304SEvan Yan * pcishpc_set_bus_speed() 201126947304SEvan Yan * 201226947304SEvan Yan * Set the bus speed and mode. 201326947304SEvan Yan */ 201426947304SEvan Yan static int 201526947304SEvan Yan pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p) 201626947304SEvan Yan { 201726947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 201826947304SEvan Yan int curr_speed = ctrl_p->hc_curr_bus_speed; 201926947304SEvan Yan int speed = -1; 202026947304SEvan Yan int avail_slots; 202126947304SEvan Yan uint32_t status, slots_avail1_reg, slots_avail2_reg; 202226947304SEvan Yan 202326947304SEvan Yan ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex)); 202426947304SEvan Yan 202526947304SEvan Yan /* Make sure that the slot is in a correct state */ 202626947304SEvan Yan status = pcishpc_read_reg(ctrl_p, 202726947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 202826947304SEvan Yan 202926947304SEvan Yan /* Return failure if the slot is empty */ 203026947304SEvan Yan if ((status & PCI_HP_SLOT_CARD_EMPTY_MASK) == 203126947304SEvan Yan PCI_HP_SLOT_CARD_EMPTY_MASK) { 203226947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() failed: " 203326947304SEvan Yan "the slot is empty\n"); 203426947304SEvan Yan return (DDI_FAILURE); 203526947304SEvan Yan } 203626947304SEvan Yan 203726947304SEvan Yan /* Return failure if the slot is not in disabled state */ 203826947304SEvan Yan if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_DISABLED) { 203926947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() failed: " 204026947304SEvan Yan "incorrect slot state\n"); 204126947304SEvan Yan return (DDI_FAILURE); 204226947304SEvan Yan } 204326947304SEvan Yan 204426947304SEvan Yan /* Set the "power-only" mode for the slot */ 204526947304SEvan Yan if (pcishpc_issue_command(ctrl_p, ((1+slot_p->hs_num)<<8) | 204626947304SEvan Yan PCI_HP_SLOT_POWER_ONLY) != DDI_SUCCESS) { 204726947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() failed to set " 204826947304SEvan Yan "the slot %d in the power-only mode\n", slot_p->hs_num); 204926947304SEvan Yan return (DDI_FAILURE); 205026947304SEvan Yan } 205126947304SEvan Yan 205226947304SEvan Yan /* Wait for power good */ 205326947304SEvan Yan delay(drv_usectohz(PCIE_HP_POWER_GOOD_WAIT_TIME)); 205426947304SEvan Yan 205526947304SEvan Yan /* Make sure that the slot is in "power-only" state */ 205626947304SEvan Yan status = pcishpc_read_reg(ctrl_p, 205726947304SEvan Yan PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num); 205826947304SEvan Yan 205926947304SEvan Yan if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_POWER_ONLY) { 206026947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() " 206126947304SEvan Yan "power-only failed: incorrect slot state\n"); 206226947304SEvan Yan return (DDI_FAILURE); 206326947304SEvan Yan } 206426947304SEvan Yan 206526947304SEvan Yan slots_avail1_reg = pcishpc_read_reg(ctrl_p, 206626947304SEvan Yan PCI_HP_SLOTS_AVAIL_I_REG); 206726947304SEvan Yan slots_avail2_reg = pcishpc_read_reg(ctrl_p, 206826947304SEvan Yan PCI_HP_SLOTS_AVAIL_II_REG); 206926947304SEvan Yan 207026947304SEvan Yan /* 207126947304SEvan Yan * Check if SHPC has available slots and select the highest 207226947304SEvan Yan * available bus speed for the slot. 207326947304SEvan Yan * 207426947304SEvan Yan * The bus speed codes are: 207526947304SEvan Yan * 100 - 133Mhz; <--+ 207626947304SEvan Yan * 011 - 100Mhz; <--+ PCI-X 207726947304SEvan Yan * 010 - 66Mhz; <--+ 207826947304SEvan Yan * 207926947304SEvan Yan * 001 - 66Mhz; <--+ 208026947304SEvan Yan * 000 - 33Mhz <--+ Conv PCI 208126947304SEvan Yan */ 208226947304SEvan Yan switch (status & PCI_HP_SLOT_PCIX_CAPABLE_MASK) { 208326947304SEvan Yan case PCI_HP_SLOT_133MHZ_PCIX_CAPABLE: 208426947304SEvan Yan avail_slots = (slots_avail1_reg >> 208526947304SEvan Yan PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT) & 208626947304SEvan Yan PCI_HP_AVAIL_SPEED_MASK; 208726947304SEvan Yan 208826947304SEvan Yan if (((curr_speed == -1) && avail_slots) || 208926947304SEvan Yan (curr_speed == PCI_HP_SBCR_133MHZ_PCIX_SPEED)) { 209026947304SEvan Yan speed = PCI_HP_SBCR_133MHZ_PCIX_SPEED; 209126947304SEvan Yan break; 209226947304SEvan Yan } 209326947304SEvan Yan /* FALLTHROUGH */ 209426947304SEvan Yan case PCI_HP_SLOT_100MHZ_PCIX_CAPABLE: 209526947304SEvan Yan avail_slots = (slots_avail1_reg >> 209626947304SEvan Yan PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT) & 209726947304SEvan Yan PCI_HP_AVAIL_SPEED_MASK; 209826947304SEvan Yan 209926947304SEvan Yan if (((curr_speed == -1) && avail_slots) || 210026947304SEvan Yan (curr_speed == PCI_HP_SBCR_100MHZ_PCIX_SPEED)) { 210126947304SEvan Yan speed = PCI_HP_SBCR_100MHZ_PCIX_SPEED; 210226947304SEvan Yan break; 210326947304SEvan Yan } 210426947304SEvan Yan /* FALLTHROUGH */ 210526947304SEvan Yan case PCI_HP_SLOT_66MHZ_PCIX_CAPABLE: 210626947304SEvan Yan avail_slots = (slots_avail1_reg >> 210726947304SEvan Yan PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT) & 210826947304SEvan Yan PCI_HP_AVAIL_SPEED_MASK; 210926947304SEvan Yan 211026947304SEvan Yan if (((curr_speed == -1) && avail_slots) || 211126947304SEvan Yan (curr_speed == PCI_HP_SBCR_66MHZ_PCIX_SPEED)) { 211226947304SEvan Yan speed = PCI_HP_SBCR_66MHZ_PCIX_SPEED; 211326947304SEvan Yan break; 211426947304SEvan Yan } 211526947304SEvan Yan /* FALLTHROUGH */ 211626947304SEvan Yan default: 211726947304SEvan Yan avail_slots = (slots_avail2_reg >> 211826947304SEvan Yan PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT) & 211926947304SEvan Yan PCI_HP_AVAIL_SPEED_MASK; 212026947304SEvan Yan 212126947304SEvan Yan if ((status & PCI_HP_SLOT_66MHZ_CONV_CAPABLE) && 212226947304SEvan Yan (((curr_speed == -1) && avail_slots) || 212326947304SEvan Yan (curr_speed == PCI_HP_SBCR_66MHZ_CONV_SPEED))) { 212426947304SEvan Yan speed = PCI_HP_SBCR_66MHZ_CONV_SPEED; 212526947304SEvan Yan } else { 212626947304SEvan Yan avail_slots = (slots_avail1_reg >> 212726947304SEvan Yan PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT) & 212826947304SEvan Yan PCI_HP_AVAIL_SPEED_MASK; 212926947304SEvan Yan 213026947304SEvan Yan if (((curr_speed == -1) && (avail_slots)) || 213126947304SEvan Yan (curr_speed == PCI_HP_SBCR_33MHZ_CONV_SPEED)) { 213226947304SEvan Yan speed = PCI_HP_SBCR_33MHZ_CONV_SPEED; 213326947304SEvan Yan } else { 213426947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() " 213526947304SEvan Yan " failed to set the bus speed, slot# %d\n", 213626947304SEvan Yan slot_p->hs_num); 213726947304SEvan Yan return (DDI_FAILURE); 213826947304SEvan Yan } 213926947304SEvan Yan } 214026947304SEvan Yan break; 214126947304SEvan Yan } 214226947304SEvan Yan 214326947304SEvan Yan /* 214426947304SEvan Yan * If the bus segment is already running, check to see the card 214526947304SEvan Yan * in the slot can support the current bus speed. 214626947304SEvan Yan */ 214726947304SEvan Yan if (curr_speed == speed) { 214826947304SEvan Yan /* 214926947304SEvan Yan * Check to see there is any slot available for the current 215026947304SEvan Yan * bus speed. Otherwise, we need fail the current slot connect 215126947304SEvan Yan * request. 215226947304SEvan Yan */ 215326947304SEvan Yan return ((avail_slots <= ctrl_p->hc_num_slots_connected) ? 215426947304SEvan Yan DDI_FAILURE : DDI_SUCCESS); 215526947304SEvan Yan } 215626947304SEvan Yan 215726947304SEvan Yan /* Set the bus speed */ 215826947304SEvan Yan if (pcishpc_issue_command(ctrl_p, PCI_HP_COMM_STS_SET_SPEED | 215926947304SEvan Yan speed) == DDI_FAILURE) { 216026947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() failed " 216126947304SEvan Yan "to set bus %d speed\n", slot_p->hs_num); 216226947304SEvan Yan return (DDI_FAILURE); 216326947304SEvan Yan } 216426947304SEvan Yan 216526947304SEvan Yan /* Check the current bus speed */ 216626947304SEvan Yan status = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG) & 216726947304SEvan Yan PCI_HP_SBCR_SPEED_MASK; 216826947304SEvan Yan if ((status & PCI_HP_SBCR_SPEED_MASK) != speed) { 216926947304SEvan Yan PCIE_DBG("pcishpc_set_bus_speed() an incorrect " 217026947304SEvan Yan "bus speed, slot = 0x%x, speed = 0x%x\n", 217126947304SEvan Yan slot_p->hs_num, status & PCI_HP_SBCR_SPEED_MASK); 217226947304SEvan Yan return (DDI_FAILURE); 217326947304SEvan Yan } 217426947304SEvan Yan 217526947304SEvan Yan 217626947304SEvan Yan /* Save the current bus speed */ 217726947304SEvan Yan ctrl_p->hc_curr_bus_speed = speed; 217826947304SEvan Yan 217926947304SEvan Yan return (DDI_SUCCESS); 218026947304SEvan Yan } 218126947304SEvan Yan 218226947304SEvan Yan /* 218326947304SEvan Yan * pcishpc_setled() 218426947304SEvan Yan * 218526947304SEvan Yan * Change the state of a slot's LED. 218626947304SEvan Yan */ 218726947304SEvan Yan static int 218826947304SEvan Yan pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led, 218926947304SEvan Yan pcie_hp_led_state_t state) 219026947304SEvan Yan { 219126947304SEvan Yan ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex)); 219226947304SEvan Yan 219326947304SEvan Yan switch (led) { 219426947304SEvan Yan case PCIE_HP_FAULT_LED: 219526947304SEvan Yan PCIE_DBG("pcishpc_setled() - PCIE_HP_FAULT_LED " 219626947304SEvan Yan "(set %s)\n", pcishpc_slot_textledstate(state)); 219726947304SEvan Yan slot_p->hs_fault_led_state = state; 219826947304SEvan Yan break; 219926947304SEvan Yan 220026947304SEvan Yan case PCIE_HP_POWER_LED: 220126947304SEvan Yan PCIE_DBG("pcishpc_setled() - PCIE_HP_POWER_LED " 220226947304SEvan Yan "(set %s)\n", pcishpc_slot_textledstate(state)); 220326947304SEvan Yan slot_p->hs_power_led_state = state; 220426947304SEvan Yan break; 220526947304SEvan Yan 220626947304SEvan Yan case PCIE_HP_ATTN_LED: 220726947304SEvan Yan PCIE_DBG("pcishpc_setled() - PCIE_HP_ATTN_LED " 220826947304SEvan Yan "(set %s)\n", pcishpc_slot_textledstate(state)); 220926947304SEvan Yan slot_p->hs_attn_led_state = state; 221026947304SEvan Yan break; 221126947304SEvan Yan 221226947304SEvan Yan case PCIE_HP_ACTIVE_LED: 221326947304SEvan Yan PCIE_DBG("pcishpc_setled() - PCIE_HP_ACTIVE_LED " 221426947304SEvan Yan "(set %s)\n", pcishpc_slot_textledstate(state)); 221526947304SEvan Yan slot_p->hs_active_led_state = state; 221626947304SEvan Yan break; 221726947304SEvan Yan } 221826947304SEvan Yan 221926947304SEvan Yan return (pcishpc_set_slot_state(slot_p, slot_p->hs_info.cn_state)); 222026947304SEvan Yan } 222126947304SEvan Yan 222226947304SEvan Yan /* 222326947304SEvan Yan * pcishpc_led_shpc_to_hpc() 222426947304SEvan Yan * 222526947304SEvan Yan * Convert from SHPC indicator status to HPC indicator status. 222626947304SEvan Yan */ 222726947304SEvan Yan static int 222826947304SEvan Yan pcishpc_led_shpc_to_hpc(int state) 222926947304SEvan Yan { 223026947304SEvan Yan switch (state) { 223126947304SEvan Yan case 1: /* SHPC On bits b01 */ 223226947304SEvan Yan return (PCIE_HP_LED_ON); 223326947304SEvan Yan case 2: /* SHPC Blink bits b10 */ 223426947304SEvan Yan return (PCIE_HP_LED_BLINK); 223526947304SEvan Yan case 3: /* SHPC Off bits b11 */ 223626947304SEvan Yan return (PCIE_HP_LED_OFF); 223726947304SEvan Yan } 223826947304SEvan Yan 223926947304SEvan Yan return (PCIE_HP_LED_OFF); 224026947304SEvan Yan } 224126947304SEvan Yan 224226947304SEvan Yan 224326947304SEvan Yan /* 224426947304SEvan Yan * pcishpc_led_hpc_to_shpc() 224526947304SEvan Yan * 224626947304SEvan Yan * Convert from HPC indicator status to SHPC indicator status. 224726947304SEvan Yan */ 224826947304SEvan Yan static int 224926947304SEvan Yan pcishpc_led_hpc_to_shpc(int state) 225026947304SEvan Yan { 225126947304SEvan Yan switch (state) { 225226947304SEvan Yan case PCIE_HP_LED_ON: 225326947304SEvan Yan return (1); /* SHPC On bits b01 */ 225426947304SEvan Yan case PCIE_HP_LED_BLINK: 225526947304SEvan Yan return (2); /* SHPC Blink bits b10 */ 225626947304SEvan Yan case PCIE_HP_LED_OFF: 225726947304SEvan Yan return (3); /* SHPC Off bits b11 */ 225826947304SEvan Yan } 225926947304SEvan Yan 226026947304SEvan Yan return (3); /* SHPC Off bits b11 */ 226126947304SEvan Yan } 226226947304SEvan Yan 226326947304SEvan Yan /* 226426947304SEvan Yan * pcishpc_slot_shpc_to_hpc() 226526947304SEvan Yan * 226626947304SEvan Yan * Convert from SHPC slot state to HPC slot state. 226726947304SEvan Yan * The argument shpc_state is expected to be read from the slot register. 226826947304SEvan Yan */ 226926947304SEvan Yan static int 227026947304SEvan Yan pcishpc_slot_shpc_to_hpc(int shpc_state) 227126947304SEvan Yan { 227226947304SEvan Yan if ((shpc_state & PCI_HP_SLOT_CARD_EMPTY_MASK) == 227326947304SEvan Yan PCI_HP_SLOT_CARD_EMPTY_MASK) 227426947304SEvan Yan return (DDI_HP_CN_STATE_EMPTY); 227526947304SEvan Yan 227626947304SEvan Yan switch (shpc_state & PCI_HP_SLOT_STATE_MASK) { 227726947304SEvan Yan case PCI_HP_SLOT_POWER_ONLY: /* SHPC Powered Only */ 227826947304SEvan Yan return (DDI_HP_CN_STATE_POWERED); 227926947304SEvan Yan 228026947304SEvan Yan case PCI_HP_SLOT_ENABLED: /* SHPC Enabled */ 228126947304SEvan Yan return (DDI_HP_CN_STATE_ENABLED); 228226947304SEvan Yan 228326947304SEvan Yan case PCI_HP_SLOT_DISABLED: /* SHPC Disabled */ 228426947304SEvan Yan default : /* SHPC Reserved */ 228526947304SEvan Yan return (DDI_HP_CN_STATE_PRESENT); 228626947304SEvan Yan } 228726947304SEvan Yan } 228826947304SEvan Yan 228926947304SEvan Yan /* 229026947304SEvan Yan * pcishpc_slot_hpc_to_shpc() 229126947304SEvan Yan * 229226947304SEvan Yan * Convert from HPC slot state to SHPC slot state. 229326947304SEvan Yan */ 229426947304SEvan Yan static int 229526947304SEvan Yan pcishpc_slot_hpc_to_shpc(int state) 229626947304SEvan Yan { 229726947304SEvan Yan switch (state) { 229826947304SEvan Yan case DDI_HP_CN_STATE_EMPTY: 229926947304SEvan Yan return (0); 230026947304SEvan Yan 230126947304SEvan Yan case DDI_HP_CN_STATE_POWERED: 230226947304SEvan Yan return (PCI_HP_SLOT_POWER_ONLY); 230326947304SEvan Yan 230426947304SEvan Yan case DDI_HP_CN_STATE_ENABLED: 230526947304SEvan Yan return (PCI_HP_SLOT_ENABLED); 230626947304SEvan Yan 230726947304SEvan Yan default: 230826947304SEvan Yan return (PCI_HP_SLOT_DISABLED); 230926947304SEvan Yan } 231026947304SEvan Yan } 231126947304SEvan Yan 231226947304SEvan Yan /* 231326947304SEvan Yan * pcishpc_slot_textslotstate() 231426947304SEvan Yan * 231526947304SEvan Yan * Convert the request into a text message. 231626947304SEvan Yan */ 231726947304SEvan Yan static char * 231826947304SEvan Yan pcishpc_slot_textslotstate(ddi_hp_cn_state_t state) 231926947304SEvan Yan { 232026947304SEvan Yan /* Convert an HPC slot state into a textual string. */ 232126947304SEvan Yan if (state == DDI_HP_CN_STATE_EMPTY) 232226947304SEvan Yan return ("HPC_SLOT_EMPTY"); 232326947304SEvan Yan else if (state == DDI_HP_CN_STATE_ENABLED) 232426947304SEvan Yan return ("HPC_SLOT_ENABLED"); 232526947304SEvan Yan else if (state == DDI_HP_CN_STATE_POWERED) 232626947304SEvan Yan return ("HPC_SLOT_POWERED_ONLY"); 232726947304SEvan Yan else 232826947304SEvan Yan return ("HPC_SLOT_DISABLED"); 232926947304SEvan Yan } 233026947304SEvan Yan 233126947304SEvan Yan 233226947304SEvan Yan /* 233326947304SEvan Yan * pcishpc_slot_textledstate() 233426947304SEvan Yan * 233526947304SEvan Yan * Convert the led state into a text message. 233626947304SEvan Yan */ 233726947304SEvan Yan static char * 233826947304SEvan Yan pcishpc_slot_textledstate(pcie_hp_led_state_t state) 233926947304SEvan Yan { 234026947304SEvan Yan /* Convert an HPC led state into a textual string. */ 234126947304SEvan Yan switch (state) { 234226947304SEvan Yan case PCIE_HP_LED_OFF: 234326947304SEvan Yan return ("off"); 234426947304SEvan Yan 234526947304SEvan Yan case PCIE_HP_LED_ON: 234626947304SEvan Yan return ("on"); 234726947304SEvan Yan 234826947304SEvan Yan case PCIE_HP_LED_BLINK: 234926947304SEvan Yan return ("blink"); 235026947304SEvan Yan } 235126947304SEvan Yan return ("unknown"); 235226947304SEvan Yan } 235326947304SEvan Yan 235426947304SEvan Yan 235526947304SEvan Yan /* 235626947304SEvan Yan * pcishpc_read_reg() 235726947304SEvan Yan * 235826947304SEvan Yan * Read from a SHPC controller register. 235926947304SEvan Yan */ 236026947304SEvan Yan static uint32_t 236126947304SEvan Yan pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg) 236226947304SEvan Yan { 236326947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 236426947304SEvan Yan 236526947304SEvan Yan /* Setup the SHPC dword select register. */ 236626947304SEvan Yan pci_config_put8(bus_p->bus_cfg_hdl, 236726947304SEvan Yan bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg); 236826947304SEvan Yan 236926947304SEvan Yan /* Read back the SHPC dword select register and verify. */ 237026947304SEvan Yan if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off + 237126947304SEvan Yan PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) { 237226947304SEvan Yan PCIE_DBG("pcishpc_read_reg() - Failed writing DWORD " 237326947304SEvan Yan "select reg\n"); 237426947304SEvan Yan return (0xFFFFFFFF); 237526947304SEvan Yan } 237626947304SEvan Yan 237726947304SEvan Yan /* Read from the SHPC dword data register. */ 237826947304SEvan Yan return (pci_config_get32(bus_p->bus_cfg_hdl, 237926947304SEvan Yan bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF)); 238026947304SEvan Yan } 238126947304SEvan Yan 238226947304SEvan Yan 238326947304SEvan Yan /* 238426947304SEvan Yan * pcishpc_write_reg() 238526947304SEvan Yan * 238626947304SEvan Yan * Write to a SHPC controller register. 238726947304SEvan Yan */ 238826947304SEvan Yan static void 238926947304SEvan Yan pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg, uint32_t data) 239026947304SEvan Yan { 239126947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 239226947304SEvan Yan 239326947304SEvan Yan /* Setup the SHPC dword select register. */ 239426947304SEvan Yan pci_config_put8(bus_p->bus_cfg_hdl, 239526947304SEvan Yan bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg); 239626947304SEvan Yan 239726947304SEvan Yan /* Read back the SHPC dword select register and verify. */ 239826947304SEvan Yan if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off + 239926947304SEvan Yan PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) { 240026947304SEvan Yan PCIE_DBG("pcishpc_write_reg() - Failed writing " 240126947304SEvan Yan "DWORD select reg\n"); 240226947304SEvan Yan return; 240326947304SEvan Yan } 240426947304SEvan Yan 240526947304SEvan Yan /* Write to the SHPC dword data register. */ 240626947304SEvan Yan pci_config_put32(bus_p->bus_cfg_hdl, 240726947304SEvan Yan bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF, data); 240826947304SEvan Yan 240926947304SEvan Yan /* 241026947304SEvan Yan * Issue a read of the VendorID/DeviceID just to force the previous 241126947304SEvan Yan * write to complete. This is probably not necessary, but it does 241226947304SEvan Yan * help enforce ordering if there is an issue. 241326947304SEvan Yan */ 241426947304SEvan Yan (void) pci_config_get16(bus_p->bus_cfg_hdl, PCI_CONF_VENID); 241526947304SEvan Yan } 241626947304SEvan Yan 241726947304SEvan Yan 241826947304SEvan Yan #ifdef DEBUG 241926947304SEvan Yan /* 242026947304SEvan Yan * pcishpc_dump_regs() 242126947304SEvan Yan * 242226947304SEvan Yan * Dumps all of the SHPC controller registers. 242326947304SEvan Yan */ 242426947304SEvan Yan static void 242526947304SEvan Yan pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p) 242626947304SEvan Yan { 242726947304SEvan Yan int slot, numSlots; 242826947304SEvan Yan uint32_t reg; 242926947304SEvan Yan char *state; 243026947304SEvan Yan 243126947304SEvan Yan if (!pcie_debug_flags) 243226947304SEvan Yan return; 243326947304SEvan Yan 243426947304SEvan Yan PCIE_DBG("pcishpc_dump_regs() called:\n"); 243526947304SEvan Yan PCIE_DBG("=========================================================="); 243626947304SEvan Yan 243726947304SEvan Yan PCIE_DBG("SHPC Base Offset " 243826947304SEvan Yan ": 0x%08x\n", pcishpc_read_reg(ctrl_p, PCI_HP_BASE_OFFSET_REG)); 243926947304SEvan Yan 244026947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_I_REG); 244126947304SEvan Yan 244226947304SEvan Yan PCIE_DBG("Number of PCIX slots avail (33 Mhz) : %d\n", 244326947304SEvan Yan (reg & 31)); 244426947304SEvan Yan 244526947304SEvan Yan PCIE_DBG("Number of PCIX slots avail (66 Mhz) : %d\n", 244626947304SEvan Yan ((reg>>8) & 31)); 244726947304SEvan Yan 244826947304SEvan Yan PCIE_DBG("Number of PCIX slots avail (100 Mhz) : %d\n", 244926947304SEvan Yan ((reg>>16) & 31)); 245026947304SEvan Yan 245126947304SEvan Yan PCIE_DBG("Number of PCIX slots avail (133 Mhz) : %d\n", 245226947304SEvan Yan ((reg>>24) & 31)); 245326947304SEvan Yan 245426947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_II_REG); 245526947304SEvan Yan 245626947304SEvan Yan PCIE_DBG("Number of conventional PCI slots (66 Mhz) : %d\n", 245726947304SEvan Yan (reg & 31)); 245826947304SEvan Yan 245926947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG); 246026947304SEvan Yan 246126947304SEvan Yan numSlots = (reg & 31); 246226947304SEvan Yan 246326947304SEvan Yan PCIE_DBG("Number of Slots connected to this port : %d\n", 246426947304SEvan Yan numSlots); 246526947304SEvan Yan 246626947304SEvan Yan PCIE_DBG("PCI Device # for First HotPlug Slot : %d\n", 246726947304SEvan Yan ((reg>>8) & 31)); 246826947304SEvan Yan 246926947304SEvan Yan PCIE_DBG("Physical Slot # for First PCI Device # : %d\n", 247026947304SEvan Yan ((reg>>16) & 0x7ff)); 247126947304SEvan Yan 247226947304SEvan Yan PCIE_DBG("Physical Slot Number Up/Down : %d\n", 247326947304SEvan Yan ((reg>>29) & 0x1)); 247426947304SEvan Yan 247526947304SEvan Yan PCIE_DBG("MRL Sensor Implemented : %s\n", 247626947304SEvan Yan (reg & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : "No"); 247726947304SEvan Yan 247826947304SEvan Yan PCIE_DBG("Attention Button Implemented : %s\n", 247926947304SEvan Yan (reg & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : "No"); 248026947304SEvan Yan 248126947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG); 248226947304SEvan Yan 248326947304SEvan Yan switch (reg & 7) { 248426947304SEvan Yan case 0: 248526947304SEvan Yan state = "33Mhz Conventional PCI"; 248626947304SEvan Yan break; 248726947304SEvan Yan case 1: 248826947304SEvan Yan state = "66Mhz Conventional PCI"; 248926947304SEvan Yan break; 249026947304SEvan Yan case 2: 249126947304SEvan Yan state = "66Mhz PCI-X"; 249226947304SEvan Yan break; 249326947304SEvan Yan case 3: 249426947304SEvan Yan state = "100Mhz PCI-X"; 249526947304SEvan Yan break; 249626947304SEvan Yan case 4: 249726947304SEvan Yan state = "133Mhz PCI-X"; 249826947304SEvan Yan break; 249926947304SEvan Yan default: 250026947304SEvan Yan state = "Reserved (Error)"; 250126947304SEvan Yan break; 250226947304SEvan Yan } 250326947304SEvan Yan 250426947304SEvan Yan PCIE_DBG("Current Port Operation Mode : %s\n", state); 250526947304SEvan Yan 250626947304SEvan Yan PCIE_DBG("SHPC Interrupt Message Number : %d\n", 250726947304SEvan Yan ((reg>>16) &31)); 250826947304SEvan Yan 250926947304SEvan Yan PCIE_DBG("SHPC Programming Interface : %d\n", 251026947304SEvan Yan ((reg>>24) & 0xff)); 251126947304SEvan Yan 251226947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG); 251326947304SEvan Yan 251426947304SEvan Yan PCIE_DBG("SHPC Command Code : %d\n", 251526947304SEvan Yan (reg & 0xff)); 251626947304SEvan Yan 251726947304SEvan Yan PCIE_DBG("SHPC Target Slot : %d\n", 251826947304SEvan Yan ((reg>>8) & 31)); 251926947304SEvan Yan 252026947304SEvan Yan PCIE_DBG("SHPC Controller Busy : %s\n", 252126947304SEvan Yan ((reg>>16) & 1) ? "Yes" : "No"); 252226947304SEvan Yan 252326947304SEvan Yan PCIE_DBG("SHPC Controller Err: MRL Sensor : %s\n", 252426947304SEvan Yan ((reg>>17) & 1) ? "Yes" : "No"); 252526947304SEvan Yan 252626947304SEvan Yan PCIE_DBG("SHPC Controller Err: Invalid Command : %s\n", 252726947304SEvan Yan ((reg>>18) & 1) ? "Yes" : "No"); 252826947304SEvan Yan 252926947304SEvan Yan PCIE_DBG("SHPC Controller Err: Invalid Speed/Mode : %s\n", 253026947304SEvan Yan ((reg>>19) & 1) ? "Yes" : "No"); 253126947304SEvan Yan 253226947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG); 253326947304SEvan Yan 253426947304SEvan Yan PCIE_DBG("Command Completion Interrupt Pending : %s\n", 253526947304SEvan Yan (reg & PCI_HP_IRQ_CMD_COMPLETE) ? "Yes" : "No"); 253626947304SEvan Yan 253726947304SEvan Yan for (slot = 0; slot < numSlots; slot++) { 253826947304SEvan Yan PCIE_DBG("Slot %d Interrupt Pending : %s\n", slot+1, 253926947304SEvan Yan (reg & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No"); 254026947304SEvan Yan } 254126947304SEvan Yan 254226947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG); 254326947304SEvan Yan 254426947304SEvan Yan PCIE_DBG("Arbiter SERR Pending : %s\n", 254526947304SEvan Yan (reg & PCI_HP_IRQ_SERR_ARBITER_PENDING) ? "Yes" : "No"); 254626947304SEvan Yan 254726947304SEvan Yan for (slot = 0; slot < numSlots; slot++) { 254826947304SEvan Yan PCIE_DBG("Slot %d SERR Pending : %s\n", 254926947304SEvan Yan slot+1, (reg & 255026947304SEvan Yan (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot)) ? "Yes" : "No"); 255126947304SEvan Yan } 255226947304SEvan Yan 255326947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG); 255426947304SEvan Yan 255526947304SEvan Yan PCIE_DBG("Global Interrupt Mask : %s\n", 255626947304SEvan Yan (reg & PCI_HP_SERR_INT_GLOBAL_IRQ_MASK) ? "Yes" : "No"); 255726947304SEvan Yan 255826947304SEvan Yan PCIE_DBG("Global SERR Mask : %s\n", 255926947304SEvan Yan (reg & PCI_HP_SERR_INT_GLOBAL_SERR_MASK) ? "Yes" : "No"); 256026947304SEvan Yan 256126947304SEvan Yan PCIE_DBG("Command Completion Interrupt Mask : %s\n", 256226947304SEvan Yan (reg & PCI_HP_SERR_INT_CMD_COMPLETE_MASK) ? "Yes" : "No"); 256326947304SEvan Yan 256426947304SEvan Yan PCIE_DBG("Arbiter SERR Mask : %s\n", 256526947304SEvan Yan (reg & PCI_HP_SERR_INT_ARBITER_SERR_MASK) ? "Yes" : "No"); 256626947304SEvan Yan 256726947304SEvan Yan PCIE_DBG("Command Completion Detected : %s\n", 256826947304SEvan Yan (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) ? "Yes" : "No"); 256926947304SEvan Yan 257026947304SEvan Yan PCIE_DBG("Arbiter Timeout Detected : %s\n", 257126947304SEvan Yan (reg & PCI_HP_SERR_INT_ARBITER_IRQ) ? "Yes" : "No"); 257226947304SEvan Yan 257326947304SEvan Yan for (slot = 0; slot < numSlots; slot++) { 257426947304SEvan Yan PCIE_DBG("Logical Slot %d Registers:\n", slot+1); 257526947304SEvan Yan PCIE_DBG("------------------------------------\n"); 257626947304SEvan Yan 257726947304SEvan Yan reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot); 257826947304SEvan Yan 257926947304SEvan Yan PCIE_DBG("Slot %d state : %s\n", slot+1, 258026947304SEvan Yan pcishpc_slot_textslotstate(pcishpc_slot_shpc_to_hpc(reg))); 258126947304SEvan Yan 258226947304SEvan Yan PCIE_DBG("Slot %d Power Indicator State : %s\n", slot+1, 258326947304SEvan Yan pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc( 258426947304SEvan Yan (reg>>2) &3))); 258526947304SEvan Yan 258626947304SEvan Yan PCIE_DBG("Slot %d Attention Indicator State : %s\n", slot+1, 258726947304SEvan Yan pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc( 258826947304SEvan Yan (reg>>4)&3))); 258926947304SEvan Yan 259026947304SEvan Yan PCIE_DBG("Slot %d Power Fault : %s\n", slot+1, 259126947304SEvan Yan ((reg>>6)&1) ? "Fault Detected" : "No Fault"); 259226947304SEvan Yan PCIE_DBG("Slot %d Attention Button : %s\n", slot+1, 259326947304SEvan Yan ((reg>>7)&1) ? "Depressed" : "Not Depressed"); 259426947304SEvan Yan PCIE_DBG("Slot %d MRL Sensor : %s\n", slot+1, 259526947304SEvan Yan ((reg>>8)&1) ? "Not Closed" : "Closed"); 259626947304SEvan Yan PCIE_DBG("Slot %d 66mhz Capable : %s\n", slot+1, 259726947304SEvan Yan ((reg>>9)&1) ? "66mhz" : "33mgz"); 259826947304SEvan Yan 259926947304SEvan Yan switch ((reg>>10)&3) { 260026947304SEvan Yan case 0: 260126947304SEvan Yan state = "Card Present 7.5W"; 260226947304SEvan Yan break; 260326947304SEvan Yan case 1: 260426947304SEvan Yan state = "Card Present 15W"; 260526947304SEvan Yan break; 260626947304SEvan Yan case 2: 260726947304SEvan Yan state = "Card Present 25W"; 260826947304SEvan Yan break; 260926947304SEvan Yan case 3: 261026947304SEvan Yan state = "Slot Empty"; 261126947304SEvan Yan break; 261226947304SEvan Yan } 261326947304SEvan Yan 261426947304SEvan Yan PCIE_DBG("Slot %d PRSNT1#/PRSNT2# : %s\n", slot+1, 261526947304SEvan Yan state); 261626947304SEvan Yan 261726947304SEvan Yan switch ((reg>>12)&3) { 261826947304SEvan Yan case 0: 261926947304SEvan Yan state = "Non PCI-X"; 262026947304SEvan Yan break; 262126947304SEvan Yan case 1: 262226947304SEvan Yan state = "66mhz PCI-X"; 262326947304SEvan Yan break; 262426947304SEvan Yan case 2: 262526947304SEvan Yan state = "Reserved"; 262626947304SEvan Yan break; 262726947304SEvan Yan case 3: 262826947304SEvan Yan state = "133mhz PCI-X"; 262926947304SEvan Yan break; 263026947304SEvan Yan } 263126947304SEvan Yan 263226947304SEvan Yan PCIE_DBG("Slot %d Card Presence Change Detected : %s\n", 263326947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_PRESENCE_DETECTED) ? "Yes" : 263426947304SEvan Yan "No"); 263526947304SEvan Yan PCIE_DBG("Slot %d Isolated Power Fault Detected : %s\n", 263626947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_ISO_PWR_DETECTED) ? "Yes" : 263726947304SEvan Yan "No"); 263826947304SEvan Yan PCIE_DBG("Slot %d Attention Button Press Detected : %s\n", 263926947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_ATTN_DETECTED) ? "Yes" : "No"); 264026947304SEvan Yan PCIE_DBG("Slot %d MRL Sensor Change Detected : %s\n", 264126947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_MRL_DETECTED) ? "Yes" : "No"); 264226947304SEvan Yan PCIE_DBG("Slot %d Connected Power Fault Detected : %s\n", 264326947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_POWER_DETECTED) ? "Yes" : "No"); 264426947304SEvan Yan 264526947304SEvan Yan PCIE_DBG("Slot %d Card Presence IRQ Masked : %s\n", 264626947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_PRESENCE_MASK) ? "Yes" : "No"); 264726947304SEvan Yan PCIE_DBG("Slot %d Isolated Power Fault IRQ Masked : %s\n", 264826947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_ISO_PWR_MASK) ? "Yes" : "No"); 264926947304SEvan Yan PCIE_DBG("Slot %d Attention Button IRQ Masked : %s\n", 265026947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_ATTN_MASK) ? "Yes" : "No"); 265126947304SEvan Yan PCIE_DBG("Slot %d MRL Sensor IRQ Masked : %s\n", 265226947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_MRL_MASK) ? "Yes" : "No"); 265326947304SEvan Yan PCIE_DBG("Slot %d Connected Power Fault IRQ Masked : %s\n", 265426947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_POWER_MASK) ? "Yes" : "No"); 265526947304SEvan Yan PCIE_DBG("Slot %d MRL Sensor SERR Masked : %s\n", 265626947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_MRL_SERR_MASK) ? "Yes" : "No"); 265726947304SEvan Yan PCIE_DBG("Slot %d Connected Power Fault SERR Masked : %s\n", 265826947304SEvan Yan slot+1, (reg & PCI_HP_SLOT_POWER_SERR_MASK) ? "Yes" : "No"); 265926947304SEvan Yan } 266026947304SEvan Yan } 266126947304SEvan Yan #endif /* DEBUG */ 2662