170025d76Sjohnny /*
270025d76Sjohnny * CDDL HEADER START
370025d76Sjohnny *
470025d76Sjohnny * The contents of this file are subject to the terms of the
5ae115bc7Smrj * Common Development and Distribution License (the "License").
6ae115bc7Smrj * You may not use this file except in compliance with the License.
770025d76Sjohnny *
870025d76Sjohnny * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
970025d76Sjohnny * or http://www.opensolaris.org/os/licensing.
1070025d76Sjohnny * See the License for the specific language governing permissions
1170025d76Sjohnny * and limitations under the License.
1270025d76Sjohnny *
1370025d76Sjohnny * When distributing Covered Code, include this CDDL HEADER in each
1470025d76Sjohnny * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1570025d76Sjohnny * If applicable, add the following below this CDDL HEADER, with the
1670025d76Sjohnny * fields enclosed by brackets "[]" replaced with your own identifying
1770025d76Sjohnny * information: Portions Copyright [yyyy] [name of copyright owner]
1870025d76Sjohnny *
1970025d76Sjohnny * CDDL HEADER END
2070025d76Sjohnny */
2170025d76Sjohnny
2270025d76Sjohnny /*
2335786f68SRobert Mustacchi * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2435786f68SRobert Mustacchi * Use is subject to license terms.
2535786f68SRobert Mustacchi * Copyright (c) 2018, Joyent, Inc.
26*ead3c390SKeith M Wesolowski * Copyright 2024 Oxide Computer Co.
2770025d76Sjohnny */
2870025d76Sjohnny
2970025d76Sjohnny /*
3070025d76Sjohnny * ACPI interface related functions used in PCIEHPC driver module.
3126947304SEvan Yan *
3226947304SEvan Yan * NOTE: This file is compiled and delivered through misc/pcie module.
3370025d76Sjohnny */
3470025d76Sjohnny
3570025d76Sjohnny #include <sys/note.h>
3670025d76Sjohnny #include <sys/conf.h>
3770025d76Sjohnny #include <sys/kmem.h>
3870025d76Sjohnny #include <sys/debug.h>
3970025d76Sjohnny #include <sys/vtrace.h>
4070025d76Sjohnny #include <sys/varargs.h>
4170025d76Sjohnny #include <sys/ddi_impldefs.h>
4270025d76Sjohnny #include <sys/pci.h>
4370025d76Sjohnny #include <sys/ddi.h>
4470025d76Sjohnny #include <sys/sunddi.h>
4526947304SEvan Yan #include <sys/sunndi.h>
4626947304SEvan Yan #include <sys/pci_impl.h>
47fc51f9bbSKrishna Elango #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>
5170025d76Sjohnny
5270025d76Sjohnny /* 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);
6970025d76Sjohnny static void pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val,
7070025d76Sjohnny void *context);
7170025d76Sjohnny static ACPI_STATUS pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp);
72fc51f9bbSKrishna Elango
7370025d76Sjohnny /*
74fc51f9bbSKrishna Elango * Update ops vector with platform specific (ACPI, CK8-04,...) functions.
7570025d76Sjohnny */
76fc51f9bbSKrishna Elango void
pciehpc_update_ops(pcie_hp_ctrl_t * ctrl_p)7726947304SEvan Yan pciehpc_update_ops(pcie_hp_ctrl_t *ctrl_p)
7870025d76Sjohnny {
79fc51f9bbSKrishna Elango boolean_t hp_native_mode = B_FALSE;
80fc51f9bbSKrishna Elango uint32_t osc_flags = OSC_CONTROL_PCIE_NAT_HP;
8170025d76Sjohnny
8270025d76Sjohnny /*
83fc51f9bbSKrishna Elango * Call _OSC method to determine if hotplug mode is native or ACPI.
84fc51f9bbSKrishna Elango * If _OSC method succeeds hp_native_mode below will be set according to
85fc51f9bbSKrishna Elango * if native hotplug control was granted or not by BIOS.
86fdea908eSprasad *
87fc51f9bbSKrishna Elango * If _OSC method fails for any reason or if native hotplug control was
88fc51f9bbSKrishna Elango * not granted assume it's ACPI mode and update platform specific
89fc51f9bbSKrishna Elango * (ACPI, CK8-04,...) impl. ops
9070025d76Sjohnny */
9170025d76Sjohnny
9226947304SEvan Yan if (pcie_acpi_osc(ctrl_p->hc_dip, &osc_flags) == DDI_SUCCESS) {
93fc51f9bbSKrishna Elango hp_native_mode = (osc_flags & OSC_CONTROL_PCIE_NAT_HP) ?
94fc51f9bbSKrishna Elango B_TRUE : B_FALSE;
95fdea908eSprasad }
96fdea908eSprasad
97fc51f9bbSKrishna Elango if (!hp_native_mode) {
9826947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
9926947304SEvan Yan
100fc51f9bbSKrishna Elango /* update ops vector for ACPI mode */
101fc51f9bbSKrishna Elango 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;
104fc51f9bbSKrishna Elango }
10570025d76Sjohnny }
10670025d76Sjohnny
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)
10970025d76Sjohnny {
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;
11870025d76Sjohnny }
11970025d76Sjohnny
12070025d76Sjohnny /*
12170025d76Sjohnny * Intialize hot plug control for ACPI mode.
12270025d76Sjohnny */
12370025d76Sjohnny static int
pciehpc_acpi_hpc_init(pcie_hp_ctrl_t * ctrl_p)12426947304SEvan Yan pciehpc_acpi_hpc_init(pcie_hp_ctrl_t *ctrl_p)
12570025d76Sjohnny {
12670025d76Sjohnny ACPI_HANDLE pcibus_obj;
12770025d76Sjohnny int status = AE_ERROR;
12870025d76Sjohnny ACPI_HANDLE slot_dev_obj;
12970025d76Sjohnny ACPI_HANDLE hdl;
13070025d76Sjohnny pciehpc_acpi_t *acpi_p;
13170025d76Sjohnny uint16_t bus_methods = 0;
13270025d76Sjohnny uint16_t slot_methods = 0;
13370025d76Sjohnny
13470025d76Sjohnny /* get the ACPI object for the bus node */
13526947304SEvan Yan status = acpica_get_handle(ctrl_p->hc_dip, &pcibus_obj);
13670025d76Sjohnny if (status != AE_OK)
13770025d76Sjohnny return (DDI_FAILURE);
13870025d76Sjohnny
13970025d76Sjohnny /* get the ACPI object handle for the child node */
14070025d76Sjohnny status = AcpiGetNextObject(ACPI_TYPE_DEVICE, pcibus_obj,
141db2bae30SDana Myers 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");
14470025d76Sjohnny return (DDI_FAILURE);
145ed11b501SColin Zou - Sun Microsystems - Beijing China }
14670025d76Sjohnny
14770025d76Sjohnny /*
14870025d76Sjohnny * gather the info about the ACPI methods present on the bus node
14970025d76Sjohnny * and the child nodes.
15070025d76Sjohnny */
15170025d76Sjohnny if (AcpiGetHandle(pcibus_obj, "_OSC", &hdl) == AE_OK)
15270025d76Sjohnny bus_methods |= PCIEHPC_ACPI_OSC_PRESENT;
15370025d76Sjohnny if (AcpiGetHandle(pcibus_obj, "_OSHP", &hdl) == AE_OK)
15470025d76Sjohnny bus_methods |= PCIEHPC_ACPI_OSHP_PRESENT;
15570025d76Sjohnny if (AcpiGetHandle(pcibus_obj, "_HPX", &hdl) == AE_OK)
15670025d76Sjohnny bus_methods |= PCIEHPC_ACPI_HPX_PRESENT;
15770025d76Sjohnny if (AcpiGetHandle(pcibus_obj, "_HPP", &hdl) == AE_OK)
15870025d76Sjohnny bus_methods |= PCIEHPC_ACPI_HPP_PRESENT;
15970025d76Sjohnny if (AcpiGetHandle(pcibus_obj, "_DSM", &hdl) == AE_OK)
16070025d76Sjohnny bus_methods |= PCIEHPC_ACPI_DSM_PRESENT;
161fdea908eSprasad if (AcpiGetHandle(slot_dev_obj, "_SUN", &hdl) == AE_OK)
162fdea908eSprasad slot_methods |= PCIEHPC_ACPI_SUN_PRESENT;
16370025d76Sjohnny if (AcpiGetHandle(slot_dev_obj, "_PS0", &hdl) == AE_OK)
16470025d76Sjohnny slot_methods |= PCIEHPC_ACPI_PS0_PRESENT;
16570025d76Sjohnny if (AcpiGetHandle(slot_dev_obj, "_EJ0", &hdl) == AE_OK)
16670025d76Sjohnny slot_methods |= PCIEHPC_ACPI_EJ0_PRESENT;
16770025d76Sjohnny if (AcpiGetHandle(slot_dev_obj, "_STA", &hdl) == AE_OK)
16870025d76Sjohnny slot_methods |= PCIEHPC_ACPI_STA_PRESENT;
16970025d76Sjohnny
17070025d76Sjohnny /* save ACPI object handles, etc. */
17170025d76Sjohnny acpi_p = kmem_zalloc(sizeof (pciehpc_acpi_t), KM_SLEEP);
17270025d76Sjohnny acpi_p->bus_obj = pcibus_obj;
17370025d76Sjohnny acpi_p->slot_dev_obj = slot_dev_obj;
17470025d76Sjohnny acpi_p->bus_methods = bus_methods;
17570025d76Sjohnny acpi_p->slot_methods = slot_methods;
17626947304SEvan Yan ctrl_p->hc_misc_data = acpi_p;
17770025d76Sjohnny
17870025d76Sjohnny return (DDI_SUCCESS);
17970025d76Sjohnny }
18070025d76Sjohnny
18170025d76Sjohnny /*
18270025d76Sjohnny * Uninitialize HPC.
18370025d76Sjohnny */
18470025d76Sjohnny static int
pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t * ctrl_p)18526947304SEvan Yan pciehpc_acpi_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
18670025d76Sjohnny {
18770025d76Sjohnny /* 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;
19170025d76Sjohnny }
19270025d76Sjohnny
19370025d76Sjohnny return (DDI_SUCCESS);
19470025d76Sjohnny }
19570025d76Sjohnny
19670025d76Sjohnny /*
19770025d76Sjohnny * Enable interrupts. For ACPI hot plug this is a NOP.
19870025d76Sjohnny * Just return DDI_SUCCESS.
19970025d76Sjohnny */
20070025d76Sjohnny /*ARGSUSED*/
20170025d76Sjohnny static int
pciehpc_acpi_enable_intr(pcie_hp_ctrl_t * ctrl_p)20226947304SEvan Yan pciehpc_acpi_enable_intr(pcie_hp_ctrl_t *ctrl_p)
20370025d76Sjohnny {
20470025d76Sjohnny return (DDI_SUCCESS);
20570025d76Sjohnny }
20670025d76Sjohnny
20770025d76Sjohnny /*
20870025d76Sjohnny * Disable interrupts. For ACPI hot plug this is a NOP.
20970025d76Sjohnny * Just return DDI_SUCCESS.
21070025d76Sjohnny */
21170025d76Sjohnny /*ARGSUSED*/
21270025d76Sjohnny static int
pciehpc_acpi_disable_intr(pcie_hp_ctrl_t * ctrl_p)21326947304SEvan Yan pciehpc_acpi_disable_intr(pcie_hp_ctrl_t *ctrl_p)
21470025d76Sjohnny {
21570025d76Sjohnny return (DDI_SUCCESS);
21670025d76Sjohnny }
21770025d76Sjohnny
21870025d76Sjohnny /*
21970025d76Sjohnny * This function is similar to pciehpc_slotinfo_init() with some
22070025d76Sjohnny * changes:
22170025d76Sjohnny * - no need for kernel thread to handle ATTN button events
22270025d76Sjohnny * - function ops for connect/disconnect are different
22370025d76Sjohnny *
22470025d76Sjohnny * ASSUMPTION: No conflict in doing reads to HP registers directly.
22570025d76Sjohnny * Otherwise, there are no ACPI interfaces to do LED control or to get
22670025d76Sjohnny * the hot plug capabilities (ATTN button, MRL, etc.).
22770025d76Sjohnny */
22870025d76Sjohnny static int
pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t * ctrl_p)22926947304SEvan Yan pciehpc_acpi_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
23070025d76Sjohnny {
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);
23470025d76Sjohnny
23526947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex);
23670025d76Sjohnny /*
23726947304SEvan Yan * setup DDI HP framework slot information structure
23870025d76Sjohnny */
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;
24370025d76Sjohnny
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);
24770025d76Sjohnny
24870025d76Sjohnny /* read Slot Capabilities Register */
24970025d76Sjohnny slot_capabilities = pciehpc_reg_get32(ctrl_p,
25026947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTCAP);
25170025d76Sjohnny
25270025d76Sjohnny /* setup slot number/name */
25370025d76Sjohnny pciehpc_set_slot_name(ctrl_p);
25470025d76Sjohnny
25570025d76Sjohnny /* check if Attn Button present */
25626947304SEvan Yan ctrl_p->hc_has_attn = (slot_capabilities &
25726947304SEvan Yan PCIE_SLOTCAP_ATTN_BUTTON) ? B_TRUE : B_FALSE;
25870025d76Sjohnny
25970025d76Sjohnny /* check if Manual Retention Latch sensor present */
26026947304SEvan Yan ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
261db2bae30SDana Myers B_TRUE : B_FALSE;
26270025d76Sjohnny
26370025d76Sjohnny /*
26470025d76Sjohnny * PCI-E (draft) version 1.1 defines EMI Lock Present bit
26570025d76Sjohnny * in Slot Capabilities register. Check for it.
26670025d76Sjohnny */
26726947304SEvan Yan ctrl_p->hc_has_emi_lock = (slot_capabilities &
268db2bae30SDana Myers PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
26970025d76Sjohnny
270a9413143SRobert Mustacchi pciehpc_led_init(slot_p);
271a9413143SRobert Mustacchi
27270025d76Sjohnny /* 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);
27870025d76Sjohnny
279*ead3c390SKeith M Wesolowski if (!pciehpc_slot_kstat_init(slot_p)) {
280*ead3c390SKeith M Wesolowski (void) pciehpc_acpi_slotinfo_uninit(ctrl_p);
281*ead3c390SKeith M Wesolowski return (DDI_FAILURE);
282*ead3c390SKeith M Wesolowski }
283*ead3c390SKeith M Wesolowski
28470025d76Sjohnny /* 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);
28770025d76Sjohnny return (DDI_FAILURE);
288*ead3c390SKeith M Wesolowski }
28970025d76Sjohnny
29026947304SEvan Yan PCIE_DBG("ACPI hot plug is enabled for slot #%d\n",
29126947304SEvan Yan slot_p->hs_phy_slot_num);
29270025d76Sjohnny
29370025d76Sjohnny return (DDI_SUCCESS);
29470025d76Sjohnny }
29570025d76Sjohnny
29670025d76Sjohnny /*
29770025d76Sjohnny * This function is similar to pciehcp_slotinfo_uninit() but has ACPI
29870025d76Sjohnny * specific cleanup.
29970025d76Sjohnny */
30070025d76Sjohnny static int
pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t * ctrl_p)30126947304SEvan Yan pciehpc_acpi_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
30270025d76Sjohnny {
30326947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
30426947304SEvan Yan
30570025d76Sjohnny /* uninstall Notify() event handler */
30670025d76Sjohnny 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);
31070025d76Sjohnny
311*ead3c390SKeith M Wesolowski pciehpc_slot_kstat_fini(slot_p);
312*ead3c390SKeith M Wesolowski
31370025d76Sjohnny return (DDI_SUCCESS);
31470025d76Sjohnny }
31570025d76Sjohnny
31670025d76Sjohnny /*
31726947304SEvan Yan * This function is same as pciehpc_slot_poweron() except that it
31870025d76Sjohnny * uses ACPI method PS0 to enable power to the slot. If no PS0 method
31926947304SEvan Yan * is present then it returns DDI_FAILURE.
32070025d76Sjohnny */
32170025d76Sjohnny /*ARGSUSED*/
32270025d76Sjohnny 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)
32470025d76Sjohnny {
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;
32870025d76Sjohnny
32926947304SEvan Yan ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
33070025d76Sjohnny
33170025d76Sjohnny /* get the current state of the slot */
33226947304SEvan Yan pciehpc_get_slot_state(slot_p);
33370025d76Sjohnny
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) {
33670025d76Sjohnny /* 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);
34270025d76Sjohnny }
34370025d76Sjohnny
34470025d76Sjohnny /* read the Slot Status Register */
34570025d76Sjohnny status = pciehpc_reg_get16(ctrl_p,
34626947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTSTS);
34770025d76Sjohnny
34870025d76Sjohnny /* make sure the MRL switch is closed if present */
34926947304SEvan Yan if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
35070025d76Sjohnny /* MRL switch is open */
351ae115bc7Smrj cmn_err(CE_WARN, "MRL switch is open on slot %d",
35226947304SEvan Yan slot_p->hs_phy_slot_num);
35370025d76Sjohnny goto cleanup;
35470025d76Sjohnny }
35570025d76Sjohnny
35670025d76Sjohnny /* make sure the slot has a device present */
35770025d76Sjohnny if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
35870025d76Sjohnny /* slot is empty */
35926947304SEvan Yan PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num);
36070025d76Sjohnny goto cleanup;
36170025d76Sjohnny }
36270025d76Sjohnny
36370025d76Sjohnny /* get the current state of Slot Control Register */
36470025d76Sjohnny control = pciehpc_reg_get16(ctrl_p,
36526947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTCTL);
36670025d76Sjohnny
36770025d76Sjohnny /* check if the slot's power state is ON */
36870025d76Sjohnny if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
36970025d76Sjohnny /* 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);
37570025d76Sjohnny }
37670025d76Sjohnny
37770025d76Sjohnny /* turn on power to the slot using ACPI method (PS0) */
37870025d76Sjohnny if (pciehpc_acpi_power_on_slot(ctrl_p) != AE_OK)
37970025d76Sjohnny goto cleanup;
38070025d76Sjohnny
38126947304SEvan Yan *result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
38226947304SEvan Yan return (DDI_SUCCESS);
38370025d76Sjohnny
38470025d76Sjohnny cleanup:
38526947304SEvan Yan return (DDI_FAILURE);
38670025d76Sjohnny }
38770025d76Sjohnny
38870025d76Sjohnny /*
38926947304SEvan Yan * This function is same as pciehpc_slot_poweroff() except that it
39070025d76Sjohnny * uses ACPI method EJ0 to disable power to the slot. If no EJ0 method
39126947304SEvan Yan * is present then it returns DDI_FAILURE.
39270025d76Sjohnny */
39370025d76Sjohnny /*ARGSUSED*/
39470025d76Sjohnny 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)
39670025d76Sjohnny {
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;
40070025d76Sjohnny
40126947304SEvan Yan ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
40270025d76Sjohnny
40370025d76Sjohnny /* get the current state of the slot */
40426947304SEvan Yan pciehpc_get_slot_state(slot_p);
40570025d76Sjohnny
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) {
40870025d76Sjohnny /* 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);
41470025d76Sjohnny }
41570025d76Sjohnny
41670025d76Sjohnny /* read the Slot Status Register */
41770025d76Sjohnny status = pciehpc_reg_get16(ctrl_p,
41826947304SEvan Yan bus_p->bus_pcie_off + PCIE_SLOTSTS);
41970025d76Sjohnny
42070025d76Sjohnny /* make sure the slot has a device present */
42170025d76Sjohnny if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
42270025d76Sjohnny /* slot is empty */
42326947304SEvan Yan PCIE_DBG("slot %d is empty", slot_p->hs_phy_slot_num);
42470025d76Sjohnny goto cleanup;
42570025d76Sjohnny }
42670025d76Sjohnny
42770025d76Sjohnny /* turn off power to the slot using ACPI method (EJ0) */
42870025d76Sjohnny if (pciehpc_acpi_power_off_slot(ctrl_p) != AE_OK)
42970025d76Sjohnny goto cleanup;
43070025d76Sjohnny
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);
43770025d76Sjohnny
43870025d76Sjohnny cleanup:
43926947304SEvan Yan return (DDI_FAILURE);
44070025d76Sjohnny }
44170025d76Sjohnny
44270025d76Sjohnny /*
44370025d76Sjohnny * Install event handler for the hot plug events on the bus node as well
44470025d76Sjohnny * as device function (dev=0,func=0).
44570025d76Sjohnny */
44670025d76Sjohnny 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)
44870025d76Sjohnny {
44926947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
45070025d76Sjohnny int status = AE_OK;
45170025d76Sjohnny pciehpc_acpi_t *acpi_p;
45270025d76Sjohnny
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;
45670025d76Sjohnny if (acpi_p->slot_dev_obj == NULL)
45770025d76Sjohnny return (AE_NOT_FOUND);
45870025d76Sjohnny
45970025d76Sjohnny /*
46070025d76Sjohnny * Install event hanlder for events on the bus object.
46170025d76Sjohnny * (Note: Insert event (hot-insert) is delivered on this object)
46270025d76Sjohnny */
46370025d76Sjohnny status = AcpiInstallNotifyHandler(acpi_p->slot_dev_obj,
464db2bae30SDana Myers ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p);
46570025d76Sjohnny if (status != AE_OK)
46670025d76Sjohnny goto cleanup;
46770025d76Sjohnny
46870025d76Sjohnny /*
46970025d76Sjohnny * Install event hanlder for events on the device function object.
47070025d76Sjohnny * (Note: Eject device event (hot-remove) is delivered on this object)
47170025d76Sjohnny *
47270025d76Sjohnny * NOTE: Here the assumption is that Notify events are delivered
47370025d76Sjohnny * on all of the 8 possible device functions so, subscribing to
47470025d76Sjohnny * one of them is sufficient.
47570025d76Sjohnny */
47626947304SEvan Yan status = AcpiInstallNotifyHandler(acpi_p->bus_obj,
47726947304SEvan Yan ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler, (void *)ctrl_p);
47870025d76Sjohnny return (status);
47970025d76Sjohnny
48070025d76Sjohnny cleanup:
48170025d76Sjohnny (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj,
482db2bae30SDana Myers ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
48370025d76Sjohnny return (status);
48470025d76Sjohnny }
48570025d76Sjohnny
48670025d76Sjohnny /*ARGSUSED*/
48770025d76Sjohnny static void
pciehpc_acpi_notify_handler(ACPI_HANDLE device,uint32_t val,void * context)48870025d76Sjohnny pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context)
48970025d76Sjohnny {
49026947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = context;
49126947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
49270025d76Sjohnny pciehpc_acpi_t *acpi_p;
49326947304SEvan Yan ddi_hp_cn_state_t curr_state;
49470025d76Sjohnny int dev_state = 0;
49570025d76Sjohnny
49626947304SEvan Yan PCIE_DBG("received Notify(%d) event on slot #%d\n",
49726947304SEvan Yan val, slot_p->hs_phy_slot_num);
49870025d76Sjohnny
49926947304SEvan Yan mutex_enter(&ctrl_p->hc_mutex);
50070025d76Sjohnny
50170025d76Sjohnny /*
50270025d76Sjohnny * get the state of the device (from _STA method)
50370025d76Sjohnny */
50426947304SEvan Yan acpi_p = ctrl_p->hc_misc_data;
50570025d76Sjohnny if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
506db2bae30SDana Myers &dev_state) != AE_OK) {
507ae115bc7Smrj cmn_err(CE_WARN, "failed to get device status on slot %d",
50826947304SEvan Yan slot_p->hs_phy_slot_num);
50970025d76Sjohnny }
51026947304SEvan Yan PCIE_DBG("(1)device state on slot #%d: 0x%x\n",
51126947304SEvan Yan slot_p->hs_phy_slot_num, dev_state);
51270025d76Sjohnny
51326947304SEvan Yan curr_state = slot_p->hs_info.cn_state;
51426947304SEvan Yan pciehpc_get_slot_state(slot_p);
51570025d76Sjohnny
51670025d76Sjohnny switch (val) {
51770025d76Sjohnny case 0: /* (re)enumerate the device */
51870025d76Sjohnny case 3: /* Request Eject */
51926947304SEvan Yan {
52026947304SEvan Yan ddi_hp_cn_state_t target_state;
5215ae97fdcSGuoli Shu
5225ae97fdcSGuoli Shu /*
5235ae97fdcSGuoli Shu * Ignore the event if ATTN button is not present (ACPI BIOS
5245ae97fdcSGuoli Shu * problem).
5255ae97fdcSGuoli Shu *
5265ae97fdcSGuoli Shu * NOTE: This situation has been observed on some platforms
5275ae97fdcSGuoli Shu * where the ACPI BIOS is generating the event for some other
5285ae97fdcSGuoli Shu * (non hot-plug) operations (bug).
5295ae97fdcSGuoli Shu */
53026947304SEvan Yan if (ctrl_p->hc_has_attn == B_FALSE) {
53126947304SEvan Yan PCIE_DBG("Ignore the unexpected event "
5325ae97fdcSGuoli Shu "on slot #%d (state 0x%x)",
53326947304SEvan Yan slot_p->hs_phy_slot_num, dev_state);
5345ae97fdcSGuoli Shu break;
5355ae97fdcSGuoli Shu }
5365ae97fdcSGuoli Shu
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
55970025d76Sjohnny break;
56026947304SEvan Yan }
56170025d76Sjohnny default:
56270025d76Sjohnny cmn_err(CE_NOTE, "Unknown Notify() event %d on slot #%d\n",
56326947304SEvan Yan val, slot_p->hs_phy_slot_num);
56470025d76Sjohnny break;
56570025d76Sjohnny }
56626947304SEvan Yan mutex_exit(&ctrl_p->hc_mutex);
56770025d76Sjohnny }
56870025d76Sjohnny
56970025d76Sjohnny 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)
57170025d76Sjohnny {
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];
57470025d76Sjohnny
57526947304SEvan Yan PCIE_DBG("Uninstall event handler for slot #%d\n",
57626947304SEvan Yan slot_p->hs_phy_slot_num);
57770025d76Sjohnny (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj,
578db2bae30SDana Myers ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
57970025d76Sjohnny (void) AcpiRemoveNotifyHandler(acpi_p->bus_obj,
580db2bae30SDana Myers ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
58170025d76Sjohnny }
58270025d76Sjohnny
58370025d76Sjohnny /*
58470025d76Sjohnny * Run _PS0 method to turn on power to the slot.
58570025d76Sjohnny */
58670025d76Sjohnny 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)
58870025d76Sjohnny {
58970025d76Sjohnny int status = AE_OK;
59026947304SEvan Yan pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data;
59170025d76Sjohnny int dev_state = 0;
59226947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
59370025d76Sjohnny
59426947304SEvan Yan PCIE_DBG("turn ON power to the slot #%d\n", slot_p->hs_phy_slot_num);
59570025d76Sjohnny
59670025d76Sjohnny status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_PS0", NULL, NULL);
59770025d76Sjohnny
59870025d76Sjohnny /* get the state of the device (from _STA method) */
59970025d76Sjohnny if (status == AE_OK) {
600db2bae30SDana Myers if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
601db2bae30SDana Myers &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);
60470025d76Sjohnny }
60570025d76Sjohnny
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);
61070025d76Sjohnny
61126947304SEvan Yan if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
61270025d76Sjohnny cmn_err(CE_WARN, "failed to power on the slot #%d"
613db2bae30SDana Myers "(dev_state 0x%x, ACPI_STATUS 0x%x)",
61426947304SEvan Yan slot_p->hs_phy_slot_num, dev_state, status);
61545938f4fSprasad return (AE_ERROR);
61645938f4fSprasad }
61770025d76Sjohnny
61870025d76Sjohnny return (status);
61970025d76Sjohnny }
62070025d76Sjohnny
62170025d76Sjohnny /*
62270025d76Sjohnny * Run _EJ0 method to turn off power to the slot.
62370025d76Sjohnny */
62470025d76Sjohnny 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)
62670025d76Sjohnny {
62770025d76Sjohnny int status = AE_OK;
62826947304SEvan Yan pciehpc_acpi_t *acpi_p = ctrl_p->hc_misc_data;
62970025d76Sjohnny int dev_state = 0;
63026947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
63170025d76Sjohnny
63226947304SEvan Yan PCIE_DBG("turn OFF power to the slot #%d\n", slot_p->hs_phy_slot_num);
63370025d76Sjohnny
63470025d76Sjohnny status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_EJ0", NULL, NULL);
63570025d76Sjohnny
63670025d76Sjohnny /* get the state of the device (from _STA method) */
63770025d76Sjohnny if (status == AE_OK) {
638db2bae30SDana Myers if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
639db2bae30SDana Myers &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);
64270025d76Sjohnny }
64370025d76Sjohnny
64426947304SEvan Yan PCIE_DBG("(2)device state on slot #%d: 0x%x\n",
64526947304SEvan Yan slot_p->hs_phy_slot_num, dev_state);
64670025d76Sjohnny
64726947304SEvan Yan pciehpc_get_slot_state(slot_p);
64826947304SEvan Yan
64926947304SEvan Yan if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
65070025d76Sjohnny cmn_err(CE_WARN, "failed to power OFF the slot #%d"
651db2bae30SDana Myers "(dev_state 0x%x, ACPI_STATUS 0x%x)",
65226947304SEvan Yan slot_p->hs_phy_slot_num, dev_state, status);
65345938f4fSprasad return (AE_ERROR);
65445938f4fSprasad }
65570025d76Sjohnny
65670025d76Sjohnny return (status);
65770025d76Sjohnny }
65870025d76Sjohnny
65970025d76Sjohnny /*
66070025d76Sjohnny * Get the status info (as returned by _STA method) for the device.
66170025d76Sjohnny */
66270025d76Sjohnny static ACPI_STATUS
pciehpc_acpi_get_dev_state(ACPI_HANDLE obj,int * statusp)66370025d76Sjohnny pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp)
66470025d76Sjohnny {
66535786f68SRobert Mustacchi int status;
66635786f68SRobert Mustacchi ACPI_STATUS ret;
66770025d76Sjohnny
66835786f68SRobert Mustacchi ret = acpica_get_object_status(obj, &status);
66935786f68SRobert Mustacchi if (ACPI_SUCCESS(ret)) {
67035786f68SRobert Mustacchi *statusp = status;
67170025d76Sjohnny }
67270025d76Sjohnny
67370025d76Sjohnny return (ret);
67470025d76Sjohnny }
675