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 2226947304SEvan Yan /* 2326947304SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2426947304SEvan Yan * Use is subject to license terms. 25*35786f68SRobert Mustacchi * Copyright (c) 2018, Joyent, Inc. 2626947304SEvan Yan */ 2726947304SEvan Yan 2826947304SEvan Yan /* 2926947304SEvan Yan * ACPI interface related functions used in PCIEHPC driver module. 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/debug.h> 3826947304SEvan Yan #include <sys/vtrace.h> 3926947304SEvan Yan #include <sys/varargs.h> 4026947304SEvan Yan #include <sys/ddi_impldefs.h> 4126947304SEvan Yan #include <sys/pci.h> 4226947304SEvan Yan #include <sys/ddi.h> 4326947304SEvan Yan #include <sys/sunddi.h> 4426947304SEvan Yan #include <sys/sunndi.h> 4526947304SEvan Yan #include <sys/pci_impl.h> 4626947304SEvan Yan #include <sys/pcie_acpi.h> 4726947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h> 4826947304SEvan Yan #include <sys/hotplug/pci/pciehpc_acpi.h> 4926947304SEvan Yan 5026947304SEvan Yan /* local static functions */ 5126947304SEvan Yan static int pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p); 5226947304SEvan Yan static int pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p); 5326947304SEvan Yan static int pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p); 5426947304SEvan Yan static int pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p); 5526947304SEvan Yan static int pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p); 5626947304SEvan Yan static int pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p); 5726947304SEvan Yan static int pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p, 5826947304SEvan Yan ddi_hp_cn_state_t *result); 5926947304SEvan Yan static int pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p, 6026947304SEvan Yan ddi_hp_cn_state_t *result); 6126947304SEvan Yan static void pciehpc_acpi_setup_ops(pcie_hp_ctrl_t *ctrl_p); 6226947304SEvan Yan 6326947304SEvan Yan static ACPI_STATUS pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p); 6426947304SEvan Yan static void pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p); 6526947304SEvan Yan static ACPI_STATUS pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p); 6626947304SEvan Yan static ACPI_STATUS pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p); 6726947304SEvan Yan static void pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, 6826947304SEvan Yan void *context); 6926947304SEvan Yan static ACPI_STATUS pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp); 7026947304SEvan Yan 7126947304SEvan Yan /* 7226947304SEvan Yan * Update ops vector with platform specific (ACPI, CK8-04,...) functions. 7326947304SEvan Yan */ 7426947304SEvan Yan void 7526947304SEvan Yan pciehpc_update_ops(pcie_hp_ctrl_t *ctrl_p) 7626947304SEvan Yan { 7726947304SEvan Yan boolean_t hp_native_mode = B_FALSE; 7826947304SEvan Yan uint32_t osc_flags = OSC_CONTROL_PCIE_NAT_HP; 7926947304SEvan Yan 8026947304SEvan Yan /* 8126947304SEvan Yan * Call _OSC method to determine if hotplug mode is native or ACPI. 8226947304SEvan Yan * If _OSC method succeeds hp_native_mode below will be set according to 8326947304SEvan Yan * if native hotplug control was granted or not by BIOS. 8426947304SEvan Yan * 8526947304SEvan Yan * If _OSC method fails for any reason or if native hotplug control was 8626947304SEvan Yan * not granted assume it's ACPI mode and update platform specific 8726947304SEvan Yan * (ACPI, CK8-04,...) impl. ops 8826947304SEvan Yan */ 8926947304SEvan Yan 9026947304SEvan Yan if (pcie_acpi_osc(ctrl_p->hc_dip, &osc_flags) == DDI_SUCCESS) { 9126947304SEvan Yan hp_native_mode = (osc_flags & OSC_CONTROL_PCIE_NAT_HP) ? 9226947304SEvan Yan B_TRUE : B_FALSE; 9326947304SEvan Yan } 9426947304SEvan Yan 9526947304SEvan Yan if (!hp_native_mode) { 9626947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 9726947304SEvan Yan 9826947304SEvan Yan /* update ops vector for ACPI mode */ 9926947304SEvan Yan pciehpc_acpi_setup_ops(ctrl_p); 10026947304SEvan Yan bus_p->bus_hp_sup_modes |= PCIE_ACPI_HP_MODE; 10126947304SEvan Yan bus_p->bus_hp_curr_mode = PCIE_ACPI_HP_MODE; 10226947304SEvan Yan } 10326947304SEvan Yan } 10426947304SEvan Yan 10526947304SEvan Yan void 10626947304SEvan Yan pciehpc_acpi_setup_ops(pcie_hp_ctrl_t *ctrl_p) 10726947304SEvan Yan { 10826947304SEvan Yan ctrl_p->hc_ops.init_hpc_hw = pciehpc_acpi_hpc_init; 10926947304SEvan Yan ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_acpi_hpc_uninit; 11026947304SEvan Yan ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_acpi_slotinfo_init; 11126947304SEvan Yan ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_acpi_slotinfo_uninit; 11226947304SEvan Yan ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_acpi_slot_poweron; 11326947304SEvan Yan ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_acpi_slot_poweroff; 11426947304SEvan Yan ctrl_p->hc_ops.disable_hpc_intr = pciehpc_acpi_disable_intr; 11526947304SEvan Yan ctrl_p->hc_ops.enable_hpc_intr = pciehpc_acpi_enable_intr; 11626947304SEvan Yan } 11726947304SEvan Yan 11826947304SEvan Yan /* 11926947304SEvan Yan * Intialize hot plug control for ACPI mode. 12026947304SEvan Yan */ 12126947304SEvan Yan static int 12226947304SEvan Yan pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p) 12326947304SEvan Yan { 12426947304SEvan Yan ACPI_HANDLE pcibus_obj; 12526947304SEvan Yan int status = AE_ERROR; 12626947304SEvan Yan ACPI_HANDLE slot_dev_obj; 12726947304SEvan Yan ACPI_HANDLE hdl; 12826947304SEvan Yan pciehpc_acpi_t *acpi_p; 12926947304SEvan Yan uint16_t bus_methods = 0; 13026947304SEvan Yan uint16_t slot_methods = 0; 13126947304SEvan Yan 13226947304SEvan Yan /* get the ACPI object for the bus node */ 13326947304SEvan Yan status = acpica_get_handle(ctrl_p->hc_dip, &pcibus_obj); 13426947304SEvan Yan if (status != AE_OK) 13526947304SEvan Yan return (DDI_FAILURE); 13626947304SEvan Yan 13726947304SEvan Yan /* get the ACPI object handle for the child node */ 13826947304SEvan Yan status = AcpiGetNextObject(ACPI_TYPE_DEVICE, pcibus_obj, 13926947304SEvan Yan NULL, &slot_dev_obj); 140ed11b501SColin Zou - Sun Microsystems - Beijing China if (status != AE_OK) { 141ed11b501SColin Zou - Sun Microsystems - Beijing China PCIE_DBG("pciehpc_acpi_hpc_init: Get ACPI object failed\n"); 14226947304SEvan Yan return (DDI_FAILURE); 143ed11b501SColin Zou - Sun Microsystems - Beijing China } 14426947304SEvan Yan 14526947304SEvan Yan /* 14626947304SEvan Yan * gather the info about the ACPI methods present on the bus node 14726947304SEvan Yan * and the child nodes. 14826947304SEvan Yan */ 14926947304SEvan Yan if (AcpiGetHandle(pcibus_obj, "_OSC", &hdl) == AE_OK) 15026947304SEvan Yan bus_methods |= PCIEHPC_ACPI_OSC_PRESENT; 15126947304SEvan Yan if (AcpiGetHandle(pcibus_obj, "_OSHP", &hdl) == AE_OK) 15226947304SEvan Yan bus_methods |= PCIEHPC_ACPI_OSHP_PRESENT; 15326947304SEvan Yan if (AcpiGetHandle(pcibus_obj, "_HPX", &hdl) == AE_OK) 15426947304SEvan Yan bus_methods |= PCIEHPC_ACPI_HPX_PRESENT; 15526947304SEvan Yan if (AcpiGetHandle(pcibus_obj, "_HPP", &hdl) == AE_OK) 15626947304SEvan Yan bus_methods |= PCIEHPC_ACPI_HPP_PRESENT; 15726947304SEvan Yan if (AcpiGetHandle(pcibus_obj, "_DSM", &hdl) == AE_OK) 15826947304SEvan Yan bus_methods |= PCIEHPC_ACPI_DSM_PRESENT; 15926947304SEvan Yan if (AcpiGetHandle(slot_dev_obj, "_SUN", &hdl) == AE_OK) 16026947304SEvan Yan slot_methods |= PCIEHPC_ACPI_SUN_PRESENT; 16126947304SEvan Yan if (AcpiGetHandle(slot_dev_obj, "_PS0", &hdl) == AE_OK) 16226947304SEvan Yan slot_methods |= PCIEHPC_ACPI_PS0_PRESENT; 16326947304SEvan Yan if (AcpiGetHandle(slot_dev_obj, "_EJ0", &hdl) == AE_OK) 16426947304SEvan Yan slot_methods |= PCIEHPC_ACPI_EJ0_PRESENT; 16526947304SEvan Yan if (AcpiGetHandle(slot_dev_obj, "_STA", &hdl) == AE_OK) 16626947304SEvan Yan slot_methods |= PCIEHPC_ACPI_STA_PRESENT; 16726947304SEvan Yan 16826947304SEvan Yan /* save ACPI object handles, etc. */ 16926947304SEvan Yan acpi_p = kmem_zalloc(sizeof (pciehpc_acpi_t), KM_SLEEP); 17026947304SEvan Yan acpi_p->bus_obj = pcibus_obj; 17126947304SEvan Yan acpi_p->slot_dev_obj = slot_dev_obj; 17226947304SEvan Yan acpi_p->bus_methods = bus_methods; 17326947304SEvan Yan acpi_p->slot_methods = slot_methods; 17426947304SEvan Yan ctrl_p->hc_misc_data = acpi_p; 17526947304SEvan Yan 17626947304SEvan Yan return (DDI_SUCCESS); 17726947304SEvan Yan } 17826947304SEvan Yan 17926947304SEvan Yan /* 18026947304SEvan Yan * Uninitialize HPC. 18126947304SEvan Yan */ 18226947304SEvan Yan static int 18326947304SEvan Yan pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p) 18426947304SEvan Yan { 18526947304SEvan Yan /* free up buffer used for misc_data */ 18626947304SEvan Yan if (ctrl_p->hc_misc_data) { 18726947304SEvan Yan kmem_free(ctrl_p->hc_misc_data, sizeof (pciehpc_acpi_t)); 18826947304SEvan Yan ctrl_p->hc_misc_data = NULL; 18926947304SEvan Yan } 19026947304SEvan Yan 19126947304SEvan Yan return (DDI_SUCCESS); 19226947304SEvan Yan } 19326947304SEvan Yan 19426947304SEvan Yan /* 19526947304SEvan Yan * Enable interrupts. For ACPI hot plug this is a NOP. 19626947304SEvan Yan * Just return DDI_SUCCESS. 19726947304SEvan Yan */ 19826947304SEvan Yan /*ARGSUSED*/ 19926947304SEvan Yan static int 20026947304SEvan Yan pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p) 20126947304SEvan Yan { 20226947304SEvan Yan return (DDI_SUCCESS); 20326947304SEvan Yan } 20426947304SEvan Yan 20526947304SEvan Yan /* 20626947304SEvan Yan * Disable interrupts. For ACPI hot plug this is a NOP. 20726947304SEvan Yan * Just return DDI_SUCCESS. 20826947304SEvan Yan */ 20926947304SEvan Yan /*ARGSUSED*/ 21026947304SEvan Yan static int 21126947304SEvan Yan pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p) 21226947304SEvan Yan { 21326947304SEvan Yan return (DDI_SUCCESS); 21426947304SEvan Yan } 21526947304SEvan Yan 21626947304SEvan Yan /* 21726947304SEvan Yan * This function is similar to pciehpc_slotinfo_init() with some 21826947304SEvan Yan * changes: 21926947304SEvan Yan * - no need for kernel thread to handle ATTN button events 22026947304SEvan Yan * - function ops for connect/disconnect are different 22126947304SEvan Yan * 22226947304SEvan Yan * ASSUMPTION: No conflict in doing reads to HP registers directly. 22326947304SEvan Yan * Otherwise, there are no ACPI interfaces to do LED control or to get 22426947304SEvan Yan * the hot plug capabilities (ATTN button, MRL, etc.). 22526947304SEvan Yan */ 22626947304SEvan Yan static int 22726947304SEvan Yan pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p) 22826947304SEvan Yan { 22926947304SEvan Yan uint32_t slot_capabilities; 23026947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 23126947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 23226947304SEvan Yan 23326947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 23426947304SEvan Yan /* 23526947304SEvan Yan * setup DDI HP framework slot information structure 23626947304SEvan Yan */ 23726947304SEvan Yan slot_p->hs_device_num = 0; 23826947304SEvan Yan slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE; 23926947304SEvan Yan slot_p->hs_info.cn_type_str = PCIE_ACPI_HP_TYPE; 24026947304SEvan Yan slot_p->hs_info.cn_child = NULL; 24126947304SEvan Yan 24226947304SEvan Yan slot_p->hs_minor = 24326947304SEvan Yan PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip), 24426947304SEvan Yan slot_p->hs_device_num); 24526947304SEvan Yan 24626947304SEvan Yan /* read Slot Capabilities Register */ 24726947304SEvan Yan slot_capabilities = pciehpc_reg_get32(ctrl_p, 24826947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTCAP); 24926947304SEvan Yan 25026947304SEvan Yan /* setup slot number/name */ 25126947304SEvan Yan pciehpc_set_slot_name(ctrl_p); 25226947304SEvan Yan 25326947304SEvan Yan /* check if Attn Button present */ 25426947304SEvan Yan ctrl_p->hc_has_attn = (slot_capabilities & 25526947304SEvan Yan PCIE_SLOTCAP_ATTN_BUTTON) ? B_TRUE : B_FALSE; 25626947304SEvan Yan 25726947304SEvan Yan /* check if Manual Retention Latch sensor present */ 25826947304SEvan Yan ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ? 25926947304SEvan Yan B_TRUE : B_FALSE; 26026947304SEvan Yan 26126947304SEvan Yan /* 26226947304SEvan Yan * PCI-E (draft) version 1.1 defines EMI Lock Present bit 26326947304SEvan Yan * in Slot Capabilities register. Check for it. 26426947304SEvan Yan */ 26526947304SEvan Yan ctrl_p->hc_has_emi_lock = (slot_capabilities & 26626947304SEvan Yan PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE; 26726947304SEvan Yan 26826947304SEvan Yan /* get current slot state from the hw */ 26926947304SEvan Yan pciehpc_get_slot_state(slot_p); 27026947304SEvan Yan if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED) 27126947304SEvan Yan slot_p->hs_condition = AP_COND_OK; 27226947304SEvan Yan 27326947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 27426947304SEvan Yan 27526947304SEvan Yan /* setup Notify() handler for hot plug events from ACPI BIOS */ 27626947304SEvan Yan if (pciehpc_acpi_install_event_handler(ctrl_p) != AE_OK) 27726947304SEvan Yan return (DDI_FAILURE); 27826947304SEvan Yan 27926947304SEvan Yan PCIE_DBG("ACPI hot plug is enabled for slot #%d\n", 28026947304SEvan Yan slot_p->hs_phy_slot_num); 28126947304SEvan Yan 28226947304SEvan Yan return (DDI_SUCCESS); 28326947304SEvan Yan } 28426947304SEvan Yan 28526947304SEvan Yan /* 28626947304SEvan Yan * This function is similar to pciehcp_slotinfo_uninit() but has ACPI 28726947304SEvan Yan * specific cleanup. 28826947304SEvan Yan */ 28926947304SEvan Yan static int 29026947304SEvan Yan pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p) 29126947304SEvan Yan { 29226947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 29326947304SEvan Yan 29426947304SEvan Yan /* uninstall Notify() event handler */ 29526947304SEvan Yan pciehpc_acpi_uninstall_event_handler(ctrl_p); 29626947304SEvan Yan if (slot_p->hs_info.cn_name) 29726947304SEvan Yan kmem_free(slot_p->hs_info.cn_name, 29826947304SEvan Yan strlen(slot_p->hs_info.cn_name) + 1); 29926947304SEvan Yan 30026947304SEvan Yan return (DDI_SUCCESS); 30126947304SEvan Yan } 30226947304SEvan Yan 30326947304SEvan Yan /* 30426947304SEvan Yan * This function is same as pciehpc_slot_poweron() except that it 30526947304SEvan Yan * uses ACPI method PS0 to enable power to the slot. If no PS0 method 30626947304SEvan Yan * is present then it returns DDI_FAILURE. 30726947304SEvan Yan */ 30826947304SEvan Yan /*ARGSUSED*/ 30926947304SEvan Yan static int 31026947304SEvan Yan pciehpc_acpi_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result) 31126947304SEvan Yan { 31226947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 31326947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 31426947304SEvan Yan uint16_t status, control; 31526947304SEvan Yan 31626947304SEvan Yan ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 31726947304SEvan Yan 31826947304SEvan Yan /* get the current state of the slot */ 31926947304SEvan Yan pciehpc_get_slot_state(slot_p); 32026947304SEvan Yan 32126947304SEvan Yan /* check if the slot is already in the 'ENABLED' state */ 32226947304SEvan Yan if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) { 32326947304SEvan Yan /* slot is already in the 'connected' state */ 32426947304SEvan Yan PCIE_DBG("slot %d already connected\n", 32526947304SEvan Yan slot_p->hs_phy_slot_num); 32626947304SEvan Yan 32726947304SEvan Yan *result = slot_p->hs_info.cn_state; 32826947304SEvan Yan return (DDI_SUCCESS); 32926947304SEvan Yan } 33026947304SEvan Yan 33126947304SEvan Yan /* read the Slot Status Register */ 33226947304SEvan Yan status = pciehpc_reg_get16(ctrl_p, 33326947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTSTS); 33426947304SEvan Yan 33526947304SEvan Yan /* make sure the MRL switch is closed if present */ 33626947304SEvan Yan if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) { 33726947304SEvan Yan /* MRL switch is open */ 33826947304SEvan Yan cmn_err(CE_WARN, "MRL switch is open on slot %d", 33926947304SEvan Yan slot_p->hs_phy_slot_num); 34026947304SEvan Yan goto cleanup; 34126947304SEvan Yan } 34226947304SEvan Yan 34326947304SEvan Yan /* make sure the slot has a device present */ 34426947304SEvan Yan if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) { 34526947304SEvan Yan /* slot is empty */ 34626947304SEvan Yan PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num); 34726947304SEvan Yan goto cleanup; 34826947304SEvan Yan } 34926947304SEvan Yan 35026947304SEvan Yan /* get the current state of Slot Control Register */ 35126947304SEvan Yan control = pciehpc_reg_get16(ctrl_p, 35226947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTCTL); 35326947304SEvan Yan 35426947304SEvan Yan /* check if the slot's power state is ON */ 35526947304SEvan Yan if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) { 35626947304SEvan Yan /* slot is already powered up */ 35726947304SEvan Yan PCIE_DBG("slot %d already connected\n", 35826947304SEvan Yan slot_p->hs_phy_slot_num); 35926947304SEvan Yan 36026947304SEvan Yan *result = slot_p->hs_info.cn_state; 36126947304SEvan Yan return (DDI_SUCCESS); 36226947304SEvan Yan } 36326947304SEvan Yan 36426947304SEvan Yan /* turn on power to the slot using ACPI method (PS0) */ 36526947304SEvan Yan if (pciehpc_acpi_power_on_slot(ctrl_p) != AE_OK) 36626947304SEvan Yan goto cleanup; 36726947304SEvan Yan 36826947304SEvan Yan *result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED; 36926947304SEvan Yan return (DDI_SUCCESS); 37026947304SEvan Yan 37126947304SEvan Yan cleanup: 37226947304SEvan Yan return (DDI_FAILURE); 37326947304SEvan Yan } 37426947304SEvan Yan 37526947304SEvan Yan /* 37626947304SEvan Yan * This function is same as pciehpc_slot_poweroff() except that it 37726947304SEvan Yan * uses ACPI method EJ0 to disable power to the slot. If no EJ0 method 37826947304SEvan Yan * is present then it returns DDI_FAILURE. 37926947304SEvan Yan */ 38026947304SEvan Yan /*ARGSUSED*/ 38126947304SEvan Yan static int 38226947304SEvan Yan pciehpc_acpi_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result) 38326947304SEvan Yan { 38426947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 38526947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip); 38626947304SEvan Yan uint16_t status; 38726947304SEvan Yan 38826947304SEvan Yan ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex)); 38926947304SEvan Yan 39026947304SEvan Yan /* get the current state of the slot */ 39126947304SEvan Yan pciehpc_get_slot_state(slot_p); 39226947304SEvan Yan 39326947304SEvan Yan /* check if the slot is already in the state less than 'powered' */ 39426947304SEvan Yan if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 39526947304SEvan Yan /* slot is in the 'disconnected' state */ 39626947304SEvan Yan PCIE_DBG("slot %d already disconnected\n", 39726947304SEvan Yan slot_p->hs_phy_slot_num); 39826947304SEvan Yan ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF); 39926947304SEvan Yan 40026947304SEvan Yan *result = slot_p->hs_info.cn_state; 40126947304SEvan Yan return (DDI_SUCCESS); 40226947304SEvan Yan } 40326947304SEvan Yan 40426947304SEvan Yan /* read the Slot Status Register */ 40526947304SEvan Yan status = pciehpc_reg_get16(ctrl_p, 40626947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTSTS); 40726947304SEvan Yan 40826947304SEvan Yan /* make sure the slot has a device present */ 40926947304SEvan Yan if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) { 41026947304SEvan Yan /* slot is empty */ 41126947304SEvan Yan PCIE_DBG("slot %d is empty", slot_p->hs_phy_slot_num); 41226947304SEvan Yan goto cleanup; 41326947304SEvan Yan } 41426947304SEvan Yan 41526947304SEvan Yan /* turn off power to the slot using ACPI method (EJ0) */ 41626947304SEvan Yan if (pciehpc_acpi_power_off_slot(ctrl_p) != AE_OK) 41726947304SEvan Yan goto cleanup; 41826947304SEvan Yan 41926947304SEvan Yan /* get the current state of the slot */ 42026947304SEvan Yan pciehpc_get_slot_state(slot_p); 42126947304SEvan Yan 42226947304SEvan Yan *result = slot_p->hs_info.cn_state; 42326947304SEvan Yan 42426947304SEvan Yan return (DDI_SUCCESS); 42526947304SEvan Yan 42626947304SEvan Yan cleanup: 42726947304SEvan Yan return (DDI_FAILURE); 42826947304SEvan Yan } 42926947304SEvan Yan 43026947304SEvan Yan /* 43126947304SEvan Yan * Install event handler for the hot plug events on the bus node as well 43226947304SEvan Yan * as device function (dev=0,func=0). 43326947304SEvan Yan */ 43426947304SEvan Yan static ACPI_STATUS 43526947304SEvan Yan pciehpc_acpi_install_event_handler(pcie_hp_ctrl_t *ctrl_p) 43626947304SEvan Yan { 43726947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 43826947304SEvan Yan int status = AE_OK; 43926947304SEvan Yan pciehpc_acpi_t *acpi_p; 44026947304SEvan Yan 44126947304SEvan Yan PCIE_DBG("install event handler for slot %d\n", 44226947304SEvan Yan slot_p->hs_phy_slot_num); 44326947304SEvan Yan acpi_p = ctrl_p->hc_misc_data; 44426947304SEvan Yan if (acpi_p->slot_dev_obj == NULL) 44526947304SEvan Yan return (AE_NOT_FOUND); 44626947304SEvan Yan 44726947304SEvan Yan /* 44826947304SEvan Yan * Install event hanlder for events on the bus object. 44926947304SEvan Yan * (Note: Insert event (hot-insert) is delivered on this object) 45026947304SEvan Yan */ 45126947304SEvan Yan status = AcpiInstallNotifyHandler(acpi_p->slot_dev_obj, 45226947304SEvan Yan ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p); 45326947304SEvan Yan if (status != AE_OK) 45426947304SEvan Yan goto cleanup; 45526947304SEvan Yan 45626947304SEvan Yan /* 45726947304SEvan Yan * Install event hanlder for events on the device function object. 45826947304SEvan Yan * (Note: Eject device event (hot-remove) is delivered on this object) 45926947304SEvan Yan * 46026947304SEvan Yan * NOTE: Here the assumption is that Notify events are delivered 46126947304SEvan Yan * on all of the 8 possible device functions so, subscribing to 46226947304SEvan Yan * one of them is sufficient. 46326947304SEvan Yan */ 46426947304SEvan Yan status = AcpiInstallNotifyHandler(acpi_p->bus_obj, 46526947304SEvan Yan ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p); 46626947304SEvan Yan return (status); 46726947304SEvan Yan 46826947304SEvan Yan cleanup: 46926947304SEvan Yan (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj, 47026947304SEvan Yan ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 47126947304SEvan Yan return (status); 47226947304SEvan Yan } 47326947304SEvan Yan 47426947304SEvan Yan /*ARGSUSED*/ 47526947304SEvan Yan static void 47626947304SEvan Yan pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context) 47726947304SEvan Yan { 47826947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = context; 47926947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 48026947304SEvan Yan pciehpc_acpi_t *acpi_p; 48126947304SEvan Yan ddi_hp_cn_state_t curr_state; 48226947304SEvan Yan int dev_state = 0; 48326947304SEvan Yan 48426947304SEvan Yan PCIE_DBG("received Notify(%d) event on slot #%d\n", 48526947304SEvan Yan val, slot_p->hs_phy_slot_num); 48626947304SEvan Yan 48726947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex); 48826947304SEvan Yan 48926947304SEvan Yan /* 49026947304SEvan Yan * get the state of the device (from _STA method) 49126947304SEvan Yan */ 49226947304SEvan Yan acpi_p = ctrl_p->hc_misc_data; 49326947304SEvan Yan if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 49426947304SEvan Yan &dev_state) != AE_OK) { 49526947304SEvan Yan cmn_err(CE_WARN, "failed to get device status on slot %d", 49626947304SEvan Yan slot_p->hs_phy_slot_num); 49726947304SEvan Yan } 49826947304SEvan Yan PCIE_DBG("(1)device state on slot #%d: 0x%x\n", 49926947304SEvan Yan slot_p->hs_phy_slot_num, dev_state); 50026947304SEvan Yan 50126947304SEvan Yan curr_state = slot_p->hs_info.cn_state; 50226947304SEvan Yan pciehpc_get_slot_state(slot_p); 50326947304SEvan Yan 50426947304SEvan Yan switch (val) { 50526947304SEvan Yan case 0: /* (re)enumerate the device */ 50626947304SEvan Yan case 3: /* Request Eject */ 50726947304SEvan Yan { 50826947304SEvan Yan ddi_hp_cn_state_t target_state; 50926947304SEvan Yan 51026947304SEvan Yan /* 51126947304SEvan Yan * Ignore the event if ATTN button is not present (ACPI BIOS 51226947304SEvan Yan * problem). 51326947304SEvan Yan * 51426947304SEvan Yan * NOTE: This situation has been observed on some platforms 51526947304SEvan Yan * where the ACPI BIOS is generating the event for some other 51626947304SEvan Yan * (non hot-plug) operations (bug). 51726947304SEvan Yan */ 51826947304SEvan Yan if (ctrl_p->hc_has_attn == B_FALSE) { 51926947304SEvan Yan PCIE_DBG("Ignore the unexpected event " 52026947304SEvan Yan "on slot #%d (state 0x%x)", 52126947304SEvan Yan slot_p->hs_phy_slot_num, dev_state); 52226947304SEvan Yan break; 52326947304SEvan Yan } 52426947304SEvan Yan 52526947304SEvan Yan /* send the event to DDI Hotplug framework */ 52626947304SEvan Yan if (curr_state < DDI_HP_CN_STATE_POWERED) { 52726947304SEvan Yan /* Insertion. Upgrade state to ENABLED */ 52826947304SEvan Yan target_state = DDI_HP_CN_STATE_ENABLED; 52926947304SEvan Yan 53026947304SEvan Yan /* 53126947304SEvan Yan * When pressing ATTN button to enable a card, the slot 53226947304SEvan Yan * could be powered. Keep the slot state on PWOERED 53326947304SEvan Yan * other than ENABLED. 53426947304SEvan Yan */ 53526947304SEvan Yan if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) 53626947304SEvan Yan slot_p->hs_info.cn_state = 53726947304SEvan Yan DDI_HP_CN_STATE_POWERED; 53826947304SEvan Yan } else { 53926947304SEvan Yan /* Want to remove; Power off Connection */ 54026947304SEvan Yan target_state = DDI_HP_CN_STATE_EMPTY; 54126947304SEvan Yan } 54226947304SEvan Yan 54326947304SEvan Yan (void) ndi_hp_state_change_req(slot_p->hs_ctrl->hc_dip, 54426947304SEvan Yan slot_p->hs_info.cn_name, 54526947304SEvan Yan target_state, DDI_HP_REQ_ASYNC); 54626947304SEvan Yan 54726947304SEvan Yan break; 54826947304SEvan Yan } 54926947304SEvan Yan default: 55026947304SEvan Yan cmn_err(CE_NOTE, "Unknown Notify() event %d on slot #%d\n", 55126947304SEvan Yan val, slot_p->hs_phy_slot_num); 55226947304SEvan Yan break; 55326947304SEvan Yan } 55426947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex); 55526947304SEvan Yan } 55626947304SEvan Yan 55726947304SEvan Yan static void 55826947304SEvan Yan pciehpc_acpi_uninstall_event_handler(pcie_hp_ctrl_t *ctrl_p) 55926947304SEvan Yan { 56026947304SEvan Yan pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 56126947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 56226947304SEvan Yan 56326947304SEvan Yan PCIE_DBG("Uninstall event handler for slot #%d\n", 56426947304SEvan Yan slot_p->hs_phy_slot_num); 56526947304SEvan Yan (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj, 56626947304SEvan Yan ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 56726947304SEvan Yan (void) AcpiRemoveNotifyHandler(acpi_p->bus_obj, 56826947304SEvan Yan ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler); 56926947304SEvan Yan } 57026947304SEvan Yan 57126947304SEvan Yan /* 57226947304SEvan Yan * Run _PS0 method to turn on power to the slot. 57326947304SEvan Yan */ 57426947304SEvan Yan static ACPI_STATUS 57526947304SEvan Yan pciehpc_acpi_power_on_slot(pcie_hp_ctrl_t *ctrl_p) 57626947304SEvan Yan { 57726947304SEvan Yan int status = AE_OK; 57826947304SEvan Yan pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 57926947304SEvan Yan int dev_state = 0; 58026947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 58126947304SEvan Yan 58226947304SEvan Yan PCIE_DBG("turn ON power to the slot #%d\n", slot_p->hs_phy_slot_num); 58326947304SEvan Yan 58426947304SEvan Yan status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_PS0", NULL, NULL); 58526947304SEvan Yan 58626947304SEvan Yan /* get the state of the device (from _STA method) */ 58726947304SEvan Yan if (status == AE_OK) { 58826947304SEvan Yan if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 58926947304SEvan Yan &dev_state) != AE_OK) 59026947304SEvan Yan cmn_err(CE_WARN, "failed to get device status " 59126947304SEvan Yan "on slot #%d", slot_p->hs_phy_slot_num); 59226947304SEvan Yan } 59326947304SEvan Yan 59426947304SEvan Yan PCIE_DBG("(3)device state on slot #%d: 0x%x\n", 59526947304SEvan Yan slot_p->hs_phy_slot_num, dev_state); 59626947304SEvan Yan 59726947304SEvan Yan pciehpc_get_slot_state(slot_p); 59826947304SEvan Yan 59926947304SEvan Yan if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) { 60026947304SEvan Yan cmn_err(CE_WARN, "failed to power on the slot #%d" 60126947304SEvan Yan "(dev_state 0x%x, ACPI_STATUS 0x%x)", 60226947304SEvan Yan slot_p->hs_phy_slot_num, dev_state, status); 60326947304SEvan Yan return (AE_ERROR); 60426947304SEvan Yan } 60526947304SEvan Yan 60626947304SEvan Yan return (status); 60726947304SEvan Yan } 60826947304SEvan Yan 60926947304SEvan Yan /* 61026947304SEvan Yan * Run _EJ0 method to turn off power to the slot. 61126947304SEvan Yan */ 61226947304SEvan Yan static ACPI_STATUS 61326947304SEvan Yan pciehpc_acpi_power_off_slot(pcie_hp_ctrl_t *ctrl_p) 61426947304SEvan Yan { 61526947304SEvan Yan int status = AE_OK; 61626947304SEvan Yan pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data; 61726947304SEvan Yan int dev_state = 0; 61826947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0]; 61926947304SEvan Yan 62026947304SEvan Yan PCIE_DBG("turn OFF power to the slot #%d\n", slot_p->hs_phy_slot_num); 62126947304SEvan Yan 62226947304SEvan Yan status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_EJ0", NULL, NULL); 62326947304SEvan Yan 62426947304SEvan Yan /* get the state of the device (from _STA method) */ 62526947304SEvan Yan if (status == AE_OK) { 62626947304SEvan Yan if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj, 62726947304SEvan Yan &dev_state) != AE_OK) 62826947304SEvan Yan cmn_err(CE_WARN, "failed to get device status " 62926947304SEvan Yan "on slot #%d", slot_p->hs_phy_slot_num); 63026947304SEvan Yan } 63126947304SEvan Yan 63226947304SEvan Yan PCIE_DBG("(2)device state on slot #%d: 0x%x\n", 63326947304SEvan Yan slot_p->hs_phy_slot_num, dev_state); 63426947304SEvan Yan 63526947304SEvan Yan pciehpc_get_slot_state(slot_p); 63626947304SEvan Yan 63726947304SEvan Yan if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) { 63826947304SEvan Yan cmn_err(CE_WARN, "failed to power OFF the slot #%d" 63926947304SEvan Yan "(dev_state 0x%x, ACPI_STATUS 0x%x)", 64026947304SEvan Yan slot_p->hs_phy_slot_num, dev_state, status); 64126947304SEvan Yan return (AE_ERROR); 64226947304SEvan Yan } 64326947304SEvan Yan 64426947304SEvan Yan return (status); 64526947304SEvan Yan } 64626947304SEvan Yan 64726947304SEvan Yan /* 64826947304SEvan Yan * Get the status info (as returned by _STA method) for the device. 64926947304SEvan Yan */ 65026947304SEvan Yan static ACPI_STATUS 65126947304SEvan Yan pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp) 65226947304SEvan Yan { 653*35786f68SRobert Mustacchi int status; 654*35786f68SRobert Mustacchi ACPI_STATUS ret; 65526947304SEvan Yan 656*35786f68SRobert Mustacchi ret = acpica_get_object_status(obj, &status); 657*35786f68SRobert Mustacchi if (ACPI_SUCCESS(ret)) { 658*35786f68SRobert Mustacchi *statusp = status; 65926947304SEvan Yan } 66026947304SEvan Yan 66126947304SEvan Yan return (ret); 66226947304SEvan Yan } 663