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