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