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