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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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