xref: /illumos-gate/usr/src/uts/common/io/pciex/hotplug/pciehpc.c (revision fe426563f5e7383c12abf6a347e131898d1a7f6c)
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 /*
2380dc702dSColin Zou - Sun Microsystems - Beijing China  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24b3d69c05SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
25*fe426563SRick Altherr  * Copyright 2022 Oxide Computer Company
2626947304SEvan Yan  */
2726947304SEvan Yan 
2826947304SEvan Yan /*
2926947304SEvan Yan  * This file contains Standard PCI Express HotPlug functionality that is
3026947304SEvan Yan  * compatible with the PCI Express ver 1.1 specification.
3126947304SEvan Yan  *
3226947304SEvan Yan  * NOTE: This file is compiled and delivered through misc/pcie module.
3326947304SEvan Yan  */
3426947304SEvan Yan 
3526947304SEvan Yan #include <sys/types.h>
3626947304SEvan Yan #include <sys/note.h>
3726947304SEvan Yan #include <sys/conf.h>
3826947304SEvan Yan #include <sys/kmem.h>
3926947304SEvan Yan #include <sys/debug.h>
4026947304SEvan Yan #include <sys/vtrace.h>
4126947304SEvan Yan #include <sys/autoconf.h>
4226947304SEvan Yan #include <sys/varargs.h>
4326947304SEvan Yan #include <sys/ddi_impldefs.h>
4426947304SEvan Yan #include <sys/time.h>
4526947304SEvan Yan #include <sys/callb.h>
4626947304SEvan Yan #include <sys/ddi.h>
4726947304SEvan Yan #include <sys/sunddi.h>
4826947304SEvan Yan #include <sys/sunndi.h>
4926947304SEvan Yan #include <sys/sysevent/dr.h>
5026947304SEvan Yan #include <sys/pci_impl.h>
5126947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
5226947304SEvan Yan #include <sys/hotplug/pci/pciehpc.h>
5326947304SEvan Yan 
5426947304SEvan Yan typedef struct pciehpc_prop {
5526947304SEvan Yan 	char	*prop_name;
5626947304SEvan Yan 	char	*prop_value;
5726947304SEvan Yan } pciehpc_prop_t;
5826947304SEvan Yan 
5926947304SEvan Yan static pciehpc_prop_t	pciehpc_props[] = {
6026947304SEvan Yan 	{ PCIEHPC_PROP_LED_FAULT,	PCIEHPC_PROP_VALUE_LED },
6126947304SEvan Yan 	{ PCIEHPC_PROP_LED_POWER,	PCIEHPC_PROP_VALUE_LED },
6226947304SEvan Yan 	{ PCIEHPC_PROP_LED_ATTN,	PCIEHPC_PROP_VALUE_LED },
6326947304SEvan Yan 	{ PCIEHPC_PROP_LED_ACTIVE,	PCIEHPC_PROP_VALUE_LED },
6426947304SEvan Yan 	{ PCIEHPC_PROP_CARD_TYPE,	PCIEHPC_PROP_VALUE_TYPE },
6526947304SEvan Yan 	{ PCIEHPC_PROP_BOARD_TYPE,	PCIEHPC_PROP_VALUE_TYPE },
6626947304SEvan Yan 	{ PCIEHPC_PROP_SLOT_CONDITION,	PCIEHPC_PROP_VALUE_TYPE }
6726947304SEvan Yan };
6826947304SEvan Yan 
6926947304SEvan Yan /* Local functions prototype */
7026947304SEvan Yan static int pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p);
7126947304SEvan Yan static int pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
7226947304SEvan Yan static int pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
7326947304SEvan Yan static int pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
7426947304SEvan Yan static int pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p);
7526947304SEvan Yan static int pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p);
7626947304SEvan Yan static pcie_hp_ctrl_t *pciehpc_create_controller(dev_info_t *dip);
7726947304SEvan Yan static void pciehpc_destroy_controller(dev_info_t *dip);
7826947304SEvan Yan static int pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p);
7926947304SEvan Yan static int pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p);
8026947304SEvan Yan static int pciehpc_slot_get_property(pcie_hp_slot_t *slot_p,
8126947304SEvan Yan     ddi_hp_property_t *arg, ddi_hp_property_t *rval);
8226947304SEvan Yan static int pciehpc_slot_set_property(pcie_hp_slot_t *slot_p,
8326947304SEvan Yan     ddi_hp_property_t *arg, ddi_hp_property_t *rval);
8426947304SEvan Yan static void pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control);
8526947304SEvan Yan static void pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p);
8626947304SEvan Yan static pcie_hp_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
8726947304SEvan Yan static pcie_hp_led_state_t pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p,
8826947304SEvan Yan     pcie_hp_led_t led);
8926947304SEvan Yan static void pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
9026947304SEvan Yan     pcie_hp_led_state_t state);
9126947304SEvan Yan 
9226947304SEvan Yan static int pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
9326947304SEvan Yan     ddi_hp_cn_state_t target_state);
9426947304SEvan Yan static int pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
9526947304SEvan Yan     ddi_hp_cn_state_t target_state);
9626947304SEvan Yan static int pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
9726947304SEvan Yan     ddi_hp_cn_state_t target_state);
9826947304SEvan Yan static int
9926947304SEvan Yan     pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
10026947304SEvan Yan static int
10126947304SEvan Yan     pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
10226947304SEvan Yan static int pciehpc_slot_probe(pcie_hp_slot_t *slot_p);
10326947304SEvan Yan static int pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p);
10480dc702dSColin Zou - Sun Microsystems - Beijing China static void pciehpc_handle_power_fault(dev_info_t *dip);
10580dc702dSColin Zou - Sun Microsystems - Beijing China static void pciehpc_power_fault_handler(void *arg);
10626947304SEvan Yan 
10726947304SEvan Yan #ifdef	DEBUG
10826947304SEvan Yan static void pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p);
10926947304SEvan Yan #endif	/* DEBUG */
11026947304SEvan Yan 
11126947304SEvan Yan /*
11226947304SEvan Yan  * Global functions (called by other drivers/modules)
11326947304SEvan Yan  */
11426947304SEvan Yan 
11526947304SEvan Yan /*
11626947304SEvan Yan  * Initialize Hot Plug Controller if present. The arguments are:
11726947304SEvan Yan  *	dip	- Devinfo node pointer to the hot plug bus node
11826947304SEvan Yan  *	regops	- register ops to access HPC registers for non-standard
11926947304SEvan Yan  *		  HPC hw implementations (e.g: HPC in host PCI-E brdiges)
12026947304SEvan Yan  *		  This is NULL for standard HPC in PCIe bridges.
12126947304SEvan Yan  * Returns:
12226947304SEvan Yan  *	DDI_SUCCESS for successful HPC initialization
12326947304SEvan Yan  *	DDI_FAILURE for errors or if HPC hw not found
12426947304SEvan Yan  */
12526947304SEvan Yan int
12626947304SEvan Yan pciehpc_init(dev_info_t *dip, caddr_t arg)
12726947304SEvan Yan {
12826947304SEvan Yan 	pcie_hp_regops_t	*regops = (pcie_hp_regops_t *)(void *)arg;
12926947304SEvan Yan 	pcie_hp_ctrl_t		*ctrl_p;
13026947304SEvan Yan 
13126947304SEvan Yan 	PCIE_DBG("pciehpc_init() called (dip=%p)\n", (void *)dip);
13226947304SEvan Yan 
13326947304SEvan Yan 	/* Make sure that it is not already initialized */
13426947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
13526947304SEvan Yan 		PCIE_DBG("%s%d: pciehpc instance already initialized!\n",
13626947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip));
13726947304SEvan Yan 		return (DDI_SUCCESS);
13826947304SEvan Yan 	}
13926947304SEvan Yan 
14026947304SEvan Yan 	/* Allocate a new hotplug controller and slot structures */
14126947304SEvan Yan 	ctrl_p = pciehpc_create_controller(dip);
14226947304SEvan Yan 
14326947304SEvan Yan 	/* setup access handle for HPC regs */
14426947304SEvan Yan 	if (regops != NULL) {
14526947304SEvan Yan 		/* HPC access is non-standard; use the supplied reg ops */
14626947304SEvan Yan 		ctrl_p->hc_regops = *regops;
14726947304SEvan Yan 	}
14826947304SEvan Yan 
14926947304SEvan Yan 	/*
15026947304SEvan Yan 	 * Setup resource maps for this bus node.
15126947304SEvan Yan 	 */
15226947304SEvan Yan 	(void) pci_resource_setup(dip);
15326947304SEvan Yan 
15426947304SEvan Yan 	PCIE_DISABLE_ERRORS(dip);
15526947304SEvan Yan 
15626947304SEvan Yan 	/*
15726947304SEvan Yan 	 * Set the platform specific hot plug mode.
15826947304SEvan Yan 	 */
15926947304SEvan Yan 	ctrl_p->hc_ops.init_hpc_hw = pciehpc_hpc_init;
16026947304SEvan Yan 	ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_hpc_uninit;
16126947304SEvan Yan 	ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
16226947304SEvan Yan 	ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
16326947304SEvan Yan 	ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_slot_poweron;
16426947304SEvan Yan 	ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_slot_poweroff;
16526947304SEvan Yan 
16626947304SEvan Yan 	ctrl_p->hc_ops.enable_hpc_intr = pciehpc_enable_intr;
16726947304SEvan Yan 	ctrl_p->hc_ops.disable_hpc_intr = pciehpc_disable_intr;
16826947304SEvan Yan 
16986ef0a63SRichard Lowe #if	defined(__x86)
17026947304SEvan Yan 	pciehpc_update_ops(ctrl_p);
17126947304SEvan Yan #endif
17226947304SEvan Yan 
17326947304SEvan Yan 	/* initialize hot plug controller hw */
17426947304SEvan Yan 	if ((ctrl_p->hc_ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
17526947304SEvan Yan 		goto cleanup1;
17626947304SEvan Yan 
17726947304SEvan Yan 	/* initialize slot information soft state structure */
17826947304SEvan Yan 	if ((ctrl_p->hc_ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
17926947304SEvan Yan 		goto cleanup2;
18026947304SEvan Yan 
18126947304SEvan Yan 	/* register the hot plug slot with DDI HP framework */
18226947304SEvan Yan 	if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
18326947304SEvan Yan 		goto cleanup3;
18426947304SEvan Yan 
18526947304SEvan Yan 	/* create minor node for this slot */
18626947304SEvan Yan 	if (pcie_create_minor_node(ctrl_p, 0) != DDI_SUCCESS)
18726947304SEvan Yan 		goto cleanup4;
18826947304SEvan Yan 
18926947304SEvan Yan 	/* HPC initialization is complete now */
19026947304SEvan Yan 	ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
19126947304SEvan Yan 
19226947304SEvan Yan #ifdef	DEBUG
19326947304SEvan Yan 	/* For debug, dump the HPC registers */
19426947304SEvan Yan 	pciehpc_dump_hpregs(ctrl_p);
19526947304SEvan Yan #endif	/* DEBUG */
19626947304SEvan Yan 
19726947304SEvan Yan 	return (DDI_SUCCESS);
19826947304SEvan Yan cleanup4:
19926947304SEvan Yan 	(void) pciehpc_unregister_slot(ctrl_p);
20026947304SEvan Yan cleanup3:
20126947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
20226947304SEvan Yan 
20326947304SEvan Yan cleanup2:
20426947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
20526947304SEvan Yan 
20626947304SEvan Yan cleanup1:
20726947304SEvan Yan 	PCIE_ENABLE_ERRORS(dip);
20826947304SEvan Yan 	(void) pci_resource_destroy(dip);
20926947304SEvan Yan 
21026947304SEvan Yan 	pciehpc_destroy_controller(dip);
21126947304SEvan Yan 	return (DDI_FAILURE);
21226947304SEvan Yan }
21326947304SEvan Yan 
21426947304SEvan Yan /*
21526947304SEvan Yan  * Uninitialize HPC soft state structure and free up any resources
21626947304SEvan Yan  * used for the HPC instance.
21726947304SEvan Yan  */
21826947304SEvan Yan int
21926947304SEvan Yan pciehpc_uninit(dev_info_t *dip)
22026947304SEvan Yan {
22126947304SEvan Yan 	pcie_hp_ctrl_t *ctrl_p;
22226947304SEvan Yan 
22326947304SEvan Yan 	PCIE_DBG("pciehpc_uninit() called (dip=%p)\n", (void *)dip);
22426947304SEvan Yan 
22526947304SEvan Yan 	/* get the soft state structure for this dip */
22626947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
22726947304SEvan Yan 		return (DDI_FAILURE);
22826947304SEvan Yan 	}
22926947304SEvan Yan 
23026947304SEvan Yan 	pcie_remove_minor_node(ctrl_p, 0);
23126947304SEvan Yan 
23226947304SEvan Yan 	/* unregister the slot */
23326947304SEvan Yan 	(void) pciehpc_unregister_slot(ctrl_p);
23426947304SEvan Yan 
23526947304SEvan Yan 	/* uninit any slot info data structures */
23626947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
23726947304SEvan Yan 
23826947304SEvan Yan 	/* uninitialize hpc, remove interrupt handler, etc. */
23926947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
24026947304SEvan Yan 
24126947304SEvan Yan 	PCIE_ENABLE_ERRORS(dip);
24226947304SEvan Yan 
24326947304SEvan Yan 	/*
24426947304SEvan Yan 	 * Destroy resource maps for this bus node.
24526947304SEvan Yan 	 */
24626947304SEvan Yan 	(void) pci_resource_destroy(dip);
24726947304SEvan Yan 
24826947304SEvan Yan 	/* destroy the soft state structure */
24926947304SEvan Yan 	pciehpc_destroy_controller(dip);
25026947304SEvan Yan 
25126947304SEvan Yan 	return (DDI_SUCCESS);
25226947304SEvan Yan }
25326947304SEvan Yan 
25426947304SEvan Yan /*
25526947304SEvan Yan  * pciehpc_intr()
25626947304SEvan Yan  *
25726947304SEvan Yan  * Interrupt handler for PCI-E Hot plug controller interrupts.
25826947304SEvan Yan  *
25926947304SEvan Yan  * Note: This is only for native mode hot plug. This is called
26026947304SEvan Yan  * by the nexus driver at interrupt context. Interrupt Service Routine
26126947304SEvan Yan  * registration is done by the nexus driver for both hot plug and
26226947304SEvan Yan  * non-hot plug interrupts. This function is called from the ISR
26326947304SEvan Yan  * of the nexus driver to handle hot-plug interrupts.
26426947304SEvan Yan  */
26526947304SEvan Yan int
26626947304SEvan Yan pciehpc_intr(dev_info_t *dip)
26726947304SEvan Yan {
26826947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
26926947304SEvan Yan 	pcie_hp_slot_t	*slot_p;
27026947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
27126947304SEvan Yan 	uint16_t	status, control;
27226947304SEvan Yan 
27326947304SEvan Yan 	/* get the soft state structure for this dip */
27426947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
27526947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
27626947304SEvan Yan 
27726947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
27826947304SEvan Yan 
27926947304SEvan Yan 	/* make sure the controller soft state is initialized */
28026947304SEvan Yan 	if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
28126947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
28226947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
28326947304SEvan Yan 	}
28426947304SEvan Yan 
28526947304SEvan Yan 	/* if it is not NATIVE hot plug mode then return */
28626947304SEvan Yan 	if (bus_p->bus_hp_curr_mode != PCIE_NATIVE_HP_MODE) {
28726947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
28826947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
28926947304SEvan Yan 	}
29026947304SEvan Yan 
29126947304SEvan Yan 	slot_p = ctrl_p->hc_slots[0];
29226947304SEvan Yan 
29326947304SEvan Yan 	/* read the current slot status register */
29426947304SEvan Yan 	status = pciehpc_reg_get16(ctrl_p,
29526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
29626947304SEvan Yan 
29726947304SEvan Yan 	/* check if there are any hot plug interrupts occurred */
29826947304SEvan Yan 	if (!(status & PCIE_SLOTSTS_STATUS_EVENTS)) {
29926947304SEvan Yan 		/* no hot plug events occurred */
30026947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
30126947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
30226947304SEvan Yan 	}
30326947304SEvan Yan 
30426947304SEvan Yan 	/* clear the interrupt status bits */
30526947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
30626947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
30726947304SEvan Yan 
30826947304SEvan Yan 	/* check for CMD COMPLETE interrupt */
30926947304SEvan Yan 	if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
31026947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): CMD COMPLETED interrupt received\n");
31126947304SEvan Yan 		/* wake up any one waiting for Command Completion event */
31226947304SEvan Yan 		cv_signal(&ctrl_p->hc_cmd_comp_cv);
31326947304SEvan Yan 	}
31426947304SEvan Yan 
31526947304SEvan Yan 	/* check for ATTN button interrupt */
31626947304SEvan Yan 	if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
31726947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): ATTN BUTTON interrupt received\n");
31826947304SEvan Yan 
31926947304SEvan Yan 		/* if ATTN button event is still pending then cancel it */
32026947304SEvan Yan 		if (slot_p->hs_attn_btn_pending == B_TRUE)
32126947304SEvan Yan 			slot_p->hs_attn_btn_pending = B_FALSE;
32226947304SEvan Yan 		else
32326947304SEvan Yan 			slot_p->hs_attn_btn_pending = B_TRUE;
32426947304SEvan Yan 
32526947304SEvan Yan 		/* wake up the ATTN event handler */
32626947304SEvan Yan 		cv_signal(&slot_p->hs_attn_btn_cv);
32726947304SEvan Yan 	}
32826947304SEvan Yan 
32926947304SEvan Yan 	/* check for power fault interrupt */
33026947304SEvan Yan 	if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
33126947304SEvan Yan 
33226947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): POWER FAULT interrupt received"
33326947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
33426947304SEvan Yan 		control =  pciehpc_reg_get16(ctrl_p,
33526947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCTL);
33626947304SEvan Yan 
33726947304SEvan Yan 		if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
33826947304SEvan Yan 			slot_p->hs_condition = AP_COND_FAILED;
33926947304SEvan Yan 
34026947304SEvan Yan 			/* disable power fault detction interrupt */
34126947304SEvan Yan 			pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
34226947304SEvan Yan 			    PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
34326947304SEvan Yan 
34480dc702dSColin Zou - Sun Microsystems - Beijing China 			pciehpc_handle_power_fault(dip);
34526947304SEvan Yan 		}
34626947304SEvan Yan 	}
34726947304SEvan Yan 
34826947304SEvan Yan 	/* check for MRL SENSOR CHANGED interrupt */
34926947304SEvan Yan 	if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
35026947304SEvan Yan 		/* For now (phase-I), no action is taken on this event */
35126947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
35226947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
35326947304SEvan Yan 	}
35426947304SEvan Yan 
35526947304SEvan Yan 	/* check for PRESENCE CHANGED interrupt */
35626947304SEvan Yan 	if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
35726947304SEvan Yan 
35826947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): PRESENCE CHANGED interrupt received"
35926947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
36026947304SEvan Yan 
36126947304SEvan Yan 		if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
36226947304SEvan Yan 			/*
36326947304SEvan Yan 			 * card is inserted into the slot, ask DDI Hotplug
36426947304SEvan Yan 			 * framework to change state to Present.
36526947304SEvan Yan 			 */
36670f83219SEvan Yan 			cmn_err(CE_NOTE, "pciehpc (%s%d): card is inserted"
36770f83219SEvan Yan 			    " in the slot %s",
36870f83219SEvan Yan 			    ddi_driver_name(dip),
36970f83219SEvan Yan 			    ddi_get_instance(dip),
37070f83219SEvan Yan 			    slot_p->hs_info.cn_name);
37170f83219SEvan Yan 
37226947304SEvan Yan 			(void) ndi_hp_state_change_req(dip,
37326947304SEvan Yan 			    slot_p->hs_info.cn_name,
37426947304SEvan Yan 			    DDI_HP_CN_STATE_PRESENT,
37526947304SEvan Yan 			    DDI_HP_REQ_ASYNC);
37626947304SEvan Yan 		} else { /* card is removed from the slot */
37726947304SEvan Yan 			cmn_err(CE_NOTE, "pciehpc (%s%d): card is removed"
37826947304SEvan Yan 			    " from the slot %s",
37926947304SEvan Yan 			    ddi_driver_name(dip),
38026947304SEvan Yan 			    ddi_get_instance(dip),
38126947304SEvan Yan 			    slot_p->hs_info.cn_name);
38226947304SEvan Yan 
38326947304SEvan Yan 			if (slot_p->hs_info.cn_state ==
38426947304SEvan Yan 			    DDI_HP_CN_STATE_ENABLED) {
38526947304SEvan Yan 				/* Card is removed when slot is enabled */
38626947304SEvan Yan 				slot_p->hs_condition = AP_COND_FAILED;
38726947304SEvan Yan 			} else {
38826947304SEvan Yan 				slot_p->hs_condition = AP_COND_UNKNOWN;
38926947304SEvan Yan 			}
39026947304SEvan Yan 			/* make sure to disable power fault detction intr */
39126947304SEvan Yan 			control =  pciehpc_reg_get16(ctrl_p,
39226947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
39326947304SEvan Yan 
39426947304SEvan Yan 			if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
39526947304SEvan Yan 				pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
39626947304SEvan Yan 				    PCIE_SLOTCTL,
39726947304SEvan Yan 				    control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
39826947304SEvan Yan 
39926947304SEvan Yan 			/*
400ffb64830SJordan Paige Hendricks 			 * If supported, notify the child device driver that the
401ffb64830SJordan Paige Hendricks 			 * device is being removed.
402ffb64830SJordan Paige Hendricks 			 */
403ffb64830SJordan Paige Hendricks 			dev_info_t *cdip = ddi_get_child(dip);
404ffb64830SJordan Paige Hendricks 			if (cdip != NULL) {
405ffb64830SJordan Paige Hendricks 				ddi_eventcookie_t rm_cookie;
406ffb64830SJordan Paige Hendricks 				if (ddi_get_eventcookie(cdip,
407ffb64830SJordan Paige Hendricks 				    DDI_DEVI_REMOVE_EVENT,
408ffb64830SJordan Paige Hendricks 				    &rm_cookie) == DDI_SUCCESS) {
409ffb64830SJordan Paige Hendricks 					ndi_post_event(dip, cdip, rm_cookie,
410ffb64830SJordan Paige Hendricks 					    NULL);
411ffb64830SJordan Paige Hendricks 				}
412ffb64830SJordan Paige Hendricks 			}
413ffb64830SJordan Paige Hendricks 
414ffb64830SJordan Paige Hendricks 			/*
41526947304SEvan Yan 			 * Ask DDI Hotplug framework to change state to Empty
41626947304SEvan Yan 			 */
41726947304SEvan Yan 			(void) ndi_hp_state_change_req(dip,
41826947304SEvan Yan 			    slot_p->hs_info.cn_name,
41926947304SEvan Yan 			    DDI_HP_CN_STATE_EMPTY,
42026947304SEvan Yan 			    DDI_HP_REQ_ASYNC);
42126947304SEvan Yan 		}
42226947304SEvan Yan 	}
42326947304SEvan Yan 
42426947304SEvan Yan 	/* check for DLL state changed interrupt */
42526947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep &&
42626947304SEvan Yan 	    (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
42726947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): DLL STATE CHANGED interrupt received"
42826947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
42926947304SEvan Yan 
43026947304SEvan Yan 		cv_signal(&slot_p->hs_dll_active_cv);
43126947304SEvan Yan 	}
43226947304SEvan Yan 
43326947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
43426947304SEvan Yan 
43526947304SEvan Yan 	return (DDI_INTR_CLAIMED);
43626947304SEvan Yan }
43726947304SEvan Yan 
43826947304SEvan Yan /*
43926947304SEvan Yan  * Handle hotplug commands
44026947304SEvan Yan  *
44126947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
44226947304SEvan Yan  */
44326947304SEvan Yan /* ARGSUSED */
44426947304SEvan Yan int
44526947304SEvan Yan pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
44626947304SEvan Yan     void *arg, void *result)
44726947304SEvan Yan {
44826947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
44926947304SEvan Yan 	pcie_hp_slot_t	*slot_p;
45026947304SEvan Yan 	int		ret = DDI_SUCCESS;
45126947304SEvan Yan 
45226947304SEvan Yan 	PCIE_DBG("pciehpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
45326947304SEvan Yan 	    dip, cn_name, op, arg);
45426947304SEvan Yan 
45526947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
45626947304SEvan Yan 		return (DDI_FAILURE);
45726947304SEvan Yan 
45826947304SEvan Yan 	slot_p = ctrl_p->hc_slots[0];
45926947304SEvan Yan 
46026947304SEvan Yan 	if (strcmp(cn_name, slot_p->hs_info.cn_name) != 0)
46126947304SEvan Yan 		return (DDI_EINVAL);
46226947304SEvan Yan 
46326947304SEvan Yan 	switch (op) {
46426947304SEvan Yan 	case DDI_HPOP_CN_GET_STATE:
46526947304SEvan Yan 	{
46626947304SEvan Yan 		mutex_enter(&slot_p->hs_ctrl->hc_mutex);
46726947304SEvan Yan 
46826947304SEvan Yan 		/* get the current slot state */
46926947304SEvan Yan 		pciehpc_get_slot_state(slot_p);
47026947304SEvan Yan 
47126947304SEvan Yan 		*((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
47226947304SEvan Yan 
47326947304SEvan Yan 		mutex_exit(&slot_p->hs_ctrl->hc_mutex);
47426947304SEvan Yan 		break;
47526947304SEvan Yan 	}
47626947304SEvan Yan 	case DDI_HPOP_CN_CHANGE_STATE:
47726947304SEvan Yan 	{
47826947304SEvan Yan 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
47926947304SEvan Yan 
48026947304SEvan Yan 		mutex_enter(&slot_p->hs_ctrl->hc_mutex);
48126947304SEvan Yan 
48226947304SEvan Yan 		ret = pciehpc_change_slot_state(slot_p, target_state);
48326947304SEvan Yan 		*(ddi_hp_cn_state_t *)result = slot_p->hs_info.cn_state;
48426947304SEvan Yan 
48526947304SEvan Yan 		mutex_exit(&slot_p->hs_ctrl->hc_mutex);
48626947304SEvan Yan 		break;
48726947304SEvan Yan 	}
48826947304SEvan Yan 	case DDI_HPOP_CN_PROBE:
48926947304SEvan Yan 
49026947304SEvan Yan 		ret = pciehpc_slot_probe(slot_p);
49126947304SEvan Yan 
49226947304SEvan Yan 		break;
49326947304SEvan Yan 	case DDI_HPOP_CN_UNPROBE:
49426947304SEvan Yan 		ret = pciehpc_slot_unprobe(slot_p);
49526947304SEvan Yan 
49626947304SEvan Yan 		break;
49726947304SEvan Yan 	case DDI_HPOP_CN_GET_PROPERTY:
49826947304SEvan Yan 		ret = pciehpc_slot_get_property(slot_p,
49926947304SEvan Yan 		    (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
50026947304SEvan Yan 		break;
50126947304SEvan Yan 	case DDI_HPOP_CN_SET_PROPERTY:
50226947304SEvan Yan 		ret = pciehpc_slot_set_property(slot_p,
50326947304SEvan Yan 		    (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
50426947304SEvan Yan 		break;
50526947304SEvan Yan 	default:
50626947304SEvan Yan 		ret = DDI_ENOTSUP;
50726947304SEvan Yan 		break;
50826947304SEvan Yan 	}
50926947304SEvan Yan 
51026947304SEvan Yan 	return (ret);
51126947304SEvan Yan }
51226947304SEvan Yan 
51326947304SEvan Yan /*
51426947304SEvan Yan  * Get the current state of the slot from the hw.
51526947304SEvan Yan  *
51626947304SEvan Yan  * The slot state should have been initialized before this function gets called.
51726947304SEvan Yan  */
51826947304SEvan Yan void
51926947304SEvan Yan pciehpc_get_slot_state(pcie_hp_slot_t *slot_p)
52026947304SEvan Yan {
52126947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
52226947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
52326947304SEvan Yan 	uint16_t	control, status;
52426947304SEvan Yan 	ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
52526947304SEvan Yan 
52626947304SEvan Yan 	/* read the Slot Control Register */
52726947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
52826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
52926947304SEvan Yan 
53026947304SEvan Yan 	slot_p->hs_fault_led_state = PCIE_HP_LED_OFF; /* no fault led */
53126947304SEvan Yan 	slot_p->hs_active_led_state = PCIE_HP_LED_OFF; /* no active led */
53226947304SEvan Yan 
53326947304SEvan Yan 	/* read the current Slot Status Register */
53426947304SEvan Yan 	status = pciehpc_reg_get16(ctrl_p,
53526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
53626947304SEvan Yan 
53726947304SEvan Yan 	/* get POWER led state */
53826947304SEvan Yan 	slot_p->hs_power_led_state =
53926947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
54026947304SEvan Yan 
54126947304SEvan Yan 	/* get ATTN led state */
54226947304SEvan Yan 	slot_p->hs_attn_led_state =
54326947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
54426947304SEvan Yan 
54526947304SEvan Yan 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
54626947304SEvan Yan 		/* no device present; slot is empty */
54726947304SEvan Yan 		slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
54826947304SEvan Yan 
54926947304SEvan Yan 		return;
55026947304SEvan Yan 	}
55126947304SEvan Yan 
55226947304SEvan Yan 	/* device is present */
55326947304SEvan Yan 	slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
55426947304SEvan Yan 
55526947304SEvan Yan 	if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
55626947304SEvan Yan 		/*
55726947304SEvan Yan 		 * Device is powered on. Set to "ENABLED" state (skip
55826947304SEvan Yan 		 * POWERED state) because there is not a explicit "enable"
55926947304SEvan Yan 		 * action exists for PCIe.
56026947304SEvan Yan 		 * If it is already in "POWERED" state, then keep it until
56126947304SEvan Yan 		 * user explicitly change it to other states.
56226947304SEvan Yan 		 */
56326947304SEvan Yan 		if (curr_state == DDI_HP_CN_STATE_POWERED) {
56426947304SEvan Yan 			slot_p->hs_info.cn_state = curr_state;
56526947304SEvan Yan 		} else {
56626947304SEvan Yan 			slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
56726947304SEvan Yan 		}
56826947304SEvan Yan 	}
56926947304SEvan Yan }
57026947304SEvan Yan 
57126947304SEvan Yan /*
57226947304SEvan Yan  * setup slot name/slot-number info.
57326947304SEvan Yan  */
57426947304SEvan Yan void
57526947304SEvan Yan pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p)
57626947304SEvan Yan {
57726947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
57826947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
57926947304SEvan Yan 	uchar_t		*slotname_data;
58026947304SEvan Yan 	int		*slotnum;
58126947304SEvan Yan 	uint_t		count;
58226947304SEvan Yan 	int		len;
58326947304SEvan Yan 	int		invalid_slotnum = 0;
58426947304SEvan Yan 	uint32_t	slot_capabilities;
58526947304SEvan Yan 
58626947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
58726947304SEvan Yan 	    DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
58826947304SEvan Yan 	    DDI_PROP_SUCCESS) {
58926947304SEvan Yan 		slot_p->hs_phy_slot_num = slotnum[0];
59026947304SEvan Yan 		ddi_prop_free(slotnum);
59126947304SEvan Yan 	} else {
59226947304SEvan Yan 		slot_capabilities = pciehpc_reg_get32(ctrl_p,
59326947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCAP);
59426947304SEvan Yan 		slot_p->hs_phy_slot_num =
59526947304SEvan Yan 		    PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
59626947304SEvan Yan 	}
59726947304SEvan Yan 
59826947304SEvan Yan 	/* platform may not have initialized it */
59926947304SEvan Yan 	if (!slot_p->hs_phy_slot_num) {
60026947304SEvan Yan 		PCIE_DBG("%s#%d: Invalid slot number!\n",
60126947304SEvan Yan 		    ddi_driver_name(ctrl_p->hc_dip),
60226947304SEvan Yan 		    ddi_get_instance(ctrl_p->hc_dip));
60326947304SEvan Yan 		slot_p->hs_phy_slot_num = pciehpc_reg_get8(ctrl_p,
60426947304SEvan Yan 		    PCI_BCNF_SECBUS);
60526947304SEvan Yan 		invalid_slotnum = 1;
60626947304SEvan Yan 	}
60726947304SEvan Yan 	slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
60826947304SEvan Yan 	slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
60926947304SEvan Yan 
61026947304SEvan Yan 	/*
61126947304SEvan Yan 	 * construct the slot_name:
61226947304SEvan Yan 	 *	if "slot-names" property exists then use that name
61326947304SEvan Yan 	 *	else if valid slot number exists then it is "pcie<slot-num>".
61426947304SEvan Yan 	 *	else it will be "pcie<sec-bus-number>dev0"
61526947304SEvan Yan 	 */
61626947304SEvan Yan 	if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
61726947304SEvan Yan 	    "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
61826947304SEvan Yan 		char tmp_name[256];
61926947304SEvan Yan 
62026947304SEvan Yan 		/*
62126947304SEvan Yan 		 * Note: for PCI-E slots, the device number is always 0 so the
62226947304SEvan Yan 		 * first (and only) string is the slot name for this slot.
62326947304SEvan Yan 		 */
62426947304SEvan Yan 		(void) snprintf(tmp_name, sizeof (tmp_name),
62526947304SEvan Yan 		    (char *)slotname_data + 4);
62626947304SEvan Yan 		slot_p->hs_info.cn_name = ddi_strdup(tmp_name, KM_SLEEP);
62726947304SEvan Yan 		kmem_free(slotname_data, len);
62826947304SEvan Yan 	} else {
62926947304SEvan Yan 		if (invalid_slotnum) {
63026947304SEvan Yan 			/* use device number ie. 0 */
63126947304SEvan Yan 			slot_p->hs_info.cn_name = ddi_strdup("pcie0",
63226947304SEvan Yan 			    KM_SLEEP);
63326947304SEvan Yan 		} else {
63426947304SEvan Yan 			char tmp_name[256];
63526947304SEvan Yan 
63626947304SEvan Yan 			(void) snprintf(tmp_name, sizeof (tmp_name), "pcie%d",
63726947304SEvan Yan 			    slot_p->hs_phy_slot_num);
63826947304SEvan Yan 			slot_p->hs_info.cn_name = ddi_strdup(tmp_name,
63926947304SEvan Yan 			    KM_SLEEP);
64026947304SEvan Yan 		}
64126947304SEvan Yan 	}
64226947304SEvan Yan }
64326947304SEvan Yan 
64426947304SEvan Yan /*
64526947304SEvan Yan  * Read/Write access to HPC registers. If platform nexus has non-standard
64626947304SEvan Yan  * HPC access mechanism then regops functions are used to do reads/writes.
64726947304SEvan Yan  */
64826947304SEvan Yan uint8_t
64926947304SEvan Yan pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off)
65026947304SEvan Yan {
65126947304SEvan Yan 	if (ctrl_p->hc_regops.get != NULL) {
65226947304SEvan Yan 		return ((uint8_t)ctrl_p->hc_regops.get(
65326947304SEvan Yan 		    ctrl_p->hc_regops.cookie, (off_t)off));
65426947304SEvan Yan 	} else {
65526947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
65626947304SEvan Yan 
65726947304SEvan Yan 		return (pci_config_get8(bus_p->bus_cfg_hdl, off));
65826947304SEvan Yan 	}
65926947304SEvan Yan }
66026947304SEvan Yan 
66126947304SEvan Yan uint16_t
66226947304SEvan Yan pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off)
66326947304SEvan Yan {
66426947304SEvan Yan 	if (ctrl_p->hc_regops.get != NULL) {
66526947304SEvan Yan 		return ((uint16_t)ctrl_p->hc_regops.get(
66626947304SEvan Yan 		    ctrl_p->hc_regops.cookie, (off_t)off));
66726947304SEvan Yan 	} else {
66826947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
66926947304SEvan Yan 
67026947304SEvan Yan 		return (pci_config_get16(bus_p->bus_cfg_hdl, off));
67126947304SEvan Yan 	}
67226947304SEvan Yan }
67326947304SEvan Yan 
67426947304SEvan Yan uint32_t
67526947304SEvan Yan pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off)
67626947304SEvan Yan {
67726947304SEvan Yan 	if (ctrl_p->hc_regops.get != NULL) {
67826947304SEvan Yan 		return ((uint32_t)ctrl_p->hc_regops.get(
67926947304SEvan Yan 		    ctrl_p->hc_regops.cookie, (off_t)off));
68026947304SEvan Yan 	} else {
68126947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
68226947304SEvan Yan 
68326947304SEvan Yan 		return (pci_config_get32(bus_p->bus_cfg_hdl, off));
68426947304SEvan Yan 	}
68526947304SEvan Yan }
68626947304SEvan Yan 
68726947304SEvan Yan void
68826947304SEvan Yan pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val)
68926947304SEvan Yan {
69026947304SEvan Yan 	if (ctrl_p->hc_regops.put != NULL) {
69126947304SEvan Yan 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
69226947304SEvan Yan 		    (off_t)off, (uint_t)val);
69326947304SEvan Yan 	} else {
69426947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
69526947304SEvan Yan 
69626947304SEvan Yan 		pci_config_put8(bus_p->bus_cfg_hdl, off, val);
69726947304SEvan Yan 	}
69826947304SEvan Yan }
69926947304SEvan Yan 
70026947304SEvan Yan void
70126947304SEvan Yan pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val)
70226947304SEvan Yan {
70326947304SEvan Yan 	if (ctrl_p->hc_regops.put != NULL) {
70426947304SEvan Yan 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
70526947304SEvan Yan 		    (off_t)off, (uint_t)val);
70626947304SEvan Yan 	} else {
70726947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
70826947304SEvan Yan 
70926947304SEvan Yan 		pci_config_put16(bus_p->bus_cfg_hdl, off, val);
71026947304SEvan Yan 	}
71126947304SEvan Yan }
71226947304SEvan Yan 
71326947304SEvan Yan void
71426947304SEvan Yan pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val)
71526947304SEvan Yan {
71626947304SEvan Yan 	if (ctrl_p->hc_regops.put != NULL) {
71726947304SEvan Yan 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
71826947304SEvan Yan 		    (off_t)off, (uint_t)val);
71926947304SEvan Yan 	} else {
72026947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
72126947304SEvan Yan 
72226947304SEvan Yan 		pci_config_put32(bus_p->bus_cfg_hdl, off, val);
72326947304SEvan Yan 	}
72426947304SEvan Yan }
72526947304SEvan Yan 
72626947304SEvan Yan /*
72726947304SEvan Yan  * ************************************************************************
72826947304SEvan Yan  * ***	Local functions (called within this file)
72926947304SEvan Yan  * ***	PCIe Native Hotplug mode specific functions
73026947304SEvan Yan  * ************************************************************************
73126947304SEvan Yan  */
73226947304SEvan Yan 
73326947304SEvan Yan /*
73426947304SEvan Yan  * Initialize HPC hardware, install interrupt handler, etc. It doesn't
73526947304SEvan Yan  * enable hot plug interrupts.
73626947304SEvan Yan  *
73726947304SEvan Yan  * (Note: It is called only from pciehpc_init().)
73826947304SEvan Yan  */
73926947304SEvan Yan static int
74026947304SEvan Yan pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p)
74126947304SEvan Yan {
74226947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
74326947304SEvan Yan 	uint16_t	reg;
74426947304SEvan Yan 
74526947304SEvan Yan 	/* read the Slot Control Register */
74626947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
74726947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
74826947304SEvan Yan 
74926947304SEvan Yan 	/* disable all interrupts */
75026947304SEvan Yan 	reg &= ~(PCIE_SLOTCTL_INTR_MASK);
75126947304SEvan Yan 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
75226947304SEvan Yan 	    PCIE_SLOTCTL, reg);
75326947304SEvan Yan 
75426947304SEvan Yan 	/* clear any interrupt status bits */
75526947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
75626947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
75726947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
75826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
75926947304SEvan Yan 
76026947304SEvan Yan 	return (DDI_SUCCESS);
76126947304SEvan Yan }
76226947304SEvan Yan 
76326947304SEvan Yan /*
76426947304SEvan Yan  * Uninitialize HPC hardware, uninstall interrupt handler, etc.
76526947304SEvan Yan  *
76626947304SEvan Yan  * (Note: It is called only from pciehpc_uninit().)
76726947304SEvan Yan  */
76826947304SEvan Yan static int
76926947304SEvan Yan pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
77026947304SEvan Yan {
77126947304SEvan Yan 	/* disable interrupts */
77226947304SEvan Yan 	(void) pciehpc_disable_intr(ctrl_p);
77326947304SEvan Yan 
77426947304SEvan Yan 	return (DDI_SUCCESS);
77526947304SEvan Yan }
77626947304SEvan Yan 
77726947304SEvan Yan /*
77826947304SEvan Yan  * Setup slot information for use with DDI HP framework.
77926947304SEvan Yan  */
78026947304SEvan Yan static int
78126947304SEvan Yan pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
78226947304SEvan Yan {
78326947304SEvan Yan 	uint32_t	slot_capabilities, link_capabilities;
78426947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
78526947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
78626947304SEvan Yan 
78726947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
78826947304SEvan Yan 	/*
78926947304SEvan Yan 	 * setup DDI HP framework slot information structure
79026947304SEvan Yan 	 */
79126947304SEvan Yan 	slot_p->hs_device_num = 0;
79226947304SEvan Yan 
79326947304SEvan Yan 	slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
79426947304SEvan Yan 	slot_p->hs_info.cn_type_str = (ctrl_p->hc_regops.get == NULL) ?
79526947304SEvan Yan 	    PCIE_NATIVE_HP_TYPE : PCIE_PROP_HP_TYPE;
79626947304SEvan Yan 	slot_p->hs_info.cn_child = NULL;
79726947304SEvan Yan 
79826947304SEvan Yan 	slot_p->hs_minor =
79926947304SEvan Yan 	    PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
80026947304SEvan Yan 	    slot_p->hs_device_num);
80126947304SEvan Yan 	slot_p->hs_condition = AP_COND_UNKNOWN;
80226947304SEvan Yan 
80326947304SEvan Yan 	/* read Slot Capabilities Register */
80426947304SEvan Yan 	slot_capabilities = pciehpc_reg_get32(ctrl_p,
80526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
80626947304SEvan Yan 
80726947304SEvan Yan 	/* set slot-name/slot-number info */
80826947304SEvan Yan 	pciehpc_set_slot_name(ctrl_p);
80926947304SEvan Yan 
81026947304SEvan Yan 	/* check if Attn Button present */
81126947304SEvan Yan 	ctrl_p->hc_has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
81226947304SEvan Yan 	    B_TRUE : B_FALSE;
81326947304SEvan Yan 
81426947304SEvan Yan 	/* check if Manual Retention Latch sensor present */
81526947304SEvan Yan 	ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
81626947304SEvan Yan 	    B_TRUE : B_FALSE;
81726947304SEvan Yan 
81826947304SEvan Yan 	/*
81926947304SEvan Yan 	 * PCI-E version 1.1 defines EMI Lock Present bit
82026947304SEvan Yan 	 * in Slot Capabilities register. Check for it.
82126947304SEvan Yan 	 */
82226947304SEvan Yan 	ctrl_p->hc_has_emi_lock = (slot_capabilities &
82326947304SEvan Yan 	    PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
82426947304SEvan Yan 
82526947304SEvan Yan 	link_capabilities = pciehpc_reg_get32(ctrl_p,
82626947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_LINKCAP);
82726947304SEvan Yan 	ctrl_p->hc_dll_active_rep = (link_capabilities &
82826947304SEvan Yan 	    PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
82926947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep)
83026947304SEvan Yan 		cv_init(&slot_p->hs_dll_active_cv, NULL, CV_DRIVER, NULL);
83126947304SEvan Yan 
83226947304SEvan Yan 	/* setup thread for handling ATTN button events */
83326947304SEvan Yan 	if (ctrl_p->hc_has_attn) {
83426947304SEvan Yan 		PCIE_DBG("pciehpc_slotinfo_init: setting up ATTN button event "
83526947304SEvan Yan 		    "handler thread for slot %d\n", slot_p->hs_phy_slot_num);
83626947304SEvan Yan 
83726947304SEvan Yan 		cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
83826947304SEvan Yan 		slot_p->hs_attn_btn_pending = B_FALSE;
83926947304SEvan Yan 		slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
84026947304SEvan Yan 		    pciehpc_attn_btn_handler,
84126947304SEvan Yan 		    (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
84226947304SEvan Yan 		slot_p->hs_attn_btn_thread_exit = B_FALSE;
84326947304SEvan Yan 	}
84426947304SEvan Yan 
84526947304SEvan Yan 	/* get current slot state from the hw */
84626947304SEvan Yan 	slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
84726947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
84826947304SEvan Yan 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
84926947304SEvan Yan 		slot_p->hs_condition = AP_COND_OK;
85026947304SEvan Yan 
85126947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
85226947304SEvan Yan 
85326947304SEvan Yan 	return (DDI_SUCCESS);
85426947304SEvan Yan }
85526947304SEvan Yan 
85626947304SEvan Yan /*ARGSUSED*/
85726947304SEvan Yan static int
85826947304SEvan Yan pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
85926947304SEvan Yan {
86026947304SEvan Yan 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
86126947304SEvan Yan 
86226947304SEvan Yan 	if (slot_p->hs_attn_btn_threadp != NULL) {
86326947304SEvan Yan 		mutex_enter(&ctrl_p->hc_mutex);
86426947304SEvan Yan 		slot_p->hs_attn_btn_thread_exit = B_TRUE;
86526947304SEvan Yan 		cv_signal(&slot_p->hs_attn_btn_cv);
86626947304SEvan Yan 		PCIE_DBG("pciehpc_slotinfo_uninit: "
86726947304SEvan Yan 		    "waiting for ATTN thread exit\n");
86826947304SEvan Yan 		cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
86926947304SEvan Yan 		PCIE_DBG("pciehpc_slotinfo_uninit: ATTN thread exit\n");
87026947304SEvan Yan 		cv_destroy(&slot_p->hs_attn_btn_cv);
87126947304SEvan Yan 		slot_p->hs_attn_btn_threadp = NULL;
87226947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
87326947304SEvan Yan 	}
87426947304SEvan Yan 
87526947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep)
87626947304SEvan Yan 		cv_destroy(&slot_p->hs_dll_active_cv);
87726947304SEvan Yan 	if (slot_p->hs_info.cn_name)
87826947304SEvan Yan 		kmem_free(slot_p->hs_info.cn_name,
87926947304SEvan Yan 		    strlen(slot_p->hs_info.cn_name) + 1);
88026947304SEvan Yan 
88126947304SEvan Yan 	return (DDI_SUCCESS);
88226947304SEvan Yan }
88326947304SEvan Yan 
88426947304SEvan Yan /*
88526947304SEvan Yan  * Enable hot plug interrupts.
88626947304SEvan Yan  * Note: this is only for Native hot plug mode.
88726947304SEvan Yan  */
88826947304SEvan Yan static int
88926947304SEvan Yan pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p)
89026947304SEvan Yan {
89126947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
89226947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
89326947304SEvan Yan 	uint16_t	reg;
894*fe426563SRick Altherr 	uint16_t	intr_mask = PCIE_SLOTCTL_INTR_MASK;
895*fe426563SRick Altherr 
896*fe426563SRick Altherr 	/*
897*fe426563SRick Altherr 	 * power fault detection interrupt is enabled only
898*fe426563SRick Altherr 	 * when the slot is powered ON
899*fe426563SRick Altherr 	 */
900*fe426563SRick Altherr 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED)
901*fe426563SRick Altherr 		intr_mask &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
902*fe426563SRick Altherr 
903*fe426563SRick Altherr 	/*
904*fe426563SRick Altherr 	 * enable interrupt sources but leave the top-level
905*fe426563SRick Altherr 	 * interrupt disabled. some sources may generate a
906*fe426563SRick Altherr 	 * spurrious event when they are first enabled.
907*fe426563SRick Altherr 	 * by leaving the top-level interrupt disabled, those
908*fe426563SRick Altherr 	 * can be cleared first.
909*fe426563SRick Altherr 	 */
910*fe426563SRick Altherr 	reg = pciehpc_reg_get16(ctrl_p,
911*fe426563SRick Altherr 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
912*fe426563SRick Altherr 	pciehpc_reg_put16(ctrl_p,
913*fe426563SRick Altherr 	    bus_p->bus_pcie_off + PCIE_SLOTCTL,
914*fe426563SRick Altherr 	    reg | (intr_mask & ~PCIE_SLOTCTL_HP_INTR_EN));
91526947304SEvan Yan 
91626947304SEvan Yan 	/* clear any interrupt status bits */
91726947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
91826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
91926947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
92026947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
92126947304SEvan Yan 
922*fe426563SRick Altherr 	/* enable top-level interrupt */
92326947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
92426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
925*fe426563SRick Altherr 	pciehpc_reg_put16(ctrl_p,
926*fe426563SRick Altherr 	    bus_p->bus_pcie_off + PCIE_SLOTCTL,
927*fe426563SRick Altherr 	    reg | intr_mask);
92826947304SEvan Yan 
92926947304SEvan Yan 	return (DDI_SUCCESS);
93026947304SEvan Yan }
93126947304SEvan Yan 
93226947304SEvan Yan /*
93326947304SEvan Yan  * Disable hot plug interrupts.
93426947304SEvan Yan  * Note: this is only for Native hot plug mode.
93526947304SEvan Yan  */
93626947304SEvan Yan static int
93726947304SEvan Yan pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p)
93826947304SEvan Yan {
93926947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
94026947304SEvan Yan 	uint16_t	reg;
94126947304SEvan Yan 
94226947304SEvan Yan 	/* read the Slot Control Register */
94326947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
94426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
94526947304SEvan Yan 
94626947304SEvan Yan 	/* disable all interrupts */
94726947304SEvan Yan 	reg &= ~(PCIE_SLOTCTL_INTR_MASK);
94826947304SEvan Yan 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL, reg);
94926947304SEvan Yan 
95026947304SEvan Yan 	/* clear any interrupt status bits */
95126947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
95226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
95326947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
95426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
95526947304SEvan Yan 
95626947304SEvan Yan 	return (DDI_SUCCESS);
95726947304SEvan Yan }
95826947304SEvan Yan 
95926947304SEvan Yan /*
96026947304SEvan Yan  * Allocate a new hotplug controller and slot structures for HPC
96126947304SEvan Yan  * associated with this dip.
96226947304SEvan Yan  */
96326947304SEvan Yan static pcie_hp_ctrl_t *
96426947304SEvan Yan pciehpc_create_controller(dev_info_t *dip)
96526947304SEvan Yan {
96626947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
96726947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
96826947304SEvan Yan 
96926947304SEvan Yan 	ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
97026947304SEvan Yan 	ctrl_p->hc_dip = dip;
97126947304SEvan Yan 
97226947304SEvan Yan 	/* Allocate a new slot structure. */
97326947304SEvan Yan 	ctrl_p->hc_slots[0] = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
97426947304SEvan Yan 	ctrl_p->hc_slots[0]->hs_num = 0;
97526947304SEvan Yan 	ctrl_p->hc_slots[0]->hs_ctrl = ctrl_p;
97626947304SEvan Yan 
97726947304SEvan Yan 	/* Initialize the interrupt mutex */
97826947304SEvan Yan 	mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER,
97926947304SEvan Yan 	    (void *)PCIE_INTR_PRI);
98026947304SEvan Yan 
98126947304SEvan Yan 	/* Initialize synchronization conditional variable */
98226947304SEvan Yan 	cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
98326947304SEvan Yan 	ctrl_p->hc_cmd_pending = B_FALSE;
98426947304SEvan Yan 
98526947304SEvan Yan 	bus_p->bus_hp_curr_mode = PCIE_NATIVE_HP_MODE;
98626947304SEvan Yan 	PCIE_SET_HP_CTRL(dip, ctrl_p);
98726947304SEvan Yan 
98826947304SEvan Yan 	return (ctrl_p);
98926947304SEvan Yan }
99026947304SEvan Yan 
99126947304SEvan Yan /*
99226947304SEvan Yan  * Remove the HPC controller and slot structures
99326947304SEvan Yan  */
99426947304SEvan Yan static void
99526947304SEvan Yan pciehpc_destroy_controller(dev_info_t *dip)
99626947304SEvan Yan {
99726947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
99826947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
99926947304SEvan Yan 
100026947304SEvan Yan 	/* get the soft state structure for this dip */
100126947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
100226947304SEvan Yan 		return;
100326947304SEvan Yan 
100426947304SEvan Yan 	PCIE_SET_HP_CTRL(dip, NULL);
100526947304SEvan Yan 	bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
100626947304SEvan Yan 
100726947304SEvan Yan 	mutex_destroy(&ctrl_p->hc_mutex);
100826947304SEvan Yan 	cv_destroy(&ctrl_p->hc_cmd_comp_cv);
100926947304SEvan Yan 	kmem_free(ctrl_p->hc_slots[0], sizeof (pcie_hp_slot_t));
101026947304SEvan Yan 	kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
101126947304SEvan Yan }
101226947304SEvan Yan 
101326947304SEvan Yan /*
101426947304SEvan Yan  * Register the PCI-E hot plug slot with DDI HP framework.
101526947304SEvan Yan  */
101626947304SEvan Yan static int
101726947304SEvan Yan pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p)
101826947304SEvan Yan {
101926947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
102026947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
102126947304SEvan Yan 
102226947304SEvan Yan 	/* register the slot with DDI HP framework */
102326947304SEvan Yan 	if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
102426947304SEvan Yan 		PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
102526947304SEvan Yan 		    slot_p->hs_phy_slot_num);
102626947304SEvan Yan 		return (DDI_FAILURE);
102726947304SEvan Yan 	}
102826947304SEvan Yan 
102926947304SEvan Yan 	pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
103026947304SEvan Yan 	    slot_p->hs_minor), slot_p->hs_device_num);
103126947304SEvan Yan 
103226947304SEvan Yan 	PCIE_DBG("pciehpc_register_slot(): registered slot %d\n",
103326947304SEvan Yan 	    slot_p->hs_phy_slot_num);
103426947304SEvan Yan 
103526947304SEvan Yan 	return (DDI_SUCCESS);
103626947304SEvan Yan }
103726947304SEvan Yan 
103826947304SEvan Yan /*
103926947304SEvan Yan  * Unregister the PCI-E hot plug slot from DDI HP framework.
104026947304SEvan Yan  */
104126947304SEvan Yan static int
104226947304SEvan Yan pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p)
104326947304SEvan Yan {
104426947304SEvan Yan 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
104526947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
104626947304SEvan Yan 
104726947304SEvan Yan 	pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
104826947304SEvan Yan 	    slot_p->hs_minor));
104926947304SEvan Yan 
105026947304SEvan Yan 	/* unregister the slot with DDI HP framework */
105126947304SEvan Yan 	if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != NDI_SUCCESS) {
105226947304SEvan Yan 		PCIE_DBG("pciehpc_unregister_slot() "
105326947304SEvan Yan 		    "failed to unregister slot %d\n", slot_p->hs_phy_slot_num);
105426947304SEvan Yan 		return (DDI_FAILURE);
105526947304SEvan Yan 	}
105626947304SEvan Yan 
105726947304SEvan Yan 	PCIE_DBG("pciehpc_unregister_slot(): unregistered slot %d\n",
105826947304SEvan Yan 	    slot_p->hs_phy_slot_num);
105926947304SEvan Yan 
106026947304SEvan Yan 	return (DDI_SUCCESS);
106126947304SEvan Yan }
106226947304SEvan Yan 
106326947304SEvan Yan /*
106426947304SEvan Yan  * pciehpc_slot_poweron()
106526947304SEvan Yan  *
106626947304SEvan Yan  * Poweron/Enable the slot.
106726947304SEvan Yan  *
106826947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
106926947304SEvan Yan  */
107026947304SEvan Yan /*ARGSUSED*/
107126947304SEvan Yan static int
107226947304SEvan Yan pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
107326947304SEvan Yan {
107426947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
107526947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
107626947304SEvan Yan 	uint16_t	status, control;
107726947304SEvan Yan 
107826947304SEvan Yan 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
107926947304SEvan Yan 
108026947304SEvan Yan 	/* get the current state of the slot */
108126947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
108226947304SEvan Yan 
108326947304SEvan Yan 	/* check if the slot is already in the 'enabled' state */
108426947304SEvan Yan 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
108526947304SEvan Yan 		/* slot is already in the 'enabled' state */
108626947304SEvan Yan 		PCIE_DBG("pciehpc_slot_poweron() slot %d already enabled\n",
108726947304SEvan Yan 		    slot_p->hs_phy_slot_num);
108826947304SEvan Yan 
108926947304SEvan Yan 		*result = slot_p->hs_info.cn_state;
109026947304SEvan Yan 		return (DDI_SUCCESS);
109126947304SEvan Yan 	}
109226947304SEvan Yan 
109326947304SEvan Yan 	/* read the Slot Status Register */
109426947304SEvan Yan 	status =  pciehpc_reg_get16(ctrl_p,
109526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
109626947304SEvan Yan 
109726947304SEvan Yan 	/* make sure the MRL switch is closed if present */
109826947304SEvan Yan 	if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
109926947304SEvan Yan 		/* MRL switch is open */
110026947304SEvan Yan 		cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
110126947304SEvan Yan 		    slot_p->hs_phy_slot_num);
110226947304SEvan Yan 		goto cleanup;
110326947304SEvan Yan 	}
110426947304SEvan Yan 
110526947304SEvan Yan 	/* make sure the slot has a device present */
110626947304SEvan Yan 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
110726947304SEvan Yan 		/* slot is empty */
110826947304SEvan Yan 		PCIE_DBG("slot %d is empty\n", slot_p->hs_phy_slot_num);
110926947304SEvan Yan 		goto cleanup;
111026947304SEvan Yan 	}
111126947304SEvan Yan 
111226947304SEvan Yan 	/* get the current state of Slot Control Register */
111326947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
111426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
111526947304SEvan Yan 
111626947304SEvan Yan 	/*
111726947304SEvan Yan 	 * Enable power to the slot involves:
111826947304SEvan Yan 	 *	1. Set power LED to blink and ATTN led to OFF.
111926947304SEvan Yan 	 *	2. Set power control ON in Slot Control Reigster and
112026947304SEvan Yan 	 *	   wait for Command Completed Interrupt or 1 sec timeout.
112126947304SEvan Yan 	 *	3. If Data Link Layer State Changed events are supported
112226947304SEvan Yan 	 *	   then wait for the event to indicate Data Layer Link
112326947304SEvan Yan 	 *	   is active. The time out value for this event is 1 second.
112426947304SEvan Yan 	 *	   This is specified in PCI-E version 1.1.
112526947304SEvan Yan 	 *	4. Set power LED to be ON.
112626947304SEvan Yan 	 */
112726947304SEvan Yan 
112826947304SEvan Yan 	/* 1. set power LED to blink & ATTN led to OFF */
112926947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
113026947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
113126947304SEvan Yan 
113226947304SEvan Yan 	/* 2. set power control to ON */
113326947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
113426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
113526947304SEvan Yan 	control &= ~PCIE_SLOTCTL_PWR_CONTROL;
113626947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
113726947304SEvan Yan 
113826947304SEvan Yan 	/* 3. wait for DLL State Change event, if it's supported */
113926947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep) {
114026947304SEvan Yan 		status =  pciehpc_reg_get16(ctrl_p,
114126947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_LINKSTS);
114226947304SEvan Yan 
114326947304SEvan Yan 		if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE)) {
114426947304SEvan Yan 			/* wait 1 sec for the DLL State Changed event */
114526947304SEvan Yan 			(void) cv_timedwait(&slot_p->hs_dll_active_cv,
114626947304SEvan Yan 			    &ctrl_p->hc_mutex,
114726947304SEvan Yan 			    ddi_get_lbolt() +
114826947304SEvan Yan 			    SEC_TO_TICK(PCIE_HP_DLL_STATE_CHANGE_TIMEOUT));
114926947304SEvan Yan 
115026947304SEvan Yan 			/* check Link status */
115126947304SEvan Yan 			status =  pciehpc_reg_get16(ctrl_p,
115226947304SEvan Yan 			    bus_p->bus_pcie_off +
115326947304SEvan Yan 			    PCIE_LINKSTS);
115426947304SEvan Yan 			if (!(status & PCIE_LINKSTS_DLL_LINK_ACTIVE))
115526947304SEvan Yan 				goto cleanup2;
115626947304SEvan Yan 		}
115726947304SEvan Yan 	}
115826947304SEvan Yan 
115926947304SEvan Yan 	/* wait 1 sec for link to come up */
116026947304SEvan Yan 	delay(drv_usectohz(1000000));
116126947304SEvan Yan 
116226947304SEvan Yan 	/* check power is really turned ON */
116326947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
116426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
116526947304SEvan Yan 
116626947304SEvan Yan 	if (control & PCIE_SLOTCTL_PWR_CONTROL) {
116726947304SEvan Yan 		PCIE_DBG("slot %d fails to turn on power on connect\n",
116826947304SEvan Yan 		    slot_p->hs_phy_slot_num);
116926947304SEvan Yan 
117026947304SEvan Yan 		goto cleanup1;
117126947304SEvan Yan 	}
117226947304SEvan Yan 
117326947304SEvan Yan 	/* clear power fault status */
117426947304SEvan Yan 	status =  pciehpc_reg_get16(ctrl_p,
117526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
117626947304SEvan Yan 	status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
117726947304SEvan Yan 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS,
117826947304SEvan Yan 	    status);
117926947304SEvan Yan 
118026947304SEvan Yan 	/* enable power fault detection interrupt */
118126947304SEvan Yan 	control |= PCIE_SLOTCTL_PWR_FAULT_EN;
118226947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
118326947304SEvan Yan 
118426947304SEvan Yan 	/* 4. Set power LED to be ON */
118526947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
118626947304SEvan Yan 
118726947304SEvan Yan 	/* if EMI is present, turn it ON */
118826947304SEvan Yan 	if (ctrl_p->hc_has_emi_lock) {
118926947304SEvan Yan 		status =  pciehpc_reg_get16(ctrl_p,
119026947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
119126947304SEvan Yan 
119226947304SEvan Yan 		if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
119326947304SEvan Yan 			control =  pciehpc_reg_get16(ctrl_p,
119426947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
119526947304SEvan Yan 			control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
119626947304SEvan Yan 			pciehpc_issue_hpc_command(ctrl_p, control);
119726947304SEvan Yan 
119826947304SEvan Yan 			/* wait 1 sec after toggling the state of EMI lock */
119926947304SEvan Yan 			delay(drv_usectohz(1000000));
120026947304SEvan Yan 		}
120126947304SEvan Yan 	}
120226947304SEvan Yan 
120326947304SEvan Yan 	*result = slot_p->hs_info.cn_state =
120426947304SEvan Yan 	    DDI_HP_CN_STATE_POWERED;
120526947304SEvan Yan 
120626947304SEvan Yan 	return (DDI_SUCCESS);
120726947304SEvan Yan 
120826947304SEvan Yan cleanup2:
120926947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
121026947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
121126947304SEvan Yan 
121226947304SEvan Yan 	/* if power is ON, set power control to OFF */
121326947304SEvan Yan 	if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
121426947304SEvan Yan 		control |= PCIE_SLOTCTL_PWR_CONTROL;
121526947304SEvan Yan 		pciehpc_issue_hpc_command(ctrl_p, control);
121626947304SEvan Yan 	}
121726947304SEvan Yan 
121826947304SEvan Yan cleanup1:
121926947304SEvan Yan 	/* set power led to OFF */
122026947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
122126947304SEvan Yan 
122226947304SEvan Yan cleanup:
122326947304SEvan Yan 	return (DDI_FAILURE);
122426947304SEvan Yan }
122526947304SEvan Yan 
122626947304SEvan Yan /*ARGSUSED*/
122726947304SEvan Yan static int
122826947304SEvan Yan pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
122926947304SEvan Yan {
123026947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
123126947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
123226947304SEvan Yan 	uint16_t	status, control;
123326947304SEvan Yan 
123426947304SEvan Yan 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
123526947304SEvan Yan 
123626947304SEvan Yan 	/* get the current state of the slot */
123726947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
123826947304SEvan Yan 
123926947304SEvan Yan 	/* check if the slot is not in the "enabled' state */
124026947304SEvan Yan 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
124126947304SEvan Yan 		/* slot is in the 'disabled' state */
124226947304SEvan Yan 		PCIE_DBG("pciehpc_slot_poweroff(): "
124326947304SEvan Yan 		    "slot %d already disabled\n", slot_p->hs_phy_slot_num);
124426947304SEvan Yan 		ASSERT(slot_p->hs_power_led_state == PCIE_HP_LED_OFF);
124526947304SEvan Yan 
124626947304SEvan Yan 		*result = slot_p->hs_info.cn_state;
124726947304SEvan Yan 		return (DDI_SUCCESS);
124826947304SEvan Yan 	}
124926947304SEvan Yan 
125026947304SEvan Yan 	/* read the Slot Status Register */
125126947304SEvan Yan 	status =  pciehpc_reg_get16(ctrl_p,
125226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
125326947304SEvan Yan 
125426947304SEvan Yan 	/* make sure the slot has a device present */
125526947304SEvan Yan 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
125626947304SEvan Yan 		/* slot is empty */
125726947304SEvan Yan 		PCIE_DBG("pciehpc_slot_poweroff(): slot %d is empty\n",
125826947304SEvan Yan 		    slot_p->hs_phy_slot_num);
125926947304SEvan Yan 		goto cleanup;
126026947304SEvan Yan 	}
126126947304SEvan Yan 
126226947304SEvan Yan 	/*
126326947304SEvan Yan 	 * Disable power to the slot involves:
126426947304SEvan Yan 	 *	1. Set power LED to blink.
126526947304SEvan Yan 	 *	2. Set power control OFF in Slot Control Reigster and
126626947304SEvan Yan 	 *	   wait for Command Completed Interrupt or 1 sec timeout.
126726947304SEvan Yan 	 *	3. Set POWER led and ATTN led to be OFF.
126826947304SEvan Yan 	 */
126926947304SEvan Yan 
127026947304SEvan Yan 	/* 1. set power LED to blink */
127126947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
127226947304SEvan Yan 
127326947304SEvan Yan 	/* disable power fault detection interrupt */
127426947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
127526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
127626947304SEvan Yan 	control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
127726947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
127826947304SEvan Yan 
127926947304SEvan Yan 	/* 2. set power control to OFF */
128026947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
128126947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
128226947304SEvan Yan 	control |= PCIE_SLOTCTL_PWR_CONTROL;
128326947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
128426947304SEvan Yan 
128526947304SEvan Yan #ifdef DEBUG
128626947304SEvan Yan 	/* check for power control bit to be OFF */
128726947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
128826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
128926947304SEvan Yan 	ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
129026947304SEvan Yan #endif
129126947304SEvan Yan 
129226947304SEvan Yan 	/* 3. Set power LED to be OFF */
129326947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
129426947304SEvan Yan 	pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
129526947304SEvan Yan 
129626947304SEvan Yan 	/* if EMI is present, turn it OFF */
129726947304SEvan Yan 	if (ctrl_p->hc_has_emi_lock) {
129826947304SEvan Yan 		status =  pciehpc_reg_get16(ctrl_p,
129926947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
130026947304SEvan Yan 
130126947304SEvan Yan 		if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
130226947304SEvan Yan 			control =  pciehpc_reg_get16(ctrl_p,
130326947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
130426947304SEvan Yan 			control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
130526947304SEvan Yan 			pciehpc_issue_hpc_command(ctrl_p, control);
130626947304SEvan Yan 
130726947304SEvan Yan 			/* wait 1 sec after toggling the state of EMI lock */
130826947304SEvan Yan 			delay(drv_usectohz(1000000));
130926947304SEvan Yan 		}
131026947304SEvan Yan 	}
131126947304SEvan Yan 
131226947304SEvan Yan 	/* get the current state of the slot */
131326947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
131426947304SEvan Yan 
131526947304SEvan Yan 	*result = slot_p->hs_info.cn_state;
131626947304SEvan Yan 
131726947304SEvan Yan 	return (DDI_SUCCESS);
131826947304SEvan Yan 
131926947304SEvan Yan cleanup:
132026947304SEvan Yan 	return (DDI_FAILURE);
132126947304SEvan Yan }
132226947304SEvan Yan 
132326947304SEvan Yan /*
132426947304SEvan Yan  * pciehpc_slot_probe()
132526947304SEvan Yan  *
132626947304SEvan Yan  * Probe the slot.
132726947304SEvan Yan  *
132826947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
132926947304SEvan Yan  */
133026947304SEvan Yan /*ARGSUSED*/
133126947304SEvan Yan static int
133226947304SEvan Yan pciehpc_slot_probe(pcie_hp_slot_t *slot_p)
133326947304SEvan Yan {
133426947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
133526947304SEvan Yan 	int		ret = DDI_SUCCESS;
133626947304SEvan Yan 
133726947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
133826947304SEvan Yan 
133926947304SEvan Yan 	/* get the current state of the slot */
134026947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
134126947304SEvan Yan 
134226947304SEvan Yan 	/*
134326947304SEvan Yan 	 * Probe a given PCIe Hotplug Connection (CN).
134426947304SEvan Yan 	 */
134526947304SEvan Yan 	PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
134626947304SEvan Yan 	ret = pcie_hp_probe(slot_p);
134726947304SEvan Yan 
134826947304SEvan Yan 	if (ret != DDI_SUCCESS) {
134926947304SEvan Yan 		PCIE_DBG("pciehpc_slot_probe() failed\n");
135026947304SEvan Yan 
135126947304SEvan Yan 		/* turn the ATTN led ON for configure failure */
135226947304SEvan Yan 		pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_ON);
135326947304SEvan Yan 
135426947304SEvan Yan 		/* if power to the slot is still on then set Power led to ON */
135526947304SEvan Yan 		if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
135626947304SEvan Yan 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
135726947304SEvan Yan 			    PCIE_HP_LED_ON);
135826947304SEvan Yan 
135926947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
136026947304SEvan Yan 		return (DDI_FAILURE);
136126947304SEvan Yan 	}
136226947304SEvan Yan 
136326947304SEvan Yan 	PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
136426947304SEvan Yan 
136526947304SEvan Yan 	/* get the current state of the slot */
136626947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
136726947304SEvan Yan 
136826947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
136926947304SEvan Yan 	return (DDI_SUCCESS);
137026947304SEvan Yan }
137126947304SEvan Yan 
137226947304SEvan Yan /*
137326947304SEvan Yan  * pciehpc_slot_unprobe()
137426947304SEvan Yan  *
137526947304SEvan Yan  * Unprobe the slot.
137626947304SEvan Yan  *
137726947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
137826947304SEvan Yan  */
137926947304SEvan Yan /*ARGSUSED*/
138026947304SEvan Yan static int
138126947304SEvan Yan pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p)
138226947304SEvan Yan {
138326947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
138426947304SEvan Yan 	int		ret;
138526947304SEvan Yan 
138626947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
138726947304SEvan Yan 
138826947304SEvan Yan 	/* get the current state of the slot */
138926947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
139026947304SEvan Yan 
139126947304SEvan Yan 	/*
139226947304SEvan Yan 	 * Unprobe a given PCIe Hotplug Connection (CN).
139326947304SEvan Yan 	 */
139426947304SEvan Yan 	PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
139526947304SEvan Yan 	ret = pcie_hp_unprobe(slot_p);
139626947304SEvan Yan 
139726947304SEvan Yan 	if (ret != DDI_SUCCESS) {
139826947304SEvan Yan 		PCIE_DBG("pciehpc_slot_unprobe() failed\n");
139926947304SEvan Yan 
140026947304SEvan Yan 		/* if power to the slot is still on then set Power led to ON */
140126947304SEvan Yan 		if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
140226947304SEvan Yan 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
140326947304SEvan Yan 			    PCIE_HP_LED_ON);
140426947304SEvan Yan 
140526947304SEvan Yan 		PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
140626947304SEvan Yan 
140726947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
140826947304SEvan Yan 		return (DDI_FAILURE);
140926947304SEvan Yan 	}
141026947304SEvan Yan 
141126947304SEvan Yan 	/* get the current state of the slot */
141226947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
141326947304SEvan Yan 
141426947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
141526947304SEvan Yan 	return (DDI_SUCCESS);
141626947304SEvan Yan }
141726947304SEvan Yan 
141826947304SEvan Yan static int
141926947304SEvan Yan pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
142026947304SEvan Yan     ddi_hp_cn_state_t target_state)
142126947304SEvan Yan {
142226947304SEvan Yan 	ddi_hp_cn_state_t curr_state;
142326947304SEvan Yan 	int rv = DDI_SUCCESS;
142426947304SEvan Yan 
142526947304SEvan Yan 	if (target_state > DDI_HP_CN_STATE_ENABLED) {
142626947304SEvan Yan 		return (DDI_EINVAL);
142726947304SEvan Yan 	}
142826947304SEvan Yan 
142926947304SEvan Yan 	curr_state = slot_p->hs_info.cn_state;
143026947304SEvan Yan 	while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
143126947304SEvan Yan 
143226947304SEvan Yan 		switch (curr_state) {
143326947304SEvan Yan 		case DDI_HP_CN_STATE_EMPTY:
143426947304SEvan Yan 			/*
143526947304SEvan Yan 			 * From EMPTY to PRESENT, just check the hardware
143626947304SEvan Yan 			 * slot state.
143726947304SEvan Yan 			 */
143826947304SEvan Yan 			pciehpc_get_slot_state(slot_p);
143926947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state;
144026947304SEvan Yan 			if (curr_state < DDI_HP_CN_STATE_PRESENT)
144126947304SEvan Yan 				rv = DDI_FAILURE;
144226947304SEvan Yan 			break;
144326947304SEvan Yan 		case DDI_HP_CN_STATE_PRESENT:
144426947304SEvan Yan 			rv = (slot_p->hs_ctrl->hc_ops.poweron_hpc_slot)(slot_p,
144526947304SEvan Yan 			    &curr_state);
144626947304SEvan Yan 
144726947304SEvan Yan 			break;
144826947304SEvan Yan 		case DDI_HP_CN_STATE_POWERED:
144926947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state =
145026947304SEvan Yan 			    DDI_HP_CN_STATE_ENABLED;
145126947304SEvan Yan 			break;
145226947304SEvan Yan 		default:
145326947304SEvan Yan 			/* should never reach here */
145426947304SEvan Yan 			ASSERT("unknown devinfo state");
145526947304SEvan Yan 		}
145626947304SEvan Yan 	}
145726947304SEvan Yan 
145826947304SEvan Yan 	return (rv);
145926947304SEvan Yan }
146026947304SEvan Yan 
146126947304SEvan Yan static int
146226947304SEvan Yan pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
146326947304SEvan Yan     ddi_hp_cn_state_t target_state)
146426947304SEvan Yan {
146526947304SEvan Yan 	ddi_hp_cn_state_t curr_state;
146626947304SEvan Yan 	int rv = DDI_SUCCESS;
146726947304SEvan Yan 
146826947304SEvan Yan 
146926947304SEvan Yan 	curr_state = slot_p->hs_info.cn_state;
147026947304SEvan Yan 	while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
147126947304SEvan Yan 
147226947304SEvan Yan 		switch (curr_state) {
147326947304SEvan Yan 		case DDI_HP_CN_STATE_PRESENT:
147426947304SEvan Yan 			/*
147526947304SEvan Yan 			 * From PRESENT to EMPTY, just check hardware slot
147626947304SEvan Yan 			 * state.
147726947304SEvan Yan 			 */
147826947304SEvan Yan 			pciehpc_get_slot_state(slot_p);
147926947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state;
148026947304SEvan Yan 			if (curr_state >= DDI_HP_CN_STATE_PRESENT)
148126947304SEvan Yan 				rv = DDI_FAILURE;
148226947304SEvan Yan 			break;
148326947304SEvan Yan 		case DDI_HP_CN_STATE_POWERED:
148426947304SEvan Yan 			rv = (slot_p->hs_ctrl->hc_ops.poweroff_hpc_slot)(
148526947304SEvan Yan 			    slot_p, &curr_state);
148626947304SEvan Yan 
148726947304SEvan Yan 			break;
148826947304SEvan Yan 		case DDI_HP_CN_STATE_ENABLED:
148926947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state =
149026947304SEvan Yan 			    DDI_HP_CN_STATE_POWERED;
149126947304SEvan Yan 
149226947304SEvan Yan 			break;
149326947304SEvan Yan 		default:
149426947304SEvan Yan 			/* should never reach here */
149526947304SEvan Yan 			ASSERT("unknown devinfo state");
149626947304SEvan Yan 		}
149726947304SEvan Yan 	}
149826947304SEvan Yan 
149926947304SEvan Yan 	return (rv);
150026947304SEvan Yan }
150126947304SEvan Yan 
150226947304SEvan Yan /* Change slot state to a target state */
150326947304SEvan Yan static int
150426947304SEvan Yan pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
150526947304SEvan Yan     ddi_hp_cn_state_t target_state)
150626947304SEvan Yan {
150726947304SEvan Yan 	ddi_hp_cn_state_t curr_state;
150826947304SEvan Yan 	int rv;
150926947304SEvan Yan 
151026947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
151126947304SEvan Yan 	curr_state = slot_p->hs_info.cn_state;
151226947304SEvan Yan 
151326947304SEvan Yan 	if (curr_state == target_state) {
151426947304SEvan Yan 		return (DDI_SUCCESS);
151526947304SEvan Yan 	}
151626947304SEvan Yan 	if (curr_state < target_state) {
151726947304SEvan Yan 
151826947304SEvan Yan 		rv = pciehpc_upgrade_slot_state(slot_p, target_state);
151926947304SEvan Yan 	} else {
152026947304SEvan Yan 		rv = pciehpc_downgrade_slot_state(slot_p, target_state);
152126947304SEvan Yan 	}
152226947304SEvan Yan 
152326947304SEvan Yan 	return (rv);
152426947304SEvan Yan }
152526947304SEvan Yan 
152626947304SEvan Yan int
152726947304SEvan Yan pciehpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
152826947304SEvan Yan     ddi_hp_property_t *rval)
152926947304SEvan Yan {
153026947304SEvan Yan 	ddi_hp_property_t request, result;
153126947304SEvan Yan #ifdef _SYSCALL32_IMPL
153226947304SEvan Yan 	ddi_hp_property32_t request32, result32;
153326947304SEvan Yan #endif
153426947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
153526947304SEvan Yan 	nvlist_t	*prop_list;
153626947304SEvan Yan 	nvlist_t	*prop_rlist; /* nvlist for return values */
153726947304SEvan Yan 	nvpair_t	*prop_pair;
153826947304SEvan Yan 	char		*name, *value;
153926947304SEvan Yan 	int		ret = DDI_SUCCESS;
154026947304SEvan Yan 	int		i, n;
154126947304SEvan Yan 	boolean_t	get_all_prop = B_FALSE;
154226947304SEvan Yan 
154326947304SEvan Yan 	if (get_udatamodel() == DATAMODEL_NATIVE) {
154426947304SEvan Yan 		if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
154526947304SEvan Yan 		    copyin(rval, &result, sizeof (ddi_hp_property_t)))
154626947304SEvan Yan 			return (DDI_FAILURE);
154726947304SEvan Yan 	}
154826947304SEvan Yan #ifdef _SYSCALL32_IMPL
154926947304SEvan Yan 	else {
155026947304SEvan Yan 		bzero(&request, sizeof (request));
155126947304SEvan Yan 		bzero(&result, sizeof (result));
155226947304SEvan Yan 		if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
155326947304SEvan Yan 		    copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
155426947304SEvan Yan 			return (DDI_FAILURE);
155526947304SEvan Yan 		request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
155626947304SEvan Yan 		request.buf_size = request32.buf_size;
155726947304SEvan Yan 		result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
155826947304SEvan Yan 		result.buf_size = result32.buf_size;
155926947304SEvan Yan 	}
156026947304SEvan Yan #endif
156126947304SEvan Yan 
156226947304SEvan Yan 	if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
156326947304SEvan Yan 	    &prop_list)) != DDI_SUCCESS)
156426947304SEvan Yan 		return (ret);
156526947304SEvan Yan 
156626947304SEvan Yan 	if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
156726947304SEvan Yan 		ret = DDI_ENOMEM;
156826947304SEvan Yan 		goto get_prop_cleanup;
156926947304SEvan Yan 	}
157026947304SEvan Yan 
157126947304SEvan Yan 	/* check whether the requested property is "all" or "help" */
157226947304SEvan Yan 	prop_pair = nvlist_next_nvpair(prop_list, NULL);
157326947304SEvan Yan 	if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
157426947304SEvan Yan 		name = nvpair_name(prop_pair);
157526947304SEvan Yan 		n = sizeof (pciehpc_props) / sizeof (pciehpc_prop_t);
157626947304SEvan Yan 
157726947304SEvan Yan 		if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
157826947304SEvan Yan 			(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
157926947304SEvan Yan 
158026947304SEvan Yan 			/*
158126947304SEvan Yan 			 * Add all properties into the request list, so that we
158226947304SEvan Yan 			 * will get the values in the following for loop.
158326947304SEvan Yan 			 */
158426947304SEvan Yan 			for (i = 0; i < n; i++) {
158526947304SEvan Yan 				if (nvlist_add_string(prop_list,
158626947304SEvan Yan 				    pciehpc_props[i].prop_name, "") != 0) {
158726947304SEvan Yan 					ret = DDI_FAILURE;
158826947304SEvan Yan 					goto get_prop_cleanup1;
158926947304SEvan Yan 				}
159026947304SEvan Yan 			}
159126947304SEvan Yan 			get_all_prop = B_TRUE;
159226947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
159326947304SEvan Yan 			/*
159426947304SEvan Yan 			 * Empty the request list, and add help strings into the
159526947304SEvan Yan 			 * return list. We will pass the following for loop.
159626947304SEvan Yan 			 */
159726947304SEvan Yan 			(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
159826947304SEvan Yan 
159926947304SEvan Yan 			for (i = 0; i < n; i++) {
160026947304SEvan Yan 				if (nvlist_add_string(prop_rlist,
160126947304SEvan Yan 				    pciehpc_props[i].prop_name,
160226947304SEvan Yan 				    pciehpc_props[i].prop_value) != 0) {
160326947304SEvan Yan 					ret = DDI_FAILURE;
160426947304SEvan Yan 					goto get_prop_cleanup1;
160526947304SEvan Yan 				}
160626947304SEvan Yan 			}
160726947304SEvan Yan 		}
160826947304SEvan Yan 	}
160926947304SEvan Yan 
161026947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
161126947304SEvan Yan 
161226947304SEvan Yan 	/* get the current slot state */
161326947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
161426947304SEvan Yan 
161526947304SEvan Yan 	/* for each requested property, get the value and add it to nvlist */
161626947304SEvan Yan 	prop_pair = NULL;
1617b3d69c05SRobert Mustacchi 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
161826947304SEvan Yan 		name = nvpair_name(prop_pair);
1619b3d69c05SRobert Mustacchi 		value = NULL;
162026947304SEvan Yan 
162126947304SEvan Yan 		if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
162226947304SEvan Yan 			value = pcie_led_state_text(
162326947304SEvan Yan 			    slot_p->hs_fault_led_state);
162426947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
162526947304SEvan Yan 			value = pcie_led_state_text(
162626947304SEvan Yan 			    slot_p->hs_power_led_state);
162726947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
162826947304SEvan Yan 			value = pcie_led_state_text(
162926947304SEvan Yan 			    slot_p->hs_attn_led_state);
163026947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
163126947304SEvan Yan 			value = pcie_led_state_text(
163226947304SEvan Yan 			    slot_p->hs_active_led_state);
163326947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
163426947304SEvan Yan 			ddi_acc_handle_t handle;
163526947304SEvan Yan 			dev_info_t	*cdip;
163626947304SEvan Yan 			uint8_t		prog_class, base_class, sub_class;
163726947304SEvan Yan 			int		i;
163826947304SEvan Yan 
163926947304SEvan Yan 			mutex_exit(&ctrl_p->hc_mutex);
164026947304SEvan Yan 			cdip = pcie_hp_devi_find(
164126947304SEvan Yan 			    ctrl_p->hc_dip, slot_p->hs_device_num, 0);
164226947304SEvan Yan 			mutex_enter(&ctrl_p->hc_mutex);
164326947304SEvan Yan 
164426947304SEvan Yan 			if ((slot_p->hs_info.cn_state
164526947304SEvan Yan 			    != DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
164626947304SEvan Yan 				/*
164726947304SEvan Yan 				 * When getting all properties, just ignore the
164826947304SEvan Yan 				 * one that's not available under certain state.
164926947304SEvan Yan 				 */
165026947304SEvan Yan 				if (get_all_prop)
165126947304SEvan Yan 					continue;
165226947304SEvan Yan 
165326947304SEvan Yan 				ret = DDI_ENOTSUP;
165426947304SEvan Yan 				goto get_prop_cleanup2;
165526947304SEvan Yan 			}
165626947304SEvan Yan 
165726947304SEvan Yan 			if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
165826947304SEvan Yan 				ret = DDI_FAILURE;
165926947304SEvan Yan 				goto get_prop_cleanup2;
166026947304SEvan Yan 			}
166126947304SEvan Yan 
166226947304SEvan Yan 			prog_class = pci_config_get8(handle,
166326947304SEvan Yan 			    PCI_CONF_PROGCLASS);
166426947304SEvan Yan 			base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
166526947304SEvan Yan 			sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
166626947304SEvan Yan 			pci_config_teardown(&handle);
166726947304SEvan Yan 
166826947304SEvan Yan 			for (i = 0; i < class_pci_items; i++) {
166926947304SEvan Yan 				if ((base_class == class_pci[i].base_class) &&
167026947304SEvan Yan 				    (sub_class == class_pci[i].sub_class) &&
167126947304SEvan Yan 				    (prog_class == class_pci[i].prog_class)) {
167226947304SEvan Yan 					value = class_pci[i].short_desc;
167326947304SEvan Yan 					break;
167426947304SEvan Yan 				}
167526947304SEvan Yan 			}
167626947304SEvan Yan 			if (i == class_pci_items)
167726947304SEvan Yan 				value = PCIEHPC_PROP_VALUE_UNKNOWN;
167826947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
167926947304SEvan Yan 			if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
168026947304SEvan Yan 				value = PCIEHPC_PROP_VALUE_UNKNOWN;
168126947304SEvan Yan 			else
168226947304SEvan Yan 				value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
168326947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
168426947304SEvan Yan 			value = pcie_slot_condition_text(slot_p->hs_condition);
168526947304SEvan Yan 		} else {
168626947304SEvan Yan 			/* unsupported property */
168724fd5dc4SEvan Yan 			PCIE_DBG("Unsupported property: %s\n", name);
168826947304SEvan Yan 
168926947304SEvan Yan 			ret = DDI_ENOTSUP;
169026947304SEvan Yan 			goto get_prop_cleanup2;
169126947304SEvan Yan 		}
169226947304SEvan Yan 		if (nvlist_add_string(prop_rlist, name, value) != 0) {
169326947304SEvan Yan 			ret = DDI_FAILURE;
169426947304SEvan Yan 			goto get_prop_cleanup2;
169526947304SEvan Yan 		}
169626947304SEvan Yan 	}
169726947304SEvan Yan 
169826947304SEvan Yan 	/* pack nvlist and copyout */
169926947304SEvan Yan 	if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
170026947304SEvan Yan 	    &result.buf_size)) != DDI_SUCCESS) {
170126947304SEvan Yan 		goto get_prop_cleanup2;
170226947304SEvan Yan 	}
170326947304SEvan Yan 	if (get_udatamodel() == DATAMODEL_NATIVE) {
170426947304SEvan Yan 		if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
170526947304SEvan Yan 			ret = DDI_FAILURE;
170626947304SEvan Yan 	}
170726947304SEvan Yan #ifdef _SYSCALL32_IMPL
170826947304SEvan Yan 	else {
170926947304SEvan Yan 		if (result.buf_size > UINT32_MAX) {
171026947304SEvan Yan 			ret = DDI_FAILURE;
171126947304SEvan Yan 		} else {
171226947304SEvan Yan 			result32.buf_size = (uint32_t)result.buf_size;
171326947304SEvan Yan 			if (copyout(&result32, rval,
171426947304SEvan Yan 			    sizeof (ddi_hp_property32_t)))
171526947304SEvan Yan 				ret = DDI_FAILURE;
171626947304SEvan Yan 		}
171726947304SEvan Yan 	}
171826947304SEvan Yan #endif
171926947304SEvan Yan 
172026947304SEvan Yan get_prop_cleanup2:
172126947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
172226947304SEvan Yan get_prop_cleanup1:
172326947304SEvan Yan 	nvlist_free(prop_rlist);
172426947304SEvan Yan get_prop_cleanup:
172526947304SEvan Yan 	nvlist_free(prop_list);
172626947304SEvan Yan 	return (ret);
172726947304SEvan Yan }
172826947304SEvan Yan 
172926947304SEvan Yan int
173026947304SEvan Yan pciehpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
173126947304SEvan Yan     ddi_hp_property_t *rval)
173226947304SEvan Yan {
173326947304SEvan Yan 	ddi_hp_property_t	request, result;
173426947304SEvan Yan #ifdef _SYSCALL32_IMPL
173526947304SEvan Yan 	ddi_hp_property32_t	request32, result32;
173626947304SEvan Yan #endif
173726947304SEvan Yan 	pcie_hp_ctrl_t		*ctrl_p = slot_p->hs_ctrl;
173826947304SEvan Yan 	nvlist_t		*prop_list;
173926947304SEvan Yan 	nvlist_t		*prop_rlist;
174026947304SEvan Yan 	nvpair_t		*prop_pair;
174126947304SEvan Yan 	char			*name, *value;
174226947304SEvan Yan 	pcie_hp_led_state_t	led_state;
174326947304SEvan Yan 	int			ret = DDI_SUCCESS;
174426947304SEvan Yan 
174526947304SEvan Yan 	if (get_udatamodel() == DATAMODEL_NATIVE) {
174626947304SEvan Yan 		if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
174726947304SEvan Yan 			return (DDI_FAILURE);
174826947304SEvan Yan 		if (rval &&
174926947304SEvan Yan 		    copyin(rval, &result, sizeof (ddi_hp_property_t)))
175026947304SEvan Yan 			return (DDI_FAILURE);
175126947304SEvan Yan 	}
175226947304SEvan Yan #ifdef _SYSCALL32_IMPL
175326947304SEvan Yan 	else {
175426947304SEvan Yan 		bzero(&request, sizeof (request));
175526947304SEvan Yan 		bzero(&result, sizeof (result));
175626947304SEvan Yan 		if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
175726947304SEvan Yan 			return (DDI_FAILURE);
175826947304SEvan Yan 		if (rval &&
175926947304SEvan Yan 		    copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
176026947304SEvan Yan 			return (DDI_FAILURE);
176126947304SEvan Yan 		request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
176226947304SEvan Yan 		request.buf_size = request32.buf_size;
176326947304SEvan Yan 		if (rval) {
176426947304SEvan Yan 			result.nvlist_buf =
176526947304SEvan Yan 			    (char *)(uintptr_t)result32.nvlist_buf;
176626947304SEvan Yan 			result.buf_size = result32.buf_size;
176726947304SEvan Yan 		}
176826947304SEvan Yan 	}
176926947304SEvan Yan #endif
177026947304SEvan Yan 
177126947304SEvan Yan 	if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
177226947304SEvan Yan 	    &prop_list)) != DDI_SUCCESS)
177326947304SEvan Yan 		return (ret);
177426947304SEvan Yan 
177526947304SEvan Yan 	/* check whether the requested property is "help" */
177626947304SEvan Yan 	prop_pair = nvlist_next_nvpair(prop_list, NULL);
177726947304SEvan Yan 	if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
177826947304SEvan Yan 	    (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
177926947304SEvan Yan 		if (!rval) {
178026947304SEvan Yan 			ret = DDI_ENOTSUP;
178126947304SEvan Yan 			goto set_prop_cleanup;
178226947304SEvan Yan 		}
178326947304SEvan Yan 
178426947304SEvan Yan 		if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
178526947304SEvan Yan 			ret = DDI_ENOMEM;
178626947304SEvan Yan 			goto set_prop_cleanup;
178726947304SEvan Yan 		}
178826947304SEvan Yan 		if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
178926947304SEvan Yan 		    PCIEHPC_PROP_VALUE_LED) != 0) {
179026947304SEvan Yan 			ret = DDI_FAILURE;
179126947304SEvan Yan 			goto set_prop_cleanup1;
179226947304SEvan Yan 		}
179326947304SEvan Yan 
179426947304SEvan Yan 		if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
179526947304SEvan Yan 		    &result.buf_size)) != DDI_SUCCESS) {
179626947304SEvan Yan 			goto set_prop_cleanup1;
179726947304SEvan Yan 		}
179826947304SEvan Yan 		if (get_udatamodel() == DATAMODEL_NATIVE) {
179926947304SEvan Yan 			if (copyout(&result, rval,
180026947304SEvan Yan 			    sizeof (ddi_hp_property_t))) {
180126947304SEvan Yan 				ret =  DDI_FAILURE;
180226947304SEvan Yan 				goto set_prop_cleanup1;
180326947304SEvan Yan 			}
180426947304SEvan Yan 		}
180526947304SEvan Yan #ifdef _SYSCALL32_IMPL
180626947304SEvan Yan 		else {
180726947304SEvan Yan 			if (result.buf_size > UINT32_MAX) {
180826947304SEvan Yan 				ret =  DDI_FAILURE;
180926947304SEvan Yan 				goto set_prop_cleanup1;
181026947304SEvan Yan 			} else {
181126947304SEvan Yan 				result32.buf_size = (uint32_t)result.buf_size;
181226947304SEvan Yan 				if (copyout(&result32, rval,
181326947304SEvan Yan 				    sizeof (ddi_hp_property32_t))) {
181426947304SEvan Yan 					ret =  DDI_FAILURE;
181526947304SEvan Yan 					goto set_prop_cleanup1;
181626947304SEvan Yan 				}
181726947304SEvan Yan 			}
181826947304SEvan Yan 		}
181926947304SEvan Yan #endif
182026947304SEvan Yan set_prop_cleanup1:
182126947304SEvan Yan 		nvlist_free(prop_rlist);
182226947304SEvan Yan 		nvlist_free(prop_list);
182326947304SEvan Yan 		return (ret);
182426947304SEvan Yan 	}
182526947304SEvan Yan 
182626947304SEvan Yan 	/* Validate the request */
182726947304SEvan Yan 	prop_pair = NULL;
1828b3d69c05SRobert Mustacchi 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
182926947304SEvan Yan 		name = nvpair_name(prop_pair);
183026947304SEvan Yan 		if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
183124fd5dc4SEvan Yan 			PCIE_DBG("Unexpected data type of setting "
183226947304SEvan Yan 			    "property %s.\n", name);
183326947304SEvan Yan 			ret = DDI_EINVAL;
183426947304SEvan Yan 			goto set_prop_cleanup;
183526947304SEvan Yan 		}
183626947304SEvan Yan 		if (nvpair_value_string(prop_pair, &value)) {
183724fd5dc4SEvan Yan 			PCIE_DBG("Get string value failed for property %s.\n",
183824fd5dc4SEvan Yan 			    name);
183926947304SEvan Yan 			ret = DDI_FAILURE;
184026947304SEvan Yan 			goto set_prop_cleanup;
184126947304SEvan Yan 		}
184226947304SEvan Yan 
184326947304SEvan Yan 		if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
184426947304SEvan Yan 			if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
184526947304SEvan Yan 			    (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
184626947304SEvan Yan 			    (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
184724fd5dc4SEvan Yan 				PCIE_DBG("Unsupported value of setting "
184826947304SEvan Yan 				    "property %s\n", name);
184926947304SEvan Yan 				ret = DDI_ENOTSUP;
185026947304SEvan Yan 				goto set_prop_cleanup;
185126947304SEvan Yan 			}
185226947304SEvan Yan 		} else {
185324fd5dc4SEvan Yan 			PCIE_DBG("Unsupported property: %s\n", name);
185426947304SEvan Yan 			ret = DDI_ENOTSUP;
185526947304SEvan Yan 			goto set_prop_cleanup;
185626947304SEvan Yan 		}
185726947304SEvan Yan 	}
185826947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
185926947304SEvan Yan 
186026947304SEvan Yan 	/* get the current slot state */
186126947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
186226947304SEvan Yan 
186326947304SEvan Yan 	/* set each property */
186426947304SEvan Yan 	prop_pair = NULL;
1865b3d69c05SRobert Mustacchi 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
186626947304SEvan Yan 		name = nvpair_name(prop_pair);
186726947304SEvan Yan 
1868b3d69c05SRobert Mustacchi 		/*
1869b3d69c05SRobert Mustacchi 		 * The validity of the property was checked above.
1870b3d69c05SRobert Mustacchi 		 */
187126947304SEvan Yan 		if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
187226947304SEvan Yan 			if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
187326947304SEvan Yan 				led_state = PCIE_HP_LED_ON;
187426947304SEvan Yan 			else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
187526947304SEvan Yan 				led_state = PCIE_HP_LED_OFF;
187626947304SEvan Yan 			else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
187726947304SEvan Yan 				led_state = PCIE_HP_LED_BLINK;
1878b3d69c05SRobert Mustacchi 			else
1879b3d69c05SRobert Mustacchi 				continue;
188026947304SEvan Yan 
188126947304SEvan Yan 			pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
188226947304SEvan Yan 			    led_state);
188326947304SEvan Yan 		}
188426947304SEvan Yan 	}
188524fd5dc4SEvan Yan 	if (rval) {
188624fd5dc4SEvan Yan 		if (get_udatamodel() == DATAMODEL_NATIVE) {
188724fd5dc4SEvan Yan 			result.buf_size = 0;
188824fd5dc4SEvan Yan 			if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
188924fd5dc4SEvan Yan 				ret =  DDI_FAILURE;
189024fd5dc4SEvan Yan 		}
189124fd5dc4SEvan Yan #ifdef _SYSCALL32_IMPL
189224fd5dc4SEvan Yan 		else {
189324fd5dc4SEvan Yan 			result32.buf_size = 0;
189424fd5dc4SEvan Yan 			if (copyout(&result32, rval,
189524fd5dc4SEvan Yan 			    sizeof (ddi_hp_property32_t)))
189624fd5dc4SEvan Yan 				ret =  DDI_FAILURE;
189724fd5dc4SEvan Yan 		}
189824fd5dc4SEvan Yan #endif
189924fd5dc4SEvan Yan 	}
190026947304SEvan Yan 
190126947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
190226947304SEvan Yan set_prop_cleanup:
190326947304SEvan Yan 	nvlist_free(prop_list);
190426947304SEvan Yan 	return (ret);
190526947304SEvan Yan }
190626947304SEvan Yan 
190726947304SEvan Yan /*
190826947304SEvan Yan  * Send a command to the PCI-E Hot Plug Controller.
190926947304SEvan Yan  *
191026947304SEvan Yan  * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
191126947304SEvan Yan  * commands.
191226947304SEvan Yan  * 1) If Command Complete events/interrupts are supported then software
191326947304SEvan Yan  *    waits for Command Complete event after issuing a command (i.e writing
191426947304SEvan Yan  *    to the Slot Control register). The command completion could take as
191526947304SEvan Yan  *    long as 1 second so software should be prepared to wait for 1 second
191626947304SEvan Yan  *    before issuing another command.
191726947304SEvan Yan  *
191826947304SEvan Yan  * 2) If Command Complete events/interrupts are not supported then
191926947304SEvan Yan  *    software could issue multiple Slot Control writes without any delay
192026947304SEvan Yan  *    between writes.
192126947304SEvan Yan  */
192226947304SEvan Yan static void
192326947304SEvan Yan pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control)
192426947304SEvan Yan {
192526947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
192626947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
192726947304SEvan Yan 	uint16_t	status;
192826947304SEvan Yan 	uint32_t	slot_cap;
192926947304SEvan Yan 
193026947304SEvan Yan 	/*
193126947304SEvan Yan 	 * PCI-E version 1.1 spec defines No Command Completed
193226947304SEvan Yan 	 * Support bit (bit#18) in Slot Capabilities register. If this
193326947304SEvan Yan 	 * bit is set then slot doesn't support notification of command
193426947304SEvan Yan 	 * completion events.
193526947304SEvan Yan 	 */
193626947304SEvan Yan 	slot_cap =  pciehpc_reg_get32(ctrl_p,
193726947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
193826947304SEvan Yan 
193926947304SEvan Yan 	/*
194026947304SEvan Yan 	 * If no Command Completion event is supported or it is ACPI
194126947304SEvan Yan 	 * hot plug mode then just issue the command and return.
194226947304SEvan Yan 	 */
194326947304SEvan Yan 	if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
194426947304SEvan Yan 	    (bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE)) {
194526947304SEvan Yan 		pciehpc_reg_put16(ctrl_p,
194626947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
194726947304SEvan Yan 		return;
194826947304SEvan Yan 	}
194926947304SEvan Yan 
195026947304SEvan Yan 	/*
195126947304SEvan Yan 	 * **************************************
195226947304SEvan Yan 	 * Command Complete events are supported.
195326947304SEvan Yan 	 * **************************************
195426947304SEvan Yan 	 */
195526947304SEvan Yan 
195626947304SEvan Yan 	/*
195726947304SEvan Yan 	 * If HPC is not yet initialized then just poll for the Command
195826947304SEvan Yan 	 * Completion interrupt.
195926947304SEvan Yan 	 */
196026947304SEvan Yan 	if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
196126947304SEvan Yan 		int retry = PCIE_HP_CMD_WAIT_RETRY;
196226947304SEvan Yan 
196326947304SEvan Yan 		/* write the command to the HPC */
196426947304SEvan Yan 		pciehpc_reg_put16(ctrl_p,
196526947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
196626947304SEvan Yan 
196726947304SEvan Yan 		/* poll for status completion */
196826947304SEvan Yan 		while (retry--) {
196926947304SEvan Yan 			/* wait for 10 msec before checking the status */
197026947304SEvan Yan 			delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
197126947304SEvan Yan 
197226947304SEvan Yan 			status = pciehpc_reg_get16(ctrl_p,
197326947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTSTS);
197426947304SEvan Yan 
197526947304SEvan Yan 			if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
197626947304SEvan Yan 				/* clear the status bits */
197726947304SEvan Yan 				pciehpc_reg_put16(ctrl_p,
197826947304SEvan Yan 				    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
197926947304SEvan Yan 				break;
198026947304SEvan Yan 			}
198126947304SEvan Yan 		}
198226947304SEvan Yan 		return;
198326947304SEvan Yan 	}
198426947304SEvan Yan 
198526947304SEvan Yan 	/* HPC is already initialized */
198626947304SEvan Yan 
198726947304SEvan Yan 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
198826947304SEvan Yan 
198926947304SEvan Yan 	/*
199026947304SEvan Yan 	 * If previous command is still pending then wait for its
199126947304SEvan Yan 	 * completion. i.e cv_wait()
199226947304SEvan Yan 	 */
199326947304SEvan Yan 
199426947304SEvan Yan 	while (ctrl_p->hc_cmd_pending == B_TRUE)
199526947304SEvan Yan 		cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
199626947304SEvan Yan 
199726947304SEvan Yan 	/*
199826947304SEvan Yan 	 * Issue the command and wait for Command Completion or
199926947304SEvan Yan 	 * the 1 sec timeout.
200026947304SEvan Yan 	 */
200126947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
200226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
200326947304SEvan Yan 
200426947304SEvan Yan 	ctrl_p->hc_cmd_pending = B_TRUE;
200526947304SEvan Yan 
200626947304SEvan Yan 	if (cv_timedwait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex,
200726947304SEvan Yan 	    ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
200826947304SEvan Yan 
200926947304SEvan Yan 		/* it is a timeout */
201026947304SEvan Yan 		PCIE_DBG("pciehpc_issue_hpc_command: Command Complete"
201126947304SEvan Yan 		    " interrupt is not received for slot %d\n",
201226947304SEvan Yan 		    slot_p->hs_phy_slot_num);
201326947304SEvan Yan 
201426947304SEvan Yan 		/* clear the status info in case interrupts are disabled? */
201526947304SEvan Yan 		status = pciehpc_reg_get16(ctrl_p,
201626947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
201726947304SEvan Yan 
201826947304SEvan Yan 		if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
201926947304SEvan Yan 			/* clear the status bits */
202026947304SEvan Yan 			pciehpc_reg_put16(ctrl_p,
202126947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
202226947304SEvan Yan 		}
202326947304SEvan Yan 	}
202426947304SEvan Yan 
202526947304SEvan Yan 	ctrl_p->hc_cmd_pending = B_FALSE;
202626947304SEvan Yan 
202726947304SEvan Yan 	/* wake up any one waiting for issuing another command to HPC */
202826947304SEvan Yan 	cv_signal(&ctrl_p->hc_cmd_comp_cv);
202926947304SEvan Yan }
203026947304SEvan Yan 
203126947304SEvan Yan /*
203226947304SEvan Yan  * pciehcp_attn_btn_handler()
203326947304SEvan Yan  *
203426947304SEvan Yan  * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
203526947304SEvan Yan  */
203626947304SEvan Yan static void
203726947304SEvan Yan pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p)
203826947304SEvan Yan {
203926947304SEvan Yan 	pcie_hp_slot_t		*slot_p = ctrl_p->hc_slots[0];
204026947304SEvan Yan 	pcie_hp_led_state_t	power_led_state;
204126947304SEvan Yan 	callb_cpr_t		cprinfo;
204226947304SEvan Yan 
204326947304SEvan Yan 	PCIE_DBG("pciehpc_attn_btn_handler: thread started\n");
204426947304SEvan Yan 
204526947304SEvan Yan 	CALLB_CPR_INIT(&cprinfo, &ctrl_p->hc_mutex, callb_generic_cpr,
204626947304SEvan Yan 	    "pciehpc_attn_btn_handler");
204726947304SEvan Yan 
204826947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
204926947304SEvan Yan 
205026947304SEvan Yan 	/* wait for ATTN button event */
205126947304SEvan Yan 	cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
205226947304SEvan Yan 
205326947304SEvan Yan 	while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
205426947304SEvan Yan 		if (slot_p->hs_attn_btn_pending == B_TRUE) {
205526947304SEvan Yan 			/* get the current state of power LED */
205626947304SEvan Yan 			power_led_state = pciehpc_get_led_state(ctrl_p,
205726947304SEvan Yan 			    PCIE_HP_POWER_LED);
205826947304SEvan Yan 
205926947304SEvan Yan 			/* Blink the Power LED while we wait for 5 seconds */
206026947304SEvan Yan 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
206126947304SEvan Yan 			    PCIE_HP_LED_BLINK);
206226947304SEvan Yan 
206326947304SEvan Yan 			/* wait for 5 seconds before taking any action */
206426947304SEvan Yan 			if (cv_timedwait(&slot_p->hs_attn_btn_cv,
206526947304SEvan Yan 			    &ctrl_p->hc_mutex,
206626947304SEvan Yan 			    ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
206726947304SEvan Yan 				/*
206826947304SEvan Yan 				 * It is a time out; make sure the ATTN pending
206926947304SEvan Yan 				 * flag is still ON before sending the event to
207026947304SEvan Yan 				 * DDI HP framework.
207126947304SEvan Yan 				 */
207226947304SEvan Yan 				if (slot_p->hs_attn_btn_pending == B_TRUE) {
207326947304SEvan Yan 					int hint;
207426947304SEvan Yan 
207526947304SEvan Yan 					slot_p->hs_attn_btn_pending = B_FALSE;
207626947304SEvan Yan 					pciehpc_get_slot_state(slot_p);
207726947304SEvan Yan 
207826947304SEvan Yan 					if (slot_p->hs_info.cn_state <=
207926947304SEvan Yan 					    DDI_HP_CN_STATE_PRESENT) {
208026947304SEvan Yan 						/*
208126947304SEvan Yan 						 * Insertion.
208226947304SEvan Yan 						 */
208326947304SEvan Yan 						hint = SE_INCOMING_RES;
208426947304SEvan Yan 					} else {
208526947304SEvan Yan 						/*
208626947304SEvan Yan 						 * Want to remove;
208726947304SEvan Yan 						 */
208826947304SEvan Yan 						hint = SE_OUTGOING_RES;
208926947304SEvan Yan 					}
209026947304SEvan Yan 
209126947304SEvan Yan 					/*
209226947304SEvan Yan 					 * We can't call ddihp_cn_gen_sysevent
209326947304SEvan Yan 					 * here since it's not a DDI interface.
209426947304SEvan Yan 					 */
209526947304SEvan Yan 					pcie_hp_gen_sysevent_req(
209626947304SEvan Yan 					    slot_p->hs_info.cn_name,
209726947304SEvan Yan 					    hint,
209826947304SEvan Yan 					    ctrl_p->hc_dip,
209926947304SEvan Yan 					    KM_SLEEP);
210026947304SEvan Yan 				}
210126947304SEvan Yan 			}
210226947304SEvan Yan 
210326947304SEvan Yan 			/* restore the power LED state */
210426947304SEvan Yan 			pciehpc_set_led_state(ctrl_p, PCIE_HP_POWER_LED,
210526947304SEvan Yan 			    power_led_state);
210626947304SEvan Yan 			continue;
210726947304SEvan Yan 		}
210826947304SEvan Yan 
210926947304SEvan Yan 		/* wait for another ATTN button event */
211026947304SEvan Yan 		cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
211126947304SEvan Yan 	}
211226947304SEvan Yan 
211326947304SEvan Yan 	PCIE_DBG("pciehpc_attn_btn_handler: thread exit\n");
211426947304SEvan Yan 	cv_signal(&slot_p->hs_attn_btn_cv);
211526947304SEvan Yan 	CALLB_CPR_EXIT(&cprinfo);
211626947304SEvan Yan 	thread_exit();
211726947304SEvan Yan }
211826947304SEvan Yan 
211926947304SEvan Yan /*
212026947304SEvan Yan  * convert LED state from PCIE HPC definition to pcie_hp_led_state_t
212126947304SEvan Yan  * definition.
212226947304SEvan Yan  */
212326947304SEvan Yan static pcie_hp_led_state_t
212426947304SEvan Yan pciehpc_led_state_to_hpc(uint16_t state)
212526947304SEvan Yan {
212626947304SEvan Yan 	switch (state) {
212726947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_ON:
212826947304SEvan Yan 		return (PCIE_HP_LED_ON);
212926947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
213026947304SEvan Yan 		return (PCIE_HP_LED_BLINK);
213126947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
213226947304SEvan Yan 	default:
213326947304SEvan Yan 		return (PCIE_HP_LED_OFF);
213426947304SEvan Yan 	}
213526947304SEvan Yan }
213626947304SEvan Yan 
213726947304SEvan Yan /*
213826947304SEvan Yan  * Get the state of an LED.
213926947304SEvan Yan  */
214026947304SEvan Yan static pcie_hp_led_state_t
214126947304SEvan Yan pciehpc_get_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led)
214226947304SEvan Yan {
214326947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
214426947304SEvan Yan 	uint16_t	control, state;
214526947304SEvan Yan 
214626947304SEvan Yan 	/* get the current state of Slot Control register */
214726947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
214826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
214926947304SEvan Yan 
215026947304SEvan Yan 	switch (led) {
215126947304SEvan Yan 	case PCIE_HP_POWER_LED:
215226947304SEvan Yan 		state = pcie_slotctl_pwr_indicator_get(control);
215326947304SEvan Yan 		break;
215426947304SEvan Yan 	case PCIE_HP_ATTN_LED:
215526947304SEvan Yan 		state = pcie_slotctl_attn_indicator_get(control);
215626947304SEvan Yan 		break;
215726947304SEvan Yan 	default:
215826947304SEvan Yan 		PCIE_DBG("pciehpc_get_led_state() invalid LED %d\n", led);
215926947304SEvan Yan 		return (PCIE_HP_LED_OFF);
216026947304SEvan Yan 	}
216126947304SEvan Yan 
216226947304SEvan Yan 	switch (state) {
216326947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_ON:
216426947304SEvan Yan 		return (PCIE_HP_LED_ON);
216526947304SEvan Yan 
216626947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
216726947304SEvan Yan 		return (PCIE_HP_LED_BLINK);
216826947304SEvan Yan 
216926947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
217026947304SEvan Yan 	default:
217126947304SEvan Yan 		return (PCIE_HP_LED_OFF);
217226947304SEvan Yan 	}
217326947304SEvan Yan }
217426947304SEvan Yan 
217526947304SEvan Yan /*
217626947304SEvan Yan  * Set the state of an LED. It updates both hw and sw state.
217726947304SEvan Yan  */
217826947304SEvan Yan static void
217926947304SEvan Yan pciehpc_set_led_state(pcie_hp_ctrl_t *ctrl_p, pcie_hp_led_t led,
218026947304SEvan Yan     pcie_hp_led_state_t state)
218126947304SEvan Yan {
218226947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
218326947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
218426947304SEvan Yan 	uint16_t	control;
218526947304SEvan Yan 
218626947304SEvan Yan 	/* get the current state of Slot Control register */
218726947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
218826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
218926947304SEvan Yan 
219026947304SEvan Yan 	switch (led) {
219126947304SEvan Yan 	case PCIE_HP_POWER_LED:
219226947304SEvan Yan 		/* clear led mask */
219326947304SEvan Yan 		control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
219426947304SEvan Yan 		slot_p->hs_power_led_state = state;
219526947304SEvan Yan 		break;
219626947304SEvan Yan 	case PCIE_HP_ATTN_LED:
219726947304SEvan Yan 		/* clear led mask */
219826947304SEvan Yan 		control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
219926947304SEvan Yan 		slot_p->hs_attn_led_state = state;
220026947304SEvan Yan 		break;
220126947304SEvan Yan 	default:
220226947304SEvan Yan 		PCIE_DBG("pciehpc_set_led_state() invalid LED %d\n", led);
220326947304SEvan Yan 		return;
220426947304SEvan Yan 	}
220526947304SEvan Yan 
220626947304SEvan Yan 	switch (state) {
220726947304SEvan Yan 	case PCIE_HP_LED_ON:
220826947304SEvan Yan 		if (led == PCIE_HP_POWER_LED)
220926947304SEvan Yan 			control = pcie_slotctl_pwr_indicator_set(control,
221026947304SEvan Yan 			    PCIE_SLOTCTL_INDICATOR_STATE_ON);
221126947304SEvan Yan 		else if (led == PCIE_HP_ATTN_LED)
221226947304SEvan Yan 			control = pcie_slotctl_attn_indicator_set(control,
221326947304SEvan Yan 			    PCIE_SLOTCTL_INDICATOR_STATE_ON);
221426947304SEvan Yan 		break;
221526947304SEvan Yan 	case PCIE_HP_LED_OFF:
221626947304SEvan Yan 		if (led == PCIE_HP_POWER_LED)
221726947304SEvan Yan 			control = pcie_slotctl_pwr_indicator_set(control,
221826947304SEvan Yan 			    PCIE_SLOTCTL_INDICATOR_STATE_OFF);
221926947304SEvan Yan 		else if (led == PCIE_HP_ATTN_LED)
222026947304SEvan Yan 			control = pcie_slotctl_attn_indicator_set(control,
222126947304SEvan Yan 			    PCIE_SLOTCTL_INDICATOR_STATE_OFF);
222226947304SEvan Yan 		break;
222326947304SEvan Yan 	case PCIE_HP_LED_BLINK:
222426947304SEvan Yan 		if (led == PCIE_HP_POWER_LED)
222526947304SEvan Yan 			control = pcie_slotctl_pwr_indicator_set(control,
222626947304SEvan Yan 			    PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
222726947304SEvan Yan 		else if (led == PCIE_HP_ATTN_LED)
222826947304SEvan Yan 			control = pcie_slotctl_attn_indicator_set(control,
222926947304SEvan Yan 			    PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
223026947304SEvan Yan 		break;
223126947304SEvan Yan 
223226947304SEvan Yan 	default:
223326947304SEvan Yan 		PCIE_DBG("pciehpc_set_led_state() invalid LED state %d\n",
223426947304SEvan Yan 		    state);
223526947304SEvan Yan 		return;
223626947304SEvan Yan 	}
223726947304SEvan Yan 
223826947304SEvan Yan 	/* update the Slot Control Register */
223926947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
224026947304SEvan Yan 
224126947304SEvan Yan #ifdef DEBUG
224226947304SEvan Yan 	/* get the current state of Slot Control register */
224326947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
224426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
224526947304SEvan Yan 
224626947304SEvan Yan 	PCIE_DBG("pciehpc_set_led_state: slot %d power-led %s attn-led %s\n",
224726947304SEvan Yan 	    slot_p->hs_phy_slot_num, pcie_led_state_text(
224826947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))),
224926947304SEvan Yan 	    pcie_led_state_text(pciehpc_led_state_to_hpc(
225026947304SEvan Yan 	    pcie_slotctl_attn_indicator_get(control))));
225126947304SEvan Yan #endif
225226947304SEvan Yan }
225326947304SEvan Yan 
225480dc702dSColin Zou - Sun Microsystems - Beijing China static void
225580dc702dSColin Zou - Sun Microsystems - Beijing China pciehpc_handle_power_fault(dev_info_t *dip)
225680dc702dSColin Zou - Sun Microsystems - Beijing China {
225780dc702dSColin Zou - Sun Microsystems - Beijing China 	/*
225880dc702dSColin Zou - Sun Microsystems - Beijing China 	 * Hold the parent's ref so that it won't disappear when the taskq is
225980dc702dSColin Zou - Sun Microsystems - Beijing China 	 * scheduled to run.
226080dc702dSColin Zou - Sun Microsystems - Beijing China 	 */
226180dc702dSColin Zou - Sun Microsystems - Beijing China 	ndi_hold_devi(dip);
226280dc702dSColin Zou - Sun Microsystems - Beijing China 
2263fc8ae2ecSToomas Soome 	if (taskq_dispatch(system_taskq, pciehpc_power_fault_handler, dip,
2264fc8ae2ecSToomas Soome 	    TQ_NOSLEEP) == TASKQID_INVALID) {
226580dc702dSColin Zou - Sun Microsystems - Beijing China 		ndi_rele_devi(dip);
226680dc702dSColin Zou - Sun Microsystems - Beijing China 		PCIE_DBG("pciehpc_intr(): "
226780dc702dSColin Zou - Sun Microsystems - Beijing China 		    "Failed to dispatch power fault handler, dip %p\n", dip);
226880dc702dSColin Zou - Sun Microsystems - Beijing China 	}
226980dc702dSColin Zou - Sun Microsystems - Beijing China }
227080dc702dSColin Zou - Sun Microsystems - Beijing China 
227180dc702dSColin Zou - Sun Microsystems - Beijing China static void
227280dc702dSColin Zou - Sun Microsystems - Beijing China pciehpc_power_fault_handler(void *arg)
227380dc702dSColin Zou - Sun Microsystems - Beijing China {
227480dc702dSColin Zou - Sun Microsystems - Beijing China 	dev_info_t *dip = (dev_info_t *)arg;
227580dc702dSColin Zou - Sun Microsystems - Beijing China 	pcie_hp_ctrl_t  *ctrl_p;
227680dc702dSColin Zou - Sun Microsystems - Beijing China 	pcie_hp_slot_t  *slot_p;
227780dc702dSColin Zou - Sun Microsystems - Beijing China 
227880dc702dSColin Zou - Sun Microsystems - Beijing China 	/* get the soft state structure for this dip */
227980dc702dSColin Zou - Sun Microsystems - Beijing China 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
228080dc702dSColin Zou - Sun Microsystems - Beijing China 		ndi_rele_devi(dip);
228180dc702dSColin Zou - Sun Microsystems - Beijing China 		return;
228280dc702dSColin Zou - Sun Microsystems - Beijing China 	}
228380dc702dSColin Zou - Sun Microsystems - Beijing China 	slot_p = ctrl_p->hc_slots[0];
228480dc702dSColin Zou - Sun Microsystems - Beijing China 
228580dc702dSColin Zou - Sun Microsystems - Beijing China 	/*
228680dc702dSColin Zou - Sun Microsystems - Beijing China 	 * Send the event to DDI Hotplug framework, power off
228780dc702dSColin Zou - Sun Microsystems - Beijing China 	 * the slot
228880dc702dSColin Zou - Sun Microsystems - Beijing China 	 */
228980dc702dSColin Zou - Sun Microsystems - Beijing China 	(void) ndi_hp_state_change_req(dip,
229080dc702dSColin Zou - Sun Microsystems - Beijing China 	    slot_p->hs_info.cn_name,
229180dc702dSColin Zou - Sun Microsystems - Beijing China 	    DDI_HP_CN_STATE_EMPTY, DDI_HP_REQ_SYNC);
229280dc702dSColin Zou - Sun Microsystems - Beijing China 
229380dc702dSColin Zou - Sun Microsystems - Beijing China 	mutex_enter(&ctrl_p->hc_mutex);
229480dc702dSColin Zou - Sun Microsystems - Beijing China 	pciehpc_set_led_state(ctrl_p, PCIE_HP_ATTN_LED,
229580dc702dSColin Zou - Sun Microsystems - Beijing China 	    PCIE_HP_LED_ON);
229680dc702dSColin Zou - Sun Microsystems - Beijing China 	mutex_exit(&ctrl_p->hc_mutex);
229780dc702dSColin Zou - Sun Microsystems - Beijing China 	ndi_rele_devi(dip);
229880dc702dSColin Zou - Sun Microsystems - Beijing China }
229980dc702dSColin Zou - Sun Microsystems - Beijing China 
230026947304SEvan Yan #ifdef DEBUG
230126947304SEvan Yan /*
230226947304SEvan Yan  * Dump PCI-E Hot Plug registers.
230326947304SEvan Yan  */
230426947304SEvan Yan static void
230526947304SEvan Yan pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p)
230626947304SEvan Yan {
230726947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
230826947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
230926947304SEvan Yan 	uint16_t	control;
231026947304SEvan Yan 	uint32_t	capabilities;
231126947304SEvan Yan 
231226947304SEvan Yan 	if (!pcie_debug_flags)
231326947304SEvan Yan 		return;
231426947304SEvan Yan 
231526947304SEvan Yan 	capabilities = pciehpc_reg_get32(ctrl_p,
231626947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
231726947304SEvan Yan 
231826947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
231926947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
232026947304SEvan Yan 
232126947304SEvan Yan 	PCIE_DBG("pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
232226947304SEvan Yan 	    slot_p->hs_phy_slot_num);
232326947304SEvan Yan 
232426947304SEvan Yan 	PCIE_DBG("Attention Button Present = %s\n",
232526947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
232626947304SEvan Yan 
232726947304SEvan Yan 	PCIE_DBG("Power controller Present = %s\n",
232826947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
232926947304SEvan Yan 
233026947304SEvan Yan 	PCIE_DBG("MRL Sensor Present	   = %s\n",
233126947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
233226947304SEvan Yan 
233326947304SEvan Yan 	PCIE_DBG("Attn Indicator Present   = %s\n",
233426947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
233526947304SEvan Yan 
233626947304SEvan Yan 	PCIE_DBG("Power Indicator Present  = %s\n",
233726947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
233826947304SEvan Yan 
233926947304SEvan Yan 	PCIE_DBG("HotPlug Surprise	   = %s\n",
234026947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
234126947304SEvan Yan 
234226947304SEvan Yan 	PCIE_DBG("HotPlug Capable	   = %s\n",
234326947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
234426947304SEvan Yan 
234526947304SEvan Yan 	PCIE_DBG("Physical Slot Number	   = %d\n",
234626947304SEvan Yan 	    PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
234726947304SEvan Yan 
234826947304SEvan Yan 	PCIE_DBG("Attn Button interrupt Enabled  = %s\n",
234926947304SEvan Yan 	    control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
235026947304SEvan Yan 
235126947304SEvan Yan 	PCIE_DBG("Power Fault interrupt Enabled  = %s\n",
235226947304SEvan Yan 	    control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
235326947304SEvan Yan 
235426947304SEvan Yan 	PCIE_DBG("MRL Sensor INTR Enabled   = %s\n",
235526947304SEvan Yan 	    control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
235626947304SEvan Yan 
235726947304SEvan Yan 	PCIE_DBG("Presence interrupt Enabled	 = %s\n",
235826947304SEvan Yan 	    control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
235926947304SEvan Yan 
236026947304SEvan Yan 	PCIE_DBG("Cmd Complete interrupt Enabled = %s\n",
236126947304SEvan Yan 	    control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
236226947304SEvan Yan 
236326947304SEvan Yan 	PCIE_DBG("HotPlug interrupt Enabled	 = %s\n",
236426947304SEvan Yan 	    control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
236526947304SEvan Yan 
236626947304SEvan Yan 	PCIE_DBG("Power Indicator LED = %s", pcie_led_state_text(
236726947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
236826947304SEvan Yan 
236926947304SEvan Yan 	PCIE_DBG("Attn Indicator LED = %s\n",
237026947304SEvan Yan 	    pcie_led_state_text(pciehpc_led_state_to_hpc(
237126947304SEvan Yan 	    pcie_slotctl_attn_indicator_get(control))));
237226947304SEvan Yan }
237326947304SEvan Yan #endif	/* DEBUG */
2374