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