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*ead3c390SKeith M Wesolowski  * Copyright 2024 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.
33350b2114SRobert Mustacchi  *
34350b2114SRobert Mustacchi  * The main purpose of this is to take the PCIe slot logic, which is found on a
35350b2114SRobert Mustacchi  * PCIe bridge that indicates it is hotplug capable, and map the DDI hotplug
36350b2114SRobert Mustacchi  * controller states to this. This is an imperfect mapping as the definition of
37350b2114SRobert Mustacchi  * the pciehpc_slot_power_t shows. This file assumes that a device can be
38350b2114SRobert Mustacchi  * removed at any time without notice. This is what the spec calls 'surprise
39350b2114SRobert Mustacchi  * removal'.
40350b2114SRobert Mustacchi  *
41350b2114SRobert Mustacchi  * Not all PCIe slots are the same. In particular this can handle the following
42350b2114SRobert Mustacchi  * features which may or may not exist on the slot:
43350b2114SRobert Mustacchi  *
44350b2114SRobert Mustacchi  *  o Power Controllers: With the rise of NVMe based hotplug and the Enterprise
45350b2114SRobert Mustacchi  *    SSD specification, you can have hotplug, but not specific power control
46350b2114SRobert Mustacchi  *    over the device.
47350b2114SRobert Mustacchi  *  o MRL sensor: Manually-operated Retention latches are an optional item and
48350b2114SRobert Mustacchi  *    less common with U.2, E.1, and E.3 based form factors, but there is the
49350b2114SRobert Mustacchi  *    ability to see their state.
50350b2114SRobert Mustacchi  *  o EMI: Electromechanical Interlock. This is used to lock the device in place
51350b2114SRobert Mustacchi  *    and is often paired with an MRL. This similarly isn't as common.
52350b2114SRobert Mustacchi  *  o Attention Button: A button which can be pressed to say please do
53350b2114SRobert Mustacchi  *    something. This is more of a holdover from the world of coordinated
54350b2114SRobert Mustacchi  *    removal from the PCI Standard Hot-Plug Controller (SHPC).
55350b2114SRobert Mustacchi  *  o Power Fault: The ability to tell whether or not a power fault has
56350b2114SRobert Mustacchi  *    occurred.
57350b2114SRobert Mustacchi  *  o Power and Attention Indicators: These are LEDs that are supposed to be
58350b2114SRobert Mustacchi  *    enabled in response to specific actions in the spec, but some of that is
59350b2114SRobert Mustacchi  *    ultimately policy. It's worth noting that not all controllers support both
60350b2114SRobert Mustacchi  *    LEDs and so platforms may want to munge the logical states here a bit
61350b2114SRobert Mustacchi  *    more.
62350b2114SRobert Mustacchi  *
63350b2114SRobert Mustacchi  * There are four primary states that a slot is considered to exist in that
64350b2114SRobert Mustacchi  * roughly have the following state transition diagram:
65350b2114SRobert Mustacchi  *
66350b2114SRobert Mustacchi  *      +-------+
67350b2114SRobert Mustacchi  *      | Empty |<---------------<------------+
68350b2114SRobert Mustacchi  *      +-------+                             |
69350b2114SRobert Mustacchi  *          |                                 |
70350b2114SRobert Mustacchi  * Device   |                                 |
71350b2114SRobert Mustacchi  * Inserted .                                 ^
72350b2114SRobert Mustacchi  *          |                                 |
73350b2114SRobert Mustacchi  *          |                                 |
74350b2114SRobert Mustacchi  *          v                                 |
75350b2114SRobert Mustacchi  *     +---------+                            . . . Device
76350b2114SRobert Mustacchi  *     | Present |<------+                    |     Removed
77350b2114SRobert Mustacchi  *     +---------+       |                    |
78350b2114SRobert Mustacchi  *          |            |                    |
79350b2114SRobert Mustacchi  * Slot     |            |                    |
80350b2114SRobert Mustacchi  * Power  . .            . . Slot Power       |
81350b2114SRobert Mustacchi  * Enabled  |            |   Disabled,        |
82350b2114SRobert Mustacchi  *          |            |   Power Fault,     |
83350b2114SRobert Mustacchi  *          v            |   or specific      |
84350b2114SRobert Mustacchi  *     +---------+       |   request          |
85350b2114SRobert Mustacchi  *     | Powered |-->----+                    |
86350b2114SRobert Mustacchi  *     +---------+       |                    |
87350b2114SRobert Mustacchi  *          |            |                    |
88350b2114SRobert Mustacchi  *          |            |                    |
89350b2114SRobert Mustacchi  * Request  |            ^                    ^
90350b2114SRobert Mustacchi  * or auto- |            |                    |
91350b2114SRobert Mustacchi  * online . *            |                    |
92350b2114SRobert Mustacchi  *          |            |                    |
93350b2114SRobert Mustacchi  *          v            |                    |
94350b2114SRobert Mustacchi  *     +---------+       |                    |
95350b2114SRobert Mustacchi  *     | Enabled |-->----+--------->----------+
96350b2114SRobert Mustacchi  *     +---------+
97350b2114SRobert Mustacchi  *
98350b2114SRobert Mustacchi  * These four states are all related to the DDI_HP_CN_STATE_* values. For
99350b2114SRobert Mustacchi  * example, the empty state above is DDI_HP_CN_STATE_EMPTY and enabled is
100350b2114SRobert Mustacchi  * DDI_HP_CN_STATE_ENABLED. These changes can happen initially because of
101350b2114SRobert Mustacchi  * outside action that is taken or because an explicit state change has been
102350b2114SRobert Mustacchi  * requested via cfgadm/libhotplug. Note that one cannot enter or leave empty
103350b2114SRobert Mustacchi  * without removing or inserting a device.
104350b2114SRobert Mustacchi  *
105350b2114SRobert Mustacchi  * A device node is created in the devinfo tree as a side effect of
106350b2114SRobert Mustacchi  * transitioning to the enabled state and removed when transitioning away from
107350b2114SRobert Mustacchi  * enabled. This is initiated by the DDI hotplug framework making a probe
108350b2114SRobert Mustacchi  * (DDI_HPOP_CN_PROBE) and unprobe (DDI_HPOP_CN_UNPROBE) request which will
109350b2114SRobert Mustacchi  * ultimately get us to pcicfg_configure() which dynamically sets up child
110350b2114SRobert Mustacchi  * nodes.
111350b2114SRobert Mustacchi  *
112350b2114SRobert Mustacchi  * State Initialization
113350b2114SRobert Mustacchi  * --------------------
114350b2114SRobert Mustacchi  *
115350b2114SRobert Mustacchi  * Initializing the state of the world is a bit tricky here. In particular,
116350b2114SRobert Mustacchi  * there are a few things that we need to understand and deal with:
117350b2114SRobert Mustacchi  *
118350b2114SRobert Mustacchi  * 1. A PCIe slot may or may not have been powered prior to us initializing this
119350b2114SRobert Mustacchi  * module. In particular, the PCIe firmware specification generally expects
120350b2114SRobert Mustacchi  * occupied slots to have both their MRL and power indicator match the slot
121350b2114SRobert Mustacchi  * occupancy state (3.5 Device State at Firmware/Operating System Handoff). Of
122350b2114SRobert Mustacchi  * course, we must not assume that firmware has done this or not.
123350b2114SRobert Mustacchi  *
124350b2114SRobert Mustacchi  * This is further complicated by the fact that while the PCIe default is that
125350b2114SRobert Mustacchi  * power is enabled at reset, some controllers require an explicit first write
126350b2114SRobert Mustacchi  * to enact the reset behavior. You cannot do things like enable or disable
127350b2114SRobert Mustacchi  * interrupts without doing a write to the PCIe Slot Control register and
128350b2114SRobert Mustacchi  * turning power on. Those are just the breaks from the spec. The spec also
129350b2114SRobert Mustacchi  * doesn't have a way to tell us if power is actually on or not, we just have to
130350b2114SRobert Mustacchi  * hope. All we can see is if we've commanded power on and if a power fault was
131350b2114SRobert Mustacchi  * detected at some point.
132350b2114SRobert Mustacchi  *
133350b2114SRobert Mustacchi  * 2. Because of (1), the normal platform-specific startup logic for PCIe may or
134350b2114SRobert Mustacchi  * may not have found any devices and initialized them depending on at what
135350b2114SRobert Mustacchi  * state in the initialization point it was at.
136350b2114SRobert Mustacchi  *
137350b2114SRobert Mustacchi  * 3. To actually enumerate a hotplug device, our DDI_HPOP_CN_PROBE entry point
138350b2114SRobert Mustacchi  * needs to be called, which is pciehpc_slot_probe(). This will ultimately call
139350b2114SRobert Mustacchi  * pcicfg_configure(). There is a wrinkle here. If we have done (2), we don't
140350b2114SRobert Mustacchi  * want to call the probe entry point. However, if we haven't, then at start up,
141350b2114SRobert Mustacchi  * the broader hotplug unfortunately, won't assume that there is anything to do
142350b2114SRobert Mustacchi  * here to make this happen. The kernel framework won't call this unless it sees
143350b2114SRobert Mustacchi  * a transition from a lesser state to DDI_HP_CN_STATE_ENABLED.
144350b2114SRobert Mustacchi  *
145350b2114SRobert Mustacchi  * The cases described above are not our only problem. In particular, there are
146350b2114SRobert Mustacchi  * some other complications that happen here. In particular, it's worth
147350b2114SRobert Mustacchi  * understanding how we actually keep all of our state in sync. The core idea is
148350b2114SRobert Mustacchi  * that changes are coming from one of two places: either a user has explicitly
149350b2114SRobert Mustacchi  * requested a state change or we have had external activity that has injected a
150350b2114SRobert Mustacchi  * hotplug interrupt. This is due to something such as a surprise insertion,
151350b2114SRobert Mustacchi  * removal, power fault, or similar activity.
152350b2114SRobert Mustacchi  *
153350b2114SRobert Mustacchi  * The general construction and assumption is that we know the valid state at
154350b2114SRobert Mustacchi  * the moment before an interrupt occurs, so then the slot status register will
155350b2114SRobert Mustacchi  * indicate to us what has changed. Once we know what we should transition to,
156350b2114SRobert Mustacchi  * then we will go ahead and ask the system to make a state change request to
157350b2114SRobert Mustacchi  * change our state to a given target. While this is similar in spirit to what a
158350b2114SRobert Mustacchi  * user could request, they could not imitate a state transition to EMPTY. The
159350b2114SRobert Mustacchi  * transition to EMPTY or to ENABLED is what kicks off the probe and unprobe
160350b2114SRobert Mustacchi  * operations.
161350b2114SRobert Mustacchi  *
162350b2114SRobert Mustacchi  * This system is all well and good, but it is dependent on the fact that we
163350b2114SRobert Mustacchi  * have an interrupt enabled for changes and that the various interrupt cause
164350b2114SRobert Mustacchi  * bits in the slot status register have been cleared as they are generally RW1C
165350b2114SRobert Mustacchi  * (read, write 1 to clear). This means that in addition to the issues with case
166350b2114SRobert Mustacchi  * (1) and what firmware has or hasn't done, it is also possible that additional
167350b2114SRobert Mustacchi  * changes may occur without us recording them or noticing them in an interrupt.
168350b2114SRobert Mustacchi  *
169350b2114SRobert Mustacchi  * This steady state is a great place to be, but because of the races we
170350b2114SRobert Mustacchi  * discussed above, we need to do a bit of additional work here to ensure that
171350b2114SRobert Mustacchi  * we can reliably enter it. As such, we're going to address the three
172350b2114SRobert Mustacchi  * complications above in reverse order. If we start with (3), while in the
173350b2114SRobert Mustacchi  * steady state, we basically treat the DDI states as the main states we can
174350b2114SRobert Mustacchi  * transition to and from (again see the pciehpc_slot_power_t comment for the
175350b2114SRobert Mustacchi  * fact that this is somewhat factious). This means that if we are ENABLED, a
176350b2114SRobert Mustacchi  * probe must have happened (or the semi-equivalent in (2)).
177350b2114SRobert Mustacchi  *
178350b2114SRobert Mustacchi  * Normally, we assume that if we got all the way up and have a powered device
179350b2114SRobert Mustacchi  * that the state we should return to the system is ENABLED. However, that only
180350b2114SRobert Mustacchi  * works if we can ensure that the state transition from less than ENABLED to
181350b2114SRobert Mustacchi  * ENABLED occurs so a probe can occur.
182350b2114SRobert Mustacchi  *
183350b2114SRobert Mustacchi  * This window is made larger because of (1) and (2). However, this is not
184350b2114SRobert Mustacchi  * unique to the ENABLED state and these cases can happen by having a device
185350b2114SRobert Mustacchi  * that was probed at initial time be removed prior to the interrupt being
186350b2114SRobert Mustacchi  * enabled. While this is honestly a very tight window and something that may
187350b2114SRobert Mustacchi  * not happen in practice, it highlights many of the things that can occur and
188350b2114SRobert Mustacchi  * that we need to handle.
189350b2114SRobert Mustacchi  *
190350b2114SRobert Mustacchi  * To deal with this we are a little more careful with our startup state. When
191350b2114SRobert Mustacchi  * we reach our module's main initialization entry point for a given controller,
192350b2114SRobert Mustacchi  * pciehpc_init(), we know that at that point (2) has completed. We also know
193350b2114SRobert Mustacchi  * that the interrupt shouldn't be initiated at that point, but that isn't
194350b2114SRobert Mustacchi  * guaranteed until we finish calling the pciehpc_hpc_init() entry point. We
195350b2114SRobert Mustacchi  * subsequently will enable the interrupt via the enable_phc_intr() function
196350b2114SRobert Mustacchi  * pointer, which is called from pcie_hpintr_enable(). This gap is to allow the
197350b2114SRobert Mustacchi  * overall driver (say pcieb) to ensure that it has allocated and attached
198350b2114SRobert Mustacchi  * interrupts prior to us enabling it.
199350b2114SRobert Mustacchi  *
200350b2114SRobert Mustacchi  * At the point that we are initialized, we can look and see if we have any
201350b2114SRobert Mustacchi  * children. If we do, then we know that (2) performed initialization and it's
202350b2114SRobert Mustacchi  * safe for us to set our initial state to ENABLED and allow that to be the
203350b2114SRobert Mustacchi  * first thing the kernel hotplug framework sees, assuming our state would
204350b2114SRobert Mustacchi  * otherwise suggest we'd be here. If we do not see a child device and we have
205350b2114SRobert Mustacchi  * enabled power, then we basically need to mimic the normal act of having
206350b2114SRobert Mustacchi  * transitioned to an ENABLED state. This needs to happen ahead of us first
207350b2114SRobert Mustacchi  * communicating our state to the DDI.
208350b2114SRobert Mustacchi  *
209350b2114SRobert Mustacchi  * The next set of things we need to do happen when we go to enable interrupts.
210350b2114SRobert Mustacchi  * It's worth keeping in mind that at this point the rest of the system is fully
211350b2114SRobert Mustacchi  * operational. One of three events can be injected at this point, a removal,
212350b2114SRobert Mustacchi  * insertion, or a power fault. We also need to consider that a removal or
213350b2114SRobert Mustacchi  * insertion happened in an intervening point. To make this all happen, let's
214350b2114SRobert Mustacchi  * discuss the different pieces that are involved in tracking what's going on:
215350b2114SRobert Mustacchi  *
216350b2114SRobert Mustacchi  * 1) During pciehpc_slotinfo_init() we check to see if we have a child devinfo
217350b2114SRobert Mustacchi  * node or not. We only mark a node as ENABLED if we have a child and it is
218350b2114SRobert Mustacchi  * already POWERED. This ensures that we aren't ahead of ourselves. The normal
219350b2114SRobert Mustacchi  * state determination logic will not put us at enabled prior to being there.
220350b2114SRobert Mustacchi  *
221350b2114SRobert Mustacchi  * 2) We have added a pair of flags to the pcie_hp_ctrl_t called
222350b2114SRobert Mustacchi  * PCIE_HP_SYNC_PENDING and PCIE_HP_SYNC_RUNNING. The former indicates that we
223350b2114SRobert Mustacchi  * have identified that we need to perform a state correction and have
224350b2114SRobert Mustacchi  * dispatched a task to the system taskq to deal with it. The
225350b2114SRobert Mustacchi  * PCIE_HP_SYNC_RUNNING flag is used to indicate that a state transition request
226350b2114SRobert Mustacchi  * is actually being made due to this right now. This is used to tweak a bit of
227350b2114SRobert Mustacchi  * the slot upgrade path, discussed below.
228350b2114SRobert Mustacchi  *
229350b2114SRobert Mustacchi  * 3) Immediately after enabling interrupts, while still holding the hotplug
230350b2114SRobert Mustacchi  * controller mutex, we investigate what our current state is and what we have
231350b2114SRobert Mustacchi  * previously set it to. Depending on the transition that needs to occur and if
232350b2114SRobert Mustacchi  * it has a side effect of needing to probe or unprobe a connection, then we'll
233350b2114SRobert Mustacchi  * end up scheduling a task in the system taskq to perform that transition.
234350b2114SRobert Mustacchi  * Otherwise, we will simply fix up the LED state as we have no reason to
235350b2114SRobert Mustacchi  * believe that it is currently correct for our state.
236350b2114SRobert Mustacchi  *
237350b2114SRobert Mustacchi  * Using the taskq has a major benefit for us in that it allows us to leverage
238350b2114SRobert Mustacchi  * the existing code paths for state transitions. This means that if things are
239350b2114SRobert Mustacchi  * already powered on and the data link layer is active, there won't be any
240350b2114SRobert Mustacchi  * extra delay and if not, it will honor the same 1s timeout, take advantage of
241350b2114SRobert Mustacchi  * the datalink layer active bit if supported, and on failure it will turn off
242350b2114SRobert Mustacchi  * the controller.
243350b2114SRobert Mustacchi  *
244350b2114SRobert Mustacchi  * 4) We are reliant on an important property of pciehpc_get_slot_state(): if it
245350b2114SRobert Mustacchi  * finds itself in the POWERED state, it will not change from that. This is half
246350b2114SRobert Mustacchi  * of the reason that we opt to go to the POWERED state when this occurs. The
247350b2114SRobert Mustacchi  * other half is that it is factually accurate and doing otherwise would get in
248350b2114SRobert Mustacchi  * the way of our logic which attempts to correct the state in
249350b2114SRobert Mustacchi  * pciehpc_change_slot_state() which corrects for the state being incorrect.
250350b2114SRobert Mustacchi  * While it is tempting to use the PRESENT state and try to avoid a special case
251350b2114SRobert Mustacchi  * in pciehpc_upgrade_slot_state(), that ends up breaking more invariants than
252350b2114SRobert Mustacchi  * the logic described below.
253350b2114SRobert Mustacchi  *
254350b2114SRobert Mustacchi  * 5) Finally, when the PCIE_HP_SYNC_RUNNING bit is set, that tells us when
255350b2114SRobert Mustacchi  * we're doing a power on exercise that we need to do so again regardless of
256350b2114SRobert Mustacchi  * what we think we've done. Because of our attempts to try to have things be
257350b2114SRobert Mustacchi  * idempotent, this ends up being a relatively safe operation to perform again
258350b2114SRobert Mustacchi  * and being able to reuse this helps a lot.
259350b2114SRobert Mustacchi  *
260350b2114SRobert Mustacchi  * It is our hope that after this point everything will be in line such that we
261350b2114SRobert Mustacchi  * can enter the steady state. If devices have come or gone, the use of the
262350b2114SRobert Mustacchi  * normal state machine transitions should allow us to get them to be attached
263350b2114SRobert Mustacchi  * or not.
264a9413143SRobert Mustacchi  *
265a9413143SRobert Mustacchi  * LED Management
266a9413143SRobert Mustacchi  * --------------
267a9413143SRobert Mustacchi  *
268a9413143SRobert Mustacchi  * The PCIe specifications define two LEDs that may optionally exist on a slot:
269a9413143SRobert Mustacchi  * a power LED and an attention LED. In the earliest version of the PCIe
270a9413143SRobert Mustacchi  * specification, these were maintained as device capabilities, but by PCIe 1.1
271a9413143SRobert Mustacchi  * they are solely on the slot. The PCIe specification provides very specific
272a9413143SRobert Mustacchi  * meanings for what these LEDs indicate. However, different systems implement a
273a9413143SRobert Mustacchi  * subset of these LEDs and may not want to use them for the same purposes. The
274a9413143SRobert Mustacchi  * PCIe Native HotPlug defined in S6.7 describes the following roles for each
275a9413143SRobert Mustacchi  * LED (and also defines the colors):
276a9413143SRobert Mustacchi  *
277a9413143SRobert Mustacchi  * POWER	Off:	Device not powered
278a9413143SRobert Mustacchi  *		On:	Device powered
279a9413143SRobert Mustacchi  *		Blink:	Power transition occurring
280a9413143SRobert Mustacchi  *
281a9413143SRobert Mustacchi  * ATTENTION	Off:	Everything's fine, how are you?
282a9413143SRobert Mustacchi  *		On:	There's a problem here.
283a9413143SRobert Mustacchi  *		Blink:	Attention
284a9413143SRobert Mustacchi  *
285a9413143SRobert Mustacchi  * While this is the standard, it's not always the case that everything actually
286a9413143SRobert Mustacchi  * has implemented these, that all platforms want to use the same definitions,
287a9413143SRobert Mustacchi  * or even have both LEDs. The above definitions are strictly meant for hotplug,
288a9413143SRobert Mustacchi  * but one could have LEDs without hotplug (though it's rare). In addition,
289a9413143SRobert Mustacchi  * there are cases that the operating system adds additional semantics as to
290a9413143SRobert Mustacchi  * what the LEDs should mean. For example, while holding the attention button,
291a9413143SRobert Mustacchi  * the power LED blinks, or if probe fails, then we assume that attention should
292a9413143SRobert Mustacchi  * be asserted. Finally, there is the unfortunate reality that many systems
293a9413143SRobert Mustacchi  * which implement a more limited form of Enterprise SSD take away LED
294a9413143SRobert Mustacchi  * management from the OS entirely and it is left entirely up to firmware's
295a9413143SRobert Mustacchi  * druthers (possibly allowing for interaction via IPMI or something similar).
296a9413143SRobert Mustacchi  *
297a9413143SRobert Mustacchi  * The next consideration for LED management is that there is an ability for
298a9413143SRobert Mustacchi  * this to be overridden by userland. For example, there is no first class blink
299a9413143SRobert Mustacchi  * or locate ioctl and instead that is something that is created by an operator
300a9413143SRobert Mustacchi  * using libhotplug or say an FMA topology. This means that the state that we
301a9413143SRobert Mustacchi  * have displayed can be overridden at any time. As such, while libhotplug and
302a9413143SRobert Mustacchi  * other consumers may chance the LED state, we instead think of this as an
303a9413143SRobert Mustacchi  * override. When they are done, they can restore the default behavior and we
304a9413143SRobert Mustacchi  * will figure out what that means.
305a9413143SRobert Mustacchi  *
306a9413143SRobert Mustacchi  * For the most part our LED state can be determined based upon the slot's
307a9413143SRobert Mustacchi  * state. However, there are a few specific transitions that can occur that need
308a9413143SRobert Mustacchi  * to be captured like while we are powering, attention button activity, or
309a9413143SRobert Mustacchi  * probe failures. A particular part of this is what are the lifetimes of these
310a9413143SRobert Mustacchi  * and when should they be cleared out. There is a bunch of complexity here that
311a9413143SRobert Mustacchi  * suggests this subsystem should perhaps be driven more by userland (where it's
312a9413143SRobert Mustacchi  * a bit easier to inject what someone wants). For now, here is how we scope
313a9413143SRobert Mustacchi  * those special cases:
314a9413143SRobert Mustacchi  *
315a9413143SRobert Mustacchi  * POWER FAULT		When a power fault occurs, this normally automatically
316a9413143SRobert Mustacchi  *			causes us to power off the device (if there is a power
317a9413143SRobert Mustacchi  *			controller). As such the only things that clear out a
318a9413143SRobert Mustacchi  *			power fault is another attempt to power on the device
319a9413143SRobert Mustacchi  *			(because we have no way of knowing whether or not it's
320a9413143SRobert Mustacchi  *			still faulted, thanks specs) or until the device is
321a9413143SRobert Mustacchi  *			removed.
322a9413143SRobert Mustacchi  *
323a9413143SRobert Mustacchi  * ATTENTION BUTTON	Pushing the attention button is supposed to cause a five
324a9413143SRobert Mustacchi  *			second timer to begin. This state persists across its
325a9413143SRobert Mustacchi  *			entire lifetime. Once that is done, then it will be
326a9413143SRobert Mustacchi  *			turned off. Unlike other things it is not bound to a
327a9413143SRobert Mustacchi  *			power transition.
328a9413143SRobert Mustacchi  *
329a9413143SRobert Mustacchi  * PROBE FAILURE	This is not a PCIe concept, but something that we do.
330a9413143SRobert Mustacchi  *			This is meant to last until a subsequent attempt to
331a9413143SRobert Mustacchi  *			probe the device occurs, the device is powered off, or
332a9413143SRobert Mustacchi  *			the device is removed. This is different from a power
333a9413143SRobert Mustacchi  *			fault in so far as we honor power off as a transition
334a9413143SRobert Mustacchi  *			point here. This was chosen because powering off
335a9413143SRobert Mustacchi  *			represents an active action to try to and change the
336a9413143SRobert Mustacchi  *			state away from probing, so it no longer serves to blink
337a9413143SRobert Mustacchi  *			the LED because of a probe failure.
338a9413143SRobert Mustacchi  *
339a9413143SRobert Mustacchi  * To deal with this, we have a series of logical LED states that can be enabled
340a9413143SRobert Mustacchi  * which are then evaluated in a preferential ordering that describe what to do
341a9413143SRobert Mustacchi  * for each logical item we want to do. These include things like power
342a9413143SRobert Mustacchi  * transitions, the device being powered, or things like the power fault or
343a9413143SRobert Mustacchi  * probe failing. This is tracked in the slot's hs_led_plat_en[] data structure.
344a9413143SRobert Mustacchi  * Higher states are preferential which is why we work through the states
345a9413143SRobert Mustacchi  * backwards. Each state can opt to take an explicit state or punt to the next.
346a9413143SRobert Mustacchi  *
347a9413143SRobert Mustacchi  * We map the logical state to LED actions through a platform-specific set of
348a9413143SRobert Mustacchi  * definitions. Pretty much everything will use the standard PCIe LED behavior
349a9413143SRobert Mustacchi  * (pciehpc_pcie_std_leds[]), but this can be overridden for particular
350a9413143SRobert Mustacchi  * platforms.
351a9413143SRobert Mustacchi  *
352a9413143SRobert Mustacchi  * Because of our startup races, we defer actually setting the external LED
353a9413143SRobert Mustacchi  * state until we first get through pciehpc_enable_state_sync() or a
354a9413143SRobert Mustacchi  * corresponding state transition that it forces to happen.
35526947304SEvan Yan  */
35626947304SEvan Yan 
35726947304SEvan Yan #include <sys/types.h>
35826947304SEvan Yan #include <sys/note.h>
35926947304SEvan Yan #include <sys/conf.h>
36026947304SEvan Yan #include <sys/kmem.h>
36126947304SEvan Yan #include <sys/debug.h>
36226947304SEvan Yan #include <sys/vtrace.h>
36326947304SEvan Yan #include <sys/autoconf.h>
36426947304SEvan Yan #include <sys/varargs.h>
36526947304SEvan Yan #include <sys/ddi_impldefs.h>
36626947304SEvan Yan #include <sys/time.h>
36726947304SEvan Yan #include <sys/callb.h>
36826947304SEvan Yan #include <sys/ddi.h>
36926947304SEvan Yan #include <sys/sunddi.h>
37026947304SEvan Yan #include <sys/sunndi.h>
371a9413143SRobert Mustacchi #include <sys/sysmacros.h>
37226947304SEvan Yan #include <sys/sysevent/dr.h>
37326947304SEvan Yan #include <sys/pci_impl.h>
374*ead3c390SKeith M Wesolowski #include <sys/kstat.h>
375*ead3c390SKeith M Wesolowski #include <sys/zone.h>
37626947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
37726947304SEvan Yan #include <sys/hotplug/pci/pciehpc.h>
37826947304SEvan Yan 
379350b2114SRobert Mustacchi /* XXX /etc/system is NOT a policy interface */
380350b2114SRobert Mustacchi int pcie_auto_online = 1;
381350b2114SRobert Mustacchi 
382350b2114SRobert Mustacchi /*
383350b2114SRobert Mustacchi  * Ideally, it would be possible to represent the state of a slot with a single
384350b2114SRobert Mustacchi  * ddi_hp_cn_state_t; after all, that's the purpose of that data type.
385350b2114SRobert Mustacchi  * Unfortunately it wasn't designed very well and cannot even represent the
386350b2114SRobert Mustacchi  * range of possible power states of a PCIe slot.  It is possible for a slot to
387350b2114SRobert Mustacchi  * be powered on or off with or without a device present, and it is possible for
388350b2114SRobert Mustacchi  * a slot not to have a power controller at all.  Finally, it's possible for a
389350b2114SRobert Mustacchi  * power fault to be detected regardless of whether power is on or off or a
390350b2114SRobert Mustacchi  * device is present or not.  This state attempts to represent all possible
391350b2114SRobert Mustacchi  * power states that a slot can have, which is important for implementing our
392350b2114SRobert Mustacchi  * state machine that has to expose only the very limited DDI states.
393350b2114SRobert Mustacchi  *
394350b2114SRobert Mustacchi  * These are bits that may be ORed together.  Not all combinations comply with
395350b2114SRobert Mustacchi  * the standards, but these definitions were chosen to make it harder to
396350b2114SRobert Mustacchi  * construct invalid combinations.  In particular, if there is no controller,
397350b2114SRobert Mustacchi  * there is also no possibility of the slot being turned off, nor is it possible
398350b2114SRobert Mustacchi  * for there to be a fault.
399350b2114SRobert Mustacchi  */
400350b2114SRobert Mustacchi typedef enum pciehpc_slot_power {
401350b2114SRobert Mustacchi 	PSP_NO_CONTROLLER = 0,
402350b2114SRobert Mustacchi 	PSP_HAS_CONTROLLER = (1U << 0),
403350b2114SRobert Mustacchi 	PSP_OFF = (1U << 1),
404350b2114SRobert Mustacchi 	PSP_FAULT = (1U << 2)
405350b2114SRobert Mustacchi } pciehpc_slot_power_t;
406350b2114SRobert Mustacchi 
407350b2114SRobert Mustacchi typedef struct {
408350b2114SRobert Mustacchi 	pcie_hp_ctrl_t *pst_ctrl;
409350b2114SRobert Mustacchi 	ddi_hp_cn_state_t pst_targ;
410350b2114SRobert Mustacchi 	ddi_hp_cn_state_t pst_cur;
411350b2114SRobert Mustacchi } pciehpc_sync_task_t;
412350b2114SRobert Mustacchi 
413a9413143SRobert Mustacchi static const pciehpc_led_plat_state_t pciehpc_std_leds[PCIEHPC_LED_NSTATES] = {
414a9413143SRobert Mustacchi 	[PCIE_LL_BASE] = { PCIE_HLA_OFF, PCIE_HLA_OFF },
415a9413143SRobert Mustacchi 	[PCIE_LL_POWER_TRANSITION] = { PCIE_HLA_BLINK, PCIE_HLA_PASS },
416a9413143SRobert Mustacchi 	[PCIE_LL_POWERED] = { PCIE_HLA_ON, PCIE_HLA_OFF },
417a9413143SRobert Mustacchi 	[PCIE_LL_PROBE_FAILED] = { PCIE_HLA_PASS, PCIE_HLA_ON },
418a9413143SRobert Mustacchi 	[PCIE_LL_POWER_FAULT] = { PCIE_HLA_BLINK, PCIE_HLA_PASS },
419a9413143SRobert Mustacchi 	[PCIE_LL_ATTENTION_BUTTON] = { PCIE_HLA_BLINK, PCIE_HLA_PASS }
420a9413143SRobert Mustacchi };
421a9413143SRobert Mustacchi 
422a9413143SRobert Mustacchi /*
423a9413143SRobert Mustacchi  * The Gimlet hardware platform only has a single attention LED that is used for
424a9413143SRobert Mustacchi  * multiple purposes.
425a9413143SRobert Mustacchi  */
426a9413143SRobert Mustacchi static const pciehpc_led_plat_state_t pciehpc_gimlet_leds[PCIEHPC_LED_NSTATES] =
427a9413143SRobert Mustacchi {
428a9413143SRobert Mustacchi 	[PCIE_LL_BASE] = { PCIE_HLA_OFF, PCIE_HLA_OFF },
429a9413143SRobert Mustacchi 	[PCIE_LL_POWER_TRANSITION] = { PCIE_HLA_PASS, PCIE_HLA_PASS },
430a9413143SRobert Mustacchi 	[PCIE_LL_POWERED] = { PCIE_HLA_PASS, PCIE_HLA_ON },
431a9413143SRobert Mustacchi 	[PCIE_LL_PROBE_FAILED] = { PCIE_HLA_PASS, PCIE_HLA_BLINK },
432a9413143SRobert Mustacchi 	[PCIE_LL_POWER_FAULT] = { PCIE_HLA_PASS, PCIE_HLA_BLINK },
433a9413143SRobert Mustacchi 	[PCIE_LL_ATTENTION_BUTTON] = { PCIE_HLA_PASS, PCIE_HLA_PASS }
434a9413143SRobert Mustacchi };
435a9413143SRobert Mustacchi 
436a9413143SRobert Mustacchi typedef struct pciehpc_led_plat_map {
437a9413143SRobert Mustacchi 	uint16_t plpm_vid;
438a9413143SRobert Mustacchi 	uint16_t plpm_did;
439a9413143SRobert Mustacchi 	uint16_t plpm_svid;
440a9413143SRobert Mustacchi 	uint16_t plpm_ssys;
441a9413143SRobert Mustacchi 	const pciehpc_led_plat_state_t *plpm_map;
442a9413143SRobert Mustacchi } pciehpc_led_plat_map_t;
443a9413143SRobert Mustacchi 
444a9413143SRobert Mustacchi /*
445a9413143SRobert Mustacchi  * This table represents platforms that have different values than the default
446a9413143SRobert Mustacchi  * PCIe LED usage that should be applied.
447a9413143SRobert Mustacchi  */
448a9413143SRobert Mustacchi static const pciehpc_led_plat_map_t pciehpc_led_plat_map[] = {
449a9413143SRobert Mustacchi 	{ .plpm_vid = 0x1022, .plpm_did = 0x1483, .plpm_svid = 0x1de,
450a9413143SRobert Mustacchi 	    .plpm_ssys = 0xfff9, .plpm_map = pciehpc_gimlet_leds }
451a9413143SRobert Mustacchi };
452a9413143SRobert Mustacchi 
45326947304SEvan Yan /* Local functions prototype */
45426947304SEvan Yan static int pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p);
45526947304SEvan Yan static int pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p);
45626947304SEvan Yan static int pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p);
45726947304SEvan Yan static int pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p);
45826947304SEvan Yan static int pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p);
45926947304SEvan Yan static int pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p);
46026947304SEvan Yan static pcie_hp_ctrl_t *pciehpc_create_controller(dev_info_t *dip);
46126947304SEvan Yan static void pciehpc_destroy_controller(dev_info_t *dip);
46226947304SEvan Yan static int pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p);
46326947304SEvan Yan static int pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p);
46426947304SEvan Yan static int pciehpc_slot_get_property(pcie_hp_slot_t *slot_p,
465a9413143SRobert Mustacchi     uintptr_t arg, uintptr_t rval);
46626947304SEvan Yan static int pciehpc_slot_set_property(pcie_hp_slot_t *slot_p,
467a9413143SRobert Mustacchi     uintptr_t arg, uintptr_t rval);
46826947304SEvan Yan static void pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control);
46926947304SEvan Yan static void pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p);
47026947304SEvan Yan static pcie_hp_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
47126947304SEvan Yan 
47226947304SEvan Yan static int pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
47326947304SEvan Yan     ddi_hp_cn_state_t target_state);
47426947304SEvan Yan static int pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
47526947304SEvan Yan     ddi_hp_cn_state_t target_state);
47626947304SEvan Yan static int pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
47726947304SEvan Yan     ddi_hp_cn_state_t target_state);
47826947304SEvan Yan static int
47926947304SEvan Yan     pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
48026947304SEvan Yan static int
48126947304SEvan Yan     pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result);
48226947304SEvan Yan static int pciehpc_slot_probe(pcie_hp_slot_t *slot_p);
48326947304SEvan Yan static int pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p);
48480dc702dSColin Zou - Sun Microsystems - Beijing China static void pciehpc_handle_power_fault(dev_info_t *dip);
48580dc702dSColin Zou - Sun Microsystems - Beijing China static void pciehpc_power_fault_handler(void *arg);
48626947304SEvan Yan 
48726947304SEvan Yan #ifdef	DEBUG
48826947304SEvan Yan static void pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p);
48926947304SEvan Yan #endif	/* DEBUG */
49026947304SEvan Yan 
491*ead3c390SKeith M Wesolowski typedef struct pciehpc_stat_data {
492*ead3c390SKeith M Wesolowski 	kstat_named_t psd_attnsw_count;
493*ead3c390SKeith M Wesolowski 	kstat_named_t psd_attnsw_ts;
494*ead3c390SKeith M Wesolowski 	kstat_named_t psd_pwrflt_count;
495*ead3c390SKeith M Wesolowski 	kstat_named_t psd_pwrflt_ts;
496*ead3c390SKeith M Wesolowski 	kstat_named_t psd_mrl_chg_count;
497*ead3c390SKeith M Wesolowski 	kstat_named_t psd_mrl_chg_ts;
498*ead3c390SKeith M Wesolowski 	kstat_named_t psd_pres_chg_count;
499*ead3c390SKeith M Wesolowski 	kstat_named_t psd_pres_chg_ts;
500*ead3c390SKeith M Wesolowski 	kstat_named_t psd_cmd_cpl_count;
501*ead3c390SKeith M Wesolowski 	kstat_named_t psd_cmd_cpl_ts;
502*ead3c390SKeith M Wesolowski 	kstat_named_t psd_dll_chg_count;
503*ead3c390SKeith M Wesolowski 	kstat_named_t psd_dll_chg_ts;
504*ead3c390SKeith M Wesolowski 	kstat_named_t psd_intr_count;
505*ead3c390SKeith M Wesolowski 	kstat_named_t psd_intr_ts;
506*ead3c390SKeith M Wesolowski } pciehpc_stat_data_t;
507*ead3c390SKeith M Wesolowski 
508*ead3c390SKeith M Wesolowski static inline void
pciehpc_kstat_event(kstat_named_t * countp,kstat_named_t * tsp,hrtime_t now)509*ead3c390SKeith M Wesolowski pciehpc_kstat_event(kstat_named_t *countp, kstat_named_t *tsp, hrtime_t now)
510*ead3c390SKeith M Wesolowski {
511*ead3c390SKeith M Wesolowski 	atomic_inc_64(&countp->value.ui64);
512*ead3c390SKeith M Wesolowski 	tsp->value.ui64 = now;
513*ead3c390SKeith M Wesolowski }
514*ead3c390SKeith M Wesolowski 
515*ead3c390SKeith M Wesolowski #define	KSTAT_EVENT(_slotp, _evname, _now)	\
516*ead3c390SKeith M Wesolowski     pciehpc_kstat_event((&(_slotp)->hs_stat_data->psd_ ## _evname ## _count), \
517*ead3c390SKeith M Wesolowski 	(&(_slotp)->hs_stat_data->psd_ ## _evname ## _ts), (_now))
518*ead3c390SKeith M Wesolowski 
51926947304SEvan Yan /*
52026947304SEvan Yan  * Global functions (called by other drivers/modules)
52126947304SEvan Yan  */
52226947304SEvan Yan 
52326947304SEvan Yan /*
52426947304SEvan Yan  * Initialize Hot Plug Controller if present. The arguments are:
525a9413143SRobert Mustacchi  *	dip	- devinfo node pointer to the hot plug bus node
52626947304SEvan Yan  *	regops	- register ops to access HPC registers for non-standard
527a9413143SRobert Mustacchi  *		  HPC hw implementations (e.g: HPC in host PCI-E bridges)
52826947304SEvan Yan  *		  This is NULL for standard HPC in PCIe bridges.
52926947304SEvan Yan  * Returns:
53026947304SEvan Yan  *	DDI_SUCCESS for successful HPC initialization
53126947304SEvan Yan  *	DDI_FAILURE for errors or if HPC hw not found
53226947304SEvan Yan  */
53326947304SEvan Yan int
pciehpc_init(dev_info_t * dip,caddr_t arg)53426947304SEvan Yan pciehpc_init(dev_info_t *dip, caddr_t arg)
53526947304SEvan Yan {
53626947304SEvan Yan 	pcie_hp_regops_t	*regops = (pcie_hp_regops_t *)(void *)arg;
53726947304SEvan Yan 	pcie_hp_ctrl_t		*ctrl_p;
53826947304SEvan Yan 
53926947304SEvan Yan 	PCIE_DBG("pciehpc_init() called (dip=%p)\n", (void *)dip);
54026947304SEvan Yan 
54126947304SEvan Yan 	/* Make sure that it is not already initialized */
54226947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
54326947304SEvan Yan 		PCIE_DBG("%s%d: pciehpc instance already initialized!\n",
54426947304SEvan Yan 		    ddi_driver_name(dip), ddi_get_instance(dip));
54526947304SEvan Yan 		return (DDI_SUCCESS);
54626947304SEvan Yan 	}
54726947304SEvan Yan 
54826947304SEvan Yan 	/* Allocate a new hotplug controller and slot structures */
54926947304SEvan Yan 	ctrl_p = pciehpc_create_controller(dip);
55026947304SEvan Yan 
55126947304SEvan Yan 	/* setup access handle for HPC regs */
55226947304SEvan Yan 	if (regops != NULL) {
55326947304SEvan Yan 		/* HPC access is non-standard; use the supplied reg ops */
55426947304SEvan Yan 		ctrl_p->hc_regops = *regops;
55526947304SEvan Yan 	}
55626947304SEvan Yan 
55726947304SEvan Yan 	/*
55826947304SEvan Yan 	 * Setup resource maps for this bus node.
55926947304SEvan Yan 	 */
56026947304SEvan Yan 	(void) pci_resource_setup(dip);
56126947304SEvan Yan 
56226947304SEvan Yan 	PCIE_DISABLE_ERRORS(dip);
56326947304SEvan Yan 
56426947304SEvan Yan 	/*
56526947304SEvan Yan 	 * Set the platform specific hot plug mode.
56626947304SEvan Yan 	 */
56726947304SEvan Yan 	ctrl_p->hc_ops.init_hpc_hw = pciehpc_hpc_init;
56826947304SEvan Yan 	ctrl_p->hc_ops.uninit_hpc_hw = pciehpc_hpc_uninit;
56926947304SEvan Yan 	ctrl_p->hc_ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
57026947304SEvan Yan 	ctrl_p->hc_ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
57126947304SEvan Yan 	ctrl_p->hc_ops.poweron_hpc_slot = pciehpc_slot_poweron;
57226947304SEvan Yan 	ctrl_p->hc_ops.poweroff_hpc_slot = pciehpc_slot_poweroff;
57326947304SEvan Yan 
57426947304SEvan Yan 	ctrl_p->hc_ops.enable_hpc_intr = pciehpc_enable_intr;
57526947304SEvan Yan 	ctrl_p->hc_ops.disable_hpc_intr = pciehpc_disable_intr;
57626947304SEvan Yan 
57786ef0a63SRichard Lowe #if	defined(__x86)
57826947304SEvan Yan 	pciehpc_update_ops(ctrl_p);
57926947304SEvan Yan #endif
58026947304SEvan Yan 
58126947304SEvan Yan 	/* initialize hot plug controller hw */
58226947304SEvan Yan 	if ((ctrl_p->hc_ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
58326947304SEvan Yan 		goto cleanup1;
58426947304SEvan Yan 
58526947304SEvan Yan 	/* initialize slot information soft state structure */
58626947304SEvan Yan 	if ((ctrl_p->hc_ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
58726947304SEvan Yan 		goto cleanup2;
58826947304SEvan Yan 
58926947304SEvan Yan 	/* register the hot plug slot with DDI HP framework */
59026947304SEvan Yan 	if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
59126947304SEvan Yan 		goto cleanup3;
59226947304SEvan Yan 
59326947304SEvan Yan 	/* create minor node for this slot */
59426947304SEvan Yan 	if (pcie_create_minor_node(ctrl_p, 0) != DDI_SUCCESS)
59526947304SEvan Yan 		goto cleanup4;
59626947304SEvan Yan 
597350b2114SRobert Mustacchi 	/*
598350b2114SRobert Mustacchi 	 * While we disabled errors upon entry, if we were initialized and
599350b2114SRobert Mustacchi 	 * entered the ENABLED state that indicates we have children and
600350b2114SRobert Mustacchi 	 * therefore we should go back and enable errors.
601350b2114SRobert Mustacchi 	 */
602350b2114SRobert Mustacchi 	if (ctrl_p->hc_slots[0]->hs_info.cn_state == DDI_HP_CN_STATE_ENABLED) {
603350b2114SRobert Mustacchi 		PCIE_ENABLE_ERRORS(dip);
604350b2114SRobert Mustacchi 	}
605350b2114SRobert Mustacchi 
60626947304SEvan Yan 	/* HPC initialization is complete now */
607350b2114SRobert Mustacchi 	ctrl_p->hc_flags |= PCIE_HP_INITIALIZED_FLAG;
60826947304SEvan Yan 
60926947304SEvan Yan #ifdef	DEBUG
61026947304SEvan Yan 	/* For debug, dump the HPC registers */
61126947304SEvan Yan 	pciehpc_dump_hpregs(ctrl_p);
61226947304SEvan Yan #endif	/* DEBUG */
61326947304SEvan Yan 
61426947304SEvan Yan 	return (DDI_SUCCESS);
61526947304SEvan Yan cleanup4:
61626947304SEvan Yan 	(void) pciehpc_unregister_slot(ctrl_p);
61726947304SEvan Yan cleanup3:
61826947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
61926947304SEvan Yan 
62026947304SEvan Yan cleanup2:
62126947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
62226947304SEvan Yan 
62326947304SEvan Yan cleanup1:
62426947304SEvan Yan 	PCIE_ENABLE_ERRORS(dip);
62526947304SEvan Yan 	(void) pci_resource_destroy(dip);
62626947304SEvan Yan 
62726947304SEvan Yan 	pciehpc_destroy_controller(dip);
62826947304SEvan Yan 	return (DDI_FAILURE);
62926947304SEvan Yan }
63026947304SEvan Yan 
63126947304SEvan Yan /*
63226947304SEvan Yan  * Uninitialize HPC soft state structure and free up any resources
63326947304SEvan Yan  * used for the HPC instance.
63426947304SEvan Yan  */
63526947304SEvan Yan int
pciehpc_uninit(dev_info_t * dip)63626947304SEvan Yan pciehpc_uninit(dev_info_t *dip)
63726947304SEvan Yan {
63826947304SEvan Yan 	pcie_hp_ctrl_t *ctrl_p;
639350b2114SRobert Mustacchi 	taskqid_t id;
64026947304SEvan Yan 
64126947304SEvan Yan 	PCIE_DBG("pciehpc_uninit() called (dip=%p)\n", (void *)dip);
64226947304SEvan Yan 
64326947304SEvan Yan 	/* get the soft state structure for this dip */
64426947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
64526947304SEvan Yan 		return (DDI_FAILURE);
64626947304SEvan Yan 	}
64726947304SEvan Yan 
648350b2114SRobert Mustacchi 	/*
649350b2114SRobert Mustacchi 	 * Prior to taking any action, we want to remove the initialized flag.
650350b2114SRobert Mustacchi 	 * Any interrupts should have already been quiesced prior to this. There
651350b2114SRobert Mustacchi 	 * may be an outstanding startup synchronization timeout(9F) call.
652350b2114SRobert Mustacchi 	 */
653350b2114SRobert Mustacchi 	mutex_enter(&ctrl_p->hc_mutex);
654350b2114SRobert Mustacchi 	ctrl_p->hc_flags &= ~PCIE_HP_INITIALIZED_FLAG;
655350b2114SRobert Mustacchi 	id = ctrl_p->hc_startup_sync;
656350b2114SRobert Mustacchi 	ctrl_p->hc_startup_sync = TASKQID_INVALID;
657350b2114SRobert Mustacchi 	mutex_exit(&ctrl_p->hc_mutex);
658350b2114SRobert Mustacchi 
659350b2114SRobert Mustacchi 	if (id != TASKQID_INVALID)
660350b2114SRobert Mustacchi 		taskq_wait_id(system_taskq, id);
661350b2114SRobert Mustacchi 
66226947304SEvan Yan 	pcie_remove_minor_node(ctrl_p, 0);
66326947304SEvan Yan 
66426947304SEvan Yan 	/* unregister the slot */
66526947304SEvan Yan 	(void) pciehpc_unregister_slot(ctrl_p);
66626947304SEvan Yan 
66726947304SEvan Yan 	/* uninit any slot info data structures */
66826947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_slotinfo)(ctrl_p);
66926947304SEvan Yan 
67026947304SEvan Yan 	/* uninitialize hpc, remove interrupt handler, etc. */
67126947304SEvan Yan 	(void) (ctrl_p->hc_ops.uninit_hpc_hw)(ctrl_p);
67226947304SEvan Yan 
67326947304SEvan Yan 	PCIE_ENABLE_ERRORS(dip);
67426947304SEvan Yan 
67526947304SEvan Yan 	/*
67626947304SEvan Yan 	 * Destroy resource maps for this bus node.
67726947304SEvan Yan 	 */
67826947304SEvan Yan 	(void) pci_resource_destroy(dip);
67926947304SEvan Yan 
68026947304SEvan Yan 	/* destroy the soft state structure */
68126947304SEvan Yan 	pciehpc_destroy_controller(dip);
68226947304SEvan Yan 
68326947304SEvan Yan 	return (DDI_SUCCESS);
68426947304SEvan Yan }
68526947304SEvan Yan 
68626947304SEvan Yan /*
68726947304SEvan Yan  * pciehpc_intr()
68826947304SEvan Yan  *
68926947304SEvan Yan  * Interrupt handler for PCI-E Hot plug controller interrupts.
69026947304SEvan Yan  *
69126947304SEvan Yan  * Note: This is only for native mode hot plug. This is called
69226947304SEvan Yan  * by the nexus driver at interrupt context. Interrupt Service Routine
69326947304SEvan Yan  * registration is done by the nexus driver for both hot plug and
69426947304SEvan Yan  * non-hot plug interrupts. This function is called from the ISR
69526947304SEvan Yan  * of the nexus driver to handle hot-plug interrupts.
696350b2114SRobert Mustacchi  *
697350b2114SRobert Mustacchi  * We must check whether or not we have a pending synchronization event and if
698350b2114SRobert Mustacchi  * so, cancel it. In particular, there are several cases that may cause us to
699350b2114SRobert Mustacchi  * request an asynchronous state transition (e.g. a drive was removed or
700350b2114SRobert Mustacchi  * inserted). When that occurs, we effectively cancel the pending
701350b2114SRobert Mustacchi  * synchronization taskq activity. It will still execute, but do nothing. If it
702350b2114SRobert Mustacchi  * has already started executing, then its state change request will already
703350b2114SRobert Mustacchi  * have been dispatched and we let things shake out with the additional logic we
704350b2114SRobert Mustacchi  * have present in pciehpc_change_slot_state().
70526947304SEvan Yan  */
70626947304SEvan Yan int
pciehpc_intr(dev_info_t * dip)70726947304SEvan Yan pciehpc_intr(dev_info_t *dip)
70826947304SEvan Yan {
70926947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
71026947304SEvan Yan 	pcie_hp_slot_t	*slot_p;
71126947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
71226947304SEvan Yan 	uint16_t	status, control;
713350b2114SRobert Mustacchi 	boolean_t	clear_pend = B_FALSE;
714*ead3c390SKeith M Wesolowski 	hrtime_t	now;
71526947304SEvan Yan 
71626947304SEvan Yan 	/* get the soft state structure for this dip */
71726947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
71826947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
71926947304SEvan Yan 
720*ead3c390SKeith M Wesolowski 	now = gethrtime();
721*ead3c390SKeith M Wesolowski 
72226947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
72326947304SEvan Yan 
72426947304SEvan Yan 	/* make sure the controller soft state is initialized */
72526947304SEvan Yan 	if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
72626947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
72726947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
72826947304SEvan Yan 	}
72926947304SEvan Yan 
73026947304SEvan Yan 	/* if it is not NATIVE hot plug mode then return */
73126947304SEvan Yan 	if (bus_p->bus_hp_curr_mode != PCIE_NATIVE_HP_MODE) {
73226947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
73326947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
73426947304SEvan Yan 	}
73526947304SEvan Yan 
73626947304SEvan Yan 	slot_p = ctrl_p->hc_slots[0];
73726947304SEvan Yan 
73826947304SEvan Yan 	/* read the current slot status register */
73926947304SEvan Yan 	status = pciehpc_reg_get16(ctrl_p,
74026947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
74126947304SEvan Yan 
74226947304SEvan Yan 	/* check if there are any hot plug interrupts occurred */
74326947304SEvan Yan 	if (!(status & PCIE_SLOTSTS_STATUS_EVENTS)) {
74426947304SEvan Yan 		/* no hot plug events occurred */
74526947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
74626947304SEvan Yan 		return (DDI_INTR_UNCLAIMED);
74726947304SEvan Yan 	}
74826947304SEvan Yan 
749*ead3c390SKeith M Wesolowski 	/* We are now committed to claiming the interrupt; record it. */
750*ead3c390SKeith M Wesolowski 	KSTAT_EVENT(slot_p, intr, now);
751*ead3c390SKeith M Wesolowski 
75226947304SEvan Yan 	/* clear the interrupt status bits */
75326947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
75426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
75526947304SEvan Yan 
75626947304SEvan Yan 	/* check for CMD COMPLETE interrupt */
75726947304SEvan Yan 	if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
758*ead3c390SKeith M Wesolowski 		KSTAT_EVENT(slot_p, cmd_cpl, now);
75926947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): CMD COMPLETED interrupt received\n");
76026947304SEvan Yan 		/* wake up any one waiting for Command Completion event */
76126947304SEvan Yan 		cv_signal(&ctrl_p->hc_cmd_comp_cv);
76226947304SEvan Yan 	}
76326947304SEvan Yan 
76426947304SEvan Yan 	/* check for ATTN button interrupt */
76526947304SEvan Yan 	if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
766*ead3c390SKeith M Wesolowski 		KSTAT_EVENT(slot_p, attnsw, now);
76726947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): ATTN BUTTON interrupt received\n");
76826947304SEvan Yan 
76926947304SEvan Yan 		/* if ATTN button event is still pending then cancel it */
77026947304SEvan Yan 		if (slot_p->hs_attn_btn_pending == B_TRUE)
77126947304SEvan Yan 			slot_p->hs_attn_btn_pending = B_FALSE;
77226947304SEvan Yan 		else
77326947304SEvan Yan 			slot_p->hs_attn_btn_pending = B_TRUE;
77426947304SEvan Yan 
77526947304SEvan Yan 		/* wake up the ATTN event handler */
77626947304SEvan Yan 		cv_signal(&slot_p->hs_attn_btn_cv);
77726947304SEvan Yan 	}
77826947304SEvan Yan 
77926947304SEvan Yan 	/* check for power fault interrupt */
78026947304SEvan Yan 	if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
781*ead3c390SKeith M Wesolowski 		KSTAT_EVENT(slot_p, pwrflt, now);
78226947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): POWER FAULT interrupt received"
78326947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
78426947304SEvan Yan 		control =  pciehpc_reg_get16(ctrl_p,
78526947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCTL);
78626947304SEvan Yan 
78726947304SEvan Yan 		if (control & PCIE_SLOTCTL_PWR_FAULT_EN) {
78826947304SEvan Yan 			slot_p->hs_condition = AP_COND_FAILED;
78926947304SEvan Yan 
790350b2114SRobert Mustacchi 			/* disable power fault detection interrupt */
79126947304SEvan Yan 			pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
79226947304SEvan Yan 			    PCIE_SLOTCTL, control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
79326947304SEvan Yan 
79480dc702dSColin Zou - Sun Microsystems - Beijing China 			pciehpc_handle_power_fault(dip);
795350b2114SRobert Mustacchi 			clear_pend = B_TRUE;
79626947304SEvan Yan 		}
79726947304SEvan Yan 	}
79826947304SEvan Yan 
79926947304SEvan Yan 	/* check for MRL SENSOR CHANGED interrupt */
80026947304SEvan Yan 	if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
801*ead3c390SKeith M Wesolowski 		KSTAT_EVENT(slot_p, mrl_chg, now);
80226947304SEvan Yan 		/* For now (phase-I), no action is taken on this event */
80326947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
80426947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
80526947304SEvan Yan 	}
80626947304SEvan Yan 
80726947304SEvan Yan 	/* check for PRESENCE CHANGED interrupt */
80826947304SEvan Yan 	if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
809*ead3c390SKeith M Wesolowski 		KSTAT_EVENT(slot_p, pres_chg, now);
81026947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): PRESENCE CHANGED interrupt received"
81126947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
81226947304SEvan Yan 
81326947304SEvan Yan 		if (status & PCIE_SLOTSTS_PRESENCE_DETECTED) {
814350b2114SRobert Mustacchi 			ddi_hp_cn_state_t tgt_state = (pcie_auto_online != 0) ?
815350b2114SRobert Mustacchi 			    DDI_HP_CN_STATE_ENABLED : DDI_HP_CN_STATE_PRESENT;
81626947304SEvan Yan 			/*
81726947304SEvan Yan 			 * card is inserted into the slot, ask DDI Hotplug
81826947304SEvan Yan 			 * framework to change state to Present.
81926947304SEvan Yan 			 */
82070f83219SEvan Yan 			cmn_err(CE_NOTE, "pciehpc (%s%d): card is inserted"
82170f83219SEvan Yan 			    " in the slot %s",
82270f83219SEvan Yan 			    ddi_driver_name(dip),
82370f83219SEvan Yan 			    ddi_get_instance(dip),
82470f83219SEvan Yan 			    slot_p->hs_info.cn_name);
82570f83219SEvan Yan 
82626947304SEvan Yan 			(void) ndi_hp_state_change_req(dip,
82726947304SEvan Yan 			    slot_p->hs_info.cn_name,
828350b2114SRobert Mustacchi 			    tgt_state, DDI_HP_REQ_ASYNC);
82926947304SEvan Yan 		} else { /* card is removed from the slot */
83026947304SEvan Yan 			cmn_err(CE_NOTE, "pciehpc (%s%d): card is removed"
83126947304SEvan Yan 			    " from the slot %s",
83226947304SEvan Yan 			    ddi_driver_name(dip),
83326947304SEvan Yan 			    ddi_get_instance(dip),
83426947304SEvan Yan 			    slot_p->hs_info.cn_name);
83526947304SEvan Yan 
83626947304SEvan Yan 			if (slot_p->hs_info.cn_state ==
83726947304SEvan Yan 			    DDI_HP_CN_STATE_ENABLED) {
83826947304SEvan Yan 				/* Card is removed when slot is enabled */
83926947304SEvan Yan 				slot_p->hs_condition = AP_COND_FAILED;
84026947304SEvan Yan 			} else {
84126947304SEvan Yan 				slot_p->hs_condition = AP_COND_UNKNOWN;
84226947304SEvan Yan 			}
84326947304SEvan Yan 			/* make sure to disable power fault detction intr */
84426947304SEvan Yan 			control =  pciehpc_reg_get16(ctrl_p,
84526947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
84626947304SEvan Yan 
84726947304SEvan Yan 			if (control & PCIE_SLOTCTL_PWR_FAULT_EN)
84826947304SEvan Yan 				pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
84926947304SEvan Yan 				    PCIE_SLOTCTL,
85026947304SEvan Yan 				    control & ~PCIE_SLOTCTL_PWR_FAULT_EN);
85126947304SEvan Yan 
85226947304SEvan Yan 			/*
853ffb64830SJordan Paige Hendricks 			 * If supported, notify the child device driver that the
854ffb64830SJordan Paige Hendricks 			 * device is being removed.
855ffb64830SJordan Paige Hendricks 			 */
856ffb64830SJordan Paige Hendricks 			dev_info_t *cdip = ddi_get_child(dip);
857ffb64830SJordan Paige Hendricks 			if (cdip != NULL) {
858ffb64830SJordan Paige Hendricks 				ddi_eventcookie_t rm_cookie;
859ffb64830SJordan Paige Hendricks 				if (ddi_get_eventcookie(cdip,
860ffb64830SJordan Paige Hendricks 				    DDI_DEVI_REMOVE_EVENT,
861ffb64830SJordan Paige Hendricks 				    &rm_cookie) == DDI_SUCCESS) {
862ffb64830SJordan Paige Hendricks 					ndi_post_event(dip, cdip, rm_cookie,
863ffb64830SJordan Paige Hendricks 					    NULL);
864ffb64830SJordan Paige Hendricks 				}
865ffb64830SJordan Paige Hendricks 			}
866ffb64830SJordan Paige Hendricks 
867ffb64830SJordan Paige Hendricks 			/*
86826947304SEvan Yan 			 * Ask DDI Hotplug framework to change state to Empty
86926947304SEvan Yan 			 */
87026947304SEvan Yan 			(void) ndi_hp_state_change_req(dip,
87126947304SEvan Yan 			    slot_p->hs_info.cn_name,
87226947304SEvan Yan 			    DDI_HP_CN_STATE_EMPTY,
87326947304SEvan Yan 			    DDI_HP_REQ_ASYNC);
87426947304SEvan Yan 		}
875350b2114SRobert Mustacchi 
876350b2114SRobert Mustacchi 		clear_pend = B_TRUE;
87726947304SEvan Yan 	}
87826947304SEvan Yan 
87926947304SEvan Yan 	/* check for DLL state changed interrupt */
88026947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep &&
88126947304SEvan Yan 	    (status & PCIE_SLOTSTS_DLL_STATE_CHANGED)) {
882*ead3c390SKeith M Wesolowski 		KSTAT_EVENT(slot_p, dll_chg, now);
88326947304SEvan Yan 		PCIE_DBG("pciehpc_intr(): DLL STATE CHANGED interrupt received"
88426947304SEvan Yan 		    " on slot %d\n", slot_p->hs_phy_slot_num);
88526947304SEvan Yan 
88626947304SEvan Yan 		cv_signal(&slot_p->hs_dll_active_cv);
88726947304SEvan Yan 	}
88826947304SEvan Yan 
889350b2114SRobert Mustacchi 	if (clear_pend) {
890350b2114SRobert Mustacchi 		ctrl_p->hc_flags &= ~PCIE_HP_SYNC_PENDING;
891350b2114SRobert Mustacchi 	}
89226947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
89326947304SEvan Yan 
89426947304SEvan Yan 	return (DDI_INTR_CLAIMED);
89526947304SEvan Yan }
89626947304SEvan Yan 
89726947304SEvan Yan /*
89826947304SEvan Yan  * Handle hotplug commands
89926947304SEvan Yan  *
90026947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
90126947304SEvan Yan  */
90226947304SEvan Yan int
pciehpc_hp_ops(dev_info_t * dip,char * cn_name,ddi_hp_op_t op,void * arg,void * result)90326947304SEvan Yan pciehpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
90426947304SEvan Yan     void *arg, void *result)
90526947304SEvan Yan {
90626947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
90726947304SEvan Yan 	pcie_hp_slot_t	*slot_p;
90826947304SEvan Yan 	int		ret = DDI_SUCCESS;
90926947304SEvan Yan 
91026947304SEvan Yan 	PCIE_DBG("pciehpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
91126947304SEvan Yan 	    dip, cn_name, op, arg);
91226947304SEvan Yan 
91326947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
91426947304SEvan Yan 		return (DDI_FAILURE);
91526947304SEvan Yan 
91626947304SEvan Yan 	slot_p = ctrl_p->hc_slots[0];
91726947304SEvan Yan 
91826947304SEvan Yan 	if (strcmp(cn_name, slot_p->hs_info.cn_name) != 0)
91926947304SEvan Yan 		return (DDI_EINVAL);
92026947304SEvan Yan 
92126947304SEvan Yan 	switch (op) {
92226947304SEvan Yan 	case DDI_HPOP_CN_GET_STATE:
92326947304SEvan Yan 	{
92426947304SEvan Yan 		mutex_enter(&slot_p->hs_ctrl->hc_mutex);
92526947304SEvan Yan 
92626947304SEvan Yan 		/* get the current slot state */
92726947304SEvan Yan 		pciehpc_get_slot_state(slot_p);
92826947304SEvan Yan 
92926947304SEvan Yan 		*((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
93026947304SEvan Yan 
93126947304SEvan Yan 		mutex_exit(&slot_p->hs_ctrl->hc_mutex);
93226947304SEvan Yan 		break;
93326947304SEvan Yan 	}
93426947304SEvan Yan 	case DDI_HPOP_CN_CHANGE_STATE:
93526947304SEvan Yan 	{
93626947304SEvan Yan 		ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
93726947304SEvan Yan 
93826947304SEvan Yan 		mutex_enter(&slot_p->hs_ctrl->hc_mutex);
93926947304SEvan Yan 
94026947304SEvan Yan 		ret = pciehpc_change_slot_state(slot_p, target_state);
94126947304SEvan Yan 		*(ddi_hp_cn_state_t *)result = slot_p->hs_info.cn_state;
94226947304SEvan Yan 
94326947304SEvan Yan 		mutex_exit(&slot_p->hs_ctrl->hc_mutex);
94426947304SEvan Yan 		break;
94526947304SEvan Yan 	}
94626947304SEvan Yan 	case DDI_HPOP_CN_PROBE:
94726947304SEvan Yan 
94826947304SEvan Yan 		ret = pciehpc_slot_probe(slot_p);
94926947304SEvan Yan 
95026947304SEvan Yan 		break;
95126947304SEvan Yan 	case DDI_HPOP_CN_UNPROBE:
95226947304SEvan Yan 		ret = pciehpc_slot_unprobe(slot_p);
95326947304SEvan Yan 
95426947304SEvan Yan 		break;
95526947304SEvan Yan 	case DDI_HPOP_CN_GET_PROPERTY:
956a9413143SRobert Mustacchi 		ret = pciehpc_slot_get_property(slot_p, (uintptr_t)arg,
957a9413143SRobert Mustacchi 		    (uintptr_t)result);
95826947304SEvan Yan 		break;
95926947304SEvan Yan 	case DDI_HPOP_CN_SET_PROPERTY:
960a9413143SRobert Mustacchi 		ret = pciehpc_slot_set_property(slot_p, (uintptr_t)arg,
961a9413143SRobert Mustacchi 		    (uintptr_t)result);
96226947304SEvan Yan 		break;
96326947304SEvan Yan 	default:
96426947304SEvan Yan 		ret = DDI_ENOTSUP;
96526947304SEvan Yan 		break;
96626947304SEvan Yan 	}
96726947304SEvan Yan 
96826947304SEvan Yan 	return (ret);
96926947304SEvan Yan }
97026947304SEvan Yan 
97126947304SEvan Yan /*
97226947304SEvan Yan  * Get the current state of the slot from the hw.
97326947304SEvan Yan  *
97426947304SEvan Yan  * The slot state should have been initialized before this function gets called.
97526947304SEvan Yan  */
97626947304SEvan Yan void
pciehpc_get_slot_state(pcie_hp_slot_t * slot_p)97726947304SEvan Yan pciehpc_get_slot_state(pcie_hp_slot_t *slot_p)
97826947304SEvan Yan {
97926947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
98026947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
98126947304SEvan Yan 	uint16_t	control, status;
98226947304SEvan Yan 	ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
98326947304SEvan Yan 
984350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
985350b2114SRobert Mustacchi 
98626947304SEvan Yan 	/* read the Slot Control Register */
98726947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
98826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
98926947304SEvan Yan 
990a9413143SRobert Mustacchi 	/*
991a9413143SRobert Mustacchi 	 * These LEDs are always set to off because they don't exist. They are
992a9413143SRobert Mustacchi 	 * only here because the data structure is shared with the PCI SHPC
993a9413143SRobert Mustacchi 	 * logic. We do not pretend to implement it.
994a9413143SRobert Mustacchi 	 */
995a9413143SRobert Mustacchi 	slot_p->hs_fault_led_state = PCIE_HP_LED_OFF;
996a9413143SRobert Mustacchi 	slot_p->hs_active_led_state = PCIE_HP_LED_OFF;
99726947304SEvan Yan 
99826947304SEvan Yan 	/* read the current Slot Status Register */
99926947304SEvan Yan 	status = pciehpc_reg_get16(ctrl_p,
100026947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
100126947304SEvan Yan 
100226947304SEvan Yan 	/* get POWER led state */
100326947304SEvan Yan 	slot_p->hs_power_led_state =
100426947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
100526947304SEvan Yan 
100626947304SEvan Yan 	/* get ATTN led state */
100726947304SEvan Yan 	slot_p->hs_attn_led_state =
100826947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
100926947304SEvan Yan 
101026947304SEvan Yan 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
101126947304SEvan Yan 		/* no device present; slot is empty */
101226947304SEvan Yan 		slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
101326947304SEvan Yan 		return;
101426947304SEvan Yan 	}
101526947304SEvan Yan 
101626947304SEvan Yan 	/* device is present */
101726947304SEvan Yan 	slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
101826947304SEvan Yan 
101926947304SEvan Yan 	/*
1020350b2114SRobert Mustacchi 	 * If we have power control and power control is disabled, then we are
1021350b2114SRobert Mustacchi 	 * merely present. We cannot be POWERED or ENABLED without this being
1022350b2114SRobert Mustacchi 	 * active.
102326947304SEvan Yan 	 */
1024350b2114SRobert Mustacchi 	if (ctrl_p->hc_has_pwr && (control & PCIE_SLOTCTL_PWR_CONTROL) != 0) {
1025350b2114SRobert Mustacchi 		return;
1026350b2114SRobert Mustacchi 	}
1027350b2114SRobert Mustacchi 
1028350b2114SRobert Mustacchi 	/*
1029350b2114SRobert Mustacchi 	 * To be in the ENABLED state that means that we have verified that the
1030350b2114SRobert Mustacchi 	 * device is ready to be used. This happens at different points in time
1031350b2114SRobert Mustacchi 	 * right now depending on whether or not we have a power controller and
1032350b2114SRobert Mustacchi 	 * should be consolidated in the future. Our main constraint is that the
1033350b2114SRobert Mustacchi 	 * kernel expects that when something is in the ENABLED state that probe
1034350b2114SRobert Mustacchi 	 * should succeed.
1035350b2114SRobert Mustacchi 	 *
1036350b2114SRobert Mustacchi 	 * For devices with a power controller, this is guaranteed as part of
1037350b2114SRobert Mustacchi 	 * the PRESENT->POWERED transition. For devices without a power
1038350b2114SRobert Mustacchi 	 * controller, we must assume that power is always applied (the slot
1039350b2114SRobert Mustacchi 	 * control register bit for power status is undefined). This means that
1040350b2114SRobert Mustacchi 	 * the POWERED->ENABLED transition point is where this occurs.
1041350b2114SRobert Mustacchi 	 *
1042350b2114SRobert Mustacchi 	 * This is all a long way of justifying the logic below. If our current
1043350b2114SRobert Mustacchi 	 * state is enabled then we will stay in enabled; however, if it is
1044350b2114SRobert Mustacchi 	 * anything else we will go to powered and allow the normal state
1045350b2114SRobert Mustacchi 	 * transition to take effect.
1046350b2114SRobert Mustacchi 	 */
1047350b2114SRobert Mustacchi 	if (curr_state == DDI_HP_CN_STATE_ENABLED) {
104826947304SEvan Yan 		slot_p->hs_info.cn_state = curr_state;
104926947304SEvan Yan 	} else {
1050350b2114SRobert Mustacchi 		slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
105126947304SEvan Yan 	}
105226947304SEvan Yan }
105326947304SEvan Yan 
105426947304SEvan Yan /*
105526947304SEvan Yan  * setup slot name/slot-number info.
105626947304SEvan Yan  */
105726947304SEvan Yan void
pciehpc_set_slot_name(pcie_hp_ctrl_t * ctrl_p)105826947304SEvan Yan pciehpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p)
105926947304SEvan Yan {
106026947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
106126947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
106226947304SEvan Yan 	uchar_t		*slotname_data;
106326947304SEvan Yan 	int		*slotnum;
106426947304SEvan Yan 	uint_t		count;
106526947304SEvan Yan 	int		len;
106626947304SEvan Yan 	int		invalid_slotnum = 0;
106726947304SEvan Yan 	uint32_t	slot_capabilities;
106826947304SEvan Yan 
106926947304SEvan Yan 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
107026947304SEvan Yan 	    DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
107126947304SEvan Yan 	    DDI_PROP_SUCCESS) {
107226947304SEvan Yan 		slot_p->hs_phy_slot_num = slotnum[0];
107326947304SEvan Yan 		ddi_prop_free(slotnum);
107426947304SEvan Yan 	} else {
107526947304SEvan Yan 		slot_capabilities = pciehpc_reg_get32(ctrl_p,
107626947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCAP);
107726947304SEvan Yan 		slot_p->hs_phy_slot_num =
107826947304SEvan Yan 		    PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
107926947304SEvan Yan 	}
108026947304SEvan Yan 
108126947304SEvan Yan 	/* platform may not have initialized it */
108226947304SEvan Yan 	if (!slot_p->hs_phy_slot_num) {
108326947304SEvan Yan 		PCIE_DBG("%s#%d: Invalid slot number!\n",
108426947304SEvan Yan 		    ddi_driver_name(ctrl_p->hc_dip),
108526947304SEvan Yan 		    ddi_get_instance(ctrl_p->hc_dip));
108626947304SEvan Yan 		slot_p->hs_phy_slot_num = pciehpc_reg_get8(ctrl_p,
108726947304SEvan Yan 		    PCI_BCNF_SECBUS);
108826947304SEvan Yan 		invalid_slotnum = 1;
108926947304SEvan Yan 	}
109026947304SEvan Yan 	slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
109126947304SEvan Yan 	slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
109226947304SEvan Yan 
109326947304SEvan Yan 	/*
109426947304SEvan Yan 	 * construct the slot_name:
109526947304SEvan Yan 	 *	if "slot-names" property exists then use that name
109626947304SEvan Yan 	 *	else if valid slot number exists then it is "pcie<slot-num>".
109726947304SEvan Yan 	 *	else it will be "pcie<sec-bus-number>dev0"
109826947304SEvan Yan 	 */
109926947304SEvan Yan 	if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
110026947304SEvan Yan 	    "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
110126947304SEvan Yan 		char tmp_name[256];
110226947304SEvan Yan 
110326947304SEvan Yan 		/*
110426947304SEvan Yan 		 * Note: for PCI-E slots, the device number is always 0 so the
110526947304SEvan Yan 		 * first (and only) string is the slot name for this slot.
110626947304SEvan Yan 		 */
110726947304SEvan Yan 		(void) snprintf(tmp_name, sizeof (tmp_name),
110826947304SEvan Yan 		    (char *)slotname_data + 4);
110926947304SEvan Yan 		slot_p->hs_info.cn_name = ddi_strdup(tmp_name, KM_SLEEP);
111026947304SEvan Yan 		kmem_free(slotname_data, len);
111126947304SEvan Yan 	} else {
111226947304SEvan Yan 		if (invalid_slotnum) {
111326947304SEvan Yan 			/* use device number ie. 0 */
111426947304SEvan Yan 			slot_p->hs_info.cn_name = ddi_strdup("pcie0",
111526947304SEvan Yan 			    KM_SLEEP);
111626947304SEvan Yan 		} else {
111726947304SEvan Yan 			char tmp_name[256];
111826947304SEvan Yan 
111926947304SEvan Yan 			(void) snprintf(tmp_name, sizeof (tmp_name), "pcie%d",
112026947304SEvan Yan 			    slot_p->hs_phy_slot_num);
112126947304SEvan Yan 			slot_p->hs_info.cn_name = ddi_strdup(tmp_name,
112226947304SEvan Yan 			    KM_SLEEP);
112326947304SEvan Yan 		}
112426947304SEvan Yan 	}
112526947304SEvan Yan }
112626947304SEvan Yan 
112726947304SEvan Yan /*
112826947304SEvan Yan  * Read/Write access to HPC registers. If platform nexus has non-standard
112926947304SEvan Yan  * HPC access mechanism then regops functions are used to do reads/writes.
113026947304SEvan Yan  */
113126947304SEvan Yan uint8_t
pciehpc_reg_get8(pcie_hp_ctrl_t * ctrl_p,uint_t off)113226947304SEvan Yan pciehpc_reg_get8(pcie_hp_ctrl_t *ctrl_p, uint_t off)
113326947304SEvan Yan {
113426947304SEvan Yan 	if (ctrl_p->hc_regops.get != NULL) {
113526947304SEvan Yan 		return ((uint8_t)ctrl_p->hc_regops.get(
113626947304SEvan Yan 		    ctrl_p->hc_regops.cookie, (off_t)off));
113726947304SEvan Yan 	} else {
113826947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
113926947304SEvan Yan 
114026947304SEvan Yan 		return (pci_config_get8(bus_p->bus_cfg_hdl, off));
114126947304SEvan Yan 	}
114226947304SEvan Yan }
114326947304SEvan Yan 
114426947304SEvan Yan uint16_t
pciehpc_reg_get16(pcie_hp_ctrl_t * ctrl_p,uint_t off)114526947304SEvan Yan pciehpc_reg_get16(pcie_hp_ctrl_t *ctrl_p, uint_t off)
114626947304SEvan Yan {
114726947304SEvan Yan 	if (ctrl_p->hc_regops.get != NULL) {
114826947304SEvan Yan 		return ((uint16_t)ctrl_p->hc_regops.get(
114926947304SEvan Yan 		    ctrl_p->hc_regops.cookie, (off_t)off));
115026947304SEvan Yan 	} else {
115126947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
115226947304SEvan Yan 
115326947304SEvan Yan 		return (pci_config_get16(bus_p->bus_cfg_hdl, off));
115426947304SEvan Yan 	}
115526947304SEvan Yan }
115626947304SEvan Yan 
115726947304SEvan Yan uint32_t
pciehpc_reg_get32(pcie_hp_ctrl_t * ctrl_p,uint_t off)115826947304SEvan Yan pciehpc_reg_get32(pcie_hp_ctrl_t *ctrl_p, uint_t off)
115926947304SEvan Yan {
116026947304SEvan Yan 	if (ctrl_p->hc_regops.get != NULL) {
116126947304SEvan Yan 		return ((uint32_t)ctrl_p->hc_regops.get(
116226947304SEvan Yan 		    ctrl_p->hc_regops.cookie, (off_t)off));
116326947304SEvan Yan 	} else {
116426947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
116526947304SEvan Yan 
116626947304SEvan Yan 		return (pci_config_get32(bus_p->bus_cfg_hdl, off));
116726947304SEvan Yan 	}
116826947304SEvan Yan }
116926947304SEvan Yan 
117026947304SEvan Yan void
pciehpc_reg_put8(pcie_hp_ctrl_t * ctrl_p,uint_t off,uint8_t val)117126947304SEvan Yan pciehpc_reg_put8(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint8_t val)
117226947304SEvan Yan {
117326947304SEvan Yan 	if (ctrl_p->hc_regops.put != NULL) {
117426947304SEvan Yan 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
117526947304SEvan Yan 		    (off_t)off, (uint_t)val);
117626947304SEvan Yan 	} else {
117726947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
117826947304SEvan Yan 
117926947304SEvan Yan 		pci_config_put8(bus_p->bus_cfg_hdl, off, val);
118026947304SEvan Yan 	}
118126947304SEvan Yan }
118226947304SEvan Yan 
118326947304SEvan Yan void
pciehpc_reg_put16(pcie_hp_ctrl_t * ctrl_p,uint_t off,uint16_t val)118426947304SEvan Yan pciehpc_reg_put16(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint16_t val)
118526947304SEvan Yan {
118626947304SEvan Yan 	if (ctrl_p->hc_regops.put != NULL) {
118726947304SEvan Yan 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
118826947304SEvan Yan 		    (off_t)off, (uint_t)val);
118926947304SEvan Yan 	} else {
119026947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
119126947304SEvan Yan 
119226947304SEvan Yan 		pci_config_put16(bus_p->bus_cfg_hdl, off, val);
119326947304SEvan Yan 	}
119426947304SEvan Yan }
119526947304SEvan Yan 
119626947304SEvan Yan void
pciehpc_reg_put32(pcie_hp_ctrl_t * ctrl_p,uint_t off,uint32_t val)119726947304SEvan Yan pciehpc_reg_put32(pcie_hp_ctrl_t *ctrl_p, uint_t off, uint32_t val)
119826947304SEvan Yan {
119926947304SEvan Yan 	if (ctrl_p->hc_regops.put != NULL) {
120026947304SEvan Yan 		ctrl_p->hc_regops.put(ctrl_p->hc_regops.cookie,
120126947304SEvan Yan 		    (off_t)off, (uint_t)val);
120226947304SEvan Yan 	} else {
120326947304SEvan Yan 		pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
120426947304SEvan Yan 
120526947304SEvan Yan 		pci_config_put32(bus_p->bus_cfg_hdl, off, val);
120626947304SEvan Yan 	}
120726947304SEvan Yan }
120826947304SEvan Yan 
120926947304SEvan Yan /*
121026947304SEvan Yan  * ************************************************************************
121126947304SEvan Yan  * ***	Local functions (called within this file)
121226947304SEvan Yan  * ***	PCIe Native Hotplug mode specific functions
121326947304SEvan Yan  * ************************************************************************
121426947304SEvan Yan  */
121526947304SEvan Yan 
1216a9413143SRobert Mustacchi static uint8_t
pciehpc_led_state_to_pcie(pcie_hp_led_state_t state)1217a9413143SRobert Mustacchi pciehpc_led_state_to_pcie(pcie_hp_led_state_t state)
1218a9413143SRobert Mustacchi {
1219a9413143SRobert Mustacchi 	switch (state) {
1220a9413143SRobert Mustacchi 	case PCIE_HP_LED_ON:
1221a9413143SRobert Mustacchi 		return (PCIE_SLOTCTL_INDICATOR_STATE_ON);
1222a9413143SRobert Mustacchi 	case PCIE_HP_LED_BLINK:
1223a9413143SRobert Mustacchi 		return (PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
1224a9413143SRobert Mustacchi 	case PCIE_HP_LED_OFF:
1225a9413143SRobert Mustacchi 	default:
1226a9413143SRobert Mustacchi 		return (PCIE_SLOTCTL_INDICATOR_STATE_OFF);
1227a9413143SRobert Mustacchi 	}
1228a9413143SRobert Mustacchi }
1229a9413143SRobert Mustacchi 
1230a9413143SRobert Mustacchi /*
1231a9413143SRobert Mustacchi  * This iterates over the logical led states that we have (in descending order)
1232a9413143SRobert Mustacchi  * and computes the resulting state that a given LED should have. User overrides
1233a9413143SRobert Mustacchi  * are applied elsewhere.
1234a9413143SRobert Mustacchi  */
1235a9413143SRobert Mustacchi static pcie_hp_led_state_t
pciehpc_calc_logical_led(pcie_hp_slot_t * slot_p,pciehpc_led_plat_id_t led)1236a9413143SRobert Mustacchi pciehpc_calc_logical_led(pcie_hp_slot_t *slot_p, pciehpc_led_plat_id_t led)
1237a9413143SRobert Mustacchi {
1238a9413143SRobert Mustacchi 	pcie_hp_led_state_t ret = PCIE_HP_LED_OFF;
1239a9413143SRobert Mustacchi 
1240a9413143SRobert Mustacchi 	for (uint_t i = PCIEHPC_LED_NSTATES; i > 0; i--) {
1241a9413143SRobert Mustacchi 		const uint_t idx = i - 1;
1242a9413143SRobert Mustacchi 		if (!slot_p->hs_led_plat_en[idx])
1243a9413143SRobert Mustacchi 			continue;
1244a9413143SRobert Mustacchi 
1245a9413143SRobert Mustacchi 		switch (slot_p->hs_led_plat_conf[idx].plps_acts[led]) {
1246a9413143SRobert Mustacchi 		case PCIE_HLA_PASS:
1247a9413143SRobert Mustacchi 			continue;
1248a9413143SRobert Mustacchi 		case PCIE_HLA_OFF:
1249a9413143SRobert Mustacchi 			return (PCIE_HP_LED_OFF);
1250a9413143SRobert Mustacchi 		case PCIE_HLA_ON:
1251a9413143SRobert Mustacchi 			return (PCIE_HP_LED_ON);
1252a9413143SRobert Mustacchi 		case PCIE_HLA_BLINK:
1253a9413143SRobert Mustacchi 			return (PCIE_HP_LED_BLINK);
1254a9413143SRobert Mustacchi 		}
1255a9413143SRobert Mustacchi 	}
1256a9413143SRobert Mustacchi 
1257a9413143SRobert Mustacchi 	return (ret);
1258a9413143SRobert Mustacchi }
1259a9413143SRobert Mustacchi 
1260a9413143SRobert Mustacchi static void
pciehpc_sync_leds_to_hw(pcie_hp_slot_t * slot_p)1261a9413143SRobert Mustacchi pciehpc_sync_leds_to_hw(pcie_hp_slot_t *slot_p)
1262a9413143SRobert Mustacchi {
1263a9413143SRobert Mustacchi 	pcie_hp_led_state_t	power, attn;
1264a9413143SRobert Mustacchi 	uint16_t		orig_ctrl, ctrl;
1265a9413143SRobert Mustacchi 	pcie_hp_ctrl_t		*ctrl_p = slot_p->hs_ctrl;
1266a9413143SRobert Mustacchi 	pcie_bus_t		*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1267a9413143SRobert Mustacchi 
1268a9413143SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1269a9413143SRobert Mustacchi 
1270a9413143SRobert Mustacchi 
1271a9413143SRobert Mustacchi 	if (slot_p->hs_power_usr_ovr) {
1272a9413143SRobert Mustacchi 		power = slot_p->hs_power_usr_ovr_state;
1273a9413143SRobert Mustacchi 	} else {
1274a9413143SRobert Mustacchi 		power = pciehpc_calc_logical_led(slot_p, PCIEHPC_PLAT_ID_POWER);
1275a9413143SRobert Mustacchi 	}
1276a9413143SRobert Mustacchi 
1277a9413143SRobert Mustacchi 	if (slot_p->hs_attn_usr_ovr) {
1278a9413143SRobert Mustacchi 		attn = slot_p->hs_attn_usr_ovr_state;
1279a9413143SRobert Mustacchi 	} else {
1280a9413143SRobert Mustacchi 		attn = pciehpc_calc_logical_led(slot_p, PCIEHPC_PLAT_ID_ATTN);
1281a9413143SRobert Mustacchi 	}
1282a9413143SRobert Mustacchi 
1283a9413143SRobert Mustacchi 	slot_p->hs_power_led_state = power;
1284a9413143SRobert Mustacchi 	slot_p->hs_attn_led_state = attn;
1285a9413143SRobert Mustacchi 	orig_ctrl = ctrl = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off +
1286a9413143SRobert Mustacchi 	    PCIE_SLOTCTL);
1287a9413143SRobert Mustacchi 
1288a9413143SRobert Mustacchi 	ctrl = pcie_slotctl_attn_indicator_set(ctrl,
1289a9413143SRobert Mustacchi 	    pciehpc_led_state_to_pcie(attn));
1290a9413143SRobert Mustacchi 	ctrl = pcie_slotctl_pwr_indicator_set(ctrl,
1291a9413143SRobert Mustacchi 	    pciehpc_led_state_to_pcie(power));
1292a9413143SRobert Mustacchi 	if (orig_ctrl != ctrl) {
1293a9413143SRobert Mustacchi 		pciehpc_issue_hpc_command(ctrl_p, ctrl);
1294a9413143SRobert Mustacchi 	}
1295a9413143SRobert Mustacchi }
1296a9413143SRobert Mustacchi 
129726947304SEvan Yan /*
129826947304SEvan Yan  * Initialize HPC hardware, install interrupt handler, etc. It doesn't
129926947304SEvan Yan  * enable hot plug interrupts.
130026947304SEvan Yan  *
130126947304SEvan Yan  * (Note: It is called only from pciehpc_init().)
130226947304SEvan Yan  */
130326947304SEvan Yan static int
pciehpc_hpc_init(pcie_hp_ctrl_t * ctrl_p)130426947304SEvan Yan pciehpc_hpc_init(pcie_hp_ctrl_t *ctrl_p)
130526947304SEvan Yan {
130626947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
130726947304SEvan Yan 	uint16_t	reg;
130826947304SEvan Yan 
130926947304SEvan Yan 	/* read the Slot Control Register */
131026947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
131126947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
131226947304SEvan Yan 
131326947304SEvan Yan 	/* disable all interrupts */
131426947304SEvan Yan 	reg &= ~(PCIE_SLOTCTL_INTR_MASK);
131526947304SEvan Yan 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off +
131626947304SEvan Yan 	    PCIE_SLOTCTL, reg);
131726947304SEvan Yan 
131826947304SEvan Yan 	/* clear any interrupt status bits */
131926947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
132026947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
132126947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
132226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
132326947304SEvan Yan 
132426947304SEvan Yan 	return (DDI_SUCCESS);
132526947304SEvan Yan }
132626947304SEvan Yan 
132726947304SEvan Yan /*
132826947304SEvan Yan  * Uninitialize HPC hardware, uninstall interrupt handler, etc.
132926947304SEvan Yan  *
133026947304SEvan Yan  * (Note: It is called only from pciehpc_uninit().)
133126947304SEvan Yan  */
133226947304SEvan Yan static int
pciehpc_hpc_uninit(pcie_hp_ctrl_t * ctrl_p)133326947304SEvan Yan pciehpc_hpc_uninit(pcie_hp_ctrl_t *ctrl_p)
133426947304SEvan Yan {
133526947304SEvan Yan 	/* disable interrupts */
133626947304SEvan Yan 	(void) pciehpc_disable_intr(ctrl_p);
133726947304SEvan Yan 
133826947304SEvan Yan 	return (DDI_SUCCESS);
133926947304SEvan Yan }
134026947304SEvan Yan 
134126947304SEvan Yan /*
1342a9413143SRobert Mustacchi  * Initialize the PCIe LED tracking and policy.
1343a9413143SRobert Mustacchi  */
1344a9413143SRobert Mustacchi void
pciehpc_led_init(pcie_hp_slot_t * slot_p)1345a9413143SRobert Mustacchi pciehpc_led_init(pcie_hp_slot_t *slot_p)
1346a9413143SRobert Mustacchi {
1347a9413143SRobert Mustacchi 	int vid, did, subvid, subsys;
1348a9413143SRobert Mustacchi 	pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
1349a9413143SRobert Mustacchi 
1350a9413143SRobert Mustacchi 	slot_p->hs_power_usr_ovr = false;
1351a9413143SRobert Mustacchi 	slot_p->hs_attn_usr_ovr = false;
1352a9413143SRobert Mustacchi 	slot_p->hs_power_usr_ovr_state = PCIE_HP_LED_OFF;
1353a9413143SRobert Mustacchi 	slot_p->hs_attn_usr_ovr_state = PCIE_HP_LED_OFF;
1354a9413143SRobert Mustacchi 	bzero(slot_p->hs_led_plat_en, sizeof (slot_p->hs_led_plat_en));
1355a9413143SRobert Mustacchi 
1356a9413143SRobert Mustacchi 	slot_p->hs_led_plat_conf = pciehpc_std_leds;
1357a9413143SRobert Mustacchi 
1358a9413143SRobert Mustacchi 	vid = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
1359a9413143SRobert Mustacchi 	    "vendor-id", PCI_EINVAL16);
1360a9413143SRobert Mustacchi 	did = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
1361a9413143SRobert Mustacchi 	    "device-id", PCI_EINVAL16);
1362a9413143SRobert Mustacchi 	subvid = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip,
1363a9413143SRobert Mustacchi 	    DDI_PROP_DONTPASS, "subsystem-vendor-id", PCI_EINVAL16);
1364a9413143SRobert Mustacchi 	subsys = ddi_prop_get_int(DDI_DEV_T_ANY, ctrl_p->hc_dip,
1365a9413143SRobert Mustacchi 	    DDI_PROP_DONTPASS, "subsystem-id", PCI_EINVAL16);
1366a9413143SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(pciehpc_led_plat_map); i++) {
1367a9413143SRobert Mustacchi 		if (vid == pciehpc_led_plat_map[i].plpm_vid &&
1368a9413143SRobert Mustacchi 		    did == pciehpc_led_plat_map[i].plpm_did &&
1369a9413143SRobert Mustacchi 		    subvid == pciehpc_led_plat_map[i].plpm_svid &&
1370a9413143SRobert Mustacchi 		    subsys == pciehpc_led_plat_map[i].plpm_ssys) {
1371a9413143SRobert Mustacchi 			slot_p->hs_led_plat_conf =
1372a9413143SRobert Mustacchi 			    pciehpc_led_plat_map[i].plpm_map;
1373a9413143SRobert Mustacchi 			break;
1374a9413143SRobert Mustacchi 		}
1375a9413143SRobert Mustacchi 	}
1376a9413143SRobert Mustacchi 
1377a9413143SRobert Mustacchi 	/*
1378a9413143SRobert Mustacchi 	 * Finally, enable the base state for the slot which is meant to always
1379a9413143SRobert Mustacchi 	 * be true. We'll catch up the rest of the states when we get the slot
1380a9413143SRobert Mustacchi 	 * state there.
1381a9413143SRobert Mustacchi 	 */
1382a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_BASE] = true;
1383a9413143SRobert Mustacchi }
1384a9413143SRobert Mustacchi 
1385*ead3c390SKeith M Wesolowski bool
pciehpc_slot_kstat_init(pcie_hp_slot_t * slot_p)1386*ead3c390SKeith M Wesolowski pciehpc_slot_kstat_init(pcie_hp_slot_t *slot_p)
1387*ead3c390SKeith M Wesolowski {
1388*ead3c390SKeith M Wesolowski 	/*
1389*ead3c390SKeith M Wesolowski 	 * Many if not most PCs have firmware bugs that cause multiple slots to
1390*ead3c390SKeith M Wesolowski 	 * end up with the same physical slot number in the slot cap register.
1391*ead3c390SKeith M Wesolowski 	 * This is a clear and direct violation of PCIe4 7.5.3.9, but that
1392*ead3c390SKeith M Wesolowski 	 * doesn't seem to matter to the people who write firmware.  This is
1393*ead3c390SKeith M Wesolowski 	 * going to end up confusing the hotplug subsystem and the time to deal
1394*ead3c390SKeith M Wesolowski 	 * with it is probably before we arrive here, but in order to avoid
1395*ead3c390SKeith M Wesolowski 	 * additional warning messages during boot on such machines, we will use
1396*ead3c390SKeith M Wesolowski 	 * the hotplug-capable bridge's instance number when creating event
1397*ead3c390SKeith M Wesolowski 	 * counter kstats instead of hs_phy_slot_num (or hs_info.cn_num, which
1398*ead3c390SKeith M Wesolowski 	 * is usually the same).  In the unlikely event that the broken firmware
1399*ead3c390SKeith M Wesolowski 	 * has given us unique names (even if not slot numbers) for a slot,
1400*ead3c390SKeith M Wesolowski 	 * we'll still have a unique name for each kstat, just as we will for
1401*ead3c390SKeith M Wesolowski 	 * each AP.  Otherwise the kstat names will at least match the still
1402*ead3c390SKeith M Wesolowski 	 * possibly duplicated AP names.
1403*ead3c390SKeith M Wesolowski 	 */
1404*ead3c390SKeith M Wesolowski 	int inst = ddi_get_instance(slot_p->hs_ctrl->hc_dip);
1405*ead3c390SKeith M Wesolowski 
1406*ead3c390SKeith M Wesolowski 	/*
1407*ead3c390SKeith M Wesolowski 	 * Although the interrupts we are counting are generated by the hotplug
1408*ead3c390SKeith M Wesolowski 	 * controller, they are conceptually associated with the slot so we
1409*ead3c390SKeith M Wesolowski 	 * store them with it.  The PCIe hotplug controller as currently defined
1410*ead3c390SKeith M Wesolowski 	 * supports only a single slot (0), but in principle a future controller
1411*ead3c390SKeith M Wesolowski 	 * might support multiple slots and we would have some way of
1412*ead3c390SKeith M Wesolowski 	 * determining the set of events that have been detected for each slot
1413*ead3c390SKeith M Wesolowski 	 * even the state changes for all slots share a single interrupt.
1414*ead3c390SKeith M Wesolowski 	 *
1415*ead3c390SKeith M Wesolowski 	 * The statistics are persistent because we want to keep counting them
1416*ead3c390SKeith M Wesolowski 	 * over the entire lifetime of this kernel; if the slot's occupant is
1417*ead3c390SKeith M Wesolowski 	 * removed and that leads to pciehpc_uninit() being called, we don't
1418*ead3c390SKeith M Wesolowski 	 * want to lose the previous statistics.  Indeed, observing what led up
1419*ead3c390SKeith M Wesolowski 	 * to such an event is a key purpose of this infrastructure.
1420*ead3c390SKeith M Wesolowski 	 */
1421*ead3c390SKeith M Wesolowski 	slot_p->hs_kstat = kstat_create_zone("pcie", inst,
1422*ead3c390SKeith M Wesolowski 	    slot_p->hs_info.cn_name, "controller", KSTAT_TYPE_NAMED,
1423*ead3c390SKeith M Wesolowski 	    sizeof (pciehpc_stat_data_t) / sizeof (kstat_named_t),
1424*ead3c390SKeith M Wesolowski 	    KSTAT_FLAG_PERSISTENT, GLOBAL_ZONEID);
1425*ead3c390SKeith M Wesolowski 
1426*ead3c390SKeith M Wesolowski 	if (slot_p->hs_kstat == NULL) {
1427*ead3c390SKeith M Wesolowski 		PCIE_DBG("pciehpc_slot_kstat_init: %d:%s kstat_create failed",
1428*ead3c390SKeith M Wesolowski 		    inst, slot_p->hs_info.cn_name);
1429*ead3c390SKeith M Wesolowski 		return (false);
1430*ead3c390SKeith M Wesolowski 	}
1431*ead3c390SKeith M Wesolowski 
1432*ead3c390SKeith M Wesolowski 	slot_p->hs_stat_data = (pciehpc_stat_data_t *)slot_p->hs_kstat->ks_data;
1433*ead3c390SKeith M Wesolowski 
1434*ead3c390SKeith M Wesolowski 	/*
1435*ead3c390SKeith M Wesolowski 	 * We would like to report only the set of kstats that correspond to the
1436*ead3c390SKeith M Wesolowski 	 * events for which the controller advertises support.  However, it is
1437*ead3c390SKeith M Wesolowski 	 * possible for a buggy controller to set status bits that correspond to
1438*ead3c390SKeith M Wesolowski 	 * bits that aren't set in the capabilities register.  If this were to
1439*ead3c390SKeith M Wesolowski 	 * occur, we'll still increment the counters and the kstat framework
1440*ead3c390SKeith M Wesolowski 	 * doesn't really guarantee that we're allowed to do so.  Therefore we
1441*ead3c390SKeith M Wesolowski 	 * will initialise all the counters, even those that should not or
1442*ead3c390SKeith M Wesolowski 	 * cannot have meaningful values for this slot.
1443*ead3c390SKeith M Wesolowski 	 */
1444*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_attnsw_count,
1445*ead3c390SKeith M Wesolowski 	    "attention_switch_count", KSTAT_DATA_UINT64);
1446*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_attnsw_ts,
1447*ead3c390SKeith M Wesolowski 	    "attention_switch_ts", KSTAT_DATA_UINT64);
1448*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_pwrflt_count,
1449*ead3c390SKeith M Wesolowski 	    "power_fault_count", KSTAT_DATA_UINT64);
1450*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_pwrflt_ts,
1451*ead3c390SKeith M Wesolowski 	    "power_fault_ts", KSTAT_DATA_UINT64);
1452*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_mrl_chg_count,
1453*ead3c390SKeith M Wesolowski 	    "mrl_change_count", KSTAT_DATA_UINT64);
1454*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_mrl_chg_ts,
1455*ead3c390SKeith M Wesolowski 	    "mrl_change_ts", KSTAT_DATA_UINT64);
1456*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_pres_chg_count,
1457*ead3c390SKeith M Wesolowski 	    "presence_change_count", KSTAT_DATA_UINT64);
1458*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_pres_chg_ts,
1459*ead3c390SKeith M Wesolowski 	    "presence_change_ts", KSTAT_DATA_UINT64);
1460*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_cmd_cpl_count,
1461*ead3c390SKeith M Wesolowski 	    "command_completion_count", KSTAT_DATA_UINT64);
1462*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_cmd_cpl_ts,
1463*ead3c390SKeith M Wesolowski 	    "command_completion_ts", KSTAT_DATA_UINT64);
1464*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_dll_chg_count,
1465*ead3c390SKeith M Wesolowski 	    "dll_active_change_count", KSTAT_DATA_UINT64);
1466*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_dll_chg_ts,
1467*ead3c390SKeith M Wesolowski 	    "dll_active_change_ts", KSTAT_DATA_UINT64);
1468*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_intr_count,
1469*ead3c390SKeith M Wesolowski 	    "interrupt_count", KSTAT_DATA_UINT64);
1470*ead3c390SKeith M Wesolowski 	kstat_named_init(&slot_p->hs_stat_data->psd_intr_ts,
1471*ead3c390SKeith M Wesolowski 	    "interrupt_ts", KSTAT_DATA_UINT64);
1472*ead3c390SKeith M Wesolowski 
1473*ead3c390SKeith M Wesolowski 	kstat_install(slot_p->hs_kstat);
1474*ead3c390SKeith M Wesolowski 
1475*ead3c390SKeith M Wesolowski 	return (true);
1476*ead3c390SKeith M Wesolowski }
1477*ead3c390SKeith M Wesolowski 
1478a9413143SRobert Mustacchi /*
1479350b2114SRobert Mustacchi  * Setup slot information for use with DDI HP framework. Per the theory
1480350b2114SRobert Mustacchi  * statement, this is where we need to go through and look at whether or not we
1481350b2114SRobert Mustacchi  * have a child and whether or not we want the 1s later timeout to get things
1482350b2114SRobert Mustacchi  * into a reasonable state.
148326947304SEvan Yan  */
148426947304SEvan Yan static int
pciehpc_slotinfo_init(pcie_hp_ctrl_t * ctrl_p)148526947304SEvan Yan pciehpc_slotinfo_init(pcie_hp_ctrl_t *ctrl_p)
148626947304SEvan Yan {
148726947304SEvan Yan 	uint32_t	slot_capabilities, link_capabilities;
148826947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
148926947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1490350b2114SRobert Mustacchi 	boolean_t	have_child;
1491350b2114SRobert Mustacchi 
1492350b2114SRobert Mustacchi 	/*
1493350b2114SRobert Mustacchi 	 * First we look to see if we have any children at all. If we do, then
1494350b2114SRobert Mustacchi 	 * we assume that things were initialized prior to our existence as
1495350b2114SRobert Mustacchi 	 * discussed by state initialization (2).
1496350b2114SRobert Mustacchi 	 */
1497350b2114SRobert Mustacchi 	ndi_devi_enter(ctrl_p->hc_dip);
1498350b2114SRobert Mustacchi 	have_child = ddi_get_child(ctrl_p->hc_dip) != NULL;
1499350b2114SRobert Mustacchi 	ndi_devi_exit(ctrl_p->hc_dip);
150026947304SEvan Yan 
1501*ead3c390SKeith M Wesolowski 	/*
1502*ead3c390SKeith M Wesolowski 	 * There's no expectation for two threads to be here at the same time:
1503*ead3c390SKeith M Wesolowski 	 * we arrive here through the attach path and the DDI guarantees
1504*ead3c390SKeith M Wesolowski 	 * exclusion.  We take the mutex because some of the functions we call
1505*ead3c390SKeith M Wesolowski 	 * assert that we have it.
1506*ead3c390SKeith M Wesolowski 	 */
150726947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
150826947304SEvan Yan 	/*
150926947304SEvan Yan 	 * setup DDI HP framework slot information structure
151026947304SEvan Yan 	 */
151126947304SEvan Yan 	slot_p->hs_device_num = 0;
151226947304SEvan Yan 
151326947304SEvan Yan 	slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCIE;
151426947304SEvan Yan 	slot_p->hs_info.cn_type_str = (ctrl_p->hc_regops.get == NULL) ?
151526947304SEvan Yan 	    PCIE_NATIVE_HP_TYPE : PCIE_PROP_HP_TYPE;
151626947304SEvan Yan 	slot_p->hs_info.cn_child = NULL;
151726947304SEvan Yan 
151826947304SEvan Yan 	slot_p->hs_minor =
151926947304SEvan Yan 	    PCI_MINOR_NUM(ddi_get_instance(ctrl_p->hc_dip),
152026947304SEvan Yan 	    slot_p->hs_device_num);
152126947304SEvan Yan 	slot_p->hs_condition = AP_COND_UNKNOWN;
152226947304SEvan Yan 
152326947304SEvan Yan 	/* read Slot Capabilities Register */
152426947304SEvan Yan 	slot_capabilities = pciehpc_reg_get32(ctrl_p,
152526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
152626947304SEvan Yan 
152726947304SEvan Yan 	/* set slot-name/slot-number info */
152826947304SEvan Yan 	pciehpc_set_slot_name(ctrl_p);
152926947304SEvan Yan 
153026947304SEvan Yan 	/* check if Attn Button present */
153126947304SEvan Yan 	ctrl_p->hc_has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
153226947304SEvan Yan 	    B_TRUE : B_FALSE;
153326947304SEvan Yan 
153426947304SEvan Yan 	/* check if Manual Retention Latch sensor present */
153526947304SEvan Yan 	ctrl_p->hc_has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
153626947304SEvan Yan 	    B_TRUE : B_FALSE;
153726947304SEvan Yan 
153826947304SEvan Yan 	/*
1539b9a2a14bSRobert Mustacchi 	 * Contrary to what one might expect, not all systems actually have
1540b9a2a14bSRobert Mustacchi 	 * power control despite having hot-swap capabilities. This is most
1541b9a2a14bSRobert Mustacchi 	 * commonly due to the Enterprise SSD specification which doesn't call
1542b9a2a14bSRobert Mustacchi 	 * for power-control in the PCIe native hotplug implementation.
1543b9a2a14bSRobert Mustacchi 	 */
1544b9a2a14bSRobert Mustacchi 	ctrl_p->hc_has_pwr = (slot_capabilities &
1545b9a2a14bSRobert Mustacchi 	    PCIE_SLOTCAP_POWER_CONTROLLER) ? B_TRUE: B_FALSE;
1546b9a2a14bSRobert Mustacchi 
1547b9a2a14bSRobert Mustacchi 	/*
154826947304SEvan Yan 	 * PCI-E version 1.1 defines EMI Lock Present bit
154926947304SEvan Yan 	 * in Slot Capabilities register. Check for it.
155026947304SEvan Yan 	 */
155126947304SEvan Yan 	ctrl_p->hc_has_emi_lock = (slot_capabilities &
155226947304SEvan Yan 	    PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
155326947304SEvan Yan 
155426947304SEvan Yan 	link_capabilities = pciehpc_reg_get32(ctrl_p,
155526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_LINKCAP);
155626947304SEvan Yan 	ctrl_p->hc_dll_active_rep = (link_capabilities &
155726947304SEvan Yan 	    PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) ? B_TRUE : B_FALSE;
155826947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep)
155926947304SEvan Yan 		cv_init(&slot_p->hs_dll_active_cv, NULL, CV_DRIVER, NULL);
156026947304SEvan Yan 
156126947304SEvan Yan 	/* setup thread for handling ATTN button events */
156226947304SEvan Yan 	if (ctrl_p->hc_has_attn) {
156326947304SEvan Yan 		PCIE_DBG("pciehpc_slotinfo_init: setting up ATTN button event "
156426947304SEvan Yan 		    "handler thread for slot %d\n", slot_p->hs_phy_slot_num);
156526947304SEvan Yan 
156626947304SEvan Yan 		cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
156726947304SEvan Yan 		slot_p->hs_attn_btn_pending = B_FALSE;
156826947304SEvan Yan 		slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
156926947304SEvan Yan 		    pciehpc_attn_btn_handler,
157026947304SEvan Yan 		    (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
157126947304SEvan Yan 		slot_p->hs_attn_btn_thread_exit = B_FALSE;
157226947304SEvan Yan 	}
157326947304SEvan Yan 
1574a9413143SRobert Mustacchi 	/*
1575a9413143SRobert Mustacchi 	 * Initialize our LED state tracking.
1576a9413143SRobert Mustacchi 	 */
1577a9413143SRobert Mustacchi 	pciehpc_led_init(slot_p);
1578a9413143SRobert Mustacchi 
157926947304SEvan Yan 	/* get current slot state from the hw */
158026947304SEvan Yan 	slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
158126947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
1582350b2114SRobert Mustacchi 
1583350b2114SRobert Mustacchi 	/*
1584350b2114SRobert Mustacchi 	 * If the kernel has enumerated a device, note that we have performed
1585350b2114SRobert Mustacchi 	 * the enabled transition.
1586350b2114SRobert Mustacchi 	 */
1587350b2114SRobert Mustacchi 	if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_POWERED &&
1588350b2114SRobert Mustacchi 	    have_child) {
1589350b2114SRobert Mustacchi 		slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
1590350b2114SRobert Mustacchi 	}
1591350b2114SRobert Mustacchi 
1592a9413143SRobert Mustacchi 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED)
1593a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
1594a9413143SRobert Mustacchi 
159526947304SEvan Yan 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
159626947304SEvan Yan 		slot_p->hs_condition = AP_COND_OK;
159726947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
159826947304SEvan Yan 
1599*ead3c390SKeith M Wesolowski 	if (!pciehpc_slot_kstat_init(slot_p)) {
1600*ead3c390SKeith M Wesolowski 		(void) pciehpc_slotinfo_uninit(ctrl_p);
1601*ead3c390SKeith M Wesolowski 		return (DDI_FAILURE);
1602*ead3c390SKeith M Wesolowski 	}
1603*ead3c390SKeith M Wesolowski 
160426947304SEvan Yan 	return (DDI_SUCCESS);
160526947304SEvan Yan }
160626947304SEvan Yan 
1607*ead3c390SKeith M Wesolowski void
pciehpc_slot_kstat_fini(pcie_hp_slot_t * slot_p)1608*ead3c390SKeith M Wesolowski pciehpc_slot_kstat_fini(pcie_hp_slot_t *slot_p)
1609*ead3c390SKeith M Wesolowski {
1610*ead3c390SKeith M Wesolowski 	if (slot_p->hs_kstat != NULL) {
1611*ead3c390SKeith M Wesolowski 		kstat_delete(slot_p->hs_kstat);
1612*ead3c390SKeith M Wesolowski 		slot_p->hs_kstat = NULL;
1613*ead3c390SKeith M Wesolowski 		slot_p->hs_stat_data = NULL;
1614*ead3c390SKeith M Wesolowski 	}
1615*ead3c390SKeith M Wesolowski }
1616*ead3c390SKeith M Wesolowski 
161726947304SEvan Yan static int
pciehpc_slotinfo_uninit(pcie_hp_ctrl_t * ctrl_p)161826947304SEvan Yan pciehpc_slotinfo_uninit(pcie_hp_ctrl_t *ctrl_p)
161926947304SEvan Yan {
162026947304SEvan Yan 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
162126947304SEvan Yan 
162226947304SEvan Yan 	if (slot_p->hs_attn_btn_threadp != NULL) {
162326947304SEvan Yan 		mutex_enter(&ctrl_p->hc_mutex);
162426947304SEvan Yan 		slot_p->hs_attn_btn_thread_exit = B_TRUE;
162526947304SEvan Yan 		cv_signal(&slot_p->hs_attn_btn_cv);
162626947304SEvan Yan 		PCIE_DBG("pciehpc_slotinfo_uninit: "
162726947304SEvan Yan 		    "waiting for ATTN thread exit\n");
162826947304SEvan Yan 		cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
162926947304SEvan Yan 		PCIE_DBG("pciehpc_slotinfo_uninit: ATTN thread exit\n");
163026947304SEvan Yan 		cv_destroy(&slot_p->hs_attn_btn_cv);
163126947304SEvan Yan 		slot_p->hs_attn_btn_threadp = NULL;
163226947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
163326947304SEvan Yan 	}
163426947304SEvan Yan 
163526947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep)
163626947304SEvan Yan 		cv_destroy(&slot_p->hs_dll_active_cv);
163726947304SEvan Yan 	if (slot_p->hs_info.cn_name)
163826947304SEvan Yan 		kmem_free(slot_p->hs_info.cn_name,
163926947304SEvan Yan 		    strlen(slot_p->hs_info.cn_name) + 1);
164026947304SEvan Yan 
1641*ead3c390SKeith M Wesolowski 	pciehpc_slot_kstat_fini(slot_p);
1642*ead3c390SKeith M Wesolowski 
164326947304SEvan Yan 	return (DDI_SUCCESS);
164426947304SEvan Yan }
164526947304SEvan Yan 
164626947304SEvan Yan /*
1647350b2114SRobert Mustacchi  * This is the synchronization function that is discussed in the 'State
1648350b2114SRobert Mustacchi  * Initialization' portion of the theory statement in this file. It is
1649350b2114SRobert Mustacchi  * responsible for trying to make sure that devices are in a usable state during
1650350b2114SRobert Mustacchi  * a potentially turbulent start up sequence.
1651350b2114SRobert Mustacchi  */
1652350b2114SRobert Mustacchi static void
pciehpc_state_sync(void * arg)1653350b2114SRobert Mustacchi pciehpc_state_sync(void *arg)
1654350b2114SRobert Mustacchi {
1655350b2114SRobert Mustacchi 	pciehpc_sync_task_t *sync = arg;
1656350b2114SRobert Mustacchi 	pcie_hp_ctrl_t *ctrl_p = sync->pst_ctrl;
1657350b2114SRobert Mustacchi 	dev_info_t *dip = ctrl_p->hc_dip;
1658350b2114SRobert Mustacchi 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
1659350b2114SRobert Mustacchi 
1660350b2114SRobert Mustacchi 	mutex_enter(&ctrl_p->hc_mutex);
1661350b2114SRobert Mustacchi 	if (ctrl_p->hc_startup_sync == TASKQID_INVALID) {
1662350b2114SRobert Mustacchi 		mutex_exit(&ctrl_p->hc_mutex);
1663350b2114SRobert Mustacchi 		kmem_free(sync, sizeof (pciehpc_sync_task_t));
1664350b2114SRobert Mustacchi 		return;
1665350b2114SRobert Mustacchi 	}
1666350b2114SRobert Mustacchi 
1667350b2114SRobert Mustacchi 	if ((ctrl_p->hc_flags & PCIE_HP_SYNC_PENDING) == 0) {
1668350b2114SRobert Mustacchi 		goto done;
1669350b2114SRobert Mustacchi 	}
1670350b2114SRobert Mustacchi 
1671350b2114SRobert Mustacchi 	cmn_err(CE_NOTE, "pciehpc (%s%d): synchronizing state in slot %s to "
1672350b2114SRobert Mustacchi 	    "0x%x", ddi_driver_name(dip), ddi_get_instance(dip),
1673350b2114SRobert Mustacchi 	    slot_p->hs_info.cn_name, sync->pst_targ);
1674350b2114SRobert Mustacchi 
1675350b2114SRobert Mustacchi 	ASSERT3U(slot_p->hs_info.cn_state, ==, sync->pst_cur);
1676350b2114SRobert Mustacchi 
1677350b2114SRobert Mustacchi 	ctrl_p->hc_flags &= ~PCIE_HP_SYNC_PENDING;
1678350b2114SRobert Mustacchi 	ctrl_p->hc_flags |= PCIE_HP_SYNC_RUNNING;
1679350b2114SRobert Mustacchi 	mutex_exit(&ctrl_p->hc_mutex);
1680350b2114SRobert Mustacchi 
1681350b2114SRobert Mustacchi 	(void) ndi_hp_state_change_req(dip, slot_p->hs_info.cn_name,
1682350b2114SRobert Mustacchi 	    sync->pst_targ, DDI_HP_REQ_SYNC);
1683350b2114SRobert Mustacchi 
1684350b2114SRobert Mustacchi 	/*
1685350b2114SRobert Mustacchi 	 * Now that we're done with operating this way, go ahead and clear
1686350b2114SRobert Mustacchi 	 * things up.
1687350b2114SRobert Mustacchi 	 */
1688350b2114SRobert Mustacchi 	mutex_enter(&ctrl_p->hc_mutex);
1689350b2114SRobert Mustacchi done:
1690350b2114SRobert Mustacchi 	ctrl_p->hc_flags &= ~PCIE_HP_SYNC_RUNNING;
1691350b2114SRobert Mustacchi 	ctrl_p->hc_startup_sync = TASKQID_INVALID;
1692350b2114SRobert Mustacchi 	mutex_exit(&ctrl_p->hc_mutex);
1693350b2114SRobert Mustacchi 	kmem_free(sync, sizeof (pciehpc_sync_task_t));
1694350b2114SRobert Mustacchi }
1695350b2114SRobert Mustacchi 
1696350b2114SRobert Mustacchi static void
pciehpc_dispatch_state_sync(pcie_hp_ctrl_t * ctrl_p,ddi_hp_cn_state_t targ)1697350b2114SRobert Mustacchi pciehpc_dispatch_state_sync(pcie_hp_ctrl_t *ctrl_p, ddi_hp_cn_state_t targ)
1698350b2114SRobert Mustacchi {
1699350b2114SRobert Mustacchi 	pciehpc_sync_task_t *sync;
1700350b2114SRobert Mustacchi 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
1701350b2114SRobert Mustacchi 
1702350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1703350b2114SRobert Mustacchi 	sync = kmem_alloc(sizeof (pciehpc_sync_task_t), KM_SLEEP);
1704350b2114SRobert Mustacchi 	sync->pst_ctrl = ctrl_p;
1705350b2114SRobert Mustacchi 	sync->pst_targ = targ;
1706350b2114SRobert Mustacchi 	sync->pst_cur = slot_p->hs_info.cn_state;
1707350b2114SRobert Mustacchi 
1708350b2114SRobert Mustacchi 	ctrl_p->hc_flags |= PCIE_HP_SYNC_PENDING;
1709350b2114SRobert Mustacchi 	ctrl_p->hc_startup_sync = taskq_dispatch(system_taskq,
1710350b2114SRobert Mustacchi 	    pciehpc_state_sync, sync, TQ_SLEEP);
1711350b2114SRobert Mustacchi }
1712350b2114SRobert Mustacchi 
1713a9413143SRobert Mustacchi /*
1714a9413143SRobert Mustacchi  * The point of this is to forcibly set the logical LED states to the
1715a9413143SRobert Mustacchi  * corresponding ones that make sense for the corresponding hotplug state.
1716a9413143SRobert Mustacchi  */
1717350b2114SRobert Mustacchi static void
pciehpc_enable_state_sync_leds(pcie_hp_ctrl_t * ctrl_p)1718350b2114SRobert Mustacchi pciehpc_enable_state_sync_leds(pcie_hp_ctrl_t *ctrl_p)
1719350b2114SRobert Mustacchi {
1720350b2114SRobert Mustacchi 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
1721a9413143SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1722a9413143SRobert Mustacchi 
1723a9413143SRobert Mustacchi 	bzero(slot_p->hs_led_plat_en, sizeof (slot_p->hs_led_plat_en));
1724350b2114SRobert Mustacchi 
1725350b2114SRobert Mustacchi 	switch (slot_p->hs_info.cn_state) {
1726350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_ENABLED:
1727350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_POWERED:
1728a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
1729a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_BASE] = true;
1730350b2114SRobert Mustacchi 		break;
1731350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_PRESENT:
1732350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_EMPTY:
1733a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_BASE] = true;
1734350b2114SRobert Mustacchi 		break;
1735350b2114SRobert Mustacchi 	default:
1736350b2114SRobert Mustacchi 		dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
1737350b2114SRobert Mustacchi 		    "connector state: 0x%x", slot_p->hs_info.cn_state);
1738350b2114SRobert Mustacchi 		break;
1739350b2114SRobert Mustacchi 	}
1740a9413143SRobert Mustacchi 
1741a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
1742350b2114SRobert Mustacchi }
1743350b2114SRobert Mustacchi 
1744350b2114SRobert Mustacchi /*
1745350b2114SRobert Mustacchi  * We have just enabled interrupts and cleared any changes that may or may not
1746350b2114SRobert Mustacchi  * have been valid from the hardware perspective. There are a few key
1747350b2114SRobert Mustacchi  * assumptions that we're making right now as discussed in the theory statement:
1748350b2114SRobert Mustacchi  *
1749350b2114SRobert Mustacchi  *  o If we are currently enabled, then we know that we have children and
1750350b2114SRobert Mustacchi  *    nothing has changed from our init.
1751350b2114SRobert Mustacchi  *  o Because we have just enabled interrupts, but have not relinquished our
1752350b2114SRobert Mustacchi  *    exclusion on the controller hardware, nothing else could have come in and
1753350b2114SRobert Mustacchi  *    started reacting to an actual change.
1754350b2114SRobert Mustacchi  *  o Even though someone could come and call DDI_HPOP_CN_GET_STATE, that could
1755350b2114SRobert Mustacchi  *    not transition us to enabled yet.
1756350b2114SRobert Mustacchi  *  o Because interrupt enable is still called in attach context, we cannot have
1757350b2114SRobert Mustacchi  *    a user accessing the node and requesting a state change.
1758350b2114SRobert Mustacchi  *
1759350b2114SRobert Mustacchi  * Finally there are a few things that we need to be mindful of. We must set any
1760350b2114SRobert Mustacchi  * updates to the state prior to calling into any request to update the LED
1761350b2114SRobert Mustacchi  * state as that may rely on getting an async callback.
1762350b2114SRobert Mustacchi  */
1763350b2114SRobert Mustacchi static void
pciehpc_enable_state_sync(pcie_hp_ctrl_t * ctrl_p)1764350b2114SRobert Mustacchi pciehpc_enable_state_sync(pcie_hp_ctrl_t *ctrl_p)
1765350b2114SRobert Mustacchi {
1766350b2114SRobert Mustacchi 	pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1767350b2114SRobert Mustacchi 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
1768350b2114SRobert Mustacchi 	uint16_t control, status;
1769350b2114SRobert Mustacchi 	ddi_hp_cn_state_t curr_state, online_targ;
1770350b2114SRobert Mustacchi 
1771350b2114SRobert Mustacchi 	online_targ = (pcie_auto_online != 0) ?  DDI_HP_CN_STATE_ENABLED :
1772350b2114SRobert Mustacchi 	    DDI_HP_CN_STATE_PRESENT;
1773350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1774350b2114SRobert Mustacchi 
1775350b2114SRobert Mustacchi 	/*
1776350b2114SRobert Mustacchi 	 * We manually compute the status from a single read of things rather
1777350b2114SRobert Mustacchi 	 * than go through and use pciehpc_get_slot_state(). This is important
1778350b2114SRobert Mustacchi 	 * to make sure that we can get hardware in sync with the kernel.
1779350b2114SRobert Mustacchi 	 */
1780350b2114SRobert Mustacchi 	curr_state = slot_p->hs_info.cn_state;
1781350b2114SRobert Mustacchi 	control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
1782350b2114SRobert Mustacchi 	status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
1783350b2114SRobert Mustacchi 
1784350b2114SRobert Mustacchi 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
1785350b2114SRobert Mustacchi 		switch (curr_state) {
1786350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_ENABLED:
1787350b2114SRobert Mustacchi 			pciehpc_dispatch_state_sync(ctrl_p,
1788350b2114SRobert Mustacchi 			    DDI_HP_CN_STATE_EMPTY);
1789350b2114SRobert Mustacchi 			break;
1790350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_EMPTY:
1791350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_PRESENT:
1792350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_POWERED:
1793350b2114SRobert Mustacchi 			if (ctrl_p->hc_has_pwr &&
1794350b2114SRobert Mustacchi 			    (control & PCIE_SLOTCTL_PWR_CONTROL) == 0) {
1795350b2114SRobert Mustacchi 				slot_p->hs_info.cn_state =
1796350b2114SRobert Mustacchi 				    DDI_HP_CN_STATE_POWERED;
1797350b2114SRobert Mustacchi 				pciehpc_dispatch_state_sync(ctrl_p,
1798350b2114SRobert Mustacchi 				    DDI_HP_CN_STATE_EMPTY);
1799350b2114SRobert Mustacchi 			} else {
1800350b2114SRobert Mustacchi 				slot_p->hs_info.cn_state =
1801350b2114SRobert Mustacchi 				    DDI_HP_CN_STATE_EMPTY;
1802350b2114SRobert Mustacchi 				pciehpc_enable_state_sync_leds(ctrl_p);
1803350b2114SRobert Mustacchi 			}
1804350b2114SRobert Mustacchi 			break;
1805350b2114SRobert Mustacchi 		default:
1806350b2114SRobert Mustacchi 			dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
1807350b2114SRobert Mustacchi 			    "connector state: 0x%x", curr_state);
1808350b2114SRobert Mustacchi 			break;
1809350b2114SRobert Mustacchi 		}
1810350b2114SRobert Mustacchi 
1811350b2114SRobert Mustacchi 		return;
1812350b2114SRobert Mustacchi 	}
1813350b2114SRobert Mustacchi 
1814350b2114SRobert Mustacchi 	/*
1815350b2114SRobert Mustacchi 	 * If we don't have a power controller, don't bother looking at this.
1816350b2114SRobert Mustacchi 	 * There's nothing we can really do and we'll let the main case attempt
1817350b2114SRobert Mustacchi 	 * to online this.
1818350b2114SRobert Mustacchi 	 */
1819350b2114SRobert Mustacchi 	if (ctrl_p->hc_has_pwr && (control & PCIE_SLOTCTL_PWR_CONTROL) != 0) {
1820350b2114SRobert Mustacchi 		switch (curr_state) {
1821350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_EMPTY:
1822350b2114SRobert Mustacchi 			pciehpc_dispatch_state_sync(ctrl_p, online_targ);
1823350b2114SRobert Mustacchi 			break;
1824350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_PRESENT:
1825350b2114SRobert Mustacchi 			if (curr_state == online_targ) {
1826350b2114SRobert Mustacchi 				pciehpc_enable_state_sync_leds(ctrl_p);
1827350b2114SRobert Mustacchi 				break;
1828350b2114SRobert Mustacchi 			}
1829350b2114SRobert Mustacchi 			pciehpc_dispatch_state_sync(ctrl_p, online_targ);
1830350b2114SRobert Mustacchi 			break;
1831350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_POWERED:
1832350b2114SRobert Mustacchi 			dev_err(ctrl_p->hc_dip, CE_WARN, "device powered off "
1833350b2114SRobert Mustacchi 			    "somehow from prior powered state, attempting "
1834350b2114SRobert Mustacchi 			    "recovery");
1835350b2114SRobert Mustacchi 			slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
1836350b2114SRobert Mustacchi 			if (online_targ > DDI_HP_CN_STATE_PRESENT) {
1837350b2114SRobert Mustacchi 				pciehpc_dispatch_state_sync(ctrl_p,
1838350b2114SRobert Mustacchi 				    online_targ);
1839350b2114SRobert Mustacchi 			} else {
1840350b2114SRobert Mustacchi 				pciehpc_enable_state_sync_leds(ctrl_p);
1841350b2114SRobert Mustacchi 			}
1842350b2114SRobert Mustacchi 			break;
1843350b2114SRobert Mustacchi 		case DDI_HP_CN_STATE_ENABLED:
1844350b2114SRobert Mustacchi 			/*
1845350b2114SRobert Mustacchi 			 * This case seems very strange. We had a device that we
1846350b2114SRobert Mustacchi 			 * enumerated and was online and something that wasn't
1847350b2114SRobert Mustacchi 			 * us powerd off the slot. This is possibly a
1848350b2114SRobert Mustacchi 			 * recoverable state, but it seems hard to understand
1849350b2114SRobert Mustacchi 			 * what the proper path to go here is. While we could
1850350b2114SRobert Mustacchi 			 * try to unprobe it, it's a real mystery how that
1851350b2114SRobert Mustacchi 			 * happened and even that path might not be safe. If
1852350b2114SRobert Mustacchi 			 * this kind of state is actually encountered in the
1853350b2114SRobert Mustacchi 			 * wild and during this startup window of the device,
1854350b2114SRobert Mustacchi 			 * then we'll need to figure out how to handle it there.
1855350b2114SRobert Mustacchi 			 * Odds are it's either a software bug in this driver or
1856350b2114SRobert Mustacchi 			 * something is going very wrong with hardware and as
1857350b2114SRobert Mustacchi 			 * such, it's hard to predict what the solution is.
1858350b2114SRobert Mustacchi 			 */
1859350b2114SRobert Mustacchi 			dev_err(ctrl_p->hc_dip, CE_PANIC, "device powered off "
1860350b2114SRobert Mustacchi 			    "somehow from prior enabled state unable to "
1861350b2114SRobert Mustacchi 			    "recover");
1862350b2114SRobert Mustacchi 			break;
1863350b2114SRobert Mustacchi 		default:
1864350b2114SRobert Mustacchi 			dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
1865350b2114SRobert Mustacchi 			    "connector state: 0x%x", curr_state);
1866350b2114SRobert Mustacchi 		}
1867350b2114SRobert Mustacchi 		return;
1868350b2114SRobert Mustacchi 	}
1869350b2114SRobert Mustacchi 
1870350b2114SRobert Mustacchi 	/*
1871350b2114SRobert Mustacchi 	 * While we should consider checking for a power fault here, if it was
1872350b2114SRobert Mustacchi 	 * injected just after we cleared everythign as part of interrupt
1873350b2114SRobert Mustacchi 	 * enable, then we'll get that injected normally and allow that to
1874350b2114SRobert Mustacchi 	 * happen naturally.
1875350b2114SRobert Mustacchi 	 */
1876350b2114SRobert Mustacchi 
1877350b2114SRobert Mustacchi 	switch (curr_state) {
1878350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_ENABLED:
1879350b2114SRobert Mustacchi 		pciehpc_enable_state_sync_leds(ctrl_p);
1880350b2114SRobert Mustacchi 		break;
1881350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_POWERED:
1882350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_EMPTY:
1883350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_PRESENT:
1884350b2114SRobert Mustacchi 		if (curr_state == online_targ) {
1885350b2114SRobert Mustacchi 			pciehpc_enable_state_sync_leds(ctrl_p);
1886350b2114SRobert Mustacchi 		} else {
1887350b2114SRobert Mustacchi 			pciehpc_dispatch_state_sync(ctrl_p, online_targ);
1888350b2114SRobert Mustacchi 		}
1889350b2114SRobert Mustacchi 		break;
1890350b2114SRobert Mustacchi 	default:
1891350b2114SRobert Mustacchi 		dev_err(ctrl_p->hc_dip, CE_PANIC, "encountered invalid "
1892350b2114SRobert Mustacchi 		    "connector state: 0x%x", curr_state);
1893350b2114SRobert Mustacchi 	}
1894350b2114SRobert Mustacchi }
1895350b2114SRobert Mustacchi 
1896350b2114SRobert Mustacchi /*
189726947304SEvan Yan  * Enable hot plug interrupts.
189826947304SEvan Yan  * Note: this is only for Native hot plug mode.
189926947304SEvan Yan  */
190026947304SEvan Yan static int
pciehpc_enable_intr(pcie_hp_ctrl_t * ctrl_p)190126947304SEvan Yan pciehpc_enable_intr(pcie_hp_ctrl_t *ctrl_p)
190226947304SEvan Yan {
190326947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
190426947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
190526947304SEvan Yan 	uint16_t	reg;
1906fe426563SRick Altherr 	uint16_t	intr_mask = PCIE_SLOTCTL_INTR_MASK;
1907fe426563SRick Altherr 
1908350b2114SRobert Mustacchi 	mutex_enter(&ctrl_p->hc_mutex);
1909350b2114SRobert Mustacchi 
1910fe426563SRick Altherr 	/*
1911fe426563SRick Altherr 	 * power fault detection interrupt is enabled only
1912fe426563SRick Altherr 	 * when the slot is powered ON
1913fe426563SRick Altherr 	 */
1914fe426563SRick Altherr 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED)
1915fe426563SRick Altherr 		intr_mask &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
1916fe426563SRick Altherr 
1917fe426563SRick Altherr 	/*
1918fe426563SRick Altherr 	 * enable interrupt sources but leave the top-level
1919fe426563SRick Altherr 	 * interrupt disabled. some sources may generate a
1920fe426563SRick Altherr 	 * spurrious event when they are first enabled.
1921fe426563SRick Altherr 	 * by leaving the top-level interrupt disabled, those
1922fe426563SRick Altherr 	 * can be cleared first.
1923fe426563SRick Altherr 	 */
1924fe426563SRick Altherr 	reg = pciehpc_reg_get16(ctrl_p,
1925fe426563SRick Altherr 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1926fe426563SRick Altherr 	pciehpc_reg_put16(ctrl_p,
1927fe426563SRick Altherr 	    bus_p->bus_pcie_off + PCIE_SLOTCTL,
1928fe426563SRick Altherr 	    reg | (intr_mask & ~PCIE_SLOTCTL_HP_INTR_EN));
192926947304SEvan Yan 
193026947304SEvan Yan 	/* clear any interrupt status bits */
193126947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
193226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
193326947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
193426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
193526947304SEvan Yan 
1936fe426563SRick Altherr 	/* enable top-level interrupt */
193726947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
193826947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
1939fe426563SRick Altherr 	pciehpc_reg_put16(ctrl_p,
1940fe426563SRick Altherr 	    bus_p->bus_pcie_off + PCIE_SLOTCTL,
1941fe426563SRick Altherr 	    reg | intr_mask);
194226947304SEvan Yan 
1943350b2114SRobert Mustacchi 	/*
1944350b2114SRobert Mustacchi 	 * Now, and only now that interrupts are enabled can we go back and
1945350b2114SRobert Mustacchi 	 * perform state synchronization that is required of the system. This
1946350b2114SRobert Mustacchi 	 * happens in a few steps. We have previously checked to see if we
1947350b2114SRobert Mustacchi 	 * should be in the ENABLED or POWERED state. However, it is quite
1948350b2114SRobert Mustacchi 	 * possible that hardware was left at its PCIe default of power being
1949350b2114SRobert Mustacchi 	 * enabled, even if no device is present. Because we have interrupts
1950350b2114SRobert Mustacchi 	 * enabled, if there is a change after this point, then it will be
1951350b2114SRobert Mustacchi 	 * caught. See the theory statement for more information.
1952350b2114SRobert Mustacchi 	 */
1953350b2114SRobert Mustacchi 	pciehpc_enable_state_sync(ctrl_p);
1954350b2114SRobert Mustacchi 	mutex_exit(&ctrl_p->hc_mutex);
1955350b2114SRobert Mustacchi 
195626947304SEvan Yan 	return (DDI_SUCCESS);
195726947304SEvan Yan }
195826947304SEvan Yan 
195926947304SEvan Yan /*
196026947304SEvan Yan  * Disable hot plug interrupts.
196126947304SEvan Yan  * Note: this is only for Native hot plug mode.
196226947304SEvan Yan  */
196326947304SEvan Yan static int
pciehpc_disable_intr(pcie_hp_ctrl_t * ctrl_p)196426947304SEvan Yan pciehpc_disable_intr(pcie_hp_ctrl_t *ctrl_p)
196526947304SEvan Yan {
196626947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
196726947304SEvan Yan 	uint16_t	reg;
196826947304SEvan Yan 
196926947304SEvan Yan 	/* read the Slot Control Register */
197026947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
197126947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
197226947304SEvan Yan 
197326947304SEvan Yan 	/* disable all interrupts */
197426947304SEvan Yan 	reg &= ~(PCIE_SLOTCTL_INTR_MASK);
197526947304SEvan Yan 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL, reg);
197626947304SEvan Yan 
197726947304SEvan Yan 	/* clear any interrupt status bits */
197826947304SEvan Yan 	reg = pciehpc_reg_get16(ctrl_p,
197926947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
198026947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
198126947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS, reg);
198226947304SEvan Yan 
198326947304SEvan Yan 	return (DDI_SUCCESS);
198426947304SEvan Yan }
198526947304SEvan Yan 
198626947304SEvan Yan /*
198726947304SEvan Yan  * Allocate a new hotplug controller and slot structures for HPC
198826947304SEvan Yan  * associated with this dip.
198926947304SEvan Yan  */
199026947304SEvan Yan static pcie_hp_ctrl_t *
pciehpc_create_controller(dev_info_t * dip)199126947304SEvan Yan pciehpc_create_controller(dev_info_t *dip)
199226947304SEvan Yan {
199326947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
199426947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
199526947304SEvan Yan 
199626947304SEvan Yan 	ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
199726947304SEvan Yan 	ctrl_p->hc_dip = dip;
199826947304SEvan Yan 
199926947304SEvan Yan 	/* Allocate a new slot structure. */
200026947304SEvan Yan 	ctrl_p->hc_slots[0] = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
200126947304SEvan Yan 	ctrl_p->hc_slots[0]->hs_num = 0;
200226947304SEvan Yan 	ctrl_p->hc_slots[0]->hs_ctrl = ctrl_p;
200326947304SEvan Yan 
200426947304SEvan Yan 	/* Initialize the interrupt mutex */
200526947304SEvan Yan 	mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER,
200626947304SEvan Yan 	    (void *)PCIE_INTR_PRI);
200726947304SEvan Yan 
200826947304SEvan Yan 	/* Initialize synchronization conditional variable */
200926947304SEvan Yan 	cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
201026947304SEvan Yan 	ctrl_p->hc_cmd_pending = B_FALSE;
201126947304SEvan Yan 
201226947304SEvan Yan 	bus_p->bus_hp_curr_mode = PCIE_NATIVE_HP_MODE;
201326947304SEvan Yan 	PCIE_SET_HP_CTRL(dip, ctrl_p);
201426947304SEvan Yan 
201526947304SEvan Yan 	return (ctrl_p);
201626947304SEvan Yan }
201726947304SEvan Yan 
201826947304SEvan Yan /*
201926947304SEvan Yan  * Remove the HPC controller and slot structures
202026947304SEvan Yan  */
202126947304SEvan Yan static void
pciehpc_destroy_controller(dev_info_t * dip)202226947304SEvan Yan pciehpc_destroy_controller(dev_info_t *dip)
202326947304SEvan Yan {
202426947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p;
202526947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
202626947304SEvan Yan 
202726947304SEvan Yan 	/* get the soft state structure for this dip */
202826947304SEvan Yan 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
202926947304SEvan Yan 		return;
203026947304SEvan Yan 
203126947304SEvan Yan 	PCIE_SET_HP_CTRL(dip, NULL);
203226947304SEvan Yan 	bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
203326947304SEvan Yan 
203426947304SEvan Yan 	mutex_destroy(&ctrl_p->hc_mutex);
203526947304SEvan Yan 	cv_destroy(&ctrl_p->hc_cmd_comp_cv);
203626947304SEvan Yan 	kmem_free(ctrl_p->hc_slots[0], sizeof (pcie_hp_slot_t));
203726947304SEvan Yan 	kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
203826947304SEvan Yan }
203926947304SEvan Yan 
204026947304SEvan Yan /*
204126947304SEvan Yan  * Register the PCI-E hot plug slot with DDI HP framework.
204226947304SEvan Yan  */
204326947304SEvan Yan static int
pciehpc_register_slot(pcie_hp_ctrl_t * ctrl_p)204426947304SEvan Yan pciehpc_register_slot(pcie_hp_ctrl_t *ctrl_p)
204526947304SEvan Yan {
204626947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
204726947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
204826947304SEvan Yan 
204926947304SEvan Yan 	/* register the slot with DDI HP framework */
205026947304SEvan Yan 	if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
205126947304SEvan Yan 		PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
205226947304SEvan Yan 		    slot_p->hs_phy_slot_num);
205326947304SEvan Yan 		return (DDI_FAILURE);
205426947304SEvan Yan 	}
205526947304SEvan Yan 
205626947304SEvan Yan 	pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
205726947304SEvan Yan 	    slot_p->hs_minor), slot_p->hs_device_num);
205826947304SEvan Yan 
205926947304SEvan Yan 	PCIE_DBG("pciehpc_register_slot(): registered slot %d\n",
206026947304SEvan Yan 	    slot_p->hs_phy_slot_num);
206126947304SEvan Yan 
206226947304SEvan Yan 	return (DDI_SUCCESS);
206326947304SEvan Yan }
206426947304SEvan Yan 
206526947304SEvan Yan /*
206626947304SEvan Yan  * Unregister the PCI-E hot plug slot from DDI HP framework.
206726947304SEvan Yan  */
206826947304SEvan Yan static int
pciehpc_unregister_slot(pcie_hp_ctrl_t * ctrl_p)206926947304SEvan Yan pciehpc_unregister_slot(pcie_hp_ctrl_t *ctrl_p)
207026947304SEvan Yan {
207126947304SEvan Yan 	pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[0];
207226947304SEvan Yan 	dev_info_t	*dip = ctrl_p->hc_dip;
207326947304SEvan Yan 
207426947304SEvan Yan 	pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
207526947304SEvan Yan 	    slot_p->hs_minor));
207626947304SEvan Yan 
207726947304SEvan Yan 	/* unregister the slot with DDI HP framework */
207826947304SEvan Yan 	if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) != NDI_SUCCESS) {
207926947304SEvan Yan 		PCIE_DBG("pciehpc_unregister_slot() "
208026947304SEvan Yan 		    "failed to unregister slot %d\n", slot_p->hs_phy_slot_num);
208126947304SEvan Yan 		return (DDI_FAILURE);
208226947304SEvan Yan 	}
208326947304SEvan Yan 
208426947304SEvan Yan 	PCIE_DBG("pciehpc_unregister_slot(): unregistered slot %d\n",
208526947304SEvan Yan 	    slot_p->hs_phy_slot_num);
208626947304SEvan Yan 
208726947304SEvan Yan 	return (DDI_SUCCESS);
208826947304SEvan Yan }
208926947304SEvan Yan 
2090350b2114SRobert Mustacchi static pciehpc_slot_power_t
pciehpc_slot_power_state(pcie_hp_slot_t * slot_p)2091350b2114SRobert Mustacchi pciehpc_slot_power_state(pcie_hp_slot_t *slot_p)
209226947304SEvan Yan {
209326947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
209426947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2095350b2114SRobert Mustacchi 	uint16_t control, status;
2096350b2114SRobert Mustacchi 	pciehpc_slot_power_t state = 0;
209726947304SEvan Yan 
209826947304SEvan Yan 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
209926947304SEvan Yan 
2100b9a2a14bSRobert Mustacchi 	if (!ctrl_p->hc_has_pwr) {
2101350b2114SRobert Mustacchi 		return (PSP_NO_CONTROLLER);
2102350b2114SRobert Mustacchi 	} else {
2103350b2114SRobert Mustacchi 		state |= PSP_HAS_CONTROLLER;
2104b9a2a14bSRobert Mustacchi 	}
2105b9a2a14bSRobert Mustacchi 
2106350b2114SRobert Mustacchi 	control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
2107350b2114SRobert Mustacchi 	status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
2108350b2114SRobert Mustacchi 
2109350b2114SRobert Mustacchi 	if ((control & PCIE_SLOTCTL_PWR_CONTROL) != 0)
2110350b2114SRobert Mustacchi 		state |= PSP_OFF;
2111350b2114SRobert Mustacchi 
2112350b2114SRobert Mustacchi 	if ((status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) != 0)
2113350b2114SRobert Mustacchi 		state |= PSP_FAULT;
2114350b2114SRobert Mustacchi 
2115350b2114SRobert Mustacchi 	return (state);
2116350b2114SRobert Mustacchi }
211726947304SEvan Yan 
211826947304SEvan Yan /*
2119350b2114SRobert Mustacchi  * Wait for a PCIe slot to be considered active per the PCIe hotplug rules. If
2120350b2114SRobert Mustacchi  * there is no DLL active reporting capability then we wait up to 1 second and
2121350b2114SRobert Mustacchi  * just assume it was successful. Regardless of whether or not we have explicit
2122350b2114SRobert Mustacchi  * power control, the device is still powering on and may not be ready to work.
212326947304SEvan Yan  */
2124350b2114SRobert Mustacchi static boolean_t
pciehpc_slot_wait_for_active(pcie_hp_slot_t * slot_p)2125350b2114SRobert Mustacchi pciehpc_slot_wait_for_active(pcie_hp_slot_t *slot_p)
2126350b2114SRobert Mustacchi {
2127350b2114SRobert Mustacchi 	pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
2128350b2114SRobert Mustacchi 	pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
212926947304SEvan Yan 
2130350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
213126947304SEvan Yan 
213226947304SEvan Yan 	if (ctrl_p->hc_dll_active_rep) {
2133ac91f7fcSKeith M Wesolowski 		clock_t deadline;
2134350b2114SRobert Mustacchi 		uint16_t status;
2135ac91f7fcSKeith M Wesolowski 
2136ac91f7fcSKeith M Wesolowski 		/* wait 1 sec for the DLL State Changed event */
213726947304SEvan Yan 		status = pciehpc_reg_get16(ctrl_p,
213826947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_LINKSTS);
213926947304SEvan Yan 
2140ac91f7fcSKeith M Wesolowski 		deadline = ddi_get_lbolt() +
2141ac91f7fcSKeith M Wesolowski 		    SEC_TO_TICK(PCIE_HP_DLL_STATE_CHANGE_TIMEOUT);
2142ac91f7fcSKeith M Wesolowski 
2143ac91f7fcSKeith M Wesolowski 		while ((status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0 &&
2144ac91f7fcSKeith M Wesolowski 		    ddi_get_lbolt() < deadline) {
214526947304SEvan Yan 			(void) cv_timedwait(&slot_p->hs_dll_active_cv,
2146ac91f7fcSKeith M Wesolowski 			    &ctrl_p->hc_mutex, deadline);
214726947304SEvan Yan 
214826947304SEvan Yan 			/* check Link status */
214926947304SEvan Yan 			status =  pciehpc_reg_get16(ctrl_p,
215026947304SEvan Yan 			    bus_p->bus_pcie_off +
215126947304SEvan Yan 			    PCIE_LINKSTS);
215226947304SEvan Yan 		}
215326947304SEvan Yan 
2154350b2114SRobert Mustacchi 		if ((status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0) {
2155350b2114SRobert Mustacchi 			return (B_FALSE);
2156350b2114SRobert Mustacchi 		}
2157ac91f7fcSKeith M Wesolowski 	} else {
215826947304SEvan Yan 		/* wait 1 sec for link to come up */
215926947304SEvan Yan 		delay(drv_usectohz(1000000));
2160ac91f7fcSKeith M Wesolowski 	}
216126947304SEvan Yan 
2162350b2114SRobert Mustacchi 	return (B_TRUE);
2163350b2114SRobert Mustacchi }
2164350b2114SRobert Mustacchi 
2165350b2114SRobert Mustacchi /*
2166350b2114SRobert Mustacchi  * This takes care of all the logic for trying to verify a slot's state that
2167350b2114SRobert Mustacchi  * does not have an explicit power controller. If this is a surprise insertion,
2168350b2114SRobert Mustacchi  * we still need to wait for the data link layer to become active even if we
2169350b2114SRobert Mustacchi  * don't explicitly control power. We do this in three steps:
2170350b2114SRobert Mustacchi  *
2171350b2114SRobert Mustacchi  * 1) Verify the slot is powered at least.
2172350b2114SRobert Mustacchi  * 2) Wait for the slot to be active.
2173350b2114SRobert Mustacchi  * 3) Verify the slot is still powered after that.
2174350b2114SRobert Mustacchi  */
2175350b2114SRobert Mustacchi static int
pciehpc_slot_noctrl_active(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result)2176350b2114SRobert Mustacchi pciehpc_slot_noctrl_active(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
2177350b2114SRobert Mustacchi {
2178350b2114SRobert Mustacchi 	pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
2179350b2114SRobert Mustacchi 
2180350b2114SRobert Mustacchi 	VERIFY3U(ctrl_p->hc_has_pwr, ==, B_FALSE);
2181350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
2182350b2114SRobert Mustacchi 
2183350b2114SRobert Mustacchi 	pciehpc_get_slot_state(slot_p);
2184350b2114SRobert Mustacchi 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
2185350b2114SRobert Mustacchi 		return (DDI_FAILURE);
2186350b2114SRobert Mustacchi 	}
2187350b2114SRobert Mustacchi 
2188350b2114SRobert Mustacchi 	/*
2189350b2114SRobert Mustacchi 	 * Regardless of whether this worked or failed we must check the slot
2190350b2114SRobert Mustacchi 	 * state again.
2191350b2114SRobert Mustacchi 	 */
2192350b2114SRobert Mustacchi 	if (!pciehpc_slot_wait_for_active(slot_p)) {
2193350b2114SRobert Mustacchi 		cmn_err(CE_WARN, "pciehpc_slot_poweron_noctrl (slot %d): "
2194350b2114SRobert Mustacchi 		    "device failed to become active", slot_p->hs_phy_slot_num);
2195350b2114SRobert Mustacchi 		return (DDI_FAILURE);
2196350b2114SRobert Mustacchi 	}
2197350b2114SRobert Mustacchi 	pciehpc_get_slot_state(slot_p);
2198350b2114SRobert Mustacchi 	*result = slot_p->hs_info.cn_state;
2199350b2114SRobert Mustacchi 	if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
2200a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
2201a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = false;
2202a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
2203a9413143SRobert Mustacchi 		pciehpc_sync_leds_to_hw(slot_p);
2204350b2114SRobert Mustacchi 		return (DDI_SUCCESS);
2205350b2114SRobert Mustacchi 	} else {
2206350b2114SRobert Mustacchi 		return (DDI_FAILURE);
2207350b2114SRobert Mustacchi 	}
2208350b2114SRobert Mustacchi }
2209350b2114SRobert Mustacchi 
2210350b2114SRobert Mustacchi /*
2211350b2114SRobert Mustacchi  * Poweron/Enable the slot.
2212350b2114SRobert Mustacchi  *
2213350b2114SRobert Mustacchi  * Note: This function is called by DDI HP framework at kernel context only
2214350b2114SRobert Mustacchi  *
2215350b2114SRobert Mustacchi  * We intend for this function to be idempotent.  That is, when we return, if
2216350b2114SRobert Mustacchi  * the slot we've been asked to turn on has a device present, and has a power
2217350b2114SRobert Mustacchi  * controller, then a successful return guarantees all of the following,
2218350b2114SRobert Mustacchi  * regardless of the hardware or software state that existed when called:
2219350b2114SRobert Mustacchi  *
2220350b2114SRobert Mustacchi  * 1. The power controller enable bit is clear (asserted).
2221350b2114SRobert Mustacchi  * 2. If DLL State Change is supported by the bridge, we waited until DLL Active
2222350b2114SRobert Mustacchi  *    was asserted; otherwise we waited at least one second after the first
2223350b2114SRobert Mustacchi  *    moment we knew for certain that the power controller was enabled.
2224350b2114SRobert Mustacchi  * 3. Any power fault that was previously asserted in the status register has
2225350b2114SRobert Mustacchi  *    been acknowledged and cleared, allowing detection of subsequent faults if
2226350b2114SRobert Mustacchi  *    supported by hardware.
2227350b2114SRobert Mustacchi  * 4. The power indicator is on (if it exists).
2228350b2114SRobert Mustacchi  * 5. The MRL, if it exists, is latched.
2229350b2114SRobert Mustacchi  *
2230350b2114SRobert Mustacchi  * If we fail, either this slot has no power control capability or the following
2231350b2114SRobert Mustacchi  * guarantees are made:
2232350b2114SRobert Mustacchi  *
2233350b2114SRobert Mustacchi  * 1. We have attempted to disable the power controller for this slot.
2234350b2114SRobert Mustacchi  * 2. We have attempted to disable the power indicator for this slot.
2235350b2114SRobert Mustacchi  *
2236350b2114SRobert Mustacchi  * In the failure case, *result has undefined contents.  This function does not
2237350b2114SRobert Mustacchi  * change the contents of slot_p->hs_info.cn_state.  This allows callers to act
2238350b2114SRobert Mustacchi  * upon the previous software state (preserved by this function), the new
2239350b2114SRobert Mustacchi  * software state (in *result if successful), and the current hardware state
2240350b2114SRobert Mustacchi  * which can be obtained via pciehpc_get_slot_state().
2241350b2114SRobert Mustacchi  */
2242350b2114SRobert Mustacchi static int
pciehpc_slot_poweron(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result)2243350b2114SRobert Mustacchi pciehpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
2244350b2114SRobert Mustacchi {
2245350b2114SRobert Mustacchi 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
2246350b2114SRobert Mustacchi 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2247350b2114SRobert Mustacchi 	uint16_t	status, control;
2248350b2114SRobert Mustacchi 
2249350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
2250350b2114SRobert Mustacchi 
2251350b2114SRobert Mustacchi 	/*
2252350b2114SRobert Mustacchi 	 * If the hardware doesn't have support for a power controller, then
2253350b2114SRobert Mustacchi 	 * that generally means that power is already on or at the least there
2254350b2114SRobert Mustacchi 	 * isn't very much else we can do and the PCIe spec says it's the
2255350b2114SRobert Mustacchi 	 * responsibility of the controller to have turned it on if a device is
2256350b2114SRobert Mustacchi 	 * present.  We don't care whether a device is present in this case,
2257350b2114SRobert Mustacchi 	 * though, because we've been asked to turn on power and we know that we
2258350b2114SRobert Mustacchi 	 * cannot.  Either a device is present and power is already on, in which
2259350b2114SRobert Mustacchi 	 * case calling code can figure that out, or no device is present and
2260350b2114SRobert Mustacchi 	 * we'd fail even if we had a controller.  Either way, we still indicate
2261350b2114SRobert Mustacchi 	 * that is a failure since we can't change it and instead rely on code
2262350b2114SRobert Mustacchi 	 * executing the actual state machine to figure out how to handle this.
2263350b2114SRobert Mustacchi 	 */
2264350b2114SRobert Mustacchi 	if (!ctrl_p->hc_has_pwr) {
2265350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweron (slot %d): no power control "
2266350b2114SRobert Mustacchi 		    "capability, but was asked to power on\n",
2267350b2114SRobert Mustacchi 		    slot_p->hs_phy_slot_num);
2268350b2114SRobert Mustacchi 		return (DDI_FAILURE);
2269350b2114SRobert Mustacchi 	}
2270350b2114SRobert Mustacchi 
2271350b2114SRobert Mustacchi 	/*
2272350b2114SRobert Mustacchi 	 * We need the current state of the slot control register to figure out
2273350b2114SRobert Mustacchi 	 * whether the power controller is enabled already.  Note that this is
2274350b2114SRobert Mustacchi 	 * not a status bit: it can't tell us whether power is actually on or
2275350b2114SRobert Mustacchi 	 * off, only what the last control input was.  We also grab the status
2276350b2114SRobert Mustacchi 	 * register here as we need several bits from it.
2277350b2114SRobert Mustacchi 	 */
2278350b2114SRobert Mustacchi 	control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
2279350b2114SRobert Mustacchi 	status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
2280350b2114SRobert Mustacchi 
2281350b2114SRobert Mustacchi 	/*
2282350b2114SRobert Mustacchi 	 * If there's no device present, we need to fail.
2283350b2114SRobert Mustacchi 	 */
2284350b2114SRobert Mustacchi 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
2285350b2114SRobert Mustacchi 		/* slot is empty */
2286350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweron (slot %d): slot is empty\n",
2287350b2114SRobert Mustacchi 		    slot_p->hs_phy_slot_num);
2288350b2114SRobert Mustacchi 		goto cleanup;
2289350b2114SRobert Mustacchi 	}
2290350b2114SRobert Mustacchi 
2291350b2114SRobert Mustacchi 	/*
2292350b2114SRobert Mustacchi 	 * If there's an MRL and it's open, we need to fail.
2293350b2114SRobert Mustacchi 	 */
2294350b2114SRobert Mustacchi 	if ((ctrl_p->hc_has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
2295350b2114SRobert Mustacchi 		cmn_err(CE_WARN, "pciehpc_slot_poweron (slot %d): MRL switch "
2296350b2114SRobert Mustacchi 		    "is open", slot_p->hs_phy_slot_num);
2297350b2114SRobert Mustacchi 		goto cleanup;
2298350b2114SRobert Mustacchi 	}
2299350b2114SRobert Mustacchi 
2300350b2114SRobert Mustacchi 	/*
2301350b2114SRobert Mustacchi 	 * The power controller is already on, but we're in a state below
2302350b2114SRobert Mustacchi 	 * POWERED.  This shouldn't happen, but there are any number of ways
2303350b2114SRobert Mustacchi 	 * that it can; we simply note this if debugging and move on.
2304350b2114SRobert Mustacchi 	 */
2305350b2114SRobert Mustacchi 	if ((control & PCIE_SLOTCTL_PWR_CONTROL) == 0 &&
2306350b2114SRobert Mustacchi 	    slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
2307350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweron (slot %d): controller is "
2308350b2114SRobert Mustacchi 		    "already enabled in SW state %d; continuing\n",
2309350b2114SRobert Mustacchi 		    slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
2310350b2114SRobert Mustacchi 		goto alreadyon;
2311350b2114SRobert Mustacchi 	}
2312350b2114SRobert Mustacchi 
2313350b2114SRobert Mustacchi 	/*
2314350b2114SRobert Mustacchi 	 * The power controller has been turned off (which doesn't mean it *is*
2315350b2114SRobert Mustacchi 	 * off), but software thinks it's on.  This is pretty bad, and we
2316350b2114SRobert Mustacchi 	 * probably need to consider doing something here to reset the state
2317350b2114SRobert Mustacchi 	 * machine because upper layers are likely to be confused.  We will
2318350b2114SRobert Mustacchi 	 * nevertheless turn on the controller and hope the right things happen
2319350b2114SRobert Mustacchi 	 * above us.
2320350b2114SRobert Mustacchi 	 */
2321350b2114SRobert Mustacchi 	if ((control & PCIE_SLOTCTL_PWR_CONTROL) != 0 &&
2322350b2114SRobert Mustacchi 	    slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
2323350b2114SRobert Mustacchi 		cmn_err(CE_WARN, "pciehpc_slot_poweron (slot %d): SW state is "
2324350b2114SRobert Mustacchi 		    "already %d but power controller is disabled; continuing",
2325350b2114SRobert Mustacchi 		    slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
2326350b2114SRobert Mustacchi 	}
2327350b2114SRobert Mustacchi 
2328350b2114SRobert Mustacchi 	/*
2329a9413143SRobert Mustacchi 	 * Enabling power to the slot involves:
2330a9413143SRobert Mustacchi 	 *	1. Updating our LED state to indicate the transition is
2331a9413143SRobert Mustacchi 	 *	   beginning and clearing out other aspects.
2332350b2114SRobert Mustacchi 	 *	2. Set power control ON in Slot Control Reigster and
2333350b2114SRobert Mustacchi 	 *	   wait for Command Completed Interrupt or 1 sec timeout.
2334350b2114SRobert Mustacchi 	 *	3. If Data Link Layer State Changed events are supported
2335350b2114SRobert Mustacchi 	 *	   then wait for the event to indicate Data Layer Link
2336350b2114SRobert Mustacchi 	 *	   is active. The time out value for this event is 1 second.
2337350b2114SRobert Mustacchi 	 *	   This is specified in PCI-E version 1.1.
2338350b2114SRobert Mustacchi 	 *	4. Set power LED to be ON.
2339350b2114SRobert Mustacchi 	 */
2340350b2114SRobert Mustacchi 
2341a9413143SRobert Mustacchi 	/*
2342a9413143SRobert Mustacchi 	 * If power is already on, we skip indicating a power transition is
2343a9413143SRobert Mustacchi 	 * going on. However, we always try to clear out the error state LEDs at
2344a9413143SRobert Mustacchi 	 * this point.
2345a9413143SRobert Mustacchi 	 */
2346a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = true;
2347350b2114SRobert Mustacchi alreadyon:
2348a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
2349a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = false;
2350a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
2351350b2114SRobert Mustacchi 
2352350b2114SRobert Mustacchi 	/* 2. set power control to ON */
2353350b2114SRobert Mustacchi 	control =  pciehpc_reg_get16(ctrl_p,
2354350b2114SRobert Mustacchi 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
2355350b2114SRobert Mustacchi 	control &= ~PCIE_SLOTCTL_PWR_CONTROL;
2356350b2114SRobert Mustacchi 	pciehpc_issue_hpc_command(ctrl_p, control);
2357350b2114SRobert Mustacchi 
2358350b2114SRobert Mustacchi 	/* 3. wait for DLL State Change event, if it's supported */
2359350b2114SRobert Mustacchi 	if (!pciehpc_slot_wait_for_active(slot_p))
2360350b2114SRobert Mustacchi 		goto cleanup;
2361350b2114SRobert Mustacchi 
236226947304SEvan Yan 	/* check power is really turned ON */
236326947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
236426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
236526947304SEvan Yan 
236626947304SEvan Yan 	if (control & PCIE_SLOTCTL_PWR_CONTROL) {
2367350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweron (slot %d): power controller "
2368350b2114SRobert Mustacchi 		    "enable was disabled autonomously after SW enable",
236926947304SEvan Yan 		    slot_p->hs_phy_slot_num);
237026947304SEvan Yan 
2371350b2114SRobert Mustacchi 		goto cleanup;
237226947304SEvan Yan 	}
237326947304SEvan Yan 
237426947304SEvan Yan 	/* clear power fault status */
237526947304SEvan Yan 	status = pciehpc_reg_get16(ctrl_p,
237626947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTSTS);
237726947304SEvan Yan 	status |= PCIE_SLOTSTS_PWR_FAULT_DETECTED;
237826947304SEvan Yan 	pciehpc_reg_put16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS,
237926947304SEvan Yan 	    status);
238026947304SEvan Yan 
238126947304SEvan Yan 	/* enable power fault detection interrupt */
238226947304SEvan Yan 	control |= PCIE_SLOTCTL_PWR_FAULT_EN;
238326947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
238426947304SEvan Yan 
238526947304SEvan Yan 	/* 4. Set power LED to be ON */
2386a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
2387a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
2388a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
238926947304SEvan Yan 
239026947304SEvan Yan 	/* if EMI is present, turn it ON */
239126947304SEvan Yan 	if (ctrl_p->hc_has_emi_lock) {
239226947304SEvan Yan 		status = pciehpc_reg_get16(ctrl_p,
239326947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
239426947304SEvan Yan 
239526947304SEvan Yan 		if (!(status & PCIE_SLOTSTS_EMI_LOCK_SET)) {
239626947304SEvan Yan 			control = pciehpc_reg_get16(ctrl_p,
239726947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
239826947304SEvan Yan 			control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
239926947304SEvan Yan 			pciehpc_issue_hpc_command(ctrl_p, control);
240026947304SEvan Yan 
240126947304SEvan Yan 			/* wait 1 sec after toggling the state of EMI lock */
240226947304SEvan Yan 			delay(drv_usectohz(1000000));
240326947304SEvan Yan 		}
240426947304SEvan Yan 	}
240526947304SEvan Yan 
2406350b2114SRobert Mustacchi 	*result = slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
240726947304SEvan Yan 
240826947304SEvan Yan 	return (DDI_SUCCESS);
240926947304SEvan Yan 
2410350b2114SRobert Mustacchi cleanup:
241126947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
241226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
241326947304SEvan Yan 
241426947304SEvan Yan 	/* if power is ON, set power control to OFF */
2415350b2114SRobert Mustacchi 	if ((control & PCIE_SLOTCTL_PWR_CONTROL) == 0) {
241626947304SEvan Yan 		control |= PCIE_SLOTCTL_PWR_CONTROL;
241726947304SEvan Yan 		pciehpc_issue_hpc_command(ctrl_p, control);
241826947304SEvan Yan 	}
241926947304SEvan Yan 
2420350b2114SRobert Mustacchi 	/* set power led to OFF XXX what if HW/FW refused to turn off? */
2421a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
2422a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
2423a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
242426947304SEvan Yan 
242526947304SEvan Yan 	return (DDI_FAILURE);
242626947304SEvan Yan }
242726947304SEvan Yan 
2428350b2114SRobert Mustacchi /*
2429350b2114SRobert Mustacchi  * All the same considerations apply to poweroff; see notes above.
2430350b2114SRobert Mustacchi  */
243126947304SEvan Yan static int
pciehpc_slot_poweroff(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result)243226947304SEvan Yan pciehpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result)
243326947304SEvan Yan {
243426947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
243526947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
243626947304SEvan Yan 	uint16_t	status, control;
243726947304SEvan Yan 
243826947304SEvan Yan 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
243926947304SEvan Yan 
244026947304SEvan Yan 	/*
2441b9a2a14bSRobert Mustacchi 	 * Some devices do not have a power controller. In such cases we need to
2442b9a2a14bSRobert Mustacchi 	 * fail any request to power it off. If a device is being pulled, the
2443b9a2a14bSRobert Mustacchi 	 * state will generally have automatically been updated; however, if
2444b9a2a14bSRobert Mustacchi 	 * someone is asking for us to do something via an explicit request,
2445350b2114SRobert Mustacchi 	 * then this will fail.
2446b9a2a14bSRobert Mustacchi 	 */
2447b9a2a14bSRobert Mustacchi 	if (!ctrl_p->hc_has_pwr) {
2448350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweroff (slot %d): no power control "
2449350b2114SRobert Mustacchi 		    "capability, but was asked to power off\n",
2450b9a2a14bSRobert Mustacchi 		    slot_p->hs_phy_slot_num);
2451b9a2a14bSRobert Mustacchi 		return (DDI_ENOTSUP);
2452b9a2a14bSRobert Mustacchi 	}
2453b9a2a14bSRobert Mustacchi 
2454b9a2a14bSRobert Mustacchi 	/*
2455350b2114SRobert Mustacchi 	 * SW thinks the slot is already powered off.  Note this unexpected
2456350b2114SRobert Mustacchi 	 * condition and continue.
2457350b2114SRobert Mustacchi 	 */
2458350b2114SRobert Mustacchi 	if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
2459350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweroff (slot %d): SW state is "
2460350b2114SRobert Mustacchi 		    "already %d; continuing\n",
2461350b2114SRobert Mustacchi 		    slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
2462350b2114SRobert Mustacchi 	}
2463350b2114SRobert Mustacchi 
2464350b2114SRobert Mustacchi 	control = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTCTL);
2465350b2114SRobert Mustacchi 	status = pciehpc_reg_get16(ctrl_p, bus_p->bus_pcie_off + PCIE_SLOTSTS);
2466350b2114SRobert Mustacchi 
2467350b2114SRobert Mustacchi 	/*
2468350b2114SRobert Mustacchi 	 * The power controller has been turned off (which doesn't mean it *is*
2469350b2114SRobert Mustacchi 	 * off), but software thinks it's on.  Note this unexpected condition
2470350b2114SRobert Mustacchi 	 * for debugging and continue; we'll do what we can to get the state
2471350b2114SRobert Mustacchi 	 * machines back in sync.
2472350b2114SRobert Mustacchi 	 */
2473350b2114SRobert Mustacchi 	if ((control & PCIE_SLOTCTL_PWR_CONTROL) != 0 &&
2474350b2114SRobert Mustacchi 	    slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
2475350b2114SRobert Mustacchi 		cmn_err(CE_WARN, "pciehpc_slot_poweroff (slot %d): SW state is "
2476350b2114SRobert Mustacchi 		    "%d but power controller is already disabled; continuing",
2477350b2114SRobert Mustacchi 		    slot_p->hs_phy_slot_num, slot_p->hs_info.cn_state);
2478350b2114SRobert Mustacchi 		goto alreadyoff;
2479350b2114SRobert Mustacchi 	}
2480350b2114SRobert Mustacchi 
2481350b2114SRobert Mustacchi 	if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
2482350b2114SRobert Mustacchi 		PCIE_DBG("pciehpc_slot_poweroff (slot %d): powering off "
2483350b2114SRobert Mustacchi 		    "empty slot\n", slot_p->hs_phy_slot_num);
2484350b2114SRobert Mustacchi 	}
2485350b2114SRobert Mustacchi 
2486350b2114SRobert Mustacchi 	/*
248726947304SEvan Yan 	 * Disable power to the slot involves:
248826947304SEvan Yan 	 *	1. Set power LED to blink.
248926947304SEvan Yan 	 *	2. Set power control OFF in Slot Control Reigster and
249026947304SEvan Yan 	 *	   wait for Command Completed Interrupt or 1 sec timeout.
249126947304SEvan Yan 	 *	3. Set POWER led and ATTN led to be OFF.
249226947304SEvan Yan 	 */
249326947304SEvan Yan 
2494a9413143SRobert Mustacchi 	/*
2495a9413143SRobert Mustacchi 	 * We don't bother indicating a power transition if the device is
2496a9413143SRobert Mustacchi 	 * already off. The act of turning it off does clear out a probe
2497a9413143SRobert Mustacchi 	 * failure, but it doesn't clear out a power fault. That is only
2498a9413143SRobert Mustacchi 	 * reserved for a removal.
2499a9413143SRobert Mustacchi 	 */
2500a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = true;
2501350b2114SRobert Mustacchi alreadyoff:
2502a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
2503a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
2504a9413143SRobert Mustacchi 
250526947304SEvan Yan 	/* disable power fault detection interrupt */
250626947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
250726947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
250826947304SEvan Yan 	control &= ~PCIE_SLOTCTL_PWR_FAULT_EN;
250926947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
251026947304SEvan Yan 
251126947304SEvan Yan 	/* 2. set power control to OFF */
251226947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
251326947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
251426947304SEvan Yan 	control |= PCIE_SLOTCTL_PWR_CONTROL;
251526947304SEvan Yan 	pciehpc_issue_hpc_command(ctrl_p, control);
251626947304SEvan Yan 
2517350b2114SRobert Mustacchi 	/*
2518350b2114SRobert Mustacchi 	 * Make sure our control input has been acknowledged.  Some
2519350b2114SRobert Mustacchi 	 * implementations may clear the control bit if the power controller
2520350b2114SRobert Mustacchi 	 * couldn't be disabled for some reasons, or if firmware decided to
2521350b2114SRobert Mustacchi 	 * disallow our command.
2522350b2114SRobert Mustacchi 	 */
252326947304SEvan Yan 	control = pciehpc_reg_get16(ctrl_p,
252426947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
2525350b2114SRobert Mustacchi 	if ((control & PCIE_SLOTCTL_PWR_CONTROL) == 0) {
2526350b2114SRobert Mustacchi 		/*
2527350b2114SRobert Mustacchi 		 * Well, this is unfortunate: we couldn't turn power off.
2528350b2114SRobert Mustacchi 		 * XXX Should we turn on the ATTN indicator?  For now we just
2529350b2114SRobert Mustacchi 		 * log a warning and fail.
2530350b2114SRobert Mustacchi 		 */
2531350b2114SRobert Mustacchi 		cmn_err(CE_WARN, "pciehpc_slot_poweroff (slot %d): power "
2532350b2114SRobert Mustacchi 		    "controller completed our disable command but is still "
2533350b2114SRobert Mustacchi 		    "enabled", slot_p->hs_phy_slot_num);
2534a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
2535a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWERED] = true;
2536a9413143SRobert Mustacchi 		pciehpc_sync_leds_to_hw(slot_p);
2537350b2114SRobert Mustacchi 
2538350b2114SRobert Mustacchi 		return (DDI_FAILURE);
2539350b2114SRobert Mustacchi 	}
254026947304SEvan Yan 
254126947304SEvan Yan 	/* 3. Set power LED to be OFF */
2542a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_TRANSITION] = false;
2543a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
2544a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
254526947304SEvan Yan 
254626947304SEvan Yan 	/* if EMI is present, turn it OFF */
254726947304SEvan Yan 	if (ctrl_p->hc_has_emi_lock) {
254826947304SEvan Yan 		status =  pciehpc_reg_get16(ctrl_p,
254926947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
255026947304SEvan Yan 
255126947304SEvan Yan 		if (status & PCIE_SLOTSTS_EMI_LOCK_SET) {
255226947304SEvan Yan 			control =  pciehpc_reg_get16(ctrl_p,
255326947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTCTL);
255426947304SEvan Yan 			control |= PCIE_SLOTCTL_EMI_LOCK_CONTROL;
255526947304SEvan Yan 			pciehpc_issue_hpc_command(ctrl_p, control);
255626947304SEvan Yan 
255726947304SEvan Yan 			/* wait 1 sec after toggling the state of EMI lock */
255826947304SEvan Yan 			delay(drv_usectohz(1000000));
255926947304SEvan Yan 		}
256026947304SEvan Yan 	}
256126947304SEvan Yan 
256226947304SEvan Yan 	/* get the current state of the slot */
256326947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
256426947304SEvan Yan 
256526947304SEvan Yan 	*result = slot_p->hs_info.cn_state;
256626947304SEvan Yan 
256726947304SEvan Yan 	return (DDI_SUCCESS);
256826947304SEvan Yan }
256926947304SEvan Yan 
257026947304SEvan Yan /*
257126947304SEvan Yan  * pciehpc_slot_probe()
257226947304SEvan Yan  *
257326947304SEvan Yan  * Probe the slot.
257426947304SEvan Yan  *
257526947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
257626947304SEvan Yan  */
257726947304SEvan Yan static int
pciehpc_slot_probe(pcie_hp_slot_t * slot_p)257826947304SEvan Yan pciehpc_slot_probe(pcie_hp_slot_t *slot_p)
257926947304SEvan Yan {
258026947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
258126947304SEvan Yan 	int		ret = DDI_SUCCESS;
258226947304SEvan Yan 
258326947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
258426947304SEvan Yan 
2585a9413143SRobert Mustacchi 	/*
2586a9413143SRobert Mustacchi 	 * Because we've been asked to probe again, make sure we aren't actively
2587a9413143SRobert Mustacchi 	 * suggesting probe failed from an LED perspective.
2588a9413143SRobert Mustacchi 	 */
2589a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
2590a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
2591a9413143SRobert Mustacchi 
259226947304SEvan Yan 	/* get the current state of the slot */
259326947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
259426947304SEvan Yan 
259526947304SEvan Yan 	/*
259626947304SEvan Yan 	 * Probe a given PCIe Hotplug Connection (CN).
259726947304SEvan Yan 	 */
259826947304SEvan Yan 	PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
259926947304SEvan Yan 	ret = pcie_hp_probe(slot_p);
260026947304SEvan Yan 
260126947304SEvan Yan 	if (ret != DDI_SUCCESS) {
260226947304SEvan Yan 		PCIE_DBG("pciehpc_slot_probe() failed\n");
260326947304SEvan Yan 
260426947304SEvan Yan 		/* turn the ATTN led ON for configure failure */
2605a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = true;
2606a9413143SRobert Mustacchi 		pciehpc_sync_leds_to_hw(slot_p);
260726947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
260826947304SEvan Yan 		return (DDI_FAILURE);
260926947304SEvan Yan 	}
261026947304SEvan Yan 
261126947304SEvan Yan 	PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
261226947304SEvan Yan 
261326947304SEvan Yan 	/* get the current state of the slot */
261426947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
261526947304SEvan Yan 
261626947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
261726947304SEvan Yan 	return (DDI_SUCCESS);
261826947304SEvan Yan }
261926947304SEvan Yan 
262026947304SEvan Yan /*
262126947304SEvan Yan  * pciehpc_slot_unprobe()
262226947304SEvan Yan  *
262326947304SEvan Yan  * Unprobe the slot.
262426947304SEvan Yan  *
262526947304SEvan Yan  * Note: This function is called by DDI HP framework at kernel context only
262626947304SEvan Yan  */
262726947304SEvan Yan static int
pciehpc_slot_unprobe(pcie_hp_slot_t * slot_p)262826947304SEvan Yan pciehpc_slot_unprobe(pcie_hp_slot_t *slot_p)
262926947304SEvan Yan {
263026947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
263126947304SEvan Yan 	int		ret;
263226947304SEvan Yan 
263326947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
263426947304SEvan Yan 
263526947304SEvan Yan 	/* get the current state of the slot */
263626947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
263726947304SEvan Yan 
263826947304SEvan Yan 	/*
263926947304SEvan Yan 	 * Unprobe a given PCIe Hotplug Connection (CN).
264026947304SEvan Yan 	 */
264126947304SEvan Yan 	PCIE_DISABLE_ERRORS(ctrl_p->hc_dip);
264226947304SEvan Yan 	ret = pcie_hp_unprobe(slot_p);
264326947304SEvan Yan 
264426947304SEvan Yan 	if (ret != DDI_SUCCESS) {
264526947304SEvan Yan 		PCIE_DBG("pciehpc_slot_unprobe() failed\n");
264626947304SEvan Yan 		PCIE_ENABLE_ERRORS(ctrl_p->hc_dip);
264726947304SEvan Yan 		mutex_exit(&ctrl_p->hc_mutex);
264826947304SEvan Yan 		return (DDI_FAILURE);
264926947304SEvan Yan 	}
265026947304SEvan Yan 
265126947304SEvan Yan 	/* get the current state of the slot */
265226947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
265326947304SEvan Yan 
265426947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
265526947304SEvan Yan 	return (DDI_SUCCESS);
265626947304SEvan Yan }
265726947304SEvan Yan 
265826947304SEvan Yan static int
pciehpc_upgrade_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t target_state)265926947304SEvan Yan pciehpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
266026947304SEvan Yan     ddi_hp_cn_state_t target_state)
266126947304SEvan Yan {
266226947304SEvan Yan 	ddi_hp_cn_state_t curr_state;
266326947304SEvan Yan 	int rv = DDI_SUCCESS;
2664350b2114SRobert Mustacchi 	pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
266526947304SEvan Yan 
266626947304SEvan Yan 	if (target_state > DDI_HP_CN_STATE_ENABLED) {
266726947304SEvan Yan 		return (DDI_EINVAL);
266826947304SEvan Yan 	}
266926947304SEvan Yan 
267026947304SEvan Yan 	curr_state = slot_p->hs_info.cn_state;
267126947304SEvan Yan 	while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
267226947304SEvan Yan 
267326947304SEvan Yan 		switch (curr_state) {
267426947304SEvan Yan 		case DDI_HP_CN_STATE_EMPTY:
267526947304SEvan Yan 			/*
267626947304SEvan Yan 			 * From EMPTY to PRESENT, just check the hardware
267726947304SEvan Yan 			 * slot state.
267826947304SEvan Yan 			 */
267926947304SEvan Yan 			pciehpc_get_slot_state(slot_p);
268026947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state;
268126947304SEvan Yan 			if (curr_state < DDI_HP_CN_STATE_PRESENT)
268226947304SEvan Yan 				rv = DDI_FAILURE;
268326947304SEvan Yan 			break;
268426947304SEvan Yan 		case DDI_HP_CN_STATE_PRESENT:
2685350b2114SRobert Mustacchi 			if (!ctrl_p->hc_has_pwr) {
2686b9a2a14bSRobert Mustacchi 				pciehpc_get_slot_state(slot_p);
2687b9a2a14bSRobert Mustacchi 				curr_state = slot_p->hs_info.cn_state;
2688b9a2a14bSRobert Mustacchi 				if (curr_state < DDI_HP_CN_STATE_POWERED)
2689b9a2a14bSRobert Mustacchi 					rv = DDI_FAILURE;
2690b9a2a14bSRobert Mustacchi 				break;
2691b9a2a14bSRobert Mustacchi 			}
2692b9a2a14bSRobert Mustacchi 
2693350b2114SRobert Mustacchi 			rv = (ctrl_p->hc_ops.poweron_hpc_slot)(slot_p,
269426947304SEvan Yan 			    &curr_state);
269526947304SEvan Yan 
269626947304SEvan Yan 			break;
269726947304SEvan Yan 		case DDI_HP_CN_STATE_POWERED:
2698350b2114SRobert Mustacchi 			/*
2699350b2114SRobert Mustacchi 			 * If we're performing a synchronization, then the
2700350b2114SRobert Mustacchi 			 * POWERED state isn't quite accurate. Power is enabled,
2701350b2114SRobert Mustacchi 			 * but we haven't really done all the actual steps that
2702350b2114SRobert Mustacchi 			 * are expected. As such, we will do another call to
2703350b2114SRobert Mustacchi 			 * power on and if successful, then do the change to
2704350b2114SRobert Mustacchi 			 * ENABLED. If the call to power on did not work, then
2705350b2114SRobert Mustacchi 			 * we must transition back to PRESENT. If there is no
2706350b2114SRobert Mustacchi 			 * power controller, then this is a no-op.
2707350b2114SRobert Mustacchi 			 */
2708350b2114SRobert Mustacchi 			if ((ctrl_p->hc_flags & PCIE_HP_SYNC_RUNNING) != 0 &&
2709350b2114SRobert Mustacchi 			    ctrl_p->hc_has_pwr) {
2710350b2114SRobert Mustacchi 				rv = (ctrl_p->hc_ops.poweron_hpc_slot)(slot_p,
2711350b2114SRobert Mustacchi 				    &curr_state);
2712350b2114SRobert Mustacchi 				if (rv != DDI_SUCCESS) {
2713350b2114SRobert Mustacchi 					slot_p->hs_info.cn_state =
2714350b2114SRobert Mustacchi 					    DDI_HP_CN_STATE_PRESENT;
2715350b2114SRobert Mustacchi 					break;
2716350b2114SRobert Mustacchi 				}
2717350b2114SRobert Mustacchi 			} else if (!ctrl_p->hc_has_pwr) {
2718350b2114SRobert Mustacchi 				rv = pciehpc_slot_noctrl_active(slot_p,
2719350b2114SRobert Mustacchi 				    &curr_state);
2720350b2114SRobert Mustacchi 				if (rv != DDI_SUCCESS)
2721350b2114SRobert Mustacchi 					break;
2722350b2114SRobert Mustacchi 			}
2723350b2114SRobert Mustacchi 
272426947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state =
272526947304SEvan Yan 			    DDI_HP_CN_STATE_ENABLED;
272626947304SEvan Yan 			break;
272726947304SEvan Yan 		default:
272826947304SEvan Yan 			/* should never reach here */
272926947304SEvan Yan 			ASSERT("unknown devinfo state");
273026947304SEvan Yan 		}
273126947304SEvan Yan 	}
273226947304SEvan Yan 
273326947304SEvan Yan 	return (rv);
273426947304SEvan Yan }
273526947304SEvan Yan 
2736a9413143SRobert Mustacchi /*
2737a9413143SRobert Mustacchi  * Remove and clear out LEDs given a slot state downgrade. In particular, we
2738a9413143SRobert Mustacchi  * have three states that we need to worry about: whether the device is powered,
2739a9413143SRobert Mustacchi  * a power fault, and a probe failure.
2740a9413143SRobert Mustacchi  *
2741a9413143SRobert Mustacchi  * Removing power removes the powered state and a probe failure, but retains a
2742a9413143SRobert Mustacchi  * power fault. Removing a device ensures that all three are off.
2743a9413143SRobert Mustacchi  */
2744a9413143SRobert Mustacchi static void
pciehpc_downgrade_slot_leds(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t state)2745a9413143SRobert Mustacchi pciehpc_downgrade_slot_leds(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t state)
2746a9413143SRobert Mustacchi {
2747a9413143SRobert Mustacchi 	switch (state) {
2748a9413143SRobert Mustacchi 	case DDI_HP_CN_STATE_EMPTY:
2749a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
2750a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
2751a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = false;
2752a9413143SRobert Mustacchi 		break;
2753a9413143SRobert Mustacchi 	case DDI_HP_CN_STATE_PRESENT:
2754a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_POWERED] = false;
2755a9413143SRobert Mustacchi 		slot_p->hs_led_plat_en[PCIE_LL_PROBE_FAILED] = false;
2756a9413143SRobert Mustacchi 		break;
2757a9413143SRobert Mustacchi 	default:
2758a9413143SRobert Mustacchi 		break;
2759a9413143SRobert Mustacchi 	}
2760a9413143SRobert Mustacchi 
2761a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
2762a9413143SRobert Mustacchi }
2763a9413143SRobert Mustacchi 
276426947304SEvan Yan static int
pciehpc_downgrade_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t target_state)276526947304SEvan Yan pciehpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
276626947304SEvan Yan     ddi_hp_cn_state_t target_state)
276726947304SEvan Yan {
276826947304SEvan Yan 	ddi_hp_cn_state_t curr_state;
276926947304SEvan Yan 	int rv = DDI_SUCCESS;
277026947304SEvan Yan 
277126947304SEvan Yan 
277226947304SEvan Yan 	curr_state = slot_p->hs_info.cn_state;
277326947304SEvan Yan 	while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
277426947304SEvan Yan 
277526947304SEvan Yan 		switch (curr_state) {
277626947304SEvan Yan 		case DDI_HP_CN_STATE_PRESENT:
277726947304SEvan Yan 			/*
277826947304SEvan Yan 			 * From PRESENT to EMPTY, just check hardware slot
2779a9413143SRobert Mustacchi 			 * state. Removal of a device means we should make sure
2780a9413143SRobert Mustacchi 			 * that the LEDs are cleaned up.
278126947304SEvan Yan 			 */
278226947304SEvan Yan 			pciehpc_get_slot_state(slot_p);
278326947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state;
2784a9413143SRobert Mustacchi 			if (curr_state >= DDI_HP_CN_STATE_PRESENT) {
278526947304SEvan Yan 				rv = DDI_FAILURE;
2786a9413143SRobert Mustacchi 			} else {
2787a9413143SRobert Mustacchi 				pciehpc_downgrade_slot_leds(slot_p, curr_state);
2788a9413143SRobert Mustacchi 			}
278926947304SEvan Yan 			break;
279026947304SEvan Yan 		case DDI_HP_CN_STATE_POWERED:
2791b9a2a14bSRobert Mustacchi 			/*
2792b9a2a14bSRobert Mustacchi 			 * If the device doesn't have power control then we
2793b9a2a14bSRobert Mustacchi 			 * cannot ask it to power off the slot. However, a
2794b9a2a14bSRobert Mustacchi 			 * device may have been removed and therefore we need to
2795b9a2a14bSRobert Mustacchi 			 * manually check if the device was removed by getting
2796b9a2a14bSRobert Mustacchi 			 * the state. Otherwise we let power control do
2797b9a2a14bSRobert Mustacchi 			 * everything.
2798b9a2a14bSRobert Mustacchi 			 */
2799b9a2a14bSRobert Mustacchi 			if (!slot_p->hs_ctrl->hc_has_pwr) {
2800b9a2a14bSRobert Mustacchi 				pciehpc_get_slot_state(slot_p);
2801b9a2a14bSRobert Mustacchi 				curr_state = slot_p->hs_info.cn_state;
2802a9413143SRobert Mustacchi 				if (curr_state >= DDI_HP_CN_STATE_POWERED) {
2803b9a2a14bSRobert Mustacchi 					rv = DDI_FAILURE;
2804a9413143SRobert Mustacchi 				} else {
2805a9413143SRobert Mustacchi 					pciehpc_downgrade_slot_leds(slot_p,
2806a9413143SRobert Mustacchi 					    curr_state);
2807a9413143SRobert Mustacchi 				}
2808b9a2a14bSRobert Mustacchi 				break;
2809b9a2a14bSRobert Mustacchi 			}
2810b9a2a14bSRobert Mustacchi 
281126947304SEvan Yan 			rv = (slot_p->hs_ctrl->hc_ops.poweroff_hpc_slot)(
281226947304SEvan Yan 			    slot_p, &curr_state);
281326947304SEvan Yan 
281426947304SEvan Yan 			break;
281526947304SEvan Yan 		case DDI_HP_CN_STATE_ENABLED:
281626947304SEvan Yan 			curr_state = slot_p->hs_info.cn_state =
281726947304SEvan Yan 			    DDI_HP_CN_STATE_POWERED;
281826947304SEvan Yan 
281926947304SEvan Yan 			break;
282026947304SEvan Yan 		default:
282126947304SEvan Yan 			/* should never reach here */
282226947304SEvan Yan 			ASSERT("unknown devinfo state");
282326947304SEvan Yan 		}
282426947304SEvan Yan 	}
282526947304SEvan Yan 
282626947304SEvan Yan 	return (rv);
282726947304SEvan Yan }
282826947304SEvan Yan 
282926947304SEvan Yan /* Change slot state to a target state */
283026947304SEvan Yan static int
pciehpc_change_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t target_state)283126947304SEvan Yan pciehpc_change_slot_state(pcie_hp_slot_t *slot_p,
283226947304SEvan Yan     ddi_hp_cn_state_t target_state)
283326947304SEvan Yan {
283426947304SEvan Yan 	ddi_hp_cn_state_t curr_state;
2835350b2114SRobert Mustacchi 	pciehpc_slot_power_t pwr_state;
2836350b2114SRobert Mustacchi 	boolean_t sync = B_FALSE;
2837350b2114SRobert Mustacchi 	int rv = 0;
2838350b2114SRobert Mustacchi 
2839350b2114SRobert Mustacchi 	ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
284026947304SEvan Yan 
284126947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
284226947304SEvan Yan 	curr_state = slot_p->hs_info.cn_state;
2843350b2114SRobert Mustacchi 	pwr_state = pciehpc_slot_power_state(slot_p);
284426947304SEvan Yan 
2845350b2114SRobert Mustacchi 	/*
2846350b2114SRobert Mustacchi 	 * We've been asked to change the slot state. If we still had an
2847350b2114SRobert Mustacchi 	 * outstanding synchronization task, then we should remove that because
2848350b2114SRobert Mustacchi 	 * we've had an explicit state change. In essence we take over that sync
2849350b2114SRobert Mustacchi 	 * and note that it's running.
2850350b2114SRobert Mustacchi 	 */
2851350b2114SRobert Mustacchi 	if ((slot_p->hs_ctrl->hc_flags & PCIE_HP_SYNC_PENDING) != 0 &&
2852350b2114SRobert Mustacchi 	    slot_p->hs_info.cn_state == DDI_HP_CN_STATE_POWERED) {
2853350b2114SRobert Mustacchi 		sync = B_TRUE;
2854350b2114SRobert Mustacchi 		slot_p->hs_ctrl->hc_flags |= PCIE_HP_SYNC_RUNNING;
2855350b2114SRobert Mustacchi 	}
2856350b2114SRobert Mustacchi 	slot_p->hs_ctrl->hc_flags &= ~PCIE_HP_SYNC_PENDING;
2857350b2114SRobert Mustacchi 
2858350b2114SRobert Mustacchi 	/*
2859350b2114SRobert Mustacchi 	 * We need to see whether the power controller state (if there is one)
2860350b2114SRobert Mustacchi 	 * matches the DDI slot state.  If not, it may be necessary to perform
2861350b2114SRobert Mustacchi 	 * the upgrade or downgrade procedure even if the DDI slot state matches
2862350b2114SRobert Mustacchi 	 * the target already.  We'll make sure that curr_state reflects the
2863350b2114SRobert Mustacchi 	 * state of the power controller with respect to our desired target
2864350b2114SRobert Mustacchi 	 * state, even if the slot is empty.
2865350b2114SRobert Mustacchi 	 */
2866350b2114SRobert Mustacchi 	if (pwr_state == PSP_NO_CONTROLLER)
2867350b2114SRobert Mustacchi 		goto skip_sync;
2868350b2114SRobert Mustacchi 
2869350b2114SRobert Mustacchi 	switch (target_state) {
2870350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_EMPTY:
2871350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_PRESENT:
2872350b2114SRobert Mustacchi 		/*
2873350b2114SRobert Mustacchi 		 * Power controller is on but software doesn't know that, and
2874350b2114SRobert Mustacchi 		 * wants to enter a state in which power should be off.
2875350b2114SRobert Mustacchi 		 */
2876350b2114SRobert Mustacchi 		if ((pwr_state & PSP_OFF) == 0 &&
2877350b2114SRobert Mustacchi 		    curr_state < DDI_HP_CN_STATE_POWERED) {
2878350b2114SRobert Mustacchi 			curr_state = DDI_HP_CN_STATE_POWERED;
2879350b2114SRobert Mustacchi 		}
2880350b2114SRobert Mustacchi 		break;
2881350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_POWERED:
2882350b2114SRobert Mustacchi 	case DDI_HP_CN_STATE_ENABLED:
2883350b2114SRobert Mustacchi 		/*
2884350b2114SRobert Mustacchi 		 * Power controller is off but software doesn't know that, and
2885350b2114SRobert Mustacchi 		 * wants to enter a state in which power should be on.
2886350b2114SRobert Mustacchi 		 */
2887350b2114SRobert Mustacchi 		if ((pwr_state & PSP_OFF) != 0 &&
2888350b2114SRobert Mustacchi 		    curr_state >= DDI_HP_CN_STATE_POWERED) {
2889350b2114SRobert Mustacchi 			curr_state = DDI_HP_CN_STATE_PRESENT;
2890350b2114SRobert Mustacchi 		}
2891350b2114SRobert Mustacchi 		break;
2892350b2114SRobert Mustacchi 	default:
2893350b2114SRobert Mustacchi 		break;
2894350b2114SRobert Mustacchi 	}
2895350b2114SRobert Mustacchi 
2896350b2114SRobert Mustacchi 	slot_p->hs_info.cn_state = curr_state;
2897350b2114SRobert Mustacchi 
2898350b2114SRobert Mustacchi skip_sync:
289926947304SEvan Yan 	if (curr_state == target_state) {
290026947304SEvan Yan 		return (DDI_SUCCESS);
290126947304SEvan Yan 	}
290226947304SEvan Yan 
2903350b2114SRobert Mustacchi 	if (curr_state < target_state) {
290426947304SEvan Yan 		rv = pciehpc_upgrade_slot_state(slot_p, target_state);
290526947304SEvan Yan 	} else {
290626947304SEvan Yan 		rv = pciehpc_downgrade_slot_state(slot_p, target_state);
290726947304SEvan Yan 	}
290826947304SEvan Yan 
2909350b2114SRobert Mustacchi 	if (sync) {
2910350b2114SRobert Mustacchi 		slot_p->hs_ctrl->hc_flags &= ~PCIE_HP_SYNC_RUNNING;
2911350b2114SRobert Mustacchi 	}
2912350b2114SRobert Mustacchi 
291326947304SEvan Yan 	return (rv);
291426947304SEvan Yan }
291526947304SEvan Yan 
2916a9413143SRobert Mustacchi typedef struct pciehpc_prop {
2917a9413143SRobert Mustacchi 	const char *prop_name;
2918a9413143SRobert Mustacchi 	const char *prop_value;
2919a9413143SRobert Mustacchi 	bool (*prop_valid)(const char *);
2920a9413143SRobert Mustacchi 	int (*prop_get)(pcie_hp_slot_t *, const char **);
2921a9413143SRobert Mustacchi 	void (*prop_set)(pcie_hp_slot_t *, const char *);
2922a9413143SRobert Mustacchi } pciehpc_prop_t;
2923a9413143SRobert Mustacchi 
2924a9413143SRobert Mustacchi static bool
pciehpc_prop_led_valid(const char * value)2925a9413143SRobert Mustacchi pciehpc_prop_led_valid(const char *value)
2926a9413143SRobert Mustacchi {
2927a9413143SRobert Mustacchi 	return (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0 ||
2928a9413143SRobert Mustacchi 	    strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0 ||
2929a9413143SRobert Mustacchi 	    strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0 ||
2930a9413143SRobert Mustacchi 	    strcmp(value, PCIEHPC_PROP_VALUE_DEFAULT) == 0);
2931a9413143SRobert Mustacchi }
2932a9413143SRobert Mustacchi 
2933a9413143SRobert Mustacchi static int
pciehpc_prop_card_get(pcie_hp_slot_t * slot_p,const char ** value_p)2934a9413143SRobert Mustacchi pciehpc_prop_card_get(pcie_hp_slot_t *slot_p, const char **value_p)
2935a9413143SRobert Mustacchi {
2936a9413143SRobert Mustacchi 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
2937a9413143SRobert Mustacchi 	ddi_acc_handle_t handle;
2938a9413143SRobert Mustacchi 	dev_info_t	*cdip;
2939a9413143SRobert Mustacchi 	uint8_t		prog_class, base_class, sub_class;
2940a9413143SRobert Mustacchi 
2941a9413143SRobert Mustacchi 	mutex_exit(&ctrl_p->hc_mutex);
2942a9413143SRobert Mustacchi 	cdip = pcie_hp_devi_find(ctrl_p->hc_dip, slot_p->hs_device_num, 0);
2943a9413143SRobert Mustacchi 	mutex_enter(&ctrl_p->hc_mutex);
2944a9413143SRobert Mustacchi 
2945a9413143SRobert Mustacchi 	if ((slot_p->hs_info.cn_state != DDI_HP_CN_STATE_ENABLED) ||
2946a9413143SRobert Mustacchi 	    cdip == NULL) {
2947a9413143SRobert Mustacchi 		/*
2948a9413143SRobert Mustacchi 		 * When getting all properties, just ignore the
2949a9413143SRobert Mustacchi 		 * one that's not available under certain state.
2950a9413143SRobert Mustacchi 		 */
2951a9413143SRobert Mustacchi 		return (DDI_ENOTSUP);
2952a9413143SRobert Mustacchi 	}
2953a9413143SRobert Mustacchi 
2954a9413143SRobert Mustacchi 	if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
2955a9413143SRobert Mustacchi 		return (DDI_FAILURE);
2956a9413143SRobert Mustacchi 	}
2957a9413143SRobert Mustacchi 
2958a9413143SRobert Mustacchi 	prog_class = pci_config_get8(handle, PCI_CONF_PROGCLASS);
2959a9413143SRobert Mustacchi 	base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
2960a9413143SRobert Mustacchi 	sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
2961a9413143SRobert Mustacchi 	pci_config_teardown(&handle);
2962a9413143SRobert Mustacchi 
2963a9413143SRobert Mustacchi 	*value_p = PCIEHPC_PROP_VALUE_UNKNOWN;
2964a9413143SRobert Mustacchi 	for (size_t i = 0; i < class_pci_items; i++) {
2965a9413143SRobert Mustacchi 		if ((base_class == class_pci[i].base_class) &&
2966a9413143SRobert Mustacchi 		    (sub_class == class_pci[i].sub_class) &&
2967a9413143SRobert Mustacchi 		    (prog_class == class_pci[i].prog_class)) {
2968a9413143SRobert Mustacchi 			*value_p = class_pci[i].short_desc;
2969a9413143SRobert Mustacchi 			break;
2970a9413143SRobert Mustacchi 		}
2971a9413143SRobert Mustacchi 	}
2972a9413143SRobert Mustacchi 
2973a9413143SRobert Mustacchi 	return (DDI_SUCCESS);
2974a9413143SRobert Mustacchi }
2975a9413143SRobert Mustacchi 
2976a9413143SRobert Mustacchi static int
pciehpc_prop_board_get(pcie_hp_slot_t * slot_p,const char ** value_p)2977a9413143SRobert Mustacchi pciehpc_prop_board_get(pcie_hp_slot_t *slot_p, const char **value_p)
2978a9413143SRobert Mustacchi {
2979a9413143SRobert Mustacchi 	if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY) {
2980a9413143SRobert Mustacchi 		*value_p = PCIEHPC_PROP_VALUE_UNKNOWN;
2981a9413143SRobert Mustacchi 	} else {
2982a9413143SRobert Mustacchi 		*value_p = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
2983a9413143SRobert Mustacchi 	}
2984a9413143SRobert Mustacchi 
2985a9413143SRobert Mustacchi 	return (DDI_SUCCESS);
2986a9413143SRobert Mustacchi }
2987a9413143SRobert Mustacchi 
2988a9413143SRobert Mustacchi static int
pciehpc_prop_slot_get(pcie_hp_slot_t * slot_p,const char ** value_p)2989a9413143SRobert Mustacchi pciehpc_prop_slot_get(pcie_hp_slot_t *slot_p, const char **value_p)
2990a9413143SRobert Mustacchi {
2991a9413143SRobert Mustacchi 	*value_p = pcie_slot_condition_text(slot_p->hs_condition);
2992a9413143SRobert Mustacchi 	return (DDI_SUCCESS);
2993a9413143SRobert Mustacchi }
2994a9413143SRobert Mustacchi 
2995a9413143SRobert Mustacchi static int
pciehpc_prop_led_get_power(pcie_hp_slot_t * slot_p,const char ** value_p)2996a9413143SRobert Mustacchi pciehpc_prop_led_get_power(pcie_hp_slot_t *slot_p, const char **value_p)
2997a9413143SRobert Mustacchi {
2998a9413143SRobert Mustacchi 	if (slot_p->hs_power_usr_ovr) {
2999a9413143SRobert Mustacchi 		*value_p = pcie_led_state_text(slot_p->hs_power_usr_ovr_state);
3000a9413143SRobert Mustacchi 	} else {
3001a9413143SRobert Mustacchi 		*value_p = PCIEHPC_PROP_VALUE_DEFAULT;
3002a9413143SRobert Mustacchi 	}
3003a9413143SRobert Mustacchi 	return (DDI_SUCCESS);
3004a9413143SRobert Mustacchi }
3005a9413143SRobert Mustacchi 
3006a9413143SRobert Mustacchi static int
pciehpc_prop_led_get_attn(pcie_hp_slot_t * slot_p,const char ** value_p)3007a9413143SRobert Mustacchi pciehpc_prop_led_get_attn(pcie_hp_slot_t *slot_p, const char **value_p)
3008a9413143SRobert Mustacchi {
3009a9413143SRobert Mustacchi 	if (slot_p->hs_attn_usr_ovr) {
3010a9413143SRobert Mustacchi 		*value_p = pcie_led_state_text(slot_p->hs_attn_usr_ovr_state);
3011a9413143SRobert Mustacchi 	} else {
3012a9413143SRobert Mustacchi 		*value_p = PCIEHPC_PROP_VALUE_DEFAULT;
3013a9413143SRobert Mustacchi 	}
3014a9413143SRobert Mustacchi 	return (DDI_SUCCESS);
3015a9413143SRobert Mustacchi }
3016a9413143SRobert Mustacchi 
3017a9413143SRobert Mustacchi static void
pciehpc_prop_led_set_common(bool * ovr,pcie_hp_led_state_t * state,const char * val)3018a9413143SRobert Mustacchi pciehpc_prop_led_set_common(bool *ovr, pcie_hp_led_state_t *state,
3019a9413143SRobert Mustacchi     const char *val)
3020a9413143SRobert Mustacchi {
3021a9413143SRobert Mustacchi 	if (strcmp(val, PCIEHPC_PROP_VALUE_DEFAULT) == 0) {
3022a9413143SRobert Mustacchi 		*ovr = false;
3023a9413143SRobert Mustacchi 		return;
3024a9413143SRobert Mustacchi 	}
3025a9413143SRobert Mustacchi 
3026a9413143SRobert Mustacchi 	*ovr = true;
3027a9413143SRobert Mustacchi 	if (strcmp(val, PCIEHPC_PROP_VALUE_ON) == 0) {
3028a9413143SRobert Mustacchi 		*state = PCIE_HP_LED_ON;
3029a9413143SRobert Mustacchi 	} else if (strcmp(val, PCIEHPC_PROP_VALUE_OFF) == 0) {
3030a9413143SRobert Mustacchi 		*state = PCIE_HP_LED_OFF;
3031a9413143SRobert Mustacchi 	} else if (strcmp(val, PCIEHPC_PROP_VALUE_BLINK) == 0) {
3032a9413143SRobert Mustacchi 		*state = PCIE_HP_LED_BLINK;
3033a9413143SRobert Mustacchi 	}
3034a9413143SRobert Mustacchi }
3035a9413143SRobert Mustacchi 
3036a9413143SRobert Mustacchi static void
pciehpc_prop_led_set_attn(pcie_hp_slot_t * slot_p,const char * value)3037a9413143SRobert Mustacchi pciehpc_prop_led_set_attn(pcie_hp_slot_t *slot_p, const char *value)
3038a9413143SRobert Mustacchi {
3039a9413143SRobert Mustacchi 	pciehpc_prop_led_set_common(&slot_p->hs_attn_usr_ovr,
3040a9413143SRobert Mustacchi 	    &slot_p->hs_attn_usr_ovr_state, value);
3041a9413143SRobert Mustacchi }
3042a9413143SRobert Mustacchi 
3043a9413143SRobert Mustacchi static void
pciehpc_prop_led_set_power(pcie_hp_slot_t * slot_p,const char * value)3044a9413143SRobert Mustacchi pciehpc_prop_led_set_power(pcie_hp_slot_t *slot_p, const char *value)
3045a9413143SRobert Mustacchi {
3046a9413143SRobert Mustacchi 	pciehpc_prop_led_set_common(&slot_p->hs_power_usr_ovr,
3047a9413143SRobert Mustacchi 	    &slot_p->hs_power_usr_ovr_state, value);
3048a9413143SRobert Mustacchi }
3049a9413143SRobert Mustacchi 
3050a9413143SRobert Mustacchi static pciehpc_prop_t pciehpc_props[] = {
3051a9413143SRobert Mustacchi 	{ PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED_DEF,
3052a9413143SRobert Mustacchi 	    pciehpc_prop_led_valid, pciehpc_prop_led_get_power,
3053a9413143SRobert Mustacchi 	    pciehpc_prop_led_set_power },
3054a9413143SRobert Mustacchi 	{ PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED_DEF,
3055a9413143SRobert Mustacchi 	    pciehpc_prop_led_valid, pciehpc_prop_led_get_attn,
3056a9413143SRobert Mustacchi 	    pciehpc_prop_led_set_attn },
3057a9413143SRobert Mustacchi 	{ PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE, NULL,
3058a9413143SRobert Mustacchi 	    pciehpc_prop_card_get, NULL },
3059a9413143SRobert Mustacchi 	{ PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE, NULL,
3060a9413143SRobert Mustacchi 	    pciehpc_prop_board_get, NULL },
3061a9413143SRobert Mustacchi 	{ PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE, NULL,
3062a9413143SRobert Mustacchi 	    pciehpc_prop_slot_get, NULL },
3063a9413143SRobert Mustacchi };
3064a9413143SRobert Mustacchi 
3065a9413143SRobert Mustacchi static bool
pciehpc_slot_prop_copyin(uintptr_t arg,ddi_hp_property_t * prop)3066a9413143SRobert Mustacchi pciehpc_slot_prop_copyin(uintptr_t arg, ddi_hp_property_t *prop)
3067a9413143SRobert Mustacchi {
3068a9413143SRobert Mustacchi 	switch (ddi_model_convert_from(get_udatamodel())) {
3069a9413143SRobert Mustacchi #ifdef	_MULTI_DATAMODEL
3070a9413143SRobert Mustacchi 	case DDI_MODEL_ILP32: {
3071a9413143SRobert Mustacchi 		ddi_hp_property32_t prop32;
3072a9413143SRobert Mustacchi 		if (ddi_copyin((void *)arg, &prop32, sizeof (prop32), 0) != 0) {
3073a9413143SRobert Mustacchi 			return (false);
3074a9413143SRobert Mustacchi 		}
3075a9413143SRobert Mustacchi 		bzero(prop, sizeof (prop));
3076a9413143SRobert Mustacchi 		prop->nvlist_buf = (void *)(uintptr_t)prop32.nvlist_buf;
3077a9413143SRobert Mustacchi 		prop->buf_size = prop32.buf_size;
3078a9413143SRobert Mustacchi 		break;
3079a9413143SRobert Mustacchi 	}
3080a9413143SRobert Mustacchi #endif
3081a9413143SRobert Mustacchi 	case DDI_MODEL_NONE:
3082a9413143SRobert Mustacchi 		if (ddi_copyin((void *)arg, prop, sizeof (*prop), 0) != 0) {
3083a9413143SRobert Mustacchi 			return (false);
3084a9413143SRobert Mustacchi 		}
3085a9413143SRobert Mustacchi 		break;
3086a9413143SRobert Mustacchi 	default:
3087a9413143SRobert Mustacchi 		return (false);
3088a9413143SRobert Mustacchi 	}
3089a9413143SRobert Mustacchi 
3090a9413143SRobert Mustacchi 	return (true);
3091a9413143SRobert Mustacchi }
3092a9413143SRobert Mustacchi 
3093a9413143SRobert Mustacchi static bool
pciehpc_slot_prop_copyout(uintptr_t dest,const ddi_hp_property_t * prop)3094a9413143SRobert Mustacchi pciehpc_slot_prop_copyout(uintptr_t dest, const ddi_hp_property_t *prop)
3095a9413143SRobert Mustacchi {
3096a9413143SRobert Mustacchi 	switch (ddi_model_convert_from(get_udatamodel())) {
3097a9413143SRobert Mustacchi #ifdef	_MULTI_DATAMODEL
3098a9413143SRobert Mustacchi 	case DDI_MODEL_ILP32: {
3099a9413143SRobert Mustacchi 		ddi_hp_property32_t prop32;
3100a9413143SRobert Mustacchi 
3101a9413143SRobert Mustacchi 		if ((uintptr_t)prop->nvlist_buf > UINT32_MAX ||
3102a9413143SRobert Mustacchi 		    prop->buf_size > UINT32_MAX) {
3103a9413143SRobert Mustacchi 			return (false);
3104a9413143SRobert Mustacchi 		}
3105a9413143SRobert Mustacchi 		bzero(&prop32, sizeof (prop32));
3106a9413143SRobert Mustacchi 		prop32.nvlist_buf = (caddr32_t)(uintptr_t)prop->nvlist_buf;
3107a9413143SRobert Mustacchi 		prop32.buf_size = (uint32_t)prop->buf_size;
3108a9413143SRobert Mustacchi 		if (ddi_copyout(&prop32, (void *)dest, sizeof (prop32), 0) !=
3109a9413143SRobert Mustacchi 		    0) {
3110a9413143SRobert Mustacchi 			return (false);
3111a9413143SRobert Mustacchi 		}
3112a9413143SRobert Mustacchi 		break;
3113a9413143SRobert Mustacchi 	}
3114a9413143SRobert Mustacchi #endif
3115a9413143SRobert Mustacchi 	case DDI_MODEL_NONE:
3116a9413143SRobert Mustacchi 		if (ddi_copyout(prop, (void *)dest, sizeof (ddi_hp_property_t),
3117a9413143SRobert Mustacchi 		    0) != 0) {
3118a9413143SRobert Mustacchi 			return (false);
3119a9413143SRobert Mustacchi 		}
3120a9413143SRobert Mustacchi 		break;
3121a9413143SRobert Mustacchi 	default:
3122a9413143SRobert Mustacchi 		return (false);
3123a9413143SRobert Mustacchi 	}
3124a9413143SRobert Mustacchi 
3125a9413143SRobert Mustacchi 	return (true);
3126a9413143SRobert Mustacchi }
3127a9413143SRobert Mustacchi 
312826947304SEvan Yan int
pciehpc_slot_get_property(pcie_hp_slot_t * slot_p,uintptr_t arg,uintptr_t rval)3129a9413143SRobert Mustacchi pciehpc_slot_get_property(pcie_hp_slot_t *slot_p, uintptr_t arg, uintptr_t rval)
313026947304SEvan Yan {
313126947304SEvan Yan 	ddi_hp_property_t request, result;
313226947304SEvan Yan 	pcie_hp_ctrl_t	*ctrl_p = slot_p->hs_ctrl;
313326947304SEvan Yan 	nvlist_t	*prop_list;
313426947304SEvan Yan 	nvlist_t	*prop_rlist; /* nvlist for return values */
313526947304SEvan Yan 	nvpair_t	*prop_pair;
313626947304SEvan Yan 	int		ret = DDI_SUCCESS;
313726947304SEvan Yan 	boolean_t	get_all_prop = B_FALSE;
313826947304SEvan Yan 
3139a9413143SRobert Mustacchi 	if (!pciehpc_slot_prop_copyin(arg, &request) ||
3140a9413143SRobert Mustacchi 	    !pciehpc_slot_prop_copyin(rval, &result))
3141a9413143SRobert Mustacchi 		return (false);
314226947304SEvan Yan 
314326947304SEvan Yan 	if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
314426947304SEvan Yan 	    &prop_list)) != DDI_SUCCESS)
314526947304SEvan Yan 		return (ret);
314626947304SEvan Yan 
314726947304SEvan Yan 	if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
314826947304SEvan Yan 		ret = DDI_ENOMEM;
314926947304SEvan Yan 		goto get_prop_cleanup;
315026947304SEvan Yan 	}
315126947304SEvan Yan 
315226947304SEvan Yan 	/* check whether the requested property is "all" or "help" */
315326947304SEvan Yan 	prop_pair = nvlist_next_nvpair(prop_list, NULL);
315426947304SEvan Yan 	if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
3155a9413143SRobert Mustacchi 		const char *name = nvpair_name(prop_pair);
315626947304SEvan Yan 
315726947304SEvan Yan 		if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
315826947304SEvan Yan 			(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
315926947304SEvan Yan 
316026947304SEvan Yan 			/*
316126947304SEvan Yan 			 * Add all properties into the request list, so that we
316226947304SEvan Yan 			 * will get the values in the following for loop.
316326947304SEvan Yan 			 */
3164a9413143SRobert Mustacchi 			for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
316526947304SEvan Yan 				if (nvlist_add_string(prop_list,
316626947304SEvan Yan 				    pciehpc_props[i].prop_name, "") != 0) {
316726947304SEvan Yan 					ret = DDI_FAILURE;
316826947304SEvan Yan 					goto get_prop_cleanup1;
316926947304SEvan Yan 				}
317026947304SEvan Yan 			}
317126947304SEvan Yan 			get_all_prop = B_TRUE;
317226947304SEvan Yan 		} else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
317326947304SEvan Yan 			/*
317426947304SEvan Yan 			 * Empty the request list, and add help strings into the
317526947304SEvan Yan 			 * return list. We will pass the following for loop.
317626947304SEvan Yan 			 */
317726947304SEvan Yan 			(void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
317826947304SEvan Yan 
3179a9413143SRobert Mustacchi 			for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
318026947304SEvan Yan 				if (nvlist_add_string(prop_rlist,
318126947304SEvan Yan 				    pciehpc_props[i].prop_name,
318226947304SEvan Yan 				    pciehpc_props[i].prop_value) != 0) {
318326947304SEvan Yan 					ret = DDI_FAILURE;
318426947304SEvan Yan 					goto get_prop_cleanup1;
318526947304SEvan Yan 				}
318626947304SEvan Yan 			}
318726947304SEvan Yan 		}
318826947304SEvan Yan 	}
318926947304SEvan Yan 
319026947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
319126947304SEvan Yan 
319226947304SEvan Yan 	/* get the current slot state */
319326947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
319426947304SEvan Yan 
319526947304SEvan Yan 	/* for each requested property, get the value and add it to nvlist */
319626947304SEvan Yan 	prop_pair = NULL;
3197b3d69c05SRobert Mustacchi 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
3198a9413143SRobert Mustacchi 		const char *name = nvpair_name(prop_pair);
3199a9413143SRobert Mustacchi 		const pciehpc_prop_t *prop = NULL;
3200a9413143SRobert Mustacchi 		const char *value = NULL;
320126947304SEvan Yan 
3202a9413143SRobert Mustacchi 		for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
3203a9413143SRobert Mustacchi 			if (strcmp(pciehpc_props[i].prop_name, name) == 0) {
3204a9413143SRobert Mustacchi 				prop = &pciehpc_props[i];
320526947304SEvan Yan 				break;
320626947304SEvan Yan 			}
320726947304SEvan Yan 		}
320826947304SEvan Yan 
3209a9413143SRobert Mustacchi 		if (prop == NULL) {
3210a9413143SRobert Mustacchi 			PCIE_DBG("Unsupported property: %s\n", name);
3211a9413143SRobert Mustacchi 			ret = DDI_EINVAL;
321226947304SEvan Yan 			goto get_prop_cleanup2;
321326947304SEvan Yan 		}
3214a9413143SRobert Mustacchi 
3215a9413143SRobert Mustacchi 		/*
3216a9413143SRobert Mustacchi 		 * Get the property. Some things may fail with ENOTSUP and we
3217a9413143SRobert Mustacchi 		 * allow that if we are being asked to get everything.
3218a9413143SRobert Mustacchi 		 */
3219a9413143SRobert Mustacchi 		ret = prop->prop_get(slot_p, &value);
3220a9413143SRobert Mustacchi 		if (ret != DDI_SUCCESS &&
3221a9413143SRobert Mustacchi 		    (get_all_prop && ret != DDI_ENOTSUP)) {
3222a9413143SRobert Mustacchi 			goto get_prop_cleanup2;
3223a9413143SRobert Mustacchi 		}
3224a9413143SRobert Mustacchi 
322526947304SEvan Yan 		if (nvlist_add_string(prop_rlist, name, value) != 0) {
322626947304SEvan Yan 			ret = DDI_FAILURE;
322726947304SEvan Yan 			goto get_prop_cleanup2;
322826947304SEvan Yan 		}
322926947304SEvan Yan 	}
323026947304SEvan Yan 
323126947304SEvan Yan 	/* pack nvlist and copyout */
323226947304SEvan Yan 	if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
323326947304SEvan Yan 	    &result.buf_size)) != DDI_SUCCESS) {
323426947304SEvan Yan 		goto get_prop_cleanup2;
323526947304SEvan Yan 	}
3236a9413143SRobert Mustacchi 
3237a9413143SRobert Mustacchi 	if (!pciehpc_slot_prop_copyout(rval, &result)) {
323826947304SEvan Yan 		ret = DDI_FAILURE;
323926947304SEvan Yan 	}
324026947304SEvan Yan 
324126947304SEvan Yan get_prop_cleanup2:
324226947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
324326947304SEvan Yan get_prop_cleanup1:
324426947304SEvan Yan 	nvlist_free(prop_rlist);
324526947304SEvan Yan get_prop_cleanup:
324626947304SEvan Yan 	nvlist_free(prop_list);
324726947304SEvan Yan 	return (ret);
324826947304SEvan Yan }
324926947304SEvan Yan 
3250a9413143SRobert Mustacchi 
325126947304SEvan Yan int
pciehpc_slot_set_property(pcie_hp_slot_t * slot_p,uintptr_t arg,uintptr_t rval)3252a9413143SRobert Mustacchi pciehpc_slot_set_property(pcie_hp_slot_t *slot_p, uintptr_t arg,
3253a9413143SRobert Mustacchi     uintptr_t rval)
325426947304SEvan Yan {
325526947304SEvan Yan 	ddi_hp_property_t	request, result;
325626947304SEvan Yan 	pcie_hp_ctrl_t		*ctrl_p = slot_p->hs_ctrl;
325726947304SEvan Yan 	nvlist_t		*prop_list;
325826947304SEvan Yan 	nvlist_t		*prop_rlist;
325926947304SEvan Yan 	nvpair_t		*prop_pair;
326026947304SEvan Yan 	int			ret = DDI_SUCCESS;
326126947304SEvan Yan 
3262a9413143SRobert Mustacchi 	if (!pciehpc_slot_prop_copyin(arg, &request) ||
3263a9413143SRobert Mustacchi 	    !pciehpc_slot_prop_copyin(rval, &result))
3264a9413143SRobert Mustacchi 		return (false);
326526947304SEvan Yan 
326626947304SEvan Yan 	if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
326726947304SEvan Yan 	    &prop_list)) != DDI_SUCCESS)
326826947304SEvan Yan 		return (ret);
326926947304SEvan Yan 
327026947304SEvan Yan 	/* check whether the requested property is "help" */
327126947304SEvan Yan 	prop_pair = nvlist_next_nvpair(prop_list, NULL);
327226947304SEvan Yan 	if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
327326947304SEvan Yan 	    (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
327426947304SEvan Yan 		if (!rval) {
327526947304SEvan Yan 			ret = DDI_ENOTSUP;
327626947304SEvan Yan 			goto set_prop_cleanup;
327726947304SEvan Yan 		}
327826947304SEvan Yan 
327926947304SEvan Yan 		if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
328026947304SEvan Yan 			ret = DDI_ENOMEM;
328126947304SEvan Yan 			goto set_prop_cleanup;
328226947304SEvan Yan 		}
3283a9413143SRobert Mustacchi 
3284a9413143SRobert Mustacchi 		for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
3285a9413143SRobert Mustacchi 			if (pciehpc_props[i].prop_valid == NULL)
3286a9413143SRobert Mustacchi 				continue;
3287a9413143SRobert Mustacchi 
3288a9413143SRobert Mustacchi 			if (nvlist_add_string(prop_rlist,
3289a9413143SRobert Mustacchi 			    pciehpc_props[i].prop_name,
3290a9413143SRobert Mustacchi 			    pciehpc_props[i].prop_value) != 0) {
329126947304SEvan Yan 				ret = DDI_FAILURE;
329226947304SEvan Yan 				goto set_prop_cleanup1;
329326947304SEvan Yan 			}
3294a9413143SRobert Mustacchi 		}
329526947304SEvan Yan 
329626947304SEvan Yan 		if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
329726947304SEvan Yan 		    &result.buf_size)) != DDI_SUCCESS) {
329826947304SEvan Yan 			goto set_prop_cleanup1;
329926947304SEvan Yan 		}
3300a9413143SRobert Mustacchi 
3301a9413143SRobert Mustacchi 		if (!pciehpc_slot_prop_copyout(rval, &result)) {
330226947304SEvan Yan 			ret = DDI_FAILURE;
330326947304SEvan Yan 		}
3304a9413143SRobert Mustacchi 
330526947304SEvan Yan set_prop_cleanup1:
330626947304SEvan Yan 		nvlist_free(prop_rlist);
330726947304SEvan Yan 		nvlist_free(prop_list);
330826947304SEvan Yan 		return (ret);
330926947304SEvan Yan 	}
331026947304SEvan Yan 
331126947304SEvan Yan 	/* Validate the request */
331226947304SEvan Yan 	prop_pair = NULL;
3313b3d69c05SRobert Mustacchi 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
3314a9413143SRobert Mustacchi 		const pciehpc_prop_t *prop;
3315a9413143SRobert Mustacchi 		char *value;
3316a9413143SRobert Mustacchi 		const char *name = nvpair_name(prop_pair);
3317a9413143SRobert Mustacchi 
331826947304SEvan Yan 		if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
331924fd5dc4SEvan Yan 			PCIE_DBG("Unexpected data type of setting "
332026947304SEvan Yan 			    "property %s.\n", name);
332126947304SEvan Yan 			ret = DDI_EINVAL;
332226947304SEvan Yan 			goto set_prop_cleanup;
332326947304SEvan Yan 		}
3324a9413143SRobert Mustacchi 
332526947304SEvan Yan 		if (nvpair_value_string(prop_pair, &value)) {
332624fd5dc4SEvan Yan 			PCIE_DBG("Get string value failed for property %s.\n",
332724fd5dc4SEvan Yan 			    name);
332826947304SEvan Yan 			ret = DDI_FAILURE;
332926947304SEvan Yan 			goto set_prop_cleanup;
333026947304SEvan Yan 		}
333126947304SEvan Yan 
3332a9413143SRobert Mustacchi 		prop = NULL;
3333a9413143SRobert Mustacchi 		for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
3334a9413143SRobert Mustacchi 			if (strcmp(pciehpc_props[i].prop_name, name) == 0) {
3335a9413143SRobert Mustacchi 				prop = &pciehpc_props[i];
3336a9413143SRobert Mustacchi 				break;
3337a9413143SRobert Mustacchi 			}
3338a9413143SRobert Mustacchi 		}
3339a9413143SRobert Mustacchi 
3340a9413143SRobert Mustacchi 		if (prop == NULL) {
3341a9413143SRobert Mustacchi 			PCIE_DBG("Unsupported property: %s\n", name);
3342a9413143SRobert Mustacchi 			ret = DDI_EINVAL;
334326947304SEvan Yan 			goto set_prop_cleanup;
334426947304SEvan Yan 		}
3345a9413143SRobert Mustacchi 
3346a9413143SRobert Mustacchi 		if (prop->prop_valid == NULL) {
3347a9413143SRobert Mustacchi 			PCIE_DBG("Read only property: %s\n", name);
334826947304SEvan Yan 			ret = DDI_ENOTSUP;
334926947304SEvan Yan 			goto set_prop_cleanup;
3350a9413143SRobert Mustacchi 		} else if (!prop->prop_valid(value)) {
3351a9413143SRobert Mustacchi 			PCIE_DBG("Unsupported value ('%s') for property: %s\n",
3352a9413143SRobert Mustacchi 			    value, name);
3353a9413143SRobert Mustacchi 			ret = DDI_EINVAL;
3354a9413143SRobert Mustacchi 			goto set_prop_cleanup;
335526947304SEvan Yan 		}
335626947304SEvan Yan 	}
335726947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
335826947304SEvan Yan 
335926947304SEvan Yan 	/* get the current slot state */
336026947304SEvan Yan 	pciehpc_get_slot_state(slot_p);
336126947304SEvan Yan 
336226947304SEvan Yan 	/* set each property */
336326947304SEvan Yan 	prop_pair = NULL;
3364b3d69c05SRobert Mustacchi 	while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
3365a9413143SRobert Mustacchi 		const char *name = nvpair_name(prop_pair);
3366a9413143SRobert Mustacchi 		const pciehpc_prop_t *prop = NULL;
3367a9413143SRobert Mustacchi 		char *value;
3368a9413143SRobert Mustacchi 
3369a9413143SRobert Mustacchi 		(void) nvpair_value_string(prop_pair, &value);
3370a9413143SRobert Mustacchi 		for (size_t i = 0; i < ARRAY_SIZE(pciehpc_props); i++) {
3371a9413143SRobert Mustacchi 			if (strcmp(pciehpc_props[i].prop_name, name) == 0) {
3372a9413143SRobert Mustacchi 				prop = &pciehpc_props[i];
3373a9413143SRobert Mustacchi 				break;
3374a9413143SRobert Mustacchi 			}
3375a9413143SRobert Mustacchi 		}
3376a9413143SRobert Mustacchi 
3377a9413143SRobert Mustacchi 		ASSERT3P(prop, !=, NULL);
3378a9413143SRobert Mustacchi 		prop->prop_set(slot_p, value);
3379a9413143SRobert Mustacchi 	}
338026947304SEvan Yan 
3381b3d69c05SRobert Mustacchi 	/*
3382a9413143SRobert Mustacchi 	 * Because the only properties we can set are LED related, always
3383a9413143SRobert Mustacchi 	 * resync.
3384b3d69c05SRobert Mustacchi 	 */
3385a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
338626947304SEvan Yan 
3387a9413143SRobert Mustacchi 	if (rval != 0) {
338824fd5dc4SEvan Yan 		result.buf_size = 0;
3389a9413143SRobert Mustacchi 		if (!pciehpc_slot_prop_copyout(rval, &result)) {
339024fd5dc4SEvan Yan 			ret = DDI_FAILURE;
339124fd5dc4SEvan Yan 		}
339224fd5dc4SEvan Yan 	}
339326947304SEvan Yan 
339426947304SEvan Yan 	mutex_exit(&ctrl_p->hc_mutex);
339526947304SEvan Yan set_prop_cleanup:
339626947304SEvan Yan 	nvlist_free(prop_list);
339726947304SEvan Yan 	return (ret);
339826947304SEvan Yan }
339926947304SEvan Yan 
340026947304SEvan Yan /*
340126947304SEvan Yan  * Send a command to the PCI-E Hot Plug Controller.
340226947304SEvan Yan  *
340326947304SEvan Yan  * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
340426947304SEvan Yan  * commands.
340526947304SEvan Yan  * 1) If Command Complete events/interrupts are supported then software
340626947304SEvan Yan  *    waits for Command Complete event after issuing a command (i.e writing
340726947304SEvan Yan  *    to the Slot Control register). The command completion could take as
340826947304SEvan Yan  *    long as 1 second so software should be prepared to wait for 1 second
340926947304SEvan Yan  *    before issuing another command.
341026947304SEvan Yan  *
341126947304SEvan Yan  * 2) If Command Complete events/interrupts are not supported then
341226947304SEvan Yan  *    software could issue multiple Slot Control writes without any delay
341326947304SEvan Yan  *    between writes.
341426947304SEvan Yan  */
341526947304SEvan Yan static void
pciehpc_issue_hpc_command(pcie_hp_ctrl_t * ctrl_p,uint16_t control)341626947304SEvan Yan pciehpc_issue_hpc_command(pcie_hp_ctrl_t *ctrl_p, uint16_t control)
341726947304SEvan Yan {
341826947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
341926947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
342026947304SEvan Yan 	uint16_t	status;
342126947304SEvan Yan 	uint32_t	slot_cap;
342226947304SEvan Yan 
342326947304SEvan Yan 	/*
342426947304SEvan Yan 	 * PCI-E version 1.1 spec defines No Command Completed
342526947304SEvan Yan 	 * Support bit (bit#18) in Slot Capabilities register. If this
342626947304SEvan Yan 	 * bit is set then slot doesn't support notification of command
342726947304SEvan Yan 	 * completion events.
342826947304SEvan Yan 	 */
342926947304SEvan Yan 	slot_cap =  pciehpc_reg_get32(ctrl_p,
343026947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
343126947304SEvan Yan 
343226947304SEvan Yan 	/*
343326947304SEvan Yan 	 * If no Command Completion event is supported or it is ACPI
343426947304SEvan Yan 	 * hot plug mode then just issue the command and return.
343526947304SEvan Yan 	 */
343626947304SEvan Yan 	if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
343726947304SEvan Yan 	    (bus_p->bus_hp_curr_mode == PCIE_ACPI_HP_MODE)) {
343826947304SEvan Yan 		pciehpc_reg_put16(ctrl_p,
343926947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
344026947304SEvan Yan 		return;
344126947304SEvan Yan 	}
344226947304SEvan Yan 
344326947304SEvan Yan 	/*
344426947304SEvan Yan 	 * **************************************
344526947304SEvan Yan 	 * Command Complete events are supported.
344626947304SEvan Yan 	 * **************************************
344726947304SEvan Yan 	 */
344826947304SEvan Yan 
344926947304SEvan Yan 	/*
345026947304SEvan Yan 	 * If HPC is not yet initialized then just poll for the Command
345126947304SEvan Yan 	 * Completion interrupt.
345226947304SEvan Yan 	 */
345326947304SEvan Yan 	if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
345426947304SEvan Yan 		int retry = PCIE_HP_CMD_WAIT_RETRY;
345526947304SEvan Yan 
345626947304SEvan Yan 		/* write the command to the HPC */
345726947304SEvan Yan 		pciehpc_reg_put16(ctrl_p,
345826947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
345926947304SEvan Yan 
346026947304SEvan Yan 		/* poll for status completion */
346126947304SEvan Yan 		while (retry--) {
346226947304SEvan Yan 			/* wait for 10 msec before checking the status */
346326947304SEvan Yan 			delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
346426947304SEvan Yan 
346526947304SEvan Yan 			status = pciehpc_reg_get16(ctrl_p,
346626947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTSTS);
346726947304SEvan Yan 
346826947304SEvan Yan 			if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
346926947304SEvan Yan 				/* clear the status bits */
347026947304SEvan Yan 				pciehpc_reg_put16(ctrl_p,
347126947304SEvan Yan 				    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
347226947304SEvan Yan 				break;
347326947304SEvan Yan 			}
347426947304SEvan Yan 		}
347526947304SEvan Yan 		return;
347626947304SEvan Yan 	}
347726947304SEvan Yan 
347826947304SEvan Yan 	/* HPC is already initialized */
347926947304SEvan Yan 
348026947304SEvan Yan 	ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
348126947304SEvan Yan 
348226947304SEvan Yan 	/*
348326947304SEvan Yan 	 * If previous command is still pending then wait for its
348426947304SEvan Yan 	 * completion. i.e cv_wait()
348526947304SEvan Yan 	 */
348626947304SEvan Yan 
348726947304SEvan Yan 	while (ctrl_p->hc_cmd_pending == B_TRUE)
348826947304SEvan Yan 		cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
348926947304SEvan Yan 
349026947304SEvan Yan 	/*
349126947304SEvan Yan 	 * Issue the command and wait for Command Completion or
349226947304SEvan Yan 	 * the 1 sec timeout.
349326947304SEvan Yan 	 */
349426947304SEvan Yan 	pciehpc_reg_put16(ctrl_p,
349526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL, control);
349626947304SEvan Yan 
349726947304SEvan Yan 	ctrl_p->hc_cmd_pending = B_TRUE;
349826947304SEvan Yan 
349926947304SEvan Yan 	if (cv_timedwait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex,
350026947304SEvan Yan 	    ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
350126947304SEvan Yan 
350226947304SEvan Yan 		/* it is a timeout */
350326947304SEvan Yan 		PCIE_DBG("pciehpc_issue_hpc_command: Command Complete"
350426947304SEvan Yan 		    " interrupt is not received for slot %d\n",
350526947304SEvan Yan 		    slot_p->hs_phy_slot_num);
350626947304SEvan Yan 
350726947304SEvan Yan 		/* clear the status info in case interrupts are disabled? */
350826947304SEvan Yan 		status = pciehpc_reg_get16(ctrl_p,
350926947304SEvan Yan 		    bus_p->bus_pcie_off + PCIE_SLOTSTS);
351026947304SEvan Yan 
351126947304SEvan Yan 		if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
351226947304SEvan Yan 			/* clear the status bits */
351326947304SEvan Yan 			pciehpc_reg_put16(ctrl_p,
351426947304SEvan Yan 			    bus_p->bus_pcie_off + PCIE_SLOTSTS, status);
351526947304SEvan Yan 		}
351626947304SEvan Yan 	}
351726947304SEvan Yan 
351826947304SEvan Yan 	ctrl_p->hc_cmd_pending = B_FALSE;
351926947304SEvan Yan 
352026947304SEvan Yan 	/* wake up any one waiting for issuing another command to HPC */
352126947304SEvan Yan 	cv_signal(&ctrl_p->hc_cmd_comp_cv);
352226947304SEvan Yan }
352326947304SEvan Yan 
352426947304SEvan Yan /*
352526947304SEvan Yan  * pciehcp_attn_btn_handler()
352626947304SEvan Yan  *
352726947304SEvan Yan  * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
352826947304SEvan Yan  */
352926947304SEvan Yan static void
pciehpc_attn_btn_handler(pcie_hp_ctrl_t * ctrl_p)353026947304SEvan Yan pciehpc_attn_btn_handler(pcie_hp_ctrl_t *ctrl_p)
353126947304SEvan Yan {
353226947304SEvan Yan 	pcie_hp_slot_t		*slot_p = ctrl_p->hc_slots[0];
353326947304SEvan Yan 	callb_cpr_t		cprinfo;
353426947304SEvan Yan 
353526947304SEvan Yan 	PCIE_DBG("pciehpc_attn_btn_handler: thread started\n");
353626947304SEvan Yan 
353726947304SEvan Yan 	CALLB_CPR_INIT(&cprinfo, &ctrl_p->hc_mutex, callb_generic_cpr,
353826947304SEvan Yan 	    "pciehpc_attn_btn_handler");
353926947304SEvan Yan 
354026947304SEvan Yan 	mutex_enter(&ctrl_p->hc_mutex);
354126947304SEvan Yan 
354226947304SEvan Yan 	/* wait for ATTN button event */
354326947304SEvan Yan 	cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
354426947304SEvan Yan 
354526947304SEvan Yan 	while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
354626947304SEvan Yan 		if (slot_p->hs_attn_btn_pending == B_TRUE) {
3547a9413143SRobert Mustacchi 			/*
3548a9413143SRobert Mustacchi 			 * Allow the platform to toggle an appropriate LED while
3549a9413143SRobert Mustacchi 			 * we're waiting for this to take effect (generally
3550a9413143SRobert Mustacchi 			 * blinking the power LED).
3551a9413143SRobert Mustacchi 			 */
3552a9413143SRobert Mustacchi 			slot_p->hs_led_plat_en[PCIE_LL_ATTENTION_BUTTON] = true;
3553a9413143SRobert Mustacchi 			pciehpc_sync_leds_to_hw(slot_p);
355426947304SEvan Yan 
355526947304SEvan Yan 			/* wait for 5 seconds before taking any action */
355626947304SEvan Yan 			if (cv_timedwait(&slot_p->hs_attn_btn_cv,
355726947304SEvan Yan 			    &ctrl_p->hc_mutex,
355826947304SEvan Yan 			    ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) {
355926947304SEvan Yan 				/*
356026947304SEvan Yan 				 * It is a time out; make sure the ATTN pending
356126947304SEvan Yan 				 * flag is still ON before sending the event to
356226947304SEvan Yan 				 * DDI HP framework.
356326947304SEvan Yan 				 */
356426947304SEvan Yan 				if (slot_p->hs_attn_btn_pending == B_TRUE) {
356526947304SEvan Yan 					int hint;
356626947304SEvan Yan 
356726947304SEvan Yan 					slot_p->hs_attn_btn_pending = B_FALSE;
356826947304SEvan Yan 					pciehpc_get_slot_state(slot_p);
356926947304SEvan Yan 
357026947304SEvan Yan 					if (slot_p->hs_info.cn_state <=
357126947304SEvan Yan 					    DDI_HP_CN_STATE_PRESENT) {
357226947304SEvan Yan 						/*
357326947304SEvan Yan 						 * Insertion.
357426947304SEvan Yan 						 */
357526947304SEvan Yan 						hint = SE_INCOMING_RES;
357626947304SEvan Yan 					} else {
357726947304SEvan Yan 						/*
357826947304SEvan Yan 						 * Want to remove;
357926947304SEvan Yan 						 */
358026947304SEvan Yan 						hint = SE_OUTGOING_RES;
358126947304SEvan Yan 					}
358226947304SEvan Yan 
358326947304SEvan Yan 					/*
358426947304SEvan Yan 					 * We can't call ddihp_cn_gen_sysevent
358526947304SEvan Yan 					 * here since it's not a DDI interface.
358626947304SEvan Yan 					 */
358726947304SEvan Yan 					pcie_hp_gen_sysevent_req(
358826947304SEvan Yan 					    slot_p->hs_info.cn_name,
358926947304SEvan Yan 					    hint,
359026947304SEvan Yan 					    ctrl_p->hc_dip,
359126947304SEvan Yan 					    KM_SLEEP);
359226947304SEvan Yan 				}
359326947304SEvan Yan 			}
359426947304SEvan Yan 
359526947304SEvan Yan 			/* restore the power LED state */
3596a9413143SRobert Mustacchi 			slot_p->hs_led_plat_en[PCIE_LL_ATTENTION_BUTTON] =
3597a9413143SRobert Mustacchi 			    false;
3598a9413143SRobert Mustacchi 			pciehpc_sync_leds_to_hw(slot_p);
359926947304SEvan Yan 			continue;
360026947304SEvan Yan 		}
360126947304SEvan Yan 
360226947304SEvan Yan 		/* wait for another ATTN button event */
360326947304SEvan Yan 		cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
360426947304SEvan Yan 	}
360526947304SEvan Yan 
360626947304SEvan Yan 	PCIE_DBG("pciehpc_attn_btn_handler: thread exit\n");
360726947304SEvan Yan 	cv_signal(&slot_p->hs_attn_btn_cv);
360826947304SEvan Yan 	CALLB_CPR_EXIT(&cprinfo);
360926947304SEvan Yan 	thread_exit();
361026947304SEvan Yan }
361126947304SEvan Yan 
361226947304SEvan Yan /*
361326947304SEvan Yan  * convert LED state from PCIE HPC definition to pcie_hp_led_state_t
361426947304SEvan Yan  * definition.
361526947304SEvan Yan  */
361626947304SEvan Yan static pcie_hp_led_state_t
pciehpc_led_state_to_hpc(uint16_t state)361726947304SEvan Yan pciehpc_led_state_to_hpc(uint16_t state)
361826947304SEvan Yan {
361926947304SEvan Yan 	switch (state) {
362026947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_ON:
362126947304SEvan Yan 		return (PCIE_HP_LED_ON);
362226947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
362326947304SEvan Yan 		return (PCIE_HP_LED_BLINK);
362426947304SEvan Yan 	case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
362526947304SEvan Yan 	default:
362626947304SEvan Yan 		return (PCIE_HP_LED_OFF);
362726947304SEvan Yan 	}
362826947304SEvan Yan }
362926947304SEvan Yan 
363080dc702dSColin Zou - Sun Microsystems - Beijing China static void
pciehpc_handle_power_fault(dev_info_t * dip)363180dc702dSColin Zou - Sun Microsystems - Beijing China pciehpc_handle_power_fault(dev_info_t *dip)
363280dc702dSColin Zou - Sun Microsystems - Beijing China {
363380dc702dSColin Zou - Sun Microsystems - Beijing China 	/*
363480dc702dSColin Zou - Sun Microsystems - Beijing China 	 * Hold the parent's ref so that it won't disappear when the taskq is
363580dc702dSColin Zou - Sun Microsystems - Beijing China 	 * scheduled to run.
363680dc702dSColin Zou - Sun Microsystems - Beijing China 	 */
363780dc702dSColin Zou - Sun Microsystems - Beijing China 	ndi_hold_devi(dip);
363880dc702dSColin Zou - Sun Microsystems - Beijing China 
3639fc8ae2ecSToomas Soome 	if (taskq_dispatch(system_taskq, pciehpc_power_fault_handler, dip,
3640fc8ae2ecSToomas Soome 	    TQ_NOSLEEP) == TASKQID_INVALID) {
364180dc702dSColin Zou - Sun Microsystems - Beijing China 		ndi_rele_devi(dip);
364280dc702dSColin Zou - Sun Microsystems - Beijing China 		PCIE_DBG("pciehpc_intr(): "
364380dc702dSColin Zou - Sun Microsystems - Beijing China 		    "Failed to dispatch power fault handler, dip %p\n", dip);
364480dc702dSColin Zou - Sun Microsystems - Beijing China 	}
364580dc702dSColin Zou - Sun Microsystems - Beijing China }
364680dc702dSColin Zou - Sun Microsystems - Beijing China 
364780dc702dSColin Zou - Sun Microsystems - Beijing China static void
pciehpc_power_fault_handler(void * arg)364880dc702dSColin Zou - Sun Microsystems - Beijing China pciehpc_power_fault_handler(void *arg)
364980dc702dSColin Zou - Sun Microsystems - Beijing China {
365080dc702dSColin Zou - Sun Microsystems - Beijing China 	dev_info_t *dip = (dev_info_t *)arg;
365180dc702dSColin Zou - Sun Microsystems - Beijing China 	pcie_hp_ctrl_t  *ctrl_p;
365280dc702dSColin Zou - Sun Microsystems - Beijing China 	pcie_hp_slot_t  *slot_p;
365380dc702dSColin Zou - Sun Microsystems - Beijing China 
365480dc702dSColin Zou - Sun Microsystems - Beijing China 	/* get the soft state structure for this dip */
365580dc702dSColin Zou - Sun Microsystems - Beijing China 	if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
365680dc702dSColin Zou - Sun Microsystems - Beijing China 		ndi_rele_devi(dip);
365780dc702dSColin Zou - Sun Microsystems - Beijing China 		return;
365880dc702dSColin Zou - Sun Microsystems - Beijing China 	}
365980dc702dSColin Zou - Sun Microsystems - Beijing China 	slot_p = ctrl_p->hc_slots[0];
366080dc702dSColin Zou - Sun Microsystems - Beijing China 
366180dc702dSColin Zou - Sun Microsystems - Beijing China 	/*
366280dc702dSColin Zou - Sun Microsystems - Beijing China 	 * Send the event to DDI Hotplug framework, power off
366380dc702dSColin Zou - Sun Microsystems - Beijing China 	 * the slot
366480dc702dSColin Zou - Sun Microsystems - Beijing China 	 */
366580dc702dSColin Zou - Sun Microsystems - Beijing China 	(void) ndi_hp_state_change_req(dip,
366680dc702dSColin Zou - Sun Microsystems - Beijing China 	    slot_p->hs_info.cn_name,
3667350b2114SRobert Mustacchi 	    DDI_HP_CN_STATE_PRESENT, DDI_HP_REQ_SYNC);
366880dc702dSColin Zou - Sun Microsystems - Beijing China 
366980dc702dSColin Zou - Sun Microsystems - Beijing China 	mutex_enter(&ctrl_p->hc_mutex);
3670a9413143SRobert Mustacchi 	slot_p->hs_led_plat_en[PCIE_LL_POWER_FAULT] = true;
3671a9413143SRobert Mustacchi 	pciehpc_sync_leds_to_hw(slot_p);
367280dc702dSColin Zou - Sun Microsystems - Beijing China 	mutex_exit(&ctrl_p->hc_mutex);
367380dc702dSColin Zou - Sun Microsystems - Beijing China 	ndi_rele_devi(dip);
367480dc702dSColin Zou - Sun Microsystems - Beijing China }
367580dc702dSColin Zou - Sun Microsystems - Beijing China 
367626947304SEvan Yan #ifdef DEBUG
367726947304SEvan Yan /*
367826947304SEvan Yan  * Dump PCI-E Hot Plug registers.
367926947304SEvan Yan  */
368026947304SEvan Yan static void
pciehpc_dump_hpregs(pcie_hp_ctrl_t * ctrl_p)368126947304SEvan Yan pciehpc_dump_hpregs(pcie_hp_ctrl_t *ctrl_p)
368226947304SEvan Yan {
368326947304SEvan Yan 	pcie_hp_slot_t	*slot_p = ctrl_p->hc_slots[0];
368426947304SEvan Yan 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
368526947304SEvan Yan 	uint16_t	control;
368626947304SEvan Yan 	uint32_t	capabilities;
368726947304SEvan Yan 
368826947304SEvan Yan 	if (!pcie_debug_flags)
368926947304SEvan Yan 		return;
369026947304SEvan Yan 
369126947304SEvan Yan 	capabilities = pciehpc_reg_get32(ctrl_p,
369226947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCAP);
369326947304SEvan Yan 
369426947304SEvan Yan 	control =  pciehpc_reg_get16(ctrl_p,
369526947304SEvan Yan 	    bus_p->bus_pcie_off + PCIE_SLOTCTL);
369626947304SEvan Yan 
369726947304SEvan Yan 	PCIE_DBG("pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
369826947304SEvan Yan 	    slot_p->hs_phy_slot_num);
369926947304SEvan Yan 
370026947304SEvan Yan 	PCIE_DBG("Attention Button Present = %s\n",
370126947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
370226947304SEvan Yan 
370326947304SEvan Yan 	PCIE_DBG("Power controller Present = %s\n",
370426947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
370526947304SEvan Yan 
370626947304SEvan Yan 	PCIE_DBG("MRL Sensor Present	   = %s\n",
370726947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
370826947304SEvan Yan 
370926947304SEvan Yan 	PCIE_DBG("Attn Indicator Present   = %s\n",
371026947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
371126947304SEvan Yan 
371226947304SEvan Yan 	PCIE_DBG("Power Indicator Present  = %s\n",
371326947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
371426947304SEvan Yan 
371526947304SEvan Yan 	PCIE_DBG("HotPlug Surprise	   = %s\n",
371626947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
371726947304SEvan Yan 
371826947304SEvan Yan 	PCIE_DBG("HotPlug Capable	   = %s\n",
371926947304SEvan Yan 	    capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
372026947304SEvan Yan 
372126947304SEvan Yan 	PCIE_DBG("Physical Slot Number	   = %d\n",
372226947304SEvan Yan 	    PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
372326947304SEvan Yan 
372426947304SEvan Yan 	PCIE_DBG("Attn Button interrupt Enabled  = %s\n",
372526947304SEvan Yan 	    control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
372626947304SEvan Yan 
372726947304SEvan Yan 	PCIE_DBG("Power Fault interrupt Enabled  = %s\n",
372826947304SEvan Yan 	    control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
372926947304SEvan Yan 
373026947304SEvan Yan 	PCIE_DBG("MRL Sensor INTR Enabled   = %s\n",
373126947304SEvan Yan 	    control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
373226947304SEvan Yan 
373326947304SEvan Yan 	PCIE_DBG("Presence interrupt Enabled	 = %s\n",
373426947304SEvan Yan 	    control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
373526947304SEvan Yan 
373626947304SEvan Yan 	PCIE_DBG("Cmd Complete interrupt Enabled = %s\n",
373726947304SEvan Yan 	    control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
373826947304SEvan Yan 
373926947304SEvan Yan 	PCIE_DBG("HotPlug interrupt Enabled	 = %s\n",
374026947304SEvan Yan 	    control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
374126947304SEvan Yan 
374226947304SEvan Yan 	PCIE_DBG("Power Indicator LED = %s", pcie_led_state_text(
374326947304SEvan Yan 	    pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
374426947304SEvan Yan 
374526947304SEvan Yan 	PCIE_DBG("Attn Indicator LED = %s\n",
374626947304SEvan Yan 	    pcie_led_state_text(pciehpc_led_state_to_hpc(
374726947304SEvan Yan 	    pcie_slotctl_attn_indicator_get(control))));
374826947304SEvan Yan }
374926947304SEvan Yan #endif	/* DEBUG */
3750