xref: /illumos-gate/usr/src/uts/intel/io/pciex/hotplug/pciehpc_acpi.c (revision ead3c390933666e6c332491643357cede94c341e)
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