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