xref: /illumos-gate/usr/src/uts/common/io/pciex/pcie_pwr.c (revision 86ef0a63e1cfa5dc98606efef379365acca98063)
1d4bc0535SKrishna Elango /*
2d4bc0535SKrishna Elango  * CDDL HEADER START
3d4bc0535SKrishna Elango  *
4d4bc0535SKrishna Elango  * The contents of this file are subject to the terms of the
5d4bc0535SKrishna Elango  * Common Development and Distribution License (the "License").
6d4bc0535SKrishna Elango  * You may not use this file except in compliance with the License.
7d4bc0535SKrishna Elango  *
8d4bc0535SKrishna Elango  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d4bc0535SKrishna Elango  * or http://www.opensolaris.org/os/licensing.
10d4bc0535SKrishna Elango  * See the License for the specific language governing permissions
11d4bc0535SKrishna Elango  * and limitations under the License.
12d4bc0535SKrishna Elango  *
13d4bc0535SKrishna Elango  * When distributing Covered Code, include this CDDL HEADER in each
14d4bc0535SKrishna Elango  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d4bc0535SKrishna Elango  * If applicable, add the following below this CDDL HEADER, with the
16d4bc0535SKrishna Elango  * fields enclosed by brackets "[]" replaced with your own identifying
17d4bc0535SKrishna Elango  * information: Portions Copyright [yyyy] [name of copyright owner]
18d4bc0535SKrishna Elango  *
19d4bc0535SKrishna Elango  * CDDL HEADER END
20d4bc0535SKrishna Elango  */
21d4bc0535SKrishna Elango /*
22d4bc0535SKrishna Elango  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23d4bc0535SKrishna Elango  * Use is subject to license terms.
24b3d69c05SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
25d4bc0535SKrishna Elango  */
26d4bc0535SKrishna Elango 
27d4bc0535SKrishna Elango #include <sys/types.h>
28d4bc0535SKrishna Elango #include <sys/ddi.h>
29d4bc0535SKrishna Elango #include <sys/kmem.h>
30d4bc0535SKrishna Elango #include <sys/sysmacros.h>
31d4bc0535SKrishna Elango #include <sys/sunddi.h>
32d4bc0535SKrishna Elango #include <sys/sunpm.h>
33d4bc0535SKrishna Elango #include <sys/epm.h>
34d4bc0535SKrishna Elango #include <sys/sunndi.h>
35d4bc0535SKrishna Elango #include <sys/ddi_impldefs.h>
36d4bc0535SKrishna Elango #include <sys/ddi_implfuncs.h>
37d4bc0535SKrishna Elango #include <sys/pcie.h>
38d4bc0535SKrishna Elango #include <sys/pcie_impl.h>
39d4bc0535SKrishna Elango #include <sys/promif.h>		/* prom_printf */
40d4bc0535SKrishna Elango #include <sys/pcie_pwr.h>
41d4bc0535SKrishna Elango 
42d4bc0535SKrishna Elango /*
43d4bc0535SKrishna Elango  * This file implements the power management functionality for
44d4bc0535SKrishna Elango  * pci express switch and pci express-to-pci/pci-x bridge. All the
45d4bc0535SKrishna Elango  * code in this file is generic and is not specific to a particular chip.
46d4bc0535SKrishna Elango  * The algorithm, which decides when to go to a lower power is explained
47d4bc0535SKrishna Elango  * below:
48d4bc0535SKrishna Elango  *
49d4bc0535SKrishna Elango  *	1. Initially when no children are attached, the driver is idle from
50d4bc0535SKrishna Elango  *	PM framework point of view ( PM idle/PM busy).
51d4bc0535SKrishna Elango  *
52d4bc0535SKrishna Elango  *	2. Driver is PM busy if either a reference count called pwr_hold is
53d4bc0535SKrishna Elango  *	greater than zero or driver is already at the lowest possible power
54d4bc0535SKrishna Elango  *	level. The lowest possible power level for the driver is equal to the
55d4bc0535SKrishna Elango  *	highest power level among its children. The PM busy condition is
56d4bc0535SKrishna Elango  *	indicated by PCIE_PM_BUSY bit. At any point, only one pm_busy_component
57d4bc0535SKrishna Elango  *	call is made for a nexus driver instance.
58d4bc0535SKrishna Elango  *
59d4bc0535SKrishna Elango  *	3. Driver is PM idle if the pwr_hold is zero and the lowest
60d4bc0535SKrishna Elango  *	possible power level is less than the driver's current power level.
61d4bc0535SKrishna Elango  *	At any point, only one pm_idle_component call is made for a nexus
62d4bc0535SKrishna Elango  *	driver instance.
63d4bc0535SKrishna Elango  *
64d4bc0535SKrishna Elango  *	4. For any events like child attach, it increments pwr_hold and marks
65d4bc0535SKrishna Elango  *	itslef busy, if it is not already done so. This temporary hold is
66d4bc0535SKrishna Elango  *	removed when the event is complete.
67d4bc0535SKrishna Elango  *
68d4bc0535SKrishna Elango  *	5. Any child's power change requires the parent (this driver) to be
69d4bc0535SKrishna Elango  *	full power. So it raises its power and increments pwr_hold. It also
70d4bc0535SKrishna Elango  *	marks itself temporarily busy, if it is not already done. This hold
71d4bc0535SKrishna Elango  *	is removed when the child power change is complete.
72d4bc0535SKrishna Elango  *
73d4bc0535SKrishna Elango  *	6. After each child power change, it evaluates what is the lowest
74d4bc0535SKrishna Elango  *	possible power level. If the lowest possible power level is less than
75d4bc0535SKrishna Elango  *	the current power level and pwr_hold is zero, then it marks itself
76d4bc0535SKrishna Elango  *	idle. The lowest power level is equal or greater than the highest level
77d4bc0535SKrishna Elango  *	among the children. It keeps track of children's power level by
78d4bc0535SKrishna Elango  *	using counters.
79d4bc0535SKrishna Elango  *
80d4bc0535SKrishna Elango  *	7. Any code e.g., which is accessing the driver's own registers should
81d4bc0535SKrishna Elango  *	place a temporary hold using pcie_pm_hold.
82d4bc0535SKrishna Elango  */
83d4bc0535SKrishna Elango 
84d4bc0535SKrishna Elango static int pcie_pwr_change(dev_info_t *dip, pcie_pwr_t *pwr_p, int new);
85d4bc0535SKrishna Elango static void pwr_update_counters(int *countersp, int olevel, int nlevel);
86d4bc0535SKrishna Elango static int pwr_level_allowed(pcie_pwr_t *pwr_p);
87d4bc0535SKrishna Elango static void pcie_add_comps(dev_info_t *dip, dev_info_t *cdip,
88d4bc0535SKrishna Elango     pcie_pwr_t *pwr_p);
89d4bc0535SKrishna Elango static void pcie_remove_comps(dev_info_t *dip, dev_info_t *cdip,
90d4bc0535SKrishna Elango     pcie_pwr_t *pwr_p);
91d4bc0535SKrishna Elango static void pcie_pm_subrelease(dev_info_t *dip, pcie_pwr_t *pwr_p);
92d4bc0535SKrishna Elango static boolean_t pcie_is_pcie(dev_info_t *dip);
93d4bc0535SKrishna Elango #ifdef DEBUG
94d4bc0535SKrishna Elango static char *pcie_decode_pwr_op(pm_bus_power_op_t op);
95d4bc0535SKrishna Elango #else
96d4bc0535SKrishna Elango #define	pcie_decode_pwr_op
97d4bc0535SKrishna Elango #endif
98d4bc0535SKrishna Elango 
99d4bc0535SKrishna Elango /*
100d4bc0535SKrishna Elango  * power entry point.
101d4bc0535SKrishna Elango  *
102d4bc0535SKrishna Elango  * This function decides whether the PM request is honorable.
103d4bc0535SKrishna Elango  * If yes, it then does what's necessary for switch or
104d4bc0535SKrishna Elango  *    bridge to change its power.
105d4bc0535SKrishna Elango  */
106d4bc0535SKrishna Elango /* ARGSUSED */
107d4bc0535SKrishna Elango int
pcie_power(dev_info_t * dip,int component,int level)108d4bc0535SKrishna Elango pcie_power(dev_info_t *dip, int component, int level)
109d4bc0535SKrishna Elango {
110d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p = PCIE_NEXUS_PMINFO(dip);
111d4bc0535SKrishna Elango 	int *counters = pwr_p->pwr_counters;
112d4bc0535SKrishna Elango 	int pmcaps = pwr_p->pwr_pmcaps;
113d4bc0535SKrishna Elango 	int ret = DDI_FAILURE;
114d4bc0535SKrishna Elango 
115*86ef0a63SRichard Lowe #if defined(__x86)
116d4bc0535SKrishna Elango 	if (dip)
117d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
118*86ef0a63SRichard Lowe #endif /* defined(__x86) */
119d4bc0535SKrishna Elango 
120d4bc0535SKrishna Elango 	ASSERT(level != PM_LEVEL_UNKNOWN);
121d4bc0535SKrishna Elango 	/* PM should not asking for a level, which is unsupported */
122d4bc0535SKrishna Elango 	ASSERT(level == PM_LEVEL_D0 || level == PM_LEVEL_D3 ||
123d4bc0535SKrishna Elango 	    (level == PM_LEVEL_D1 && (pmcaps & PCIE_SUPPORTS_D1)) ||
124d4bc0535SKrishna Elango 	    (level == PM_LEVEL_D2 && (pmcaps & PCIE_SUPPORTS_D2)));
125d4bc0535SKrishna Elango 
126d4bc0535SKrishna Elango 	mutex_enter(&pwr_p->pwr_lock);
127e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pcie_power: change from %d to %d\n",
128e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(dip), ddi_get_instance(dip), pwr_p->pwr_func_lvl,
129e762302fSShesha Sreenivasamurthy 	    level);
130d4bc0535SKrishna Elango 	if (pwr_p->pwr_func_lvl == level) {
131e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pcie_power: already at %d\n",
132e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip), level);
133d4bc0535SKrishna Elango 		ret = DDI_SUCCESS;
134d4bc0535SKrishna Elango 		goto pcie_pwr_done;
135d4bc0535SKrishna Elango 	}
136d4bc0535SKrishna Elango 
137d4bc0535SKrishna Elango 	if (level < pwr_p->pwr_func_lvl) {
138d4bc0535SKrishna Elango 		/*
139d4bc0535SKrishna Elango 		 * Going to lower power. Reject this if we are either busy
140d4bc0535SKrishna Elango 		 * or there is a hold.
141d4bc0535SKrishna Elango 		 */
142d4bc0535SKrishna Elango 		if (pwr_p->pwr_flags & PCIE_PM_BUSY) {
143e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_power: rejecting change to %d "
144e762302fSShesha Sreenivasamurthy 			    "as busy\n", ddi_driver_name(dip),
145e762302fSShesha Sreenivasamurthy 			    ddi_get_instance(dip), level);
146d4bc0535SKrishna Elango 			goto pcie_pwr_done;
147d4bc0535SKrishna Elango 		}
148d4bc0535SKrishna Elango 
149d4bc0535SKrishna Elango 		/*
150d4bc0535SKrishna Elango 		 * Now we know that we are neither busy nor there is a hold.
151d4bc0535SKrishna Elango 		 * At this point none of the children should be at full power.
152d4bc0535SKrishna Elango 		 * Reject the request if level reqested is lower than the level
153d4bc0535SKrishna Elango 		 * possible.
154d4bc0535SKrishna Elango 		 */
155d4bc0535SKrishna Elango 		ASSERT(!counters[PCIE_D0_INDEX] &&
156d4bc0535SKrishna Elango 		    !counters[PCIE_UNKNOWN_INDEX]);
157d4bc0535SKrishna Elango 		if (level < pwr_level_allowed(pwr_p)) {
158e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_power: rejecting level %d as"
159e762302fSShesha Sreenivasamurthy 			    " %d is the lowest possible\n",
160e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip), level,
161d4bc0535SKrishna Elango 			    pwr_level_allowed(pwr_p));
162d4bc0535SKrishna Elango 			goto pcie_pwr_done;
163d4bc0535SKrishna Elango 		}
164d4bc0535SKrishna Elango 	}
165d4bc0535SKrishna Elango 
166d4bc0535SKrishna Elango 	if (pcie_pwr_change(dip, pwr_p, level) != DDI_SUCCESS) {
167e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pcie_power: attempt to change to %d "
168e762302fSShesha Sreenivasamurthy 		    " failed \n", ddi_driver_name(dip), ddi_get_instance(dip),
169e762302fSShesha Sreenivasamurthy 		    level);
170d4bc0535SKrishna Elango 		goto pcie_pwr_done;
171d4bc0535SKrishna Elango 	}
172d4bc0535SKrishna Elango 	pwr_p->pwr_func_lvl = level;
173e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pcie_power: level changed to %d \n",
174e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(dip), ddi_get_instance(dip), level);
175d4bc0535SKrishna Elango 	ret = DDI_SUCCESS;
176d4bc0535SKrishna Elango 
177d4bc0535SKrishna Elango pcie_pwr_done:
178d4bc0535SKrishna Elango 	mutex_exit(&pwr_p->pwr_lock);
179d4bc0535SKrishna Elango 	return (ret);
180d4bc0535SKrishna Elango }
181d4bc0535SKrishna Elango 
182d4bc0535SKrishna Elango /*
183d4bc0535SKrishna Elango  * Called by pcie_power() only. Caller holds the pwr_lock.
184d4bc0535SKrishna Elango  *
185d4bc0535SKrishna Elango  * dip - dev_info pointer
186d4bc0535SKrishna Elango  * pwr_p - pm info for the node.
187d4bc0535SKrishna Elango  * new     - new level
188d4bc0535SKrishna Elango  */
189d4bc0535SKrishna Elango static int
pcie_pwr_change(dev_info_t * dip,pcie_pwr_t * pwr_p,int new)190d4bc0535SKrishna Elango pcie_pwr_change(dev_info_t *dip, pcie_pwr_t *pwr_p, int new)
191d4bc0535SKrishna Elango {
192d4bc0535SKrishna Elango 	uint16_t pmcsr;
193d4bc0535SKrishna Elango 
194d4bc0535SKrishna Elango 	ASSERT(MUTEX_HELD(&pwr_p->pwr_lock));
195d4bc0535SKrishna Elango 	ASSERT(new != pwr_p->pwr_func_lvl);
196d4bc0535SKrishna Elango 	pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset);
197d4bc0535SKrishna Elango 	pmcsr &= ~PCI_PMCSR_STATE_MASK;
198d4bc0535SKrishna Elango 	switch (new) {
199d4bc0535SKrishna Elango 	case PM_LEVEL_D0:
200d4bc0535SKrishna Elango 		pmcsr |= PCI_PMCSR_D0;
201d4bc0535SKrishna Elango 		break;
202d4bc0535SKrishna Elango 
203d4bc0535SKrishna Elango 	case PM_LEVEL_D1:
204d4bc0535SKrishna Elango 		pmcsr |= PCI_PMCSR_D1;
205d4bc0535SKrishna Elango 		break;
206d4bc0535SKrishna Elango 
207d4bc0535SKrishna Elango 	case PM_LEVEL_D2:
208d4bc0535SKrishna Elango 		pmcsr |= PCI_PMCSR_D2;
209d4bc0535SKrishna Elango 		break;
210d4bc0535SKrishna Elango 
211d4bc0535SKrishna Elango 	case PM_LEVEL_D3:
212d4bc0535SKrishna Elango 		pmcsr |= PCI_PMCSR_D3HOT;
213d4bc0535SKrishna Elango 		break;
214d4bc0535SKrishna Elango 
215d4bc0535SKrishna Elango 	default:
216d4bc0535SKrishna Elango 		ASSERT(0);
217d4bc0535SKrishna Elango 		break;
218d4bc0535SKrishna Elango 	}
219d4bc0535SKrishna Elango 	/* Save config space, if going to D3 */
220d4bc0535SKrishna Elango 	if (new == PM_LEVEL_D3) {
221e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pwr_change: saving config space regs\n",
222e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip));
223d4bc0535SKrishna Elango 		if (pci_save_config_regs(dip) != DDI_SUCCESS) {
224e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_pwr_change: failed to save "
225e762302fSShesha Sreenivasamurthy 			    "config space regs\n", ddi_driver_name(dip),
226e762302fSShesha Sreenivasamurthy 			    ddi_get_instance(dip));
227d4bc0535SKrishna Elango 			return (DDI_FAILURE);
228d4bc0535SKrishna Elango 		}
229d4bc0535SKrishna Elango 	}
230d4bc0535SKrishna Elango 
231d4bc0535SKrishna Elango 	pci_config_put16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset, pmcsr);
232d4bc0535SKrishna Elango 
233d4bc0535SKrishna Elango 	/*
234d4bc0535SKrishna Elango 	 * TBD: Taken from pci_pci driver. Is this required?
235d4bc0535SKrishna Elango 	 * No bus transactions should occur without waiting for
236d4bc0535SKrishna Elango 	 * settle time specified in PCI PM spec rev 2.1 sec 5.6.1
237d4bc0535SKrishna Elango 	 * To make things simple, just use the max time specified for
238d4bc0535SKrishna Elango 	 * all state transitions.
239d4bc0535SKrishna Elango 	 */
240d4bc0535SKrishna Elango 	delay(drv_usectohz(PCI_CLK_SETTLE_TIME));
241d4bc0535SKrishna Elango 
242d4bc0535SKrishna Elango 	/*
243d4bc0535SKrishna Elango 	 * Restore config space if coming out of D3
244d4bc0535SKrishna Elango 	 */
245d4bc0535SKrishna Elango 	if (pwr_p->pwr_func_lvl == PM_LEVEL_D3) {
246e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pcie_pwr_change: restoring config space\n",
247e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip));
248d4bc0535SKrishna Elango 		if (pci_restore_config_regs(dip) != DDI_SUCCESS) {
249e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_pwr_change: failed to restore "
250e762302fSShesha Sreenivasamurthy 			    "config space regs\n", ddi_driver_name(dip),
251e762302fSShesha Sreenivasamurthy 			    ddi_get_instance(dip));
252d4bc0535SKrishna Elango 			return (DDI_FAILURE);
253d4bc0535SKrishna Elango 		}
254d4bc0535SKrishna Elango 	}
255d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
256d4bc0535SKrishna Elango }
257d4bc0535SKrishna Elango 
258d4bc0535SKrishna Elango /*
259d4bc0535SKrishna Elango  * bus_ctlops.bus_power function.
260d4bc0535SKrishna Elango  *
261d4bc0535SKrishna Elango  * This function handles PRE_ POST_ change notifications, sent by
262d4bc0535SKrishna Elango  * PM framework related to child's power level change. It marks itself
263d4bc0535SKrishna Elango  * idle or busy based on the children's power level.
264d4bc0535SKrishna Elango  */
265d4bc0535SKrishna Elango int
pcie_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)266d4bc0535SKrishna Elango pcie_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
267d4bc0535SKrishna Elango     void *arg, void *result)
268d4bc0535SKrishna Elango {
269d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p = PCIE_NEXUS_PMINFO(dip);
270d4bc0535SKrishna Elango 	int *counters = pwr_p->pwr_counters; /* nexus counters */
271d4bc0535SKrishna Elango 	int *child_counters; /* per child dip counters */
272d4bc0535SKrishna Elango 	pm_bp_child_pwrchg_t *bpc;
273d4bc0535SKrishna Elango 	pm_bp_has_changed_t *bphc;
274d4bc0535SKrishna Elango 	dev_info_t *cdip;
275d4bc0535SKrishna Elango 	int new_level;
276d4bc0535SKrishna Elango 	int old_level;
277d4bc0535SKrishna Elango 	int rv = DDI_SUCCESS;
278d4bc0535SKrishna Elango 	int level_allowed, comp;
279d4bc0535SKrishna Elango 
280*86ef0a63SRichard Lowe #if defined(__x86)
281d4bc0535SKrishna Elango 	if (dip)
282d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
283*86ef0a63SRichard Lowe #endif /* defined(__x86) */
284d4bc0535SKrishna Elango 
285d4bc0535SKrishna Elango 	switch (op) {
286d4bc0535SKrishna Elango 	case BUS_POWER_PRE_NOTIFICATION:
287d4bc0535SKrishna Elango 	case BUS_POWER_POST_NOTIFICATION:
288d4bc0535SKrishna Elango 		bpc = (pm_bp_child_pwrchg_t *)arg;
289d4bc0535SKrishna Elango 		cdip = bpc->bpc_dip;
290d4bc0535SKrishna Elango 		new_level = bpc->bpc_nlevel;
291d4bc0535SKrishna Elango 		old_level = bpc->bpc_olevel;
292d4bc0535SKrishna Elango 		comp = bpc->bpc_comp;
293d4bc0535SKrishna Elango 		break;
294d4bc0535SKrishna Elango 
295d4bc0535SKrishna Elango 	case BUS_POWER_HAS_CHANGED:
296d4bc0535SKrishna Elango 		bphc = (pm_bp_has_changed_t *)arg;
297d4bc0535SKrishna Elango 		cdip = bphc->bphc_dip;
298d4bc0535SKrishna Elango 		new_level = bphc->bphc_nlevel;
299d4bc0535SKrishna Elango 		old_level = bphc->bphc_olevel;
300d4bc0535SKrishna Elango 		comp = bphc->bphc_comp;
301d4bc0535SKrishna Elango 		break;
302d4bc0535SKrishna Elango 
303d4bc0535SKrishna Elango 	default:
304d4bc0535SKrishna Elango 		break;
305d4bc0535SKrishna Elango 
306d4bc0535SKrishna Elango 	}
307d4bc0535SKrishna Elango 
308d4bc0535SKrishna Elango 	ASSERT(pwr_p);
309d4bc0535SKrishna Elango 	mutex_enter(&pwr_p->pwr_lock);
310d4bc0535SKrishna Elango 	switch (op) {
311d4bc0535SKrishna Elango 	case BUS_POWER_PRE_NOTIFICATION:
312e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pcie_bus_power: %s@%d op %s %d->%d\n",
313e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip),
314d4bc0535SKrishna Elango 		    ddi_driver_name(cdip), ddi_get_instance(cdip),
315d4bc0535SKrishna Elango 		    pcie_decode_pwr_op(op), old_level, new_level);
316d4bc0535SKrishna Elango 		/*
317d4bc0535SKrishna Elango 		 * If the nexus doesn't want the child to go into
318d4bc0535SKrishna Elango 		 * non-D0 state, mark the child busy. This way PM
319d4bc0535SKrishna Elango 		 * framework will never try to lower the child's power.
320d4bc0535SKrishna Elango 		 * In case of pm_lower_power, marking busy won't help.
321d4bc0535SKrishna Elango 		 * So we need to specifically reject the attempt to
322d4bc0535SKrishna Elango 		 * go to non-D0 state.
323d4bc0535SKrishna Elango 		 */
324d4bc0535SKrishna Elango 		if (pwr_p->pwr_flags & PCIE_NO_CHILD_PM) {
325d4bc0535SKrishna Elango 			if (!PCIE_IS_COMPS_COUNTED(cdip)) {
326e762302fSShesha Sreenivasamurthy 				PCIE_DBG("%s(%d): pcie_bus_power: marking "
327e762302fSShesha Sreenivasamurthy 				    "child busy to disable pm \n",
328e762302fSShesha Sreenivasamurthy 				    ddi_driver_name(dip),
329e762302fSShesha Sreenivasamurthy 				    ddi_get_instance(dip));
330d4bc0535SKrishna Elango 				(void) pm_busy_component(cdip, 0);
331d4bc0535SKrishna Elango 			}
332d4bc0535SKrishna Elango 			if (new_level < PM_LEVEL_D0 && !comp) {
333e762302fSShesha Sreenivasamurthy 				PCIE_DBG("%s(%d): pcie_bus_power: rejecting "
334e762302fSShesha Sreenivasamurthy 				    "child's attempt to go to %d\n",
335e762302fSShesha Sreenivasamurthy 				    ddi_driver_name(dip), ddi_get_instance(dip),
336e762302fSShesha Sreenivasamurthy 				    new_level);
337d4bc0535SKrishna Elango 				rv = DDI_FAILURE;
338d4bc0535SKrishna Elango 			}
339d4bc0535SKrishna Elango 		}
340d4bc0535SKrishna Elango 		mutex_exit(&pwr_p->pwr_lock);
341d4bc0535SKrishna Elango 		if (rv == DDI_SUCCESS)
342d4bc0535SKrishna Elango 			rv = pcie_pm_hold(dip);
343d4bc0535SKrishna Elango 		return (rv);
344d4bc0535SKrishna Elango 
345d4bc0535SKrishna Elango 	case BUS_POWER_HAS_CHANGED:
346d4bc0535SKrishna Elango 	case BUS_POWER_POST_NOTIFICATION:
347e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pcie_bus_power: %s@%d op %s %d->%d\n",
348e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip),
349d4bc0535SKrishna Elango 		    ddi_driver_name(cdip), ddi_get_instance(cdip),
350d4bc0535SKrishna Elango 		    pcie_decode_pwr_op(op), old_level, new_level);
351d4bc0535SKrishna Elango 		/*
352d4bc0535SKrishna Elango 		 * Child device power changed
353d4bc0535SKrishna Elango 		 * If pm components of this child aren't accounted for
354d4bc0535SKrishna Elango 		 * then add the components to the counters. This can't
355d4bc0535SKrishna Elango 		 * be done in POST_ATTACH ctlop as pm info isn't created
356d4bc0535SKrishna Elango 		 * by then. Also because a driver can make a pm call during
357d4bc0535SKrishna Elango 		 * the attach.
358d4bc0535SKrishna Elango 		 */
359d4bc0535SKrishna Elango 		if (!PCIE_IS_COMPS_COUNTED(cdip)) {
360d4bc0535SKrishna Elango 			(void) pcie_pm_add_child(dip, cdip);
361d4bc0535SKrishna Elango 			if ((pwr_p->pwr_flags & PCIE_NO_CHILD_PM) &&
362d4bc0535SKrishna Elango 			    (op == BUS_POWER_HAS_CHANGED)) {
363e762302fSShesha Sreenivasamurthy 				PCIE_DBG("%s(%d): pcie_bus_power: marking "
364e762302fSShesha Sreenivasamurthy 				    "child busy to disable pm \n",
365e762302fSShesha Sreenivasamurthy 				    ddi_driver_name(dip),
366e762302fSShesha Sreenivasamurthy 				    ddi_get_instance(dip));
367d4bc0535SKrishna Elango 				(void) pm_busy_component(cdip, 0);
368d4bc0535SKrishna Elango 				/*
369d4bc0535SKrishna Elango 				 * If the driver has already changed to lower
370d4bc0535SKrishna Elango 				 * power(pm_power_has_changed) on its own,
371d4bc0535SKrishna Elango 				 * there is nothing we can do other than
372d4bc0535SKrishna Elango 				 * logging the warning message on the console.
373d4bc0535SKrishna Elango 				 */
374d4bc0535SKrishna Elango 				if (new_level < PM_LEVEL_D0)
375d4bc0535SKrishna Elango 					cmn_err(CE_WARN, "!Downstream device "
376d4bc0535SKrishna Elango 					    "%s@%d went to non-D0 state: "
377d4bc0535SKrishna Elango 					    "possible loss of link\n",
378d4bc0535SKrishna Elango 					    ddi_driver_name(cdip),
379d4bc0535SKrishna Elango 					    ddi_get_instance(cdip));
380d4bc0535SKrishna Elango 			}
381d4bc0535SKrishna Elango 		}
382d4bc0535SKrishna Elango 
383d4bc0535SKrishna Elango 
384d4bc0535SKrishna Elango 		/*
385d4bc0535SKrishna Elango 		 * If it is POST and device PM is supported, release the
386d4bc0535SKrishna Elango 		 * hold done in PRE.
387d4bc0535SKrishna Elango 		 */
388d4bc0535SKrishna Elango 		if (op == BUS_POWER_POST_NOTIFICATION &&
389d4bc0535SKrishna Elango 		    PCIE_SUPPORTS_DEVICE_PM(dip)) {
390d4bc0535SKrishna Elango 			pcie_pm_subrelease(dip, pwr_p);
391d4bc0535SKrishna Elango 		}
392d4bc0535SKrishna Elango 
393d4bc0535SKrishna Elango 		if (*((int *)result) == DDI_FAILURE) {
394e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_bus_power: change for %s%d "
395e762302fSShesha Sreenivasamurthy 			    "failed\n", ddi_driver_name(dip),
396e762302fSShesha Sreenivasamurthy 			    ddi_get_instance(dip), ddi_driver_name(cdip),
397e762302fSShesha Sreenivasamurthy 			    ddi_get_instance(cdip));
398d4bc0535SKrishna Elango 			break;
399d4bc0535SKrishna Elango 		}
400d4bc0535SKrishna Elango 		/* Modify counters appropriately */
401d4bc0535SKrishna Elango 		pwr_update_counters(counters, old_level, new_level);
402d4bc0535SKrishna Elango 
403d4bc0535SKrishna Elango 		child_counters = PCIE_CHILD_COUNTERS(cdip);
404d4bc0535SKrishna Elango 		pwr_update_counters(child_counters, old_level, new_level);
405d4bc0535SKrishna Elango 
406d4bc0535SKrishna Elango 		/* If no device PM, return */
407d4bc0535SKrishna Elango 		if (!PCIE_SUPPORTS_DEVICE_PM(dip))
408d4bc0535SKrishna Elango 			break;
409d4bc0535SKrishna Elango 
410d4bc0535SKrishna Elango 		level_allowed = pwr_level_allowed(pwr_p);
411d4bc0535SKrishna Elango 		/*
412d4bc0535SKrishna Elango 		 * Check conditions for marking busy
413d4bc0535SKrishna Elango 		 * Check the flag to set this busy only once for multiple
414d4bc0535SKrishna Elango 		 * busy conditions. Mark busy if our current lowest possible
415d4bc0535SKrishna Elango 		 * is equal or greater to the current level.
416d4bc0535SKrishna Elango 		 */
417d4bc0535SKrishna Elango 		if (level_allowed >= pwr_p->pwr_func_lvl &&
418d4bc0535SKrishna Elango 		    !(pwr_p->pwr_flags & PCIE_PM_BUSY)) {
419e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_bus_power: marking busy\n",
420e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip));
421d4bc0535SKrishna Elango 			(void) pm_busy_component(dip, 0);
422d4bc0535SKrishna Elango 			pwr_p->pwr_flags |= PCIE_PM_BUSY;
423d4bc0535SKrishna Elango 			break;
424d4bc0535SKrishna Elango 		}
425d4bc0535SKrishna Elango 		/*
426d4bc0535SKrishna Elango 		 * Check conditions for marking idle.
427d4bc0535SKrishna Elango 		 * If our lowest possible level is less than our current
428d4bc0535SKrishna Elango 		 * level mark idle. Mark idle only if it is not already done.
429d4bc0535SKrishna Elango 		 */
430d4bc0535SKrishna Elango 		if ((level_allowed < pwr_p->pwr_func_lvl) &&
431d4bc0535SKrishna Elango 		    (pwr_p->pwr_hold == 0) &&
432d4bc0535SKrishna Elango 		    (pwr_p->pwr_flags & PCIE_PM_BUSY)) {
433d4bc0535SKrishna Elango 			/*
434d4bc0535SKrishna Elango 			 * For pci express, we should check here whether
435d4bc0535SKrishna Elango 			 * the link is in L1 state or not.
436d4bc0535SKrishna Elango 			 */
437e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_bus_power: marking idle\n",
438e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip));
439d4bc0535SKrishna Elango 			(void) pm_idle_component(dip, 0);
440d4bc0535SKrishna Elango 			pwr_p->pwr_flags &= ~PCIE_PM_BUSY;
441d4bc0535SKrishna Elango 			break;
442d4bc0535SKrishna Elango 		}
443d4bc0535SKrishna Elango 		break;
444d4bc0535SKrishna Elango 
445d4bc0535SKrishna Elango 	default:
446d4bc0535SKrishna Elango 		mutex_exit(&pwr_p->pwr_lock);
447d4bc0535SKrishna Elango 		return (pm_busop_bus_power(dip, impl_arg, op, arg, result));
448d4bc0535SKrishna Elango 	}
449d4bc0535SKrishna Elango 	mutex_exit(&pwr_p->pwr_lock);
450d4bc0535SKrishna Elango 	return (rv);
451d4bc0535SKrishna Elango }
452d4bc0535SKrishna Elango 
453d4bc0535SKrishna Elango /*
454d4bc0535SKrishna Elango  * Decrement the count of children at olevel by one and increment
455d4bc0535SKrishna Elango  * count of children at nlevel by one.
456d4bc0535SKrishna Elango  */
457d4bc0535SKrishna Elango static void
pwr_update_counters(int * countersp,int olevel,int nlevel)458d4bc0535SKrishna Elango pwr_update_counters(int *countersp, int olevel, int nlevel)
459d4bc0535SKrishna Elango {
460d4bc0535SKrishna Elango 	uint32_t	index;
461d4bc0535SKrishna Elango 
462d4bc0535SKrishna Elango 	ASSERT(olevel >= PM_LEVEL_UNKNOWN && olevel <= PM_LEVEL_D0);
463d4bc0535SKrishna Elango 	ASSERT(nlevel >= PM_LEVEL_UNKNOWN && nlevel <= PM_LEVEL_D0);
464d4bc0535SKrishna Elango 
465d4bc0535SKrishna Elango 	index = (olevel == PM_LEVEL_UNKNOWN ? PCIE_UNKNOWN_INDEX : olevel);
466d4bc0535SKrishna Elango 	countersp[index]--;
467d4bc0535SKrishna Elango 	index = (nlevel == PM_LEVEL_UNKNOWN ? PCIE_UNKNOWN_INDEX : nlevel);
468d4bc0535SKrishna Elango 	countersp[index]++;
469d4bc0535SKrishna Elango }
470d4bc0535SKrishna Elango 
471d4bc0535SKrishna Elango /*
472d4bc0535SKrishna Elango  * Returns the lowest possible power level allowed for nexus
473d4bc0535SKrishna Elango  * based on children's power level. Lowest possible level is
474d4bc0535SKrishna Elango  * equal to the highest level among the children. It also checks
475d4bc0535SKrishna Elango  * for the supported level
476d4bc0535SKrishna Elango  * UNKNOWN = D0 > D1 > D2 > D3
477d4bc0535SKrishna Elango  */
478d4bc0535SKrishna Elango static int
pwr_level_allowed(pcie_pwr_t * pwr_p)479d4bc0535SKrishna Elango pwr_level_allowed(pcie_pwr_t *pwr_p)
480d4bc0535SKrishna Elango {
481d4bc0535SKrishna Elango 	int *counters = pwr_p->pwr_counters;
482d4bc0535SKrishna Elango 	int i, j;
483d4bc0535SKrishna Elango 
484d4bc0535SKrishna Elango 	ASSERT(MUTEX_HELD(&pwr_p->pwr_lock));
485d4bc0535SKrishna Elango 	/*
486d4bc0535SKrishna Elango 	 * Search from UNKNOWN to D2. unknown is same as D0.
487d4bc0535SKrishna Elango 	 * find the highest level among the children. If that
488d4bc0535SKrishna Elango 	 * level is supported, return that level. If not,
489d4bc0535SKrishna Elango 	 * find the next higher supported level and return that
490d4bc0535SKrishna Elango 	 * level. For example, if the D1 is the highest among
491d4bc0535SKrishna Elango 	 * children and if D1 isn't supported return D0 as the
492d4bc0535SKrishna Elango 	 * lowest possible level. We don't need to look at D3
493d4bc0535SKrishna Elango 	 * as that is the default lowest level and it is always
494d4bc0535SKrishna Elango 	 * supported.
495d4bc0535SKrishna Elango 	 */
496d4bc0535SKrishna Elango 	for (i = PCIE_UNKNOWN_INDEX; i > 0; i--) {
497d4bc0535SKrishna Elango 		if (counters[i]) {
498d4bc0535SKrishna Elango 			if (i == PCIE_UNKNOWN_INDEX)
499d4bc0535SKrishna Elango 				return (PM_LEVEL_D0);
500d4bc0535SKrishna Elango 			/*
501d4bc0535SKrishna Elango 			 * i is the highest level among children. If this is
502d4bc0535SKrishna Elango 			 * supported, return i.
503d4bc0535SKrishna Elango 			 */
504d4bc0535SKrishna Elango 			if (PCIE_LEVEL_SUPPORTED(pwr_p->pwr_pmcaps, i))
505d4bc0535SKrishna Elango 				return (i);
506d4bc0535SKrishna Elango 			/* find the next higher supported level */
507d4bc0535SKrishna Elango 			for (j = i + 1; j <= PCIE_D0_INDEX; j++) {
508d4bc0535SKrishna Elango 				if (PCIE_LEVEL_SUPPORTED(pwr_p->pwr_pmcaps, j))
509d4bc0535SKrishna Elango 					return (j);
510d4bc0535SKrishna Elango 			}
511d4bc0535SKrishna Elango 		}
512d4bc0535SKrishna Elango 	}
513d4bc0535SKrishna Elango 
514d4bc0535SKrishna Elango 	return (PM_LEVEL_D3);
515d4bc0535SKrishna Elango }
516d4bc0535SKrishna Elango 
517d4bc0535SKrishna Elango /*
518d4bc0535SKrishna Elango  * Update the counters with number pm components of the child
519d4bc0535SKrishna Elango  * all components are assumed to be at UNKNOWN level.
520d4bc0535SKrishna Elango  */
521d4bc0535SKrishna Elango static void
pcie_add_comps(dev_info_t * dip,dev_info_t * cdip,pcie_pwr_t * pwr_p)522d4bc0535SKrishna Elango pcie_add_comps(dev_info_t *dip, dev_info_t *cdip, pcie_pwr_t *pwr_p)
523d4bc0535SKrishna Elango {
524d4bc0535SKrishna Elango 	int comps = PM_NUMCMPTS(cdip);
525d4bc0535SKrishna Elango 	pcie_pm_t *pcie_pm_p;
526d4bc0535SKrishna Elango 	pcie_pwr_child_t *cpwr_p;
527d4bc0535SKrishna Elango 
528d4bc0535SKrishna Elango 	ASSERT(MUTEX_HELD(&pwr_p->pwr_lock));
529d4bc0535SKrishna Elango 	if (!comps)
530d4bc0535SKrishna Elango 		return;
531d4bc0535SKrishna Elango 
532e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pcie_add_comps: unknown level counter incremented "
533d4bc0535SKrishna Elango 	    "from %d by %d because of %s@%d\n",
534e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(dip), ddi_get_instance(dip),
535d4bc0535SKrishna Elango 	    (pwr_p->pwr_counters)[PCIE_UNKNOWN_INDEX], comps,
536d4bc0535SKrishna Elango 	    ddi_driver_name(cdip), ddi_get_instance(cdip));
537d4bc0535SKrishna Elango 	(pwr_p->pwr_counters)[PCIE_UNKNOWN_INDEX] += comps;
538d4bc0535SKrishna Elango 	/*
539d4bc0535SKrishna Elango 	 * Allocate counters per child. This is a part of pcie
540d4bc0535SKrishna Elango 	 * pm info. If there is no pcie pm info, allocate it here.
541d4bc0535SKrishna Elango 	 * pcie pm info might already be there for pci express nexus
542d4bc0535SKrishna Elango 	 * driver e.g. pcieb. For all leaf nodes, it is allocated here.
543d4bc0535SKrishna Elango 	 */
544d4bc0535SKrishna Elango 	if ((pcie_pm_p = PCIE_PMINFO(cdip)) == NULL) {
545d4bc0535SKrishna Elango 		pcie_pm_p = (pcie_pm_t *)kmem_zalloc(
546d4bc0535SKrishna Elango 		    sizeof (pcie_pm_t), KM_SLEEP);
547d4bc0535SKrishna Elango 		PCIE_SET_PMINFO(cdip, pcie_pm_p);
548d4bc0535SKrishna Elango 	}
549d4bc0535SKrishna Elango 	cpwr_p = (pcie_pwr_child_t *)kmem_zalloc(sizeof (pcie_pwr_child_t),
550d4bc0535SKrishna Elango 	    KM_SLEEP);
551d4bc0535SKrishna Elango 	pcie_pm_p->pcie_par_pminfo = cpwr_p;
552d4bc0535SKrishna Elango 	(cpwr_p->pwr_child_counters)[PCIE_UNKNOWN_INDEX] += comps;
553d4bc0535SKrishna Elango }
554d4bc0535SKrishna Elango 
555d4bc0535SKrishna Elango /*
556d4bc0535SKrishna Elango  * Remove the pm components of a child from our counters.
557d4bc0535SKrishna Elango  */
558d4bc0535SKrishna Elango static void
pcie_remove_comps(dev_info_t * dip,dev_info_t * cdip,pcie_pwr_t * pwr_p)559d4bc0535SKrishna Elango pcie_remove_comps(dev_info_t *dip, dev_info_t *cdip, pcie_pwr_t *pwr_p)
560d4bc0535SKrishna Elango {
561d4bc0535SKrishna Elango 	int i;
562d4bc0535SKrishna Elango 	int *child_counters;
563d4bc0535SKrishna Elango 
564d4bc0535SKrishna Elango 	ASSERT(MUTEX_HELD(&pwr_p->pwr_lock));
565d4bc0535SKrishna Elango 	if (!(PCIE_PMINFO(cdip)) || !PCIE_PAR_PMINFO(cdip)) {
566d4bc0535SKrishna Elango 		if (PCIE_SUPPORTS_DEVICE_PM(dip)) {
567d4bc0535SKrishna Elango 			/*
568d4bc0535SKrishna Elango 			 * Driver never made a PM call and we didn't create
569d4bc0535SKrishna Elango 			 * any counters for this device. This also means that
570d4bc0535SKrishna Elango 			 * hold made at the PRE_ATTACH time, still remains.
571d4bc0535SKrishna Elango 			 * Remove the hold now. The correct thing to do is to
572d4bc0535SKrishna Elango 			 * stay at full power when a child is at full power
573d4bc0535SKrishna Elango 			 * whether a driver is there or not. This will be
574d4bc0535SKrishna Elango 			 * implemented in the future.
575d4bc0535SKrishna Elango 			 */
576d4bc0535SKrishna Elango 			pcie_pm_subrelease(dip, pwr_p);
577d4bc0535SKrishna Elango 		}
578d4bc0535SKrishna Elango 		return;
579d4bc0535SKrishna Elango 	}
580e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pcie_remove_comps:counters decremented because of "
581e762302fSShesha Sreenivasamurthy 	    "%s@%d\n", ddi_driver_name(dip), ddi_get_instance(dip),
582e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(cdip), ddi_get_instance(cdip));
583d4bc0535SKrishna Elango 	child_counters = PCIE_CHILD_COUNTERS(cdip);
584d4bc0535SKrishna Elango 	/*
585d4bc0535SKrishna Elango 	 * Adjust the nexus counters. No need to adjust per child dip
586d4bc0535SKrishna Elango 	 * counters as we are freeing the per child dip info.
587d4bc0535SKrishna Elango 	 */
588d4bc0535SKrishna Elango 	for (i = 0; i < PCIE_MAX_PWR_LEVELS; i++) {
589d4bc0535SKrishna Elango 		ASSERT((pwr_p->pwr_counters)[i] >= child_counters[i]);
590d4bc0535SKrishna Elango 		(pwr_p->pwr_counters)[i] -= child_counters[i];
591d4bc0535SKrishna Elango 	}
592d4bc0535SKrishna Elango 	/* remove both parent pm info and pcie pminfo itself */
593d4bc0535SKrishna Elango 	kmem_free(PCIE_PAR_PMINFO(cdip), sizeof (pcie_pwr_child_t));
594d4bc0535SKrishna Elango 	kmem_free(PCIE_PMINFO(cdip), sizeof (pcie_pm_t));
595d4bc0535SKrishna Elango 	PCIE_RESET_PMINFO(cdip);
596d4bc0535SKrishna Elango }
597d4bc0535SKrishna Elango 
598d4bc0535SKrishna Elango /*
599d4bc0535SKrishna Elango  * Power management related initialization common to px and pcieb
600d4bc0535SKrishna Elango  */
601d4bc0535SKrishna Elango int
pwr_common_setup(dev_info_t * dip)602d4bc0535SKrishna Elango pwr_common_setup(dev_info_t *dip)
603d4bc0535SKrishna Elango {
604d4bc0535SKrishna Elango 	pcie_pm_t		*pcie_pm_p;
605d4bc0535SKrishna Elango 	pcie_pwr_t		*pwr_p;
606d4bc0535SKrishna Elango 	int			pminfo_created = 0;
607d4bc0535SKrishna Elango 
608d4bc0535SKrishna Elango 	/* Create pminfo, if it doesn't exist already */
609d4bc0535SKrishna Elango 	if ((pcie_pm_p = PCIE_PMINFO(dip)) == NULL) {
610d4bc0535SKrishna Elango 		pcie_pm_p = (pcie_pm_t *)kmem_zalloc(
611d4bc0535SKrishna Elango 		    sizeof (pcie_pm_t), KM_SLEEP);
612d4bc0535SKrishna Elango 		PCIE_SET_PMINFO(dip, pcie_pm_p);
613d4bc0535SKrishna Elango 		pminfo_created = 1;
614d4bc0535SKrishna Elango 	}
615d4bc0535SKrishna Elango 	pwr_p = (pcie_pwr_t *)kmem_zalloc(sizeof (pcie_pwr_t), KM_SLEEP);
616d4bc0535SKrishna Elango 	mutex_init(&pwr_p->pwr_lock, NULL, MUTEX_DRIVER, NULL);
617d4bc0535SKrishna Elango 	/* Initialize the power level and default level support */
618d4bc0535SKrishna Elango 	pwr_p->pwr_func_lvl = PM_LEVEL_UNKNOWN;
619d4bc0535SKrishna Elango 	pwr_p->pwr_pmcaps = PCIE_DEFAULT_LEVEL_SUPPORTED;
620d4bc0535SKrishna Elango 
621e762302fSShesha Sreenivasamurthy 	if (pcie_plat_pwr_setup(dip) != DDI_SUCCESS)
622d4bc0535SKrishna Elango 		goto pwr_common_err;
623d4bc0535SKrishna Elango 
624e762302fSShesha Sreenivasamurthy 	pcie_pm_p->pcie_pwr_p = pwr_p;
625d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
626d4bc0535SKrishna Elango 
627d4bc0535SKrishna Elango pwr_common_err:
628d4bc0535SKrishna Elango 	mutex_destroy(&pwr_p->pwr_lock);
629d4bc0535SKrishna Elango 	kmem_free(pwr_p, sizeof (pcie_pwr_t));
630d4bc0535SKrishna Elango 	if (pminfo_created) {
631d4bc0535SKrishna Elango 		PCIE_RESET_PMINFO(dip);
632d4bc0535SKrishna Elango 		kmem_free(pcie_pm_p, sizeof (pcie_pm_t));
633d4bc0535SKrishna Elango 	}
634d4bc0535SKrishna Elango 	return (DDI_FAILURE);
635d4bc0535SKrishna Elango 
636d4bc0535SKrishna Elango }
637d4bc0535SKrishna Elango 
638d4bc0535SKrishna Elango /*
639d4bc0535SKrishna Elango  * Undo whatever is done in pwr_common_setup. Called by px_detach or pxb_detach
640d4bc0535SKrishna Elango  */
641d4bc0535SKrishna Elango void
pwr_common_teardown(dev_info_t * dip)642d4bc0535SKrishna Elango pwr_common_teardown(dev_info_t *dip)
643d4bc0535SKrishna Elango {
644d4bc0535SKrishna Elango 	pcie_pm_t *pcie_pm_p = PCIE_PMINFO(dip);
645d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
646d4bc0535SKrishna Elango 
647d4bc0535SKrishna Elango 	if (!pcie_pm_p || !(pwr_p = PCIE_NEXUS_PMINFO(dip)))
648d4bc0535SKrishna Elango 		return;
649d4bc0535SKrishna Elango 
650e762302fSShesha Sreenivasamurthy 	pcie_plat_pwr_teardown(dip);
651d4bc0535SKrishna Elango 	mutex_destroy(&pwr_p->pwr_lock);
652d4bc0535SKrishna Elango 	pcie_pm_p->pcie_pwr_p = NULL;
653d4bc0535SKrishna Elango 	kmem_free(pwr_p, sizeof (pcie_pwr_t));
654d4bc0535SKrishna Elango 	/*
655d4bc0535SKrishna Elango 	 * If the parent didn't store have any pm info about
656d4bc0535SKrishna Elango 	 * this node, that means parent doesn't need pminfo when it handles
657d4bc0535SKrishna Elango 	 * POST_DETACH for this node. For example, if dip is the dip of
658d4bc0535SKrishna Elango 	 * root complex, then there is no parent pm info.
659d4bc0535SKrishna Elango 	 */
660d4bc0535SKrishna Elango 	if (!PCIE_PAR_PMINFO(dip)) {
661d4bc0535SKrishna Elango 		kmem_free(pcie_pm_p, sizeof (pcie_pm_t));
662d4bc0535SKrishna Elango 		PCIE_RESET_PMINFO(dip);
663d4bc0535SKrishna Elango 	}
664d4bc0535SKrishna Elango }
665d4bc0535SKrishna Elango 
666d4bc0535SKrishna Elango /*
667d4bc0535SKrishna Elango  * Raises the power and marks itself busy.
668d4bc0535SKrishna Elango  */
669d4bc0535SKrishna Elango int
pcie_pm_hold(dev_info_t * dip)670d4bc0535SKrishna Elango pcie_pm_hold(dev_info_t *dip)
671d4bc0535SKrishna Elango {
672d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
673d4bc0535SKrishna Elango 
674d4bc0535SKrishna Elango 	/* If no PM info or no device PM, return */
675d4bc0535SKrishna Elango 	if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip)) ||
676d4bc0535SKrishna Elango 	    !(PCIE_SUPPORTS_DEVICE_PM(dip)))
677d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
678d4bc0535SKrishna Elango 
679d4bc0535SKrishna Elango 	/*
680d4bc0535SKrishna Elango 	 * If we are not at full power, then powerup.
681d4bc0535SKrishna Elango 	 * Need to be at full power so that link can be
682d4bc0535SKrishna Elango 	 * at L0. Similarly for PCI/PCI-X bus, it should be
683d4bc0535SKrishna Elango 	 * at full power.
684d4bc0535SKrishna Elango 	 */
685d4bc0535SKrishna Elango 	mutex_enter(&pwr_p->pwr_lock);
686d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_hold >= 0);
687e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pm_hold: incrementing hold \n",
688e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(dip), ddi_get_instance(dip));
689d4bc0535SKrishna Elango 	pwr_p->pwr_hold++;
690d4bc0535SKrishna Elango 	/* Mark itself busy, if it is not done already */
691d4bc0535SKrishna Elango 	if (!(pwr_p->pwr_flags & PCIE_PM_BUSY)) {
692e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pm_hold: marking busy\n",
693e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip));
694d4bc0535SKrishna Elango 		pwr_p->pwr_flags |= PCIE_PM_BUSY;
695d4bc0535SKrishna Elango 		(void) pm_busy_component(dip, 0);
696d4bc0535SKrishna Elango 	}
697d4bc0535SKrishna Elango 	if (pwr_p->pwr_func_lvl == PM_LEVEL_D0) {
698d4bc0535SKrishna Elango 		mutex_exit(&pwr_p->pwr_lock);
699d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
700d4bc0535SKrishna Elango 	}
701d4bc0535SKrishna Elango 	mutex_exit(&pwr_p->pwr_lock);
702d4bc0535SKrishna Elango 	if (pm_raise_power(dip, 0, PM_LEVEL_D0) != DDI_SUCCESS) {
703e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pm_hold: attempt to raise power "
704e762302fSShesha Sreenivasamurthy 		    "from %d to %d failed\n", ddi_driver_name(dip),
705e762302fSShesha Sreenivasamurthy 		    ddi_get_instance(dip), pwr_p->pwr_func_lvl,
706d4bc0535SKrishna Elango 		    PM_LEVEL_D0);
707d4bc0535SKrishna Elango 		pcie_pm_release(dip);
708d4bc0535SKrishna Elango 		return (DDI_FAILURE);
709d4bc0535SKrishna Elango 	}
710d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
711d4bc0535SKrishna Elango }
712d4bc0535SKrishna Elango 
713d4bc0535SKrishna Elango /*
714d4bc0535SKrishna Elango  * Reverse the things done in pcie_pm_hold
715d4bc0535SKrishna Elango  */
716d4bc0535SKrishna Elango void
pcie_pm_release(dev_info_t * dip)717d4bc0535SKrishna Elango pcie_pm_release(dev_info_t *dip)
718d4bc0535SKrishna Elango {
719d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
720d4bc0535SKrishna Elango 
721d4bc0535SKrishna Elango 	/* If no PM info or no device PM, return */
722d4bc0535SKrishna Elango 	if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip)) ||
723d4bc0535SKrishna Elango 	    !(PCIE_SUPPORTS_DEVICE_PM(dip)))
724d4bc0535SKrishna Elango 		return;
725d4bc0535SKrishna Elango 
726d4bc0535SKrishna Elango 	mutex_enter(&pwr_p->pwr_lock);
727d4bc0535SKrishna Elango 	pcie_pm_subrelease(dip, pwr_p);
728d4bc0535SKrishna Elango 	mutex_exit(&pwr_p->pwr_lock);
729d4bc0535SKrishna Elango }
730d4bc0535SKrishna Elango 
731d4bc0535SKrishna Elango static void
pcie_pm_subrelease(dev_info_t * dip,pcie_pwr_t * pwr_p)732d4bc0535SKrishna Elango pcie_pm_subrelease(dev_info_t *dip, pcie_pwr_t *pwr_p)
733d4bc0535SKrishna Elango {
734d4bc0535SKrishna Elango 	int level;
735d4bc0535SKrishna Elango 
736d4bc0535SKrishna Elango 	ASSERT(MUTEX_HELD(&pwr_p->pwr_lock));
737d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_hold > 0);
738e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pm_subrelease: decrementing hold \n",
739e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(dip), ddi_get_instance(dip));
740d4bc0535SKrishna Elango 	pwr_p->pwr_hold--;
741d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_hold >= 0);
742d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_flags & PCIE_PM_BUSY);
743d4bc0535SKrishna Elango 	level = pwr_level_allowed(pwr_p);
744d4bc0535SKrishna Elango 	if (pwr_p->pwr_hold == 0 && level < pwr_p->pwr_func_lvl) {
745e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): pm_subrelease: marking idle \n",
746e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip));
747d4bc0535SKrishna Elango 		(void) pm_idle_component(dip, 0);
748d4bc0535SKrishna Elango 		pwr_p->pwr_flags &= ~PCIE_PM_BUSY;
749d4bc0535SKrishna Elango 	}
750d4bc0535SKrishna Elango }
751d4bc0535SKrishna Elango 
752d4bc0535SKrishna Elango /*
753d4bc0535SKrishna Elango  * Called when the child makes the first power management call.
754d4bc0535SKrishna Elango  * sets up the counters. All the components of the child device are
755d4bc0535SKrishna Elango  * assumed to be at unknown level. It also releases the power hold
756d4bc0535SKrishna Elango  *	pwr_p - parent's pwr_t
757d4bc0535SKrishna Elango  *	cdip   - child's dip
758d4bc0535SKrishna Elango  */
759d4bc0535SKrishna Elango int
pcie_pm_add_child(dev_info_t * dip,dev_info_t * cdip)760d4bc0535SKrishna Elango pcie_pm_add_child(dev_info_t *dip, dev_info_t *cdip)
761d4bc0535SKrishna Elango {
762d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
763d4bc0535SKrishna Elango 
764d4bc0535SKrishna Elango 	/* If no PM info, return */
765d4bc0535SKrishna Elango 	if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip)))
766d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
767d4bc0535SKrishna Elango 
768d4bc0535SKrishna Elango 	ASSERT(MUTEX_HELD(&pwr_p->pwr_lock));
769d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_func_lvl == PM_LEVEL_D0);
770d4bc0535SKrishna Elango 	pcie_add_comps(dip, cdip, pwr_p);
771d4bc0535SKrishna Elango 
772d4bc0535SKrishna Elango 	/* If no device power management then return */
773d4bc0535SKrishna Elango 	if (!PCIE_SUPPORTS_DEVICE_PM(dip))
774d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
775d4bc0535SKrishna Elango 
776d4bc0535SKrishna Elango 	/*
777d4bc0535SKrishna Elango 	 * We have informed PM that we are busy at PRE_ATTACH time for
778d4bc0535SKrishna Elango 	 * this child. Release the hold and but don't clear the busy bit.
779d4bc0535SKrishna Elango 	 * If a device never changes power, hold will not be released
780d4bc0535SKrishna Elango 	 * and we stay at full power.
781d4bc0535SKrishna Elango 	 */
782d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_hold > 0);
783e762302fSShesha Sreenivasamurthy 	PCIE_DBG("%s(%d): pm_add_child: decrementing hold \n",
784e762302fSShesha Sreenivasamurthy 	    ddi_driver_name(dip), ddi_get_instance(dip));
785d4bc0535SKrishna Elango 	pwr_p->pwr_hold--;
786d4bc0535SKrishna Elango 	/*
787d4bc0535SKrishna Elango 	 * We must have made sure that busy bit
788d4bc0535SKrishna Elango 	 * is set when we put the hold
789d4bc0535SKrishna Elango 	 */
790d4bc0535SKrishna Elango 	ASSERT(pwr_p->pwr_flags & PCIE_PM_BUSY);
791d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
792d4bc0535SKrishna Elango }
793d4bc0535SKrishna Elango 
794d4bc0535SKrishna Elango /*
795d4bc0535SKrishna Elango  * Adjust the counters when a child detaches
796d4bc0535SKrishna Elango  * Marks itself idle if the idle conditions are met.
797d4bc0535SKrishna Elango  * Called at POST_DETACH time
798d4bc0535SKrishna Elango  */
799d4bc0535SKrishna Elango int
pcie_pm_remove_child(dev_info_t * dip,dev_info_t * cdip)800d4bc0535SKrishna Elango pcie_pm_remove_child(dev_info_t *dip, dev_info_t *cdip)
801d4bc0535SKrishna Elango {
802d4bc0535SKrishna Elango 	int *counters;
803d4bc0535SKrishna Elango 	int total;
804d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p;
805d4bc0535SKrishna Elango 
806d4bc0535SKrishna Elango 	/* If no PM info, return */
807d4bc0535SKrishna Elango 	if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip)))
808d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
809d4bc0535SKrishna Elango 
810d4bc0535SKrishna Elango 	counters = pwr_p->pwr_counters;
811d4bc0535SKrishna Elango 	mutex_enter(&pwr_p->pwr_lock);
812d4bc0535SKrishna Elango 	pcie_remove_comps(dip, cdip, pwr_p);
813d4bc0535SKrishna Elango 	/* If no device power management then return */
814d4bc0535SKrishna Elango 	if (!PCIE_SUPPORTS_DEVICE_PM(dip)) {
815d4bc0535SKrishna Elango 		mutex_exit(&pwr_p->pwr_lock);
816d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
817d4bc0535SKrishna Elango 	}
818d4bc0535SKrishna Elango 	total = (counters[PCIE_D0_INDEX] + counters[PCIE_UNKNOWN_INDEX] +
819d4bc0535SKrishna Elango 	    counters[PCIE_D1_INDEX] + counters[PCIE_D2_INDEX] +
820d4bc0535SKrishna Elango 	    counters[PCIE_D3_INDEX]);
821d4bc0535SKrishna Elango 	/*
822d4bc0535SKrishna Elango 	 * Mark idle if either there are no children or our lowest
823d4bc0535SKrishna Elango 	 * possible level is less than the current level. Mark idle
824d4bc0535SKrishna Elango 	 * only if it is not already done.
825d4bc0535SKrishna Elango 	 */
826d4bc0535SKrishna Elango 	if ((pwr_p->pwr_hold == 0) &&
827d4bc0535SKrishna Elango 	    (!total || (pwr_level_allowed(pwr_p) < pwr_p->pwr_func_lvl))) {
828d4bc0535SKrishna Elango 		if (pwr_p->pwr_flags & PCIE_PM_BUSY) {
829e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): pcie_bus_power: marking idle\n",
830e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip));
831d4bc0535SKrishna Elango 			(void) pm_idle_component(dip, 0);
832d4bc0535SKrishna Elango 			pwr_p->pwr_flags &= ~PCIE_PM_BUSY;
833d4bc0535SKrishna Elango 		}
834d4bc0535SKrishna Elango 	}
835d4bc0535SKrishna Elango 	mutex_exit(&pwr_p->pwr_lock);
836d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
837d4bc0535SKrishna Elango }
838d4bc0535SKrishna Elango 
839d4bc0535SKrishna Elango boolean_t
pcie_is_pcie(dev_info_t * dip)840d4bc0535SKrishna Elango pcie_is_pcie(dev_info_t *dip)
841d4bc0535SKrishna Elango {
842d4bc0535SKrishna Elango 	pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
843d4bc0535SKrishna Elango 	ASSERT(bus_p);
844d4bc0535SKrishna Elango 	return (bus_p->bus_pcie_off != 0);
845d4bc0535SKrishna Elango }
846d4bc0535SKrishna Elango 
847d4bc0535SKrishna Elango /*
848d4bc0535SKrishna Elango  * Called by px_attach or pcieb_attach:: DDI_RESUME
849d4bc0535SKrishna Elango  */
850d4bc0535SKrishna Elango int
pcie_pwr_resume(dev_info_t * dip)851d4bc0535SKrishna Elango pcie_pwr_resume(dev_info_t *dip)
852d4bc0535SKrishna Elango {
853d4bc0535SKrishna Elango 	dev_info_t *cdip;
854d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p = NULL;
855d4bc0535SKrishna Elango 
856*86ef0a63SRichard Lowe #if defined(__x86)
857d4bc0535SKrishna Elango 	if (dip)
858d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
859*86ef0a63SRichard Lowe #endif /* defined(__x86) */
860d4bc0535SKrishna Elango 
861d4bc0535SKrishna Elango 	if (PCIE_PMINFO(dip))
862d4bc0535SKrishna Elango 		pwr_p = PCIE_NEXUS_PMINFO(dip);
863d4bc0535SKrishna Elango 
864d4bc0535SKrishna Elango 	if (pwr_p) {
865d4bc0535SKrishna Elango 		/* Inform the PM framework that dip is at full power */
866d4bc0535SKrishna Elango 		if (PCIE_SUPPORTS_DEVICE_PM(dip)) {
867d4bc0535SKrishna Elango 			ASSERT(pwr_p->pwr_func_lvl == PM_LEVEL_D0);
868d4bc0535SKrishna Elango 			(void) pm_raise_power(dip, 0,
869d4bc0535SKrishna Elango 			    pwr_p->pwr_func_lvl);
870d4bc0535SKrishna Elango 		}
871d4bc0535SKrishna Elango 	}
872d4bc0535SKrishna Elango 
873d4bc0535SKrishna Elango 	/*
874d4bc0535SKrishna Elango 	 * Code taken from pci driver.
875d4bc0535SKrishna Elango 	 * Restore config registers for children that did not save
876d4bc0535SKrishna Elango 	 * their own registers.  Children pwr states are UNKNOWN after
877d4bc0535SKrishna Elango 	 * a resume since it is possible for the PM framework to call
878d4bc0535SKrishna Elango 	 * resume without an actual power cycle. (ie if suspend fails).
879d4bc0535SKrishna Elango 	 */
880d4bc0535SKrishna Elango 	for (cdip = ddi_get_child(dip); cdip != NULL;
881d4bc0535SKrishna Elango 	    cdip = ddi_get_next_sibling(cdip)) {
882d4bc0535SKrishna Elango 		boolean_t	is_pcie;
883d4bc0535SKrishna Elango 
884d4bc0535SKrishna Elango 		/*
885d4bc0535SKrishna Elango 		 * Not interested in children who are not already
886d4bc0535SKrishna Elango 		 * init'ed.  They will be set up by init_child().
887d4bc0535SKrishna Elango 		 */
888d4bc0535SKrishna Elango 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
889e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): "
890d4bc0535SKrishna Elango 			    "DDI_RESUME: skipping %s%d not in CF1\n",
891e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip),
892d4bc0535SKrishna Elango 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
893d4bc0535SKrishna Elango 			continue;
894d4bc0535SKrishna Elango 		}
895d4bc0535SKrishna Elango 
896d4bc0535SKrishna Elango 		/*
897d4bc0535SKrishna Elango 		 * Only restore config registers if saved by nexus.
898d4bc0535SKrishna Elango 		 */
899d4bc0535SKrishna Elango 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
900d4bc0535SKrishna Elango 		    "nexus-saved-config-regs") != 1)
901d4bc0535SKrishna Elango 			continue;
902d4bc0535SKrishna Elango 
903e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): "
904d4bc0535SKrishna Elango 		    "DDI_RESUME: nexus restoring %s%d config regs\n",
905e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(dip), ddi_get_instance(dip),
906d4bc0535SKrishna Elango 		    ddi_driver_name(cdip), ddi_get_instance(cdip));
907d4bc0535SKrishna Elango 
908d4bc0535SKrishna Elango 		/* clear errors left by OBP scrubbing */
909d4bc0535SKrishna Elango 		pcie_clear_errors(cdip);
910d4bc0535SKrishna Elango 
911d4bc0535SKrishna Elango 		/* PCIe workaround: disable errors during 4K config resore */
912b3d69c05SRobert Mustacchi 		is_pcie = pcie_is_pcie(cdip);
913b3d69c05SRobert Mustacchi 		if (is_pcie)
914d4bc0535SKrishna Elango 			pcie_disable_errors(cdip);
915d4bc0535SKrishna Elango 		(void) pci_restore_config_regs(cdip);
916d4bc0535SKrishna Elango 		if (is_pcie) {
917d4bc0535SKrishna Elango 			pcie_enable_errors(cdip);
918d4bc0535SKrishna Elango 			(void) pcie_enable_ce(cdip);
919d4bc0535SKrishna Elango 		}
920d4bc0535SKrishna Elango 
921d4bc0535SKrishna Elango 		if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
922d4bc0535SKrishna Elango 		    "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
923e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): %s%d can't remove prop %s",
924e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip),
925d4bc0535SKrishna Elango 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
926d4bc0535SKrishna Elango 			    "nexus-saved-config-regs");
927d4bc0535SKrishna Elango 		}
928d4bc0535SKrishna Elango 	}
929d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
930d4bc0535SKrishna Elango }
931d4bc0535SKrishna Elango 
932d4bc0535SKrishna Elango /*
933d4bc0535SKrishna Elango  * Called by pcie_detach or pcieb_detach:: DDI_SUSPEND
934d4bc0535SKrishna Elango  */
935d4bc0535SKrishna Elango int
pcie_pwr_suspend(dev_info_t * dip)936d4bc0535SKrishna Elango pcie_pwr_suspend(dev_info_t *dip)
937d4bc0535SKrishna Elango {
938d4bc0535SKrishna Elango 	dev_info_t *cdip;
939d4bc0535SKrishna Elango 	int i, *counters; /* per nexus counters */
940d4bc0535SKrishna Elango 	int *child_counters = NULL; /* per child dip counters */
941d4bc0535SKrishna Elango 	pcie_pwr_t *pwr_p = NULL;
942d4bc0535SKrishna Elango 
943*86ef0a63SRichard Lowe #if defined(__x86)
944d4bc0535SKrishna Elango 	if (dip)
945d4bc0535SKrishna Elango 		return (DDI_SUCCESS);
946*86ef0a63SRichard Lowe #endif /* defined(__x86) */
947d4bc0535SKrishna Elango 
948d4bc0535SKrishna Elango 	if (PCIE_PMINFO(dip))
949d4bc0535SKrishna Elango 		pwr_p = PCIE_NEXUS_PMINFO(dip);
950d4bc0535SKrishna Elango 
951d4bc0535SKrishna Elango 	/*
952d4bc0535SKrishna Elango 	 * Mark all children to be unknown and bring our power level
953d4bc0535SKrishna Elango 	 * to full, if required. This is to avoid any panics while
954d4bc0535SKrishna Elango 	 * accessing the child's config space.
955d4bc0535SKrishna Elango 	 */
956d4bc0535SKrishna Elango 	if (pwr_p) {
957d4bc0535SKrishna Elango 		mutex_enter(&pwr_p->pwr_lock);
958d4bc0535SKrishna Elango 		if (PCIE_SUPPORTS_DEVICE_PM(dip) &&
959d4bc0535SKrishna Elango 		    pwr_p->pwr_func_lvl != PM_LEVEL_D0) {
960d4bc0535SKrishna Elango 			mutex_exit(&pwr_p->pwr_lock);
961d4bc0535SKrishna Elango 			if (pm_raise_power(dip, 0, PM_LEVEL_D0) !=
962d4bc0535SKrishna Elango 			    DDI_SUCCESS) {
963e762302fSShesha Sreenivasamurthy 				PCIE_DBG("%s(%d): pwr_suspend: attempt "
964d4bc0535SKrishna Elango 				    "to raise power from %d to %d "
965e762302fSShesha Sreenivasamurthy 				    "failed\n", ddi_driver_name(dip),
966e762302fSShesha Sreenivasamurthy 				    ddi_get_instance(dip), pwr_p->pwr_func_lvl,
967d4bc0535SKrishna Elango 				    PM_LEVEL_D0);
968d4bc0535SKrishna Elango 				return (DDI_FAILURE);
969d4bc0535SKrishna Elango 			}
970d4bc0535SKrishna Elango 			mutex_enter(&pwr_p->pwr_lock);
971d4bc0535SKrishna Elango 		}
972d4bc0535SKrishna Elango 		counters = pwr_p->pwr_counters;
973d4bc0535SKrishna Elango 		/*
974d4bc0535SKrishna Elango 		 * Update the nexus counters. At the resume time all
975d4bc0535SKrishna Elango 		 * components are considered to be at unknown level. Use the
976d4bc0535SKrishna Elango 		 * fact that counters for unknown level are at the end.
977d4bc0535SKrishna Elango 		 */
978d4bc0535SKrishna Elango 		for (i = 0; i < PCIE_UNKNOWN_INDEX; i++) {
979d4bc0535SKrishna Elango 			counters[PCIE_UNKNOWN_INDEX] += counters[i];
980d4bc0535SKrishna Elango 			counters[i] = 0;
981d4bc0535SKrishna Elango 		}
982d4bc0535SKrishna Elango 		mutex_exit(&pwr_p->pwr_lock);
983d4bc0535SKrishna Elango 	}
984d4bc0535SKrishna Elango 
985d4bc0535SKrishna Elango 	/*
986d4bc0535SKrishna Elango 	 * Code taken from pci driver.
987d4bc0535SKrishna Elango 	 * Save the state of the configuration headers of child
988d4bc0535SKrishna Elango 	 * nodes.
989d4bc0535SKrishna Elango 	 */
990d4bc0535SKrishna Elango 	for (cdip = ddi_get_child(dip); cdip != NULL;
991d4bc0535SKrishna Elango 	    cdip = ddi_get_next_sibling(cdip)) {
992d4bc0535SKrishna Elango 		boolean_t	is_pcie;
993d4bc0535SKrishna Elango 
994d4bc0535SKrishna Elango 		/*
995d4bc0535SKrishna Elango 		 * Not interested in children who are not already
996d4bc0535SKrishna Elango 		 * init'ed.  They will be set up in init_child().
997d4bc0535SKrishna Elango 		 */
998d4bc0535SKrishna Elango 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
999e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): DDI_SUSPEND: skipping "
1000e762302fSShesha Sreenivasamurthy 			    "%s%d not in CF1\n", ddi_driver_name(dip),
1001e762302fSShesha Sreenivasamurthy 			    ddi_get_instance(dip), ddi_driver_name(cdip),
1002d4bc0535SKrishna Elango 			    ddi_get_instance(cdip));
1003d4bc0535SKrishna Elango 			continue;
1004d4bc0535SKrishna Elango 		}
1005d4bc0535SKrishna Elango 		/*
1006d4bc0535SKrishna Elango 		 * Update per child dip counters, if any. Counters
1007d4bc0535SKrishna Elango 		 * will not exist if the child is not power manageable
1008d4bc0535SKrishna Elango 		 * or if its power entry is never invoked.
1009d4bc0535SKrishna Elango 		 */
1010d4bc0535SKrishna Elango 		if (PCIE_PMINFO(cdip) && PCIE_PAR_PMINFO(cdip))
1011d4bc0535SKrishna Elango 			child_counters = PCIE_CHILD_COUNTERS(cdip);
1012d4bc0535SKrishna Elango 		if (child_counters && pwr_p) {
1013d4bc0535SKrishna Elango 			mutex_enter(&pwr_p->pwr_lock);
1014d4bc0535SKrishna Elango 			for (i = 0; i < PCIE_UNKNOWN_INDEX; i++) {
1015d4bc0535SKrishna Elango 				child_counters[PCIE_UNKNOWN_INDEX] +=
1016d4bc0535SKrishna Elango 				    child_counters[i];
1017d4bc0535SKrishna Elango 				child_counters[i] = 0;
1018d4bc0535SKrishna Elango 			}
1019d4bc0535SKrishna Elango 			mutex_exit(&pwr_p->pwr_lock);
1020d4bc0535SKrishna Elango 		}
1021d4bc0535SKrishna Elango 
1022d4bc0535SKrishna Elango 		/*
1023d4bc0535SKrishna Elango 		 * Only save config registers if not already saved by child.
1024d4bc0535SKrishna Elango 		 */
1025d4bc0535SKrishna Elango 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
1026d4bc0535SKrishna Elango 		    SAVED_CONFIG_REGS) == 1) {
1027d4bc0535SKrishna Elango 			continue;
1028d4bc0535SKrishna Elango 		}
1029d4bc0535SKrishna Elango 
1030d4bc0535SKrishna Elango 		/*
1031d4bc0535SKrishna Elango 		 * The nexus needs to save config registers.  Create a property
1032d4bc0535SKrishna Elango 		 * so it knows to restore on resume.
1033d4bc0535SKrishna Elango 		 */
1034d4bc0535SKrishna Elango 		if (ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
1035d4bc0535SKrishna Elango 		    "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
1036e762302fSShesha Sreenivasamurthy 			PCIE_DBG("%s(%d): %s%d can't update prop %s",
1037e762302fSShesha Sreenivasamurthy 			    ddi_driver_name(dip), ddi_get_instance(dip),
1038d4bc0535SKrishna Elango 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
1039d4bc0535SKrishna Elango 			    "nexus-saved-config-regs");
1040d4bc0535SKrishna Elango 		}
1041e762302fSShesha Sreenivasamurthy 		PCIE_DBG("%s(%d): DDI_SUSPEND: saving config space for"
1042e762302fSShesha Sreenivasamurthy 		    " %s%d\n", ddi_driver_name(dip), ddi_get_instance(dip),
1043e762302fSShesha Sreenivasamurthy 		    ddi_driver_name(cdip), ddi_get_instance(cdip));
1044d4bc0535SKrishna Elango 
1045d4bc0535SKrishna Elango 		/* PCIe workaround: disable errors during 4K config save */
1046b3d69c05SRobert Mustacchi 		is_pcie = pcie_is_pcie(cdip);
1047b3d69c05SRobert Mustacchi 		if (is_pcie)
1048d4bc0535SKrishna Elango 			pcie_disable_errors(cdip);
1049d4bc0535SKrishna Elango 		(void) pci_save_config_regs(cdip);
1050d4bc0535SKrishna Elango 		if (is_pcie) {
1051d4bc0535SKrishna Elango 			pcie_enable_errors(cdip);
1052d4bc0535SKrishna Elango 			(void) pcie_enable_ce(cdip);
1053d4bc0535SKrishna Elango 		}
1054d4bc0535SKrishna Elango 	}
1055d4bc0535SKrishna Elango 	return (DDI_SUCCESS);
1056d4bc0535SKrishna Elango }
1057d4bc0535SKrishna Elango 
1058d4bc0535SKrishna Elango #ifdef DEBUG
1059d4bc0535SKrishna Elango /*
1060d4bc0535SKrishna Elango  * Description of bus_power_op.
1061d4bc0535SKrishna Elango  */
1062d4bc0535SKrishna Elango typedef struct pcie_buspwr_desc {
1063d4bc0535SKrishna Elango 	pm_bus_power_op_t pwr_op;
1064d4bc0535SKrishna Elango 	char *pwr_desc;
1065d4bc0535SKrishna Elango } pcie_buspwr_desc_t;
1066d4bc0535SKrishna Elango 
1067d4bc0535SKrishna Elango static pcie_buspwr_desc_t pcie_buspwr_desc[] = {
1068d4bc0535SKrishna Elango 	{BUS_POWER_CHILD_PWRCHG, "CHILD_PWRCHG"},
1069d4bc0535SKrishna Elango 	{BUS_POWER_NEXUS_PWRUP, "NEXUS_PWRUP"},
1070d4bc0535SKrishna Elango 	{BUS_POWER_PRE_NOTIFICATION, "PRE_NOTIFICATION"},
1071d4bc0535SKrishna Elango 	{BUS_POWER_POST_NOTIFICATION, "POST_NOTIFICATION"},
1072d4bc0535SKrishna Elango 	{BUS_POWER_HAS_CHANGED, "HAS_CHANGED"},
1073d4bc0535SKrishna Elango 	{BUS_POWER_NOINVOL, "NOINVOL"},
1074d4bc0535SKrishna Elango 	{-1, NULL}
1075d4bc0535SKrishna Elango };
1076d4bc0535SKrishna Elango 
1077d4bc0535SKrishna Elango /*
1078d4bc0535SKrishna Elango  * Returns description of the bus_power_op.
1079d4bc0535SKrishna Elango  */
1080d4bc0535SKrishna Elango static char *
pcie_decode_pwr_op(pm_bus_power_op_t op)1081d4bc0535SKrishna Elango pcie_decode_pwr_op(pm_bus_power_op_t op)
1082d4bc0535SKrishna Elango {
1083d4bc0535SKrishna Elango 	pcie_buspwr_desc_t *descp = pcie_buspwr_desc;
1084d4bc0535SKrishna Elango 
1085d4bc0535SKrishna Elango 	for (; descp->pwr_desc; descp++) {
1086d4bc0535SKrishna Elango 		if (op == descp->pwr_op)
1087d4bc0535SKrishna Elango 			return (descp->pwr_desc);
1088d4bc0535SKrishna Elango 	}
1089d4bc0535SKrishna Elango 	return ("UNKNOWN OP");
1090d4bc0535SKrishna Elango }
1091d4bc0535SKrishna Elango #endif
1092