1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * sunpm.c builds sunpm.o "power management framework" 31*7c478bd9Sstevel@tonic-gate * kernel-resident power management code. Implements power management 32*7c478bd9Sstevel@tonic-gate * policy 33*7c478bd9Sstevel@tonic-gate * Assumes: all backwards compat. device components wake up on & 34*7c478bd9Sstevel@tonic-gate * the pm_info pointer in dev_info is initially NULL 35*7c478bd9Sstevel@tonic-gate * 36*7c478bd9Sstevel@tonic-gate * PM - (device) Power Management 37*7c478bd9Sstevel@tonic-gate * 38*7c478bd9Sstevel@tonic-gate * Each device may have 0 or more components. If a device has no components, 39*7c478bd9Sstevel@tonic-gate * then it can't be power managed. Each component has 2 or more 40*7c478bd9Sstevel@tonic-gate * power states. 41*7c478bd9Sstevel@tonic-gate * 42*7c478bd9Sstevel@tonic-gate * "Backwards Compatible" (bc) devices: 43*7c478bd9Sstevel@tonic-gate * There are two different types of devices from the point of view of this 44*7c478bd9Sstevel@tonic-gate * code. The original type, left over from the original PM implementation on 45*7c478bd9Sstevel@tonic-gate * the voyager platform are known in this code as "backwards compatible" 46*7c478bd9Sstevel@tonic-gate * devices (PM_ISBC(dip) returns true). 47*7c478bd9Sstevel@tonic-gate * They are recognized by the pm code by the lack of a pm-components property 48*7c478bd9Sstevel@tonic-gate * and a call made by the driver to pm_create_components(9F). 49*7c478bd9Sstevel@tonic-gate * For these devices, component 0 is special, and represents the power state 50*7c478bd9Sstevel@tonic-gate * of the device. If component 0 is to be set to power level 0 (off), then 51*7c478bd9Sstevel@tonic-gate * the framework must first call into the driver's detach(9E) routine with 52*7c478bd9Sstevel@tonic-gate * DDI_PM_SUSPEND, to get the driver to save the hardware state of the device. 53*7c478bd9Sstevel@tonic-gate * After setting component 0 from 0 to a non-zero power level, a call must be 54*7c478bd9Sstevel@tonic-gate * made into the driver's attach(9E) routine with DDI_PM_RESUME. 55*7c478bd9Sstevel@tonic-gate * 56*7c478bd9Sstevel@tonic-gate * Currently, the only way to get a bc device power managed is via a set of 57*7c478bd9Sstevel@tonic-gate * ioctls (PM_DIRECT_PM, PM_SET_CURRENT_POWER) issued to /dev/pm. 58*7c478bd9Sstevel@tonic-gate * 59*7c478bd9Sstevel@tonic-gate * For non-bc devices, the driver describes the components by exporting a 60*7c478bd9Sstevel@tonic-gate * pm-components(9P) property that tells how many components there are, 61*7c478bd9Sstevel@tonic-gate * tells what each component's power state values are, and provides human 62*7c478bd9Sstevel@tonic-gate * readable strings (currently unused) for each component name and power state. 63*7c478bd9Sstevel@tonic-gate * Devices which export pm-components(9P) are automatically power managed 64*7c478bd9Sstevel@tonic-gate * whenever autopm is enabled (via PM_START_PM ioctl issued by pmconfig(1M) 65*7c478bd9Sstevel@tonic-gate * after parsing power.conf(4)). 66*7c478bd9Sstevel@tonic-gate * For these devices, all components are considered independent of each other, 67*7c478bd9Sstevel@tonic-gate * and it is up to the driver to decide when a transition requires saving or 68*7c478bd9Sstevel@tonic-gate * restoring hardware state. 69*7c478bd9Sstevel@tonic-gate * 70*7c478bd9Sstevel@tonic-gate * Each device component also has a threshold time associated with each power 71*7c478bd9Sstevel@tonic-gate * transition (see power.conf(4)), and a busy/idle state maintained by the 72*7c478bd9Sstevel@tonic-gate * driver calling pm_idle_component(9F) and pm_busy_component(9F). 73*7c478bd9Sstevel@tonic-gate * Components are created idle. 74*7c478bd9Sstevel@tonic-gate * 75*7c478bd9Sstevel@tonic-gate * The PM framework provides several functions: 76*7c478bd9Sstevel@tonic-gate * -implement PM policy as described in power.conf(4) 77*7c478bd9Sstevel@tonic-gate * Policy is set by pmconfig(1M) issuing pm ioctls based on power.conf(4). 78*7c478bd9Sstevel@tonic-gate * Policies consist of: 79*7c478bd9Sstevel@tonic-gate * -set threshold values (defaults if none provided by pmconfig) 80*7c478bd9Sstevel@tonic-gate * -set dependencies among devices 81*7c478bd9Sstevel@tonic-gate * -enable/disable autopm 82*7c478bd9Sstevel@tonic-gate * -turn down idle components based on thresholds (if autopm is enabled) 83*7c478bd9Sstevel@tonic-gate * (aka scanning) 84*7c478bd9Sstevel@tonic-gate * -maintain power states based on dependencies among devices 85*7c478bd9Sstevel@tonic-gate * -upon request, or when the frame buffer powers off, attempt to turn off 86*7c478bd9Sstevel@tonic-gate * all components that are idle or become idle over the next (10 sec) 87*7c478bd9Sstevel@tonic-gate * period in an attempt to get down to an EnergyStar compliant state 88*7c478bd9Sstevel@tonic-gate * -prevent powering off of a device which exported the 89*7c478bd9Sstevel@tonic-gate * pm-no-involuntary-power-cycles property without active involvement of 90*7c478bd9Sstevel@tonic-gate * the device's driver (so no removing power when the device driver is 91*7c478bd9Sstevel@tonic-gate * not attached) 92*7c478bd9Sstevel@tonic-gate * -provide a mechanism for a device driver to request that a device's component 93*7c478bd9Sstevel@tonic-gate * be brought back to the power level necessary for the use of the device 94*7c478bd9Sstevel@tonic-gate * -allow a process to directly control the power levels of device components 95*7c478bd9Sstevel@tonic-gate * (via ioctls issued to /dev/pm--see usr/src/uts/common/io/pm.c) 96*7c478bd9Sstevel@tonic-gate * -ensure that the console frame buffer is powered up before being referenced 97*7c478bd9Sstevel@tonic-gate * via prom_printf() or other prom calls that might generate console output 98*7c478bd9Sstevel@tonic-gate * -maintain implicit dependencies (e.g. parent must be powered up if child is) 99*7c478bd9Sstevel@tonic-gate * -provide "backwards compatible" behavior for devices without pm-components 100*7c478bd9Sstevel@tonic-gate * property 101*7c478bd9Sstevel@tonic-gate * 102*7c478bd9Sstevel@tonic-gate * Scanning: 103*7c478bd9Sstevel@tonic-gate * Whenever autopm is enabled, the framework attempts to bring each component 104*7c478bd9Sstevel@tonic-gate * of each device to its lowest power based on the threshold of idleness 105*7c478bd9Sstevel@tonic-gate * associated with each transition and the busy/idle state of the component. 106*7c478bd9Sstevel@tonic-gate * 107*7c478bd9Sstevel@tonic-gate * The actual work of this is done by pm_scan_dev(), which cycles through each 108*7c478bd9Sstevel@tonic-gate * component of a device, checking its idleness against its current threshold, 109*7c478bd9Sstevel@tonic-gate * and calling pm_set_power() as appropriate to change the power level. 110*7c478bd9Sstevel@tonic-gate * This function also indicates when it would next be profitable to scan the 111*7c478bd9Sstevel@tonic-gate * device again, and a new scan is scheduled after that time. 112*7c478bd9Sstevel@tonic-gate * 113*7c478bd9Sstevel@tonic-gate * Dependencies: 114*7c478bd9Sstevel@tonic-gate * It is possible to establish a dependency between the power states of two 115*7c478bd9Sstevel@tonic-gate * otherwise unrelated devices. This is currently done to ensure that the 116*7c478bd9Sstevel@tonic-gate * cdrom is always up whenever the console framebuffer is up, so that the user 117*7c478bd9Sstevel@tonic-gate * can insert a cdrom and see a popup as a result. 118*7c478bd9Sstevel@tonic-gate * 119*7c478bd9Sstevel@tonic-gate * The dependency terminology used in power.conf(4) is not easy to understand, 120*7c478bd9Sstevel@tonic-gate * so we've adopted a different terminology in the implementation. We write 121*7c478bd9Sstevel@tonic-gate * of a "keeps up" and a "kept up" device. A relationship can be established 122*7c478bd9Sstevel@tonic-gate * where one device keeps up another. That means that if the keepsup device 123*7c478bd9Sstevel@tonic-gate * has any component that is at a non-zero power level, all components of the 124*7c478bd9Sstevel@tonic-gate * "kept up" device must be brought to full power. This relationship is 125*7c478bd9Sstevel@tonic-gate * asynchronous. When the keeping device is powered up, a request is queued 126*7c478bd9Sstevel@tonic-gate * to a worker thread to bring up the kept device. The caller does not wait. 127*7c478bd9Sstevel@tonic-gate * Scan will not turn down a kept up device. 128*7c478bd9Sstevel@tonic-gate * 129*7c478bd9Sstevel@tonic-gate * Direct PM: 130*7c478bd9Sstevel@tonic-gate * A device may be directly power managed by a process. If a device is 131*7c478bd9Sstevel@tonic-gate * directly pm'd, then it will not be scanned, and dependencies will not be 132*7c478bd9Sstevel@tonic-gate * enforced. * If a directly pm'd device's driver requests a power change (via 133*7c478bd9Sstevel@tonic-gate * pm_raise_power(9F)), then the request is blocked and notification is sent 134*7c478bd9Sstevel@tonic-gate * to the controlling process, which must issue the requested power change for 135*7c478bd9Sstevel@tonic-gate * the driver to proceed. 136*7c478bd9Sstevel@tonic-gate * 137*7c478bd9Sstevel@tonic-gate */ 138*7c478bd9Sstevel@tonic-gate 139*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 140*7c478bd9Sstevel@tonic-gate #include <sys/errno.h> 141*7c478bd9Sstevel@tonic-gate #include <sys/callb.h> /* callback registration during CPR */ 142*7c478bd9Sstevel@tonic-gate #include <sys/conf.h> /* driver flags and functions */ 143*7c478bd9Sstevel@tonic-gate #include <sys/open.h> /* OTYP_CHR definition */ 144*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> /* S_IFCHR definition */ 145*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h> /* name -> dev_info xlation */ 146*7c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> /* dev_info node fields */ 147*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h> /* memory alloc stuff */ 148*7c478bd9Sstevel@tonic-gate #include <sys/debug.h> 149*7c478bd9Sstevel@tonic-gate #include <sys/archsystm.h> 150*7c478bd9Sstevel@tonic-gate #include <sys/pm.h> 151*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 152*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 153*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 154*7c478bd9Sstevel@tonic-gate #include <sys/sunpm.h> 155*7c478bd9Sstevel@tonic-gate #include <sys/epm.h> 156*7c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 157*7c478bd9Sstevel@tonic-gate #include <sys/mode.h> 158*7c478bd9Sstevel@tonic-gate #include <sys/mkdev.h> 159*7c478bd9Sstevel@tonic-gate #include <sys/promif.h> 160*7c478bd9Sstevel@tonic-gate #include <sys/consdev.h> 161*7c478bd9Sstevel@tonic-gate #include <sys/esunddi.h> 162*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 163*7c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 164*7c478bd9Sstevel@tonic-gate #include <sys/note.h> 165*7c478bd9Sstevel@tonic-gate #include <sys/taskq.h> 166*7c478bd9Sstevel@tonic-gate #include <sys/bootconf.h> 167*7c478bd9Sstevel@tonic-gate #include <sys/reboot.h> 168*7c478bd9Sstevel@tonic-gate #include <sys/spl.h> 169*7c478bd9Sstevel@tonic-gate #include <sys/disp.h> 170*7c478bd9Sstevel@tonic-gate #include <sys/sobject.h> 171*7c478bd9Sstevel@tonic-gate #include <sys/sunmdi.h> 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate 174*7c478bd9Sstevel@tonic-gate /* 175*7c478bd9Sstevel@tonic-gate * PM LOCKING 176*7c478bd9Sstevel@tonic-gate * The list of locks: 177*7c478bd9Sstevel@tonic-gate * Global pm mutex locks. 178*7c478bd9Sstevel@tonic-gate * 179*7c478bd9Sstevel@tonic-gate * pm_scan_lock: 180*7c478bd9Sstevel@tonic-gate * It protects the timeout id of the scan thread, and the value 181*7c478bd9Sstevel@tonic-gate * of autopm_enabled. This lock is not held concurrently with 182*7c478bd9Sstevel@tonic-gate * any other PM locks. 183*7c478bd9Sstevel@tonic-gate * 184*7c478bd9Sstevel@tonic-gate * pm_clone_lock: Protects the clone list and count of poll events 185*7c478bd9Sstevel@tonic-gate * pending for the pm driver. 186*7c478bd9Sstevel@tonic-gate * Lock ordering: 187*7c478bd9Sstevel@tonic-gate * pm_clone_lock -> pm_pscc_interest_rwlock, 188*7c478bd9Sstevel@tonic-gate * pm_clone_lock -> pm_pscc_direct_rwlock. 189*7c478bd9Sstevel@tonic-gate * 190*7c478bd9Sstevel@tonic-gate * pm_rsvp_lock: 191*7c478bd9Sstevel@tonic-gate * Used to synchronize the data structures used for processes 192*7c478bd9Sstevel@tonic-gate * to rendezvous with state change information when doing 193*7c478bd9Sstevel@tonic-gate * direct PM. 194*7c478bd9Sstevel@tonic-gate * Lock ordering: 195*7c478bd9Sstevel@tonic-gate * pm_rsvp_lock -> pm_pscc_interest_rwlock, 196*7c478bd9Sstevel@tonic-gate * pm_rsvp_lock -> pm_pscc_direct_rwlock, 197*7c478bd9Sstevel@tonic-gate * pm_rsvp_lock -> pm_clone_lock. 198*7c478bd9Sstevel@tonic-gate * 199*7c478bd9Sstevel@tonic-gate * ppm_lock: protects the list of registered ppm drivers 200*7c478bd9Sstevel@tonic-gate * Lock ordering: 201*7c478bd9Sstevel@tonic-gate * ppm_lock -> ppm driver unit_lock 202*7c478bd9Sstevel@tonic-gate * 203*7c478bd9Sstevel@tonic-gate * pm_compcnt_lock: 204*7c478bd9Sstevel@tonic-gate * Protects count of components that are not at their lowest 205*7c478bd9Sstevel@tonic-gate * power level. 206*7c478bd9Sstevel@tonic-gate * Lock ordering: 207*7c478bd9Sstevel@tonic-gate * pm_compcnt_lock -> ppm_lock. 208*7c478bd9Sstevel@tonic-gate * 209*7c478bd9Sstevel@tonic-gate * pm_dep_thread_lock: 210*7c478bd9Sstevel@tonic-gate * Protects work list for pm_dep_thread. Not taken concurrently 211*7c478bd9Sstevel@tonic-gate * with any other pm lock. 212*7c478bd9Sstevel@tonic-gate * 213*7c478bd9Sstevel@tonic-gate * pm_remdrv_lock: 214*7c478bd9Sstevel@tonic-gate * Serializes the operation of removing noinvol data structure 215*7c478bd9Sstevel@tonic-gate * entries for a branch of the tree when a driver has been 216*7c478bd9Sstevel@tonic-gate * removed from the system (modctl_rem_major). 217*7c478bd9Sstevel@tonic-gate * Lock ordering: 218*7c478bd9Sstevel@tonic-gate * pm_remdrv_lock -> pm_noinvol_rwlock. 219*7c478bd9Sstevel@tonic-gate * 220*7c478bd9Sstevel@tonic-gate * pm_cfb_lock: (High level spin lock) 221*7c478bd9Sstevel@tonic-gate * Protects the count of how many components of the console 222*7c478bd9Sstevel@tonic-gate * frame buffer are off (so we know if we have to bring up the 223*7c478bd9Sstevel@tonic-gate * console as a result of a prom_printf, etc. 224*7c478bd9Sstevel@tonic-gate * No other locks are taken while holding this lock. 225*7c478bd9Sstevel@tonic-gate * 226*7c478bd9Sstevel@tonic-gate * pm_loan_lock: 227*7c478bd9Sstevel@tonic-gate * Protects the lock_loan list. List is used to record that one 228*7c478bd9Sstevel@tonic-gate * thread has acquired a power lock but has launched another thread 229*7c478bd9Sstevel@tonic-gate * to complete its processing. An entry in the list indicates that 230*7c478bd9Sstevel@tonic-gate * the worker thread can borrow the lock held by the other thread, 231*7c478bd9Sstevel@tonic-gate * which must block on the completion of the worker. Use is 232*7c478bd9Sstevel@tonic-gate * specific to module loading. 233*7c478bd9Sstevel@tonic-gate * No other locks are taken while holding this lock. 234*7c478bd9Sstevel@tonic-gate * 235*7c478bd9Sstevel@tonic-gate * Global PM rwlocks 236*7c478bd9Sstevel@tonic-gate * 237*7c478bd9Sstevel@tonic-gate * pm_thresh_rwlock: 238*7c478bd9Sstevel@tonic-gate * Protects the list of thresholds recorded for future use (when 239*7c478bd9Sstevel@tonic-gate * devices attach). 240*7c478bd9Sstevel@tonic-gate * Lock ordering: 241*7c478bd9Sstevel@tonic-gate * pm_thresh_rwlock -> devi_pm_lock 242*7c478bd9Sstevel@tonic-gate * 243*7c478bd9Sstevel@tonic-gate * pm_noinvol_rwlock: 244*7c478bd9Sstevel@tonic-gate * Protects list of detached nodes that had noinvol registered. 245*7c478bd9Sstevel@tonic-gate * No other PM locks are taken while holding pm_noinvol_rwlock. 246*7c478bd9Sstevel@tonic-gate * 247*7c478bd9Sstevel@tonic-gate * pm_pscc_direct_rwlock: 248*7c478bd9Sstevel@tonic-gate * Protects the list that maps devices being directly power 249*7c478bd9Sstevel@tonic-gate * managed to the processes that manage them. 250*7c478bd9Sstevel@tonic-gate * Lock ordering: 251*7c478bd9Sstevel@tonic-gate * pm_pscc_direct_rwlock -> psce_lock 252*7c478bd9Sstevel@tonic-gate * 253*7c478bd9Sstevel@tonic-gate * pm_pscc_interest_rwlock; 254*7c478bd9Sstevel@tonic-gate * Protects the list that maps state change events to processes 255*7c478bd9Sstevel@tonic-gate * that want to know about them. 256*7c478bd9Sstevel@tonic-gate * Lock ordering: 257*7c478bd9Sstevel@tonic-gate * pm_pscc_interest_rwlock -> psce_lock 258*7c478bd9Sstevel@tonic-gate * 259*7c478bd9Sstevel@tonic-gate * per-dip locks: 260*7c478bd9Sstevel@tonic-gate * 261*7c478bd9Sstevel@tonic-gate * Each node has these per-dip locks, which are only used if the device is 262*7c478bd9Sstevel@tonic-gate * a candidate for power management (e.g. has pm components) 263*7c478bd9Sstevel@tonic-gate * 264*7c478bd9Sstevel@tonic-gate * devi_pm_lock: 265*7c478bd9Sstevel@tonic-gate * Protects all power management state of the node except for 266*7c478bd9Sstevel@tonic-gate * power level, which is protected by ndi_devi_enter(). 267*7c478bd9Sstevel@tonic-gate * Encapsulated in macros PM_LOCK_DIP()/PM_UNLOCK_DIP(). 268*7c478bd9Sstevel@tonic-gate * Lock ordering: 269*7c478bd9Sstevel@tonic-gate * devi_pm_lock -> pm_rsvp_lock, 270*7c478bd9Sstevel@tonic-gate * devi_pm_lock -> pm_dep_thread_lock, 271*7c478bd9Sstevel@tonic-gate * devi_pm_lock -> pm_noinvol_rwlock, 272*7c478bd9Sstevel@tonic-gate * devi_pm_lock -> power lock 273*7c478bd9Sstevel@tonic-gate * 274*7c478bd9Sstevel@tonic-gate * power lock (ndi_devi_enter()): 275*7c478bd9Sstevel@tonic-gate * Since changing power level is possibly a slow operation (30 276*7c478bd9Sstevel@tonic-gate * seconds to spin up a disk drive), this is locked separately. 277*7c478bd9Sstevel@tonic-gate * Since a call into the driver to change the power level of one 278*7c478bd9Sstevel@tonic-gate * component may result in a call back into the framework to change 279*7c478bd9Sstevel@tonic-gate * the power level of another, this lock allows re-entrancy by 280*7c478bd9Sstevel@tonic-gate * the same thread (ndi_devi_enter is used for this because 281*7c478bd9Sstevel@tonic-gate * the USB framework uses ndi_devi_enter in its power entry point, 282*7c478bd9Sstevel@tonic-gate * and use of any other lock would produce a deadlock. 283*7c478bd9Sstevel@tonic-gate * 284*7c478bd9Sstevel@tonic-gate * devi_pm_busy_lock: 285*7c478bd9Sstevel@tonic-gate * This lock protects the integrity of the busy count. It is 286*7c478bd9Sstevel@tonic-gate * only taken by pm_busy_component() and pm_idle_component and 287*7c478bd9Sstevel@tonic-gate * some code that adjust the busy time after the timer gets set 288*7c478bd9Sstevel@tonic-gate * up or after a CPR operation. It is per-dip to keep from 289*7c478bd9Sstevel@tonic-gate * single-threading all the disk drivers on a system. 290*7c478bd9Sstevel@tonic-gate * It could be per component instead, but most devices have 291*7c478bd9Sstevel@tonic-gate * only one component. 292*7c478bd9Sstevel@tonic-gate * No other PM locks are taken while holding this lock. 293*7c478bd9Sstevel@tonic-gate * 294*7c478bd9Sstevel@tonic-gate */ 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate static int stdout_is_framebuffer; 297*7c478bd9Sstevel@tonic-gate static kmutex_t e_pm_power_lock; 298*7c478bd9Sstevel@tonic-gate static kmutex_t pm_loan_lock; 299*7c478bd9Sstevel@tonic-gate kmutex_t pm_scan_lock; 300*7c478bd9Sstevel@tonic-gate callb_id_t pm_cpr_cb_id; 301*7c478bd9Sstevel@tonic-gate callb_id_t pm_panic_cb_id; 302*7c478bd9Sstevel@tonic-gate callb_id_t pm_halt_cb_id; 303*7c478bd9Sstevel@tonic-gate int pm_comps_notlowest; /* no. of comps not at lowest power */ 304*7c478bd9Sstevel@tonic-gate int pm_powering_down; /* cpr is source of DDI_SUSPEND calls */ 305*7c478bd9Sstevel@tonic-gate 306*7c478bd9Sstevel@tonic-gate clock_t pm_min_scan = PM_MIN_SCAN; 307*7c478bd9Sstevel@tonic-gate clock_t pm_id_ticks = 5; /* ticks to wait before scan during idle-down */ 308*7c478bd9Sstevel@tonic-gate 309*7c478bd9Sstevel@tonic-gate static int pm_busop_set_power(dev_info_t *, 310*7c478bd9Sstevel@tonic-gate void *, pm_bus_power_op_t, void *, void *); 311*7c478bd9Sstevel@tonic-gate static int pm_busop_match_request(dev_info_t *, void *); 312*7c478bd9Sstevel@tonic-gate static int pm_all_to_normal_nexus(dev_info_t *, pm_canblock_t); 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate /* 315*7c478bd9Sstevel@tonic-gate * Dependency Processing is done thru a seperate thread. 316*7c478bd9Sstevel@tonic-gate */ 317*7c478bd9Sstevel@tonic-gate kmutex_t pm_dep_thread_lock; 318*7c478bd9Sstevel@tonic-gate kcondvar_t pm_dep_thread_cv; 319*7c478bd9Sstevel@tonic-gate pm_dep_wk_t *pm_dep_thread_workq = NULL; 320*7c478bd9Sstevel@tonic-gate pm_dep_wk_t *pm_dep_thread_tail = NULL; 321*7c478bd9Sstevel@tonic-gate 322*7c478bd9Sstevel@tonic-gate /* 323*7c478bd9Sstevel@tonic-gate * Autopm must be turned on by a PM_START_PM ioctl, so we don't end up 324*7c478bd9Sstevel@tonic-gate * power managing things in single user mode that have been suppressed via 325*7c478bd9Sstevel@tonic-gate * power.conf entries. Protected by pm_scan_lock. 326*7c478bd9Sstevel@tonic-gate */ 327*7c478bd9Sstevel@tonic-gate int autopm_enabled; 328*7c478bd9Sstevel@tonic-gate 329*7c478bd9Sstevel@tonic-gate /* 330*7c478bd9Sstevel@tonic-gate * This flag is true while processes are stopped for a checkpoint/resume. 331*7c478bd9Sstevel@tonic-gate * Controlling processes of direct pm'd devices are not available to 332*7c478bd9Sstevel@tonic-gate * participate in power level changes, so we bypass them when this is set. 333*7c478bd9Sstevel@tonic-gate */ 334*7c478bd9Sstevel@tonic-gate static int pm_processes_stopped; 335*7c478bd9Sstevel@tonic-gate 336*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 337*7c478bd9Sstevel@tonic-gate 338*7c478bd9Sstevel@tonic-gate /* 339*7c478bd9Sstevel@tonic-gate * see common/sys/epm.h for PMD_* values 340*7c478bd9Sstevel@tonic-gate */ 341*7c478bd9Sstevel@tonic-gate uint_t pm_debug = 0; 342*7c478bd9Sstevel@tonic-gate 343*7c478bd9Sstevel@tonic-gate /* 344*7c478bd9Sstevel@tonic-gate * If pm_divertdebug is set, then no prom_printf calls will be made by 345*7c478bd9Sstevel@tonic-gate * PMD(), which will prevent debug output from bringing up the console 346*7c478bd9Sstevel@tonic-gate * frame buffer. Clearing this variable before setting pm_debug will result 347*7c478bd9Sstevel@tonic-gate * in PMD output going to the console. 348*7c478bd9Sstevel@tonic-gate * 349*7c478bd9Sstevel@tonic-gate * pm_divertdebug is incremented in pm_set_power() if dip == cfb_dip to avoid 350*7c478bd9Sstevel@tonic-gate * deadlocks and decremented at the end of pm_set_power() 351*7c478bd9Sstevel@tonic-gate */ 352*7c478bd9Sstevel@tonic-gate uint_t pm_divertdebug = 1; 353*7c478bd9Sstevel@tonic-gate kmutex_t pm_debug_lock; /* protects pm_divertdebug */ 354*7c478bd9Sstevel@tonic-gate 355*7c478bd9Sstevel@tonic-gate void prdeps(char *); 356*7c478bd9Sstevel@tonic-gate #endif 357*7c478bd9Sstevel@tonic-gate 358*7c478bd9Sstevel@tonic-gate /* Globals */ 359*7c478bd9Sstevel@tonic-gate 360*7c478bd9Sstevel@tonic-gate /* 361*7c478bd9Sstevel@tonic-gate * List of recorded thresholds and dependencies 362*7c478bd9Sstevel@tonic-gate */ 363*7c478bd9Sstevel@tonic-gate pm_thresh_rec_t *pm_thresh_head; 364*7c478bd9Sstevel@tonic-gate krwlock_t pm_thresh_rwlock; 365*7c478bd9Sstevel@tonic-gate 366*7c478bd9Sstevel@tonic-gate pm_pdr_t *pm_dep_head; 367*7c478bd9Sstevel@tonic-gate static int pm_unresolved_deps = 0; 368*7c478bd9Sstevel@tonic-gate static int pm_prop_deps = 0; 369*7c478bd9Sstevel@tonic-gate 370*7c478bd9Sstevel@tonic-gate /* 371*7c478bd9Sstevel@tonic-gate * List of devices that exported no-involuntary-power-cycles property 372*7c478bd9Sstevel@tonic-gate */ 373*7c478bd9Sstevel@tonic-gate pm_noinvol_t *pm_noinvol_head; 374*7c478bd9Sstevel@tonic-gate 375*7c478bd9Sstevel@tonic-gate /* 376*7c478bd9Sstevel@tonic-gate * Locks used in noinvol processing 377*7c478bd9Sstevel@tonic-gate */ 378*7c478bd9Sstevel@tonic-gate krwlock_t pm_noinvol_rwlock; 379*7c478bd9Sstevel@tonic-gate kmutex_t pm_remdrv_lock; 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate int pm_default_idle_threshold = PM_DEFAULT_SYS_IDLENESS; 382*7c478bd9Sstevel@tonic-gate int pm_system_idle_threshold; 383*7c478bd9Sstevel@tonic-gate /* 384*7c478bd9Sstevel@tonic-gate * By default nexus has 0 threshold, and depends on its children to keep it up 385*7c478bd9Sstevel@tonic-gate */ 386*7c478bd9Sstevel@tonic-gate int pm_default_nexus_threshold = 0; 387*7c478bd9Sstevel@tonic-gate 388*7c478bd9Sstevel@tonic-gate /* 389*7c478bd9Sstevel@tonic-gate * Data structures shared with common/io/pm.c 390*7c478bd9Sstevel@tonic-gate */ 391*7c478bd9Sstevel@tonic-gate kmutex_t pm_clone_lock; 392*7c478bd9Sstevel@tonic-gate kcondvar_t pm_clones_cv[PM_MAX_CLONE]; 393*7c478bd9Sstevel@tonic-gate uint_t pm_poll_cnt[PM_MAX_CLONE]; /* count of events for poll */ 394*7c478bd9Sstevel@tonic-gate unsigned char pm_interest[PM_MAX_CLONE]; 395*7c478bd9Sstevel@tonic-gate struct pollhead pm_pollhead; 396*7c478bd9Sstevel@tonic-gate 397*7c478bd9Sstevel@tonic-gate extern int hz; 398*7c478bd9Sstevel@tonic-gate extern char *platform_module_list[]; 399*7c478bd9Sstevel@tonic-gate 400*7c478bd9Sstevel@tonic-gate /* 401*7c478bd9Sstevel@tonic-gate * Wrappers for use in ddi_walk_devs 402*7c478bd9Sstevel@tonic-gate */ 403*7c478bd9Sstevel@tonic-gate 404*7c478bd9Sstevel@tonic-gate static int pm_set_dev_thr_walk(dev_info_t *, void *); 405*7c478bd9Sstevel@tonic-gate static int pm_restore_direct_lvl_walk(dev_info_t *, void *); 406*7c478bd9Sstevel@tonic-gate static int pm_save_direct_lvl_walk(dev_info_t *, void *); 407*7c478bd9Sstevel@tonic-gate static int pm_discard_dep_walk(dev_info_t *, void *); 408*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 409*7c478bd9Sstevel@tonic-gate static int pm_desc_pwrchk_walk(dev_info_t *, void *); 410*7c478bd9Sstevel@tonic-gate #endif 411*7c478bd9Sstevel@tonic-gate 412*7c478bd9Sstevel@tonic-gate /* 413*7c478bd9Sstevel@tonic-gate * Routines for managing noinvol devices 414*7c478bd9Sstevel@tonic-gate */ 415*7c478bd9Sstevel@tonic-gate int pm_noinvol_update(int, int, int, char *, dev_info_t *); 416*7c478bd9Sstevel@tonic-gate void pm_noinvol_update_node(dev_info_t *, 417*7c478bd9Sstevel@tonic-gate pm_bp_noinvol_t *req); 418*7c478bd9Sstevel@tonic-gate 419*7c478bd9Sstevel@tonic-gate kmutex_t pm_rsvp_lock; 420*7c478bd9Sstevel@tonic-gate kmutex_t pm_compcnt_lock; 421*7c478bd9Sstevel@tonic-gate krwlock_t pm_pscc_direct_rwlock; 422*7c478bd9Sstevel@tonic-gate krwlock_t pm_pscc_interest_rwlock; 423*7c478bd9Sstevel@tonic-gate 424*7c478bd9Sstevel@tonic-gate #define PSC_INTEREST 0 /* belongs to interest psc list */ 425*7c478bd9Sstevel@tonic-gate #define PSC_DIRECT 1 /* belongs to direct psc list */ 426*7c478bd9Sstevel@tonic-gate 427*7c478bd9Sstevel@tonic-gate pscc_t *pm_pscc_interest; 428*7c478bd9Sstevel@tonic-gate pscc_t *pm_pscc_direct; 429*7c478bd9Sstevel@tonic-gate 430*7c478bd9Sstevel@tonic-gate #define PM_MAJOR(dip) ddi_name_to_major(ddi_binding_name(dip)) 431*7c478bd9Sstevel@tonic-gate #define PM_IS_NEXUS(dip) NEXUS_DRV(devopsp[PM_MAJOR(dip)]) 432*7c478bd9Sstevel@tonic-gate #define POWERING_ON(old, new) ((old) == 0 && (new) != 0) 433*7c478bd9Sstevel@tonic-gate #define POWERING_OFF(old, new) ((old) != 0 && (new) == 0) 434*7c478bd9Sstevel@tonic-gate #define PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm) 435*7c478bd9Sstevel@tonic-gate 436*7c478bd9Sstevel@tonic-gate #define PM_INCR_NOTLOWEST(dip) { \ 437*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); \ 438*7c478bd9Sstevel@tonic-gate if (!PM_IS_NEXUS(dip) || \ 439*7c478bd9Sstevel@tonic-gate (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\ 440*7c478bd9Sstevel@tonic-gate if (pm_comps_notlowest == 0) \ 441*7c478bd9Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, PM_NOT_ALL_LOWEST);\ 442*7c478bd9Sstevel@tonic-gate pm_comps_notlowest++; \ 443*7c478bd9Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr notlowest->%d\n",\ 444*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_comps_notlowest)) \ 445*7c478bd9Sstevel@tonic-gate } \ 446*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); \ 447*7c478bd9Sstevel@tonic-gate } 448*7c478bd9Sstevel@tonic-gate #define PM_DECR_NOTLOWEST(dip) { \ 449*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); \ 450*7c478bd9Sstevel@tonic-gate if (!PM_IS_NEXUS(dip) || \ 451*7c478bd9Sstevel@tonic-gate (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\ 452*7c478bd9Sstevel@tonic-gate ASSERT(pm_comps_notlowest); \ 453*7c478bd9Sstevel@tonic-gate pm_comps_notlowest--; \ 454*7c478bd9Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr notlowest to " \ 455*7c478bd9Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pm_comps_notlowest))\ 456*7c478bd9Sstevel@tonic-gate if (pm_comps_notlowest == 0) \ 457*7c478bd9Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, PM_ALL_LOWEST); \ 458*7c478bd9Sstevel@tonic-gate } \ 459*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); \ 460*7c478bd9Sstevel@tonic-gate } 461*7c478bd9Sstevel@tonic-gate 462*7c478bd9Sstevel@tonic-gate /* 463*7c478bd9Sstevel@tonic-gate * console frame-buffer power-management is not enabled when 464*7c478bd9Sstevel@tonic-gate * debugging services are present. to override, set pm_cfb_override 465*7c478bd9Sstevel@tonic-gate * to non-zero. 466*7c478bd9Sstevel@tonic-gate */ 467*7c478bd9Sstevel@tonic-gate uint_t pm_cfb_comps_off = 0; /* PM_LEVEL_UNKNOWN is considered on */ 468*7c478bd9Sstevel@tonic-gate kmutex_t pm_cfb_lock; 469*7c478bd9Sstevel@tonic-gate int pm_cfb_enabled = 1; /* non-zero allows pm of console frame buffer */ 470*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 471*7c478bd9Sstevel@tonic-gate int pm_cfb_override = 1; /* non-zero allows pm of cfb with debuggers */ 472*7c478bd9Sstevel@tonic-gate #else 473*7c478bd9Sstevel@tonic-gate int pm_cfb_override = 0; /* non-zero allows pm of cfb with debuggers */ 474*7c478bd9Sstevel@tonic-gate #endif 475*7c478bd9Sstevel@tonic-gate 476*7c478bd9Sstevel@tonic-gate static dev_info_t *cfb_dip = 0; 477*7c478bd9Sstevel@tonic-gate static dev_info_t *cfb_dip_detaching = 0; 478*7c478bd9Sstevel@tonic-gate uint_t cfb_inuse = 0; 479*7c478bd9Sstevel@tonic-gate static ddi_softintr_t pm_soft_id; 480*7c478bd9Sstevel@tonic-gate static clock_t pm_soft_pending; 481*7c478bd9Sstevel@tonic-gate int pm_scans_disabled = 0; 482*7c478bd9Sstevel@tonic-gate 483*7c478bd9Sstevel@tonic-gate /* 484*7c478bd9Sstevel@tonic-gate * A structure to record the fact that one thread has borrowed a lock held 485*7c478bd9Sstevel@tonic-gate * by another thread. The context requires that the lender block on the 486*7c478bd9Sstevel@tonic-gate * completion of the borrower. 487*7c478bd9Sstevel@tonic-gate */ 488*7c478bd9Sstevel@tonic-gate typedef struct lock_loan { 489*7c478bd9Sstevel@tonic-gate struct lock_loan *pmlk_next; 490*7c478bd9Sstevel@tonic-gate kthread_t *pmlk_borrower; 491*7c478bd9Sstevel@tonic-gate kthread_t *pmlk_lender; 492*7c478bd9Sstevel@tonic-gate dev_info_t *pmlk_dip; 493*7c478bd9Sstevel@tonic-gate } lock_loan_t; 494*7c478bd9Sstevel@tonic-gate static lock_loan_t lock_loan_head; /* list head is a dummy element */ 495*7c478bd9Sstevel@tonic-gate 496*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 497*7c478bd9Sstevel@tonic-gate #define PMD_FUNC(func, name) char *(func) = (name); 498*7c478bd9Sstevel@tonic-gate #else 499*7c478bd9Sstevel@tonic-gate #define PMD_FUNC(func, name) 500*7c478bd9Sstevel@tonic-gate #endif 501*7c478bd9Sstevel@tonic-gate 502*7c478bd9Sstevel@tonic-gate 503*7c478bd9Sstevel@tonic-gate /* 504*7c478bd9Sstevel@tonic-gate * Must be called before first device (including pseudo) attach 505*7c478bd9Sstevel@tonic-gate */ 506*7c478bd9Sstevel@tonic-gate void 507*7c478bd9Sstevel@tonic-gate pm_init_locks(void) 508*7c478bd9Sstevel@tonic-gate { 509*7c478bd9Sstevel@tonic-gate mutex_init(&pm_scan_lock, NULL, MUTEX_DRIVER, NULL); 510*7c478bd9Sstevel@tonic-gate mutex_init(&pm_rsvp_lock, NULL, MUTEX_DRIVER, NULL); 511*7c478bd9Sstevel@tonic-gate mutex_init(&pm_compcnt_lock, NULL, MUTEX_DRIVER, NULL); 512*7c478bd9Sstevel@tonic-gate mutex_init(&pm_dep_thread_lock, NULL, MUTEX_DRIVER, NULL); 513*7c478bd9Sstevel@tonic-gate mutex_init(&pm_remdrv_lock, NULL, MUTEX_DRIVER, NULL); 514*7c478bd9Sstevel@tonic-gate mutex_init(&pm_loan_lock, NULL, MUTEX_DRIVER, NULL); 515*7c478bd9Sstevel@tonic-gate rw_init(&pm_thresh_rwlock, NULL, RW_DEFAULT, NULL); 516*7c478bd9Sstevel@tonic-gate rw_init(&pm_noinvol_rwlock, NULL, RW_DEFAULT, NULL); 517*7c478bd9Sstevel@tonic-gate cv_init(&pm_dep_thread_cv, NULL, CV_DEFAULT, NULL); 518*7c478bd9Sstevel@tonic-gate } 519*7c478bd9Sstevel@tonic-gate 520*7c478bd9Sstevel@tonic-gate static boolean_t 521*7c478bd9Sstevel@tonic-gate pm_cpr_callb(void *arg, int code) 522*7c478bd9Sstevel@tonic-gate { 523*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 524*7c478bd9Sstevel@tonic-gate static int auto_save; 525*7c478bd9Sstevel@tonic-gate static int pm_reset_timestamps(dev_info_t *, void *); 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate switch (code) { 528*7c478bd9Sstevel@tonic-gate case CB_CODE_CPR_CHKPT: 529*7c478bd9Sstevel@tonic-gate /* 530*7c478bd9Sstevel@tonic-gate * Cancel scan or wait for scan in progress to finish 531*7c478bd9Sstevel@tonic-gate * Other threads may be trying to restart the scan, so we 532*7c478bd9Sstevel@tonic-gate * have to keep at it unil it sticks 533*7c478bd9Sstevel@tonic-gate */ 534*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_scan_lock); 535*7c478bd9Sstevel@tonic-gate ASSERT(!pm_scans_disabled); 536*7c478bd9Sstevel@tonic-gate pm_scans_disabled = 1; 537*7c478bd9Sstevel@tonic-gate auto_save = autopm_enabled; 538*7c478bd9Sstevel@tonic-gate autopm_enabled = 0; 539*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 540*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_scan_stop_walk, NULL); 541*7c478bd9Sstevel@tonic-gate break; 542*7c478bd9Sstevel@tonic-gate 543*7c478bd9Sstevel@tonic-gate case CB_CODE_CPR_RESUME: 544*7c478bd9Sstevel@tonic-gate ASSERT(!autopm_enabled); 545*7c478bd9Sstevel@tonic-gate ASSERT(pm_scans_disabled); 546*7c478bd9Sstevel@tonic-gate pm_scans_disabled = 0; 547*7c478bd9Sstevel@tonic-gate /* 548*7c478bd9Sstevel@tonic-gate * Call pm_reset_timestamps to reset timestamps of each 549*7c478bd9Sstevel@tonic-gate * device to the time when the system is resumed so that their 550*7c478bd9Sstevel@tonic-gate * idleness can be re-calculated. That's to avoid devices from 551*7c478bd9Sstevel@tonic-gate * being powered down right after resume if the system was in 552*7c478bd9Sstevel@tonic-gate * suspended mode long enough. 553*7c478bd9Sstevel@tonic-gate */ 554*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_reset_timestamps, NULL); 555*7c478bd9Sstevel@tonic-gate 556*7c478bd9Sstevel@tonic-gate autopm_enabled = auto_save; 557*7c478bd9Sstevel@tonic-gate /* 558*7c478bd9Sstevel@tonic-gate * If there is any auto-pm device, get the scanning 559*7c478bd9Sstevel@tonic-gate * going. Otherwise don't bother. 560*7c478bd9Sstevel@tonic-gate */ 561*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_rescan_walk, NULL); 562*7c478bd9Sstevel@tonic-gate break; 563*7c478bd9Sstevel@tonic-gate } 564*7c478bd9Sstevel@tonic-gate return (B_TRUE); 565*7c478bd9Sstevel@tonic-gate } 566*7c478bd9Sstevel@tonic-gate 567*7c478bd9Sstevel@tonic-gate /* 568*7c478bd9Sstevel@tonic-gate * This callback routine is called when there is a system panic. This function 569*7c478bd9Sstevel@tonic-gate * exists for prototype matching. 570*7c478bd9Sstevel@tonic-gate */ 571*7c478bd9Sstevel@tonic-gate static boolean_t 572*7c478bd9Sstevel@tonic-gate pm_panic_callb(void *arg, int code) 573*7c478bd9Sstevel@tonic-gate { 574*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg, code)) 575*7c478bd9Sstevel@tonic-gate void pm_cfb_check_and_powerup(void); 576*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("pm_panic_callb\n")) 577*7c478bd9Sstevel@tonic-gate pm_cfb_check_and_powerup(); 578*7c478bd9Sstevel@tonic-gate return (B_TRUE); 579*7c478bd9Sstevel@tonic-gate } 580*7c478bd9Sstevel@tonic-gate 581*7c478bd9Sstevel@tonic-gate static boolean_t 582*7c478bd9Sstevel@tonic-gate pm_halt_callb(void *arg, int code) 583*7c478bd9Sstevel@tonic-gate { 584*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg, code)) 585*7c478bd9Sstevel@tonic-gate return (B_TRUE); /* XXX for now */ 586*7c478bd9Sstevel@tonic-gate } 587*7c478bd9Sstevel@tonic-gate 588*7c478bd9Sstevel@tonic-gate /* 589*7c478bd9Sstevel@tonic-gate * This needs to be called after the root and platform drivers are loaded 590*7c478bd9Sstevel@tonic-gate * and be single-threaded with respect to driver attach/detach 591*7c478bd9Sstevel@tonic-gate */ 592*7c478bd9Sstevel@tonic-gate void 593*7c478bd9Sstevel@tonic-gate pm_init(void) 594*7c478bd9Sstevel@tonic-gate { 595*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_init") 596*7c478bd9Sstevel@tonic-gate char **mod; 597*7c478bd9Sstevel@tonic-gate extern pri_t minclsyspri; 598*7c478bd9Sstevel@tonic-gate static void pm_dep_thread(void); 599*7c478bd9Sstevel@tonic-gate 600*7c478bd9Sstevel@tonic-gate pm_comps_notlowest = 0; 601*7c478bd9Sstevel@tonic-gate pm_system_idle_threshold = pm_default_idle_threshold; 602*7c478bd9Sstevel@tonic-gate 603*7c478bd9Sstevel@tonic-gate pm_cpr_cb_id = callb_add(pm_cpr_callb, (void *)NULL, 604*7c478bd9Sstevel@tonic-gate CB_CL_CPR_PM, "pm_cpr"); 605*7c478bd9Sstevel@tonic-gate pm_panic_cb_id = callb_add(pm_panic_callb, (void *)NULL, 606*7c478bd9Sstevel@tonic-gate CB_CL_PANIC, "pm_panic"); 607*7c478bd9Sstevel@tonic-gate pm_halt_cb_id = callb_add(pm_halt_callb, (void *)NULL, 608*7c478bd9Sstevel@tonic-gate CB_CL_HALT, "pm_halt"); 609*7c478bd9Sstevel@tonic-gate 610*7c478bd9Sstevel@tonic-gate /* 611*7c478bd9Sstevel@tonic-gate * Create a thread to do dependency processing. 612*7c478bd9Sstevel@tonic-gate */ 613*7c478bd9Sstevel@tonic-gate (void) thread_create(NULL, 0, (void (*)())pm_dep_thread, NULL, 0, &p0, 614*7c478bd9Sstevel@tonic-gate TS_RUN, minclsyspri); 615*7c478bd9Sstevel@tonic-gate 616*7c478bd9Sstevel@tonic-gate /* 617*7c478bd9Sstevel@tonic-gate * loadrootmodules already loaded these ppm drivers, now get them 618*7c478bd9Sstevel@tonic-gate * attached so they can claim the root drivers as they attach 619*7c478bd9Sstevel@tonic-gate */ 620*7c478bd9Sstevel@tonic-gate for (mod = platform_module_list; *mod; mod++) { 621*7c478bd9Sstevel@tonic-gate if (i_ddi_attach_hw_nodes(*mod) != DDI_SUCCESS) { 622*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!cannot load platform pm driver %s\n", 623*7c478bd9Sstevel@tonic-gate *mod); 624*7c478bd9Sstevel@tonic-gate } else { 625*7c478bd9Sstevel@tonic-gate PMD(PMD_DHR, ("%s: %s (%s)\n", pmf, *mod, 626*7c478bd9Sstevel@tonic-gate ddi_major_to_name(ddi_name_to_major(*mod)))) 627*7c478bd9Sstevel@tonic-gate } 628*7c478bd9Sstevel@tonic-gate } 629*7c478bd9Sstevel@tonic-gate } 630*7c478bd9Sstevel@tonic-gate 631*7c478bd9Sstevel@tonic-gate /* 632*7c478bd9Sstevel@tonic-gate * pm_scan_init - create pm scan data structure. Called (if autopm enabled) 633*7c478bd9Sstevel@tonic-gate * when device becomes power managed or after a failed detach and when autopm 634*7c478bd9Sstevel@tonic-gate * is started via PM_START_PM ioctl, and after a CPR resume to get all the 635*7c478bd9Sstevel@tonic-gate * devices scanning again. 636*7c478bd9Sstevel@tonic-gate */ 637*7c478bd9Sstevel@tonic-gate void 638*7c478bd9Sstevel@tonic-gate pm_scan_init(dev_info_t *dip) 639*7c478bd9Sstevel@tonic-gate { 640*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "scan_init") 641*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp; 642*7c478bd9Sstevel@tonic-gate 643*7c478bd9Sstevel@tonic-gate ASSERT(!PM_ISBC(dip)); 644*7c478bd9Sstevel@tonic-gate 645*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 646*7c478bd9Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 647*7c478bd9Sstevel@tonic-gate if (!scanp) { 648*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): create scan data\n", 649*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 650*7c478bd9Sstevel@tonic-gate scanp = kmem_zalloc(sizeof (pm_scan_t), KM_SLEEP); 651*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_scan = scanp; 652*7c478bd9Sstevel@tonic-gate } else if (scanp->ps_scan_flags & PM_SCAN_STOP) { 653*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): " 654*7c478bd9Sstevel@tonic-gate "clear PM_SCAN_STOP flag\n", pmf, PM_DEVICE(dip))) 655*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_STOP; 656*7c478bd9Sstevel@tonic-gate } 657*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 658*7c478bd9Sstevel@tonic-gate } 659*7c478bd9Sstevel@tonic-gate 660*7c478bd9Sstevel@tonic-gate /* 661*7c478bd9Sstevel@tonic-gate * pm_scan_fini - remove pm scan data structure when stopping pm on the device 662*7c478bd9Sstevel@tonic-gate */ 663*7c478bd9Sstevel@tonic-gate void 664*7c478bd9Sstevel@tonic-gate pm_scan_fini(dev_info_t *dip) 665*7c478bd9Sstevel@tonic-gate { 666*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "scan_fini") 667*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp; 668*7c478bd9Sstevel@tonic-gate 669*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 670*7c478bd9Sstevel@tonic-gate ASSERT(!PM_ISBC(dip)); 671*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 672*7c478bd9Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 673*7c478bd9Sstevel@tonic-gate if (!scanp) { 674*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 675*7c478bd9Sstevel@tonic-gate return; 676*7c478bd9Sstevel@tonic-gate } 677*7c478bd9Sstevel@tonic-gate 678*7c478bd9Sstevel@tonic-gate ASSERT(!scanp->ps_scan_id && !(scanp->ps_scan_flags & 679*7c478bd9Sstevel@tonic-gate (PM_SCANNING | PM_SCAN_DISPATCHED | PM_SCAN_AGAIN))); 680*7c478bd9Sstevel@tonic-gate 681*7c478bd9Sstevel@tonic-gate kmem_free(scanp, sizeof (pm_scan_t)); 682*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_scan = NULL; 683*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 684*7c478bd9Sstevel@tonic-gate } 685*7c478bd9Sstevel@tonic-gate 686*7c478bd9Sstevel@tonic-gate /* 687*7c478bd9Sstevel@tonic-gate * Given a pointer to a component struct, return the current power level 688*7c478bd9Sstevel@tonic-gate * (struct contains index unless it is a continuous level). 689*7c478bd9Sstevel@tonic-gate * Located here in hopes of getting both this and dev_is_needed into the 690*7c478bd9Sstevel@tonic-gate * cache together 691*7c478bd9Sstevel@tonic-gate */ 692*7c478bd9Sstevel@tonic-gate static int 693*7c478bd9Sstevel@tonic-gate cur_power(pm_component_t *cp) 694*7c478bd9Sstevel@tonic-gate { 695*7c478bd9Sstevel@tonic-gate if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN) 696*7c478bd9Sstevel@tonic-gate return (cp->pmc_cur_pwr); 697*7c478bd9Sstevel@tonic-gate 698*7c478bd9Sstevel@tonic-gate return (cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]); 699*7c478bd9Sstevel@tonic-gate } 700*7c478bd9Sstevel@tonic-gate 701*7c478bd9Sstevel@tonic-gate static char * 702*7c478bd9Sstevel@tonic-gate pm_decode_direction(int direction) 703*7c478bd9Sstevel@tonic-gate { 704*7c478bd9Sstevel@tonic-gate switch (direction) { 705*7c478bd9Sstevel@tonic-gate case PM_LEVEL_UPONLY: 706*7c478bd9Sstevel@tonic-gate return ("up"); 707*7c478bd9Sstevel@tonic-gate 708*7c478bd9Sstevel@tonic-gate case PM_LEVEL_EXACT: 709*7c478bd9Sstevel@tonic-gate return ("exact"); 710*7c478bd9Sstevel@tonic-gate 711*7c478bd9Sstevel@tonic-gate case PM_LEVEL_DOWNONLY: 712*7c478bd9Sstevel@tonic-gate return ("down"); 713*7c478bd9Sstevel@tonic-gate 714*7c478bd9Sstevel@tonic-gate default: 715*7c478bd9Sstevel@tonic-gate return ("INVALID DIRECTION"); 716*7c478bd9Sstevel@tonic-gate } 717*7c478bd9Sstevel@tonic-gate _NOTE(NOTREACHED); 718*7c478bd9Sstevel@tonic-gate ASSERT(0); 719*7c478bd9Sstevel@tonic-gate } 720*7c478bd9Sstevel@tonic-gate 721*7c478bd9Sstevel@tonic-gate char * 722*7c478bd9Sstevel@tonic-gate pm_decode_op(pm_bus_power_op_t op) 723*7c478bd9Sstevel@tonic-gate { 724*7c478bd9Sstevel@tonic-gate switch (op) { 725*7c478bd9Sstevel@tonic-gate case BUS_POWER_CHILD_PWRCHG: 726*7c478bd9Sstevel@tonic-gate return ("CHILD_PWRCHG"); 727*7c478bd9Sstevel@tonic-gate case BUS_POWER_NEXUS_PWRUP: 728*7c478bd9Sstevel@tonic-gate return ("NEXUS_PWRUP"); 729*7c478bd9Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION: 730*7c478bd9Sstevel@tonic-gate return ("PRE_NOTIFICATION"); 731*7c478bd9Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION: 732*7c478bd9Sstevel@tonic-gate return ("POST_NOTIFICATION"); 733*7c478bd9Sstevel@tonic-gate case BUS_POWER_HAS_CHANGED: 734*7c478bd9Sstevel@tonic-gate return ("HAS_CHANGED"); 735*7c478bd9Sstevel@tonic-gate case BUS_POWER_NOINVOL: 736*7c478bd9Sstevel@tonic-gate return ("NOINVOL"); 737*7c478bd9Sstevel@tonic-gate default: 738*7c478bd9Sstevel@tonic-gate return ("UNKNOWN OP"); 739*7c478bd9Sstevel@tonic-gate } 740*7c478bd9Sstevel@tonic-gate _NOTE(NOTREACHED); 741*7c478bd9Sstevel@tonic-gate ASSERT(0); 742*7c478bd9Sstevel@tonic-gate } 743*7c478bd9Sstevel@tonic-gate 744*7c478bd9Sstevel@tonic-gate /* 745*7c478bd9Sstevel@tonic-gate * Returns true if level is a possible (valid) power level for component 746*7c478bd9Sstevel@tonic-gate */ 747*7c478bd9Sstevel@tonic-gate int 748*7c478bd9Sstevel@tonic-gate e_pm_valid_power(dev_info_t *dip, int cmpt, int level) 749*7c478bd9Sstevel@tonic-gate { 750*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "e_pm_valid_power") 751*7c478bd9Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, cmpt); 752*7c478bd9Sstevel@tonic-gate int i; 753*7c478bd9Sstevel@tonic-gate int *ip = cp->pmc_comp.pmc_lvals; 754*7c478bd9Sstevel@tonic-gate int limit = cp->pmc_comp.pmc_numlevels; 755*7c478bd9Sstevel@tonic-gate 756*7c478bd9Sstevel@tonic-gate if (level < 0) 757*7c478bd9Sstevel@tonic-gate return (0); 758*7c478bd9Sstevel@tonic-gate for (i = 0; i < limit; i++) { 759*7c478bd9Sstevel@tonic-gate if (level == *ip++) 760*7c478bd9Sstevel@tonic-gate return (1); 761*7c478bd9Sstevel@tonic-gate } 762*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 763*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_FAIL) { 764*7c478bd9Sstevel@tonic-gate ip = cp->pmc_comp.pmc_lvals; 765*7c478bd9Sstevel@tonic-gate 766*7c478bd9Sstevel@tonic-gate for (i = 0; i < limit; i++) 767*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: index=%d, level=%d\n", 768*7c478bd9Sstevel@tonic-gate pmf, i, *ip++)) 769*7c478bd9Sstevel@tonic-gate } 770*7c478bd9Sstevel@tonic-gate #endif 771*7c478bd9Sstevel@tonic-gate return (0); 772*7c478bd9Sstevel@tonic-gate } 773*7c478bd9Sstevel@tonic-gate 774*7c478bd9Sstevel@tonic-gate /* 775*7c478bd9Sstevel@tonic-gate * Returns true if device is pm'd (after calling pm_start if need be) 776*7c478bd9Sstevel@tonic-gate */ 777*7c478bd9Sstevel@tonic-gate int 778*7c478bd9Sstevel@tonic-gate e_pm_valid_info(dev_info_t *dip, pm_info_t **infop) 779*7c478bd9Sstevel@tonic-gate { 780*7c478bd9Sstevel@tonic-gate pm_info_t *info; 781*7c478bd9Sstevel@tonic-gate static int pm_start(dev_info_t *dip); 782*7c478bd9Sstevel@tonic-gate 783*7c478bd9Sstevel@tonic-gate /* 784*7c478bd9Sstevel@tonic-gate * Check if the device is power managed if not. 785*7c478bd9Sstevel@tonic-gate * To make the common case (device is power managed already) 786*7c478bd9Sstevel@tonic-gate * fast, we check without the lock. If device is not already 787*7c478bd9Sstevel@tonic-gate * power managed, then we take the lock and the long route through 788*7c478bd9Sstevel@tonic-gate * go get it managed. Devices never go unmanaged until they 789*7c478bd9Sstevel@tonic-gate * detach. 790*7c478bd9Sstevel@tonic-gate */ 791*7c478bd9Sstevel@tonic-gate info = PM_GET_PM_INFO(dip); 792*7c478bd9Sstevel@tonic-gate if (!info) { 793*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) { 794*7c478bd9Sstevel@tonic-gate return (0); 795*7c478bd9Sstevel@tonic-gate } 796*7c478bd9Sstevel@tonic-gate if (pm_start(dip) != DDI_SUCCESS) { 797*7c478bd9Sstevel@tonic-gate return (0); 798*7c478bd9Sstevel@tonic-gate } 799*7c478bd9Sstevel@tonic-gate info = PM_GET_PM_INFO(dip); 800*7c478bd9Sstevel@tonic-gate } 801*7c478bd9Sstevel@tonic-gate ASSERT(info); 802*7c478bd9Sstevel@tonic-gate if (infop != NULL) 803*7c478bd9Sstevel@tonic-gate *infop = info; 804*7c478bd9Sstevel@tonic-gate return (1); 805*7c478bd9Sstevel@tonic-gate } 806*7c478bd9Sstevel@tonic-gate 807*7c478bd9Sstevel@tonic-gate int 808*7c478bd9Sstevel@tonic-gate e_pm_valid_comp(dev_info_t *dip, int cmpt, pm_component_t **cpp) 809*7c478bd9Sstevel@tonic-gate { 810*7c478bd9Sstevel@tonic-gate if (cmpt >= 0 && cmpt < PM_NUMCMPTS(dip)) { 811*7c478bd9Sstevel@tonic-gate if (cpp != NULL) 812*7c478bd9Sstevel@tonic-gate *cpp = PM_CP(dip, cmpt); 813*7c478bd9Sstevel@tonic-gate return (1); 814*7c478bd9Sstevel@tonic-gate } else { 815*7c478bd9Sstevel@tonic-gate return (0); 816*7c478bd9Sstevel@tonic-gate } 817*7c478bd9Sstevel@tonic-gate } 818*7c478bd9Sstevel@tonic-gate 819*7c478bd9Sstevel@tonic-gate /* 820*7c478bd9Sstevel@tonic-gate * Internal guts of ddi_dev_is_needed and pm_raise/lower_power 821*7c478bd9Sstevel@tonic-gate */ 822*7c478bd9Sstevel@tonic-gate static int 823*7c478bd9Sstevel@tonic-gate dev_is_needed(dev_info_t *dip, int cmpt, int level, int direction) 824*7c478bd9Sstevel@tonic-gate { 825*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "din") 826*7c478bd9Sstevel@tonic-gate pm_component_t *cp; 827*7c478bd9Sstevel@tonic-gate char *pathbuf; 828*7c478bd9Sstevel@tonic-gate int result; 829*7c478bd9Sstevel@tonic-gate 830*7c478bd9Sstevel@tonic-gate ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY); 831*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp) || 832*7c478bd9Sstevel@tonic-gate !e_pm_valid_power(dip, cmpt, level)) 833*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 834*7c478bd9Sstevel@tonic-gate 835*7c478bd9Sstevel@tonic-gate PMD(PMD_DIN, ("%s: %s@%s(%s#%d) cmpt=%d, dir=%s, new=%d, cur=%d\n", 836*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), cmpt, pm_decode_direction(direction), 837*7c478bd9Sstevel@tonic-gate level, cur_power(cp))) 838*7c478bd9Sstevel@tonic-gate 839*7c478bd9Sstevel@tonic-gate if (pm_set_power(dip, cmpt, level, direction, 840*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) { 841*7c478bd9Sstevel@tonic-gate if (direction == PM_LEVEL_UPONLY) { 842*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 843*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 844*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Device %s failed to power up.", 845*7c478bd9Sstevel@tonic-gate pathbuf); 846*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 847*7c478bd9Sstevel@tonic-gate } 848*7c478bd9Sstevel@tonic-gate PMD(PMD_DIN | PMD_FAIL, ("%s: %s@%s(%s#%d) [%d] %s->%d failed, " 849*7c478bd9Sstevel@tonic-gate "errno %d\n", pmf, PM_DEVICE(dip), cmpt, 850*7c478bd9Sstevel@tonic-gate pm_decode_direction(direction), level, result)) 851*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 852*7c478bd9Sstevel@tonic-gate } 853*7c478bd9Sstevel@tonic-gate 854*7c478bd9Sstevel@tonic-gate PMD(PMD_RESCAN | PMD_DIN, ("%s: pm_rescan %s@%s(%s#%d)\n", pmf, 855*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 856*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 857*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 858*7c478bd9Sstevel@tonic-gate } 859*7c478bd9Sstevel@tonic-gate 860*7c478bd9Sstevel@tonic-gate /* 861*7c478bd9Sstevel@tonic-gate * We can get multiple pm_rescan() threads, if one of them discovers 862*7c478bd9Sstevel@tonic-gate * that no scan is running at the moment, it kicks it into action. 863*7c478bd9Sstevel@tonic-gate * Otherwise, it tells the current scanning thread to scan again when 864*7c478bd9Sstevel@tonic-gate * it is done by asserting the PM_SCAN_AGAIN flag. The PM_SCANNING and 865*7c478bd9Sstevel@tonic-gate * PM_SCAN_AGAIN flags are used to regulate scan, to make sure only one 866*7c478bd9Sstevel@tonic-gate * thread at a time runs the pm_scan_dev() code. 867*7c478bd9Sstevel@tonic-gate */ 868*7c478bd9Sstevel@tonic-gate void 869*7c478bd9Sstevel@tonic-gate pm_rescan(void *arg) 870*7c478bd9Sstevel@tonic-gate { 871*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "rescan") 872*7c478bd9Sstevel@tonic-gate dev_info_t *dip = (dev_info_t *)arg; 873*7c478bd9Sstevel@tonic-gate pm_info_t *info; 874*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp; 875*7c478bd9Sstevel@tonic-gate timeout_id_t scanid; 876*7c478bd9Sstevel@tonic-gate 877*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 878*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 879*7c478bd9Sstevel@tonic-gate info = PM_GET_PM_INFO(dip); 880*7c478bd9Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 881*7c478bd9Sstevel@tonic-gate if (pm_scans_disabled || !autopm_enabled || !info || !scanp || 882*7c478bd9Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP)) { 883*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 884*7c478bd9Sstevel@tonic-gate return; 885*7c478bd9Sstevel@tonic-gate } 886*7c478bd9Sstevel@tonic-gate if (scanp->ps_scan_flags & PM_SCANNING) { 887*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_AGAIN; 888*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 889*7c478bd9Sstevel@tonic-gate return; 890*7c478bd9Sstevel@tonic-gate } else if (scanp->ps_scan_id) { 891*7c478bd9Sstevel@tonic-gate scanid = scanp->ps_scan_id; 892*7c478bd9Sstevel@tonic-gate scanp->ps_scan_id = 0; 893*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): cancel timeout scanid %lx\n", 894*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), (ulong_t)scanid)) 895*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 896*7c478bd9Sstevel@tonic-gate (void) untimeout(scanid); 897*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 898*7c478bd9Sstevel@tonic-gate } 899*7c478bd9Sstevel@tonic-gate 900*7c478bd9Sstevel@tonic-gate /* 901*7c478bd9Sstevel@tonic-gate * Dispatching pm_scan during attach time is risky due to the fact that 902*7c478bd9Sstevel@tonic-gate * attach might soon fail and dip dissolved, and panic may happen while 903*7c478bd9Sstevel@tonic-gate * attempting to stop scan. So schedule a pm_rescan instead. 904*7c478bd9Sstevel@tonic-gate * (Note that if either of the first two terms are true, taskq_dispatch 905*7c478bd9Sstevel@tonic-gate * will not be invoked). 906*7c478bd9Sstevel@tonic-gate * 907*7c478bd9Sstevel@tonic-gate * Multiple pm_scan dispatching is unecessary and costly to keep track 908*7c478bd9Sstevel@tonic-gate * of. The PM_SCAN_DISPATCHED flag is used between pm_rescan and pm_scan 909*7c478bd9Sstevel@tonic-gate * to regulate the dispatching. 910*7c478bd9Sstevel@tonic-gate * 911*7c478bd9Sstevel@tonic-gate * Scan is stopped before the device is detached (in pm_detaching()) 912*7c478bd9Sstevel@tonic-gate * but it may get re-started during the post_detach processing if the 913*7c478bd9Sstevel@tonic-gate * driver fails to detach. 914*7c478bd9Sstevel@tonic-gate */ 915*7c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip) || 916*7c478bd9Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_DISPATCHED) || 917*7c478bd9Sstevel@tonic-gate !taskq_dispatch(system_taskq, pm_scan, (void *)dip, TQ_NOSLEEP)) { 918*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): attaching, pm_scan already " 919*7c478bd9Sstevel@tonic-gate "dispatched or dispatching failed\n", pmf, PM_DEVICE(dip))) 920*7c478bd9Sstevel@tonic-gate if (scanp->ps_scan_id) { 921*7c478bd9Sstevel@tonic-gate scanid = scanp->ps_scan_id; 922*7c478bd9Sstevel@tonic-gate scanp->ps_scan_id = 0; 923*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 924*7c478bd9Sstevel@tonic-gate (void) untimeout(scanid); 925*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 926*7c478bd9Sstevel@tonic-gate if (scanp->ps_scan_id) { 927*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): a competing " 928*7c478bd9Sstevel@tonic-gate "thread scheduled pm_rescan, scanid %lx\n", 929*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), 930*7c478bd9Sstevel@tonic-gate (ulong_t)scanp->ps_scan_id)) 931*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 932*7c478bd9Sstevel@tonic-gate return; 933*7c478bd9Sstevel@tonic-gate } 934*7c478bd9Sstevel@tonic-gate } 935*7c478bd9Sstevel@tonic-gate scanp->ps_scan_id = timeout(pm_rescan, (void *)dip, 936*7c478bd9Sstevel@tonic-gate (scanp->ps_idle_down ? pm_id_ticks : 937*7c478bd9Sstevel@tonic-gate (pm_min_scan * hz))); 938*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): scheduled next pm_rescan, " 939*7c478bd9Sstevel@tonic-gate "scanid %lx\n", pmf, PM_DEVICE(dip), 940*7c478bd9Sstevel@tonic-gate (ulong_t)scanp->ps_scan_id)) 941*7c478bd9Sstevel@tonic-gate } else { 942*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: dispatched pm_scan for %s@%s(%s#%d)\n", 943*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 944*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_DISPATCHED; 945*7c478bd9Sstevel@tonic-gate } 946*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 947*7c478bd9Sstevel@tonic-gate } 948*7c478bd9Sstevel@tonic-gate 949*7c478bd9Sstevel@tonic-gate void 950*7c478bd9Sstevel@tonic-gate pm_scan(void *arg) 951*7c478bd9Sstevel@tonic-gate { 952*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "scan") 953*7c478bd9Sstevel@tonic-gate dev_info_t *dip = (dev_info_t *)arg; 954*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp; 955*7c478bd9Sstevel@tonic-gate time_t nextscan; 956*7c478bd9Sstevel@tonic-gate 957*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 958*7c478bd9Sstevel@tonic-gate 959*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 960*7c478bd9Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 961*7c478bd9Sstevel@tonic-gate ASSERT(scanp && PM_GET_PM_INFO(dip)); 962*7c478bd9Sstevel@tonic-gate 963*7c478bd9Sstevel@tonic-gate if (pm_scans_disabled || !autopm_enabled || 964*7c478bd9Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP)) { 965*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags &= ~(PM_SCAN_AGAIN | PM_SCAN_DISPATCHED); 966*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 967*7c478bd9Sstevel@tonic-gate return; 968*7c478bd9Sstevel@tonic-gate } 969*7c478bd9Sstevel@tonic-gate 970*7c478bd9Sstevel@tonic-gate if (scanp->ps_idle_down) { 971*7c478bd9Sstevel@tonic-gate /* 972*7c478bd9Sstevel@tonic-gate * make sure we remember idledown was in affect until 973*7c478bd9Sstevel@tonic-gate * we've completed the scan 974*7c478bd9Sstevel@tonic-gate */ 975*7c478bd9Sstevel@tonic-gate PMID_SET_SCANS(scanp->ps_idle_down) 976*7c478bd9Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown starts " 977*7c478bd9Sstevel@tonic-gate "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down)) 978*7c478bd9Sstevel@tonic-gate } 979*7c478bd9Sstevel@tonic-gate 980*7c478bd9Sstevel@tonic-gate /* possible having two threads running pm_scan() */ 981*7c478bd9Sstevel@tonic-gate if (scanp->ps_scan_flags & PM_SCANNING) { 982*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_AGAIN; 983*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: scanning, will scan %s@%s(%s#%d) again\n", 984*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 985*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED; 986*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 987*7c478bd9Sstevel@tonic-gate return; 988*7c478bd9Sstevel@tonic-gate } 989*7c478bd9Sstevel@tonic-gate 990*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCANNING; 991*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED; 992*7c478bd9Sstevel@tonic-gate do { 993*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_AGAIN; 994*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 995*7c478bd9Sstevel@tonic-gate nextscan = pm_scan_dev(dip); 996*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 997*7c478bd9Sstevel@tonic-gate } while (scanp->ps_scan_flags & PM_SCAN_AGAIN); 998*7c478bd9Sstevel@tonic-gate 999*7c478bd9Sstevel@tonic-gate ASSERT(scanp->ps_scan_flags & PM_SCANNING); 1000*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCANNING; 1001*7c478bd9Sstevel@tonic-gate 1002*7c478bd9Sstevel@tonic-gate if (scanp->ps_idle_down) { 1003*7c478bd9Sstevel@tonic-gate scanp->ps_idle_down &= ~PMID_SCANS; 1004*7c478bd9Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown ends " 1005*7c478bd9Sstevel@tonic-gate "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down)) 1006*7c478bd9Sstevel@tonic-gate } 1007*7c478bd9Sstevel@tonic-gate 1008*7c478bd9Sstevel@tonic-gate /* schedule for next idle check */ 1009*7c478bd9Sstevel@tonic-gate if (nextscan != LONG_MAX) { 1010*7c478bd9Sstevel@tonic-gate if (nextscan > (LONG_MAX / hz)) 1011*7c478bd9Sstevel@tonic-gate nextscan = (LONG_MAX - 1) / hz; 1012*7c478bd9Sstevel@tonic-gate if (scanp->ps_scan_id) { 1013*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): while scanning " 1014*7c478bd9Sstevel@tonic-gate "another rescan scheduled scanid(%lx)\n", pmf, 1015*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), (ulong_t)scanp->ps_scan_id)) 1016*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1017*7c478bd9Sstevel@tonic-gate return; 1018*7c478bd9Sstevel@tonic-gate } else if (!(scanp->ps_scan_flags & PM_SCAN_STOP)) { 1019*7c478bd9Sstevel@tonic-gate scanp->ps_scan_id = timeout(pm_rescan, (void *)dip, 1020*7c478bd9Sstevel@tonic-gate (clock_t)(nextscan * hz)); 1021*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: nextscan for %s@%s(%s#%d) in " 1022*7c478bd9Sstevel@tonic-gate "%lx sec, scanid(%lx) \n", pmf, PM_DEVICE(dip), 1023*7c478bd9Sstevel@tonic-gate (ulong_t)nextscan, (ulong_t)scanp->ps_scan_id)) 1024*7c478bd9Sstevel@tonic-gate } 1025*7c478bd9Sstevel@tonic-gate } 1026*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1027*7c478bd9Sstevel@tonic-gate } 1028*7c478bd9Sstevel@tonic-gate 1029*7c478bd9Sstevel@tonic-gate void 1030*7c478bd9Sstevel@tonic-gate pm_get_timestamps(dev_info_t *dip, time_t *valuep) 1031*7c478bd9Sstevel@tonic-gate { 1032*7c478bd9Sstevel@tonic-gate int components = PM_NUMCMPTS(dip); 1033*7c478bd9Sstevel@tonic-gate int i; 1034*7c478bd9Sstevel@tonic-gate 1035*7c478bd9Sstevel@tonic-gate ASSERT(components > 0); 1036*7c478bd9Sstevel@tonic-gate PM_LOCK_BUSY(dip); /* so we get a consistent view */ 1037*7c478bd9Sstevel@tonic-gate for (i = 0; i < components; i++) { 1038*7c478bd9Sstevel@tonic-gate valuep[i] = PM_CP(dip, i)->pmc_timestamp; 1039*7c478bd9Sstevel@tonic-gate } 1040*7c478bd9Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 1041*7c478bd9Sstevel@tonic-gate } 1042*7c478bd9Sstevel@tonic-gate 1043*7c478bd9Sstevel@tonic-gate /* 1044*7c478bd9Sstevel@tonic-gate * Returns true if device needs to be kept up because it exported the 1045*7c478bd9Sstevel@tonic-gate * "no-involuntary-power-cycles" property or we're pretending it did (console 1046*7c478bd9Sstevel@tonic-gate * fb case) or it is an ancestor of such a device and has used up the "one 1047*7c478bd9Sstevel@tonic-gate * free cycle" allowed when all such leaf nodes have voluntarily powered down 1048*7c478bd9Sstevel@tonic-gate * upon detach 1049*7c478bd9Sstevel@tonic-gate */ 1050*7c478bd9Sstevel@tonic-gate int 1051*7c478bd9Sstevel@tonic-gate pm_noinvol(dev_info_t *dip) 1052*7c478bd9Sstevel@tonic-gate { 1053*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol") 1054*7c478bd9Sstevel@tonic-gate 1055*7c478bd9Sstevel@tonic-gate /* 1056*7c478bd9Sstevel@tonic-gate * This doesn't change over the life of a driver, so no locking needed 1057*7c478bd9Sstevel@tonic-gate */ 1058*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 1059*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB %s@%s(%s#%d)\n", 1060*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 1061*7c478bd9Sstevel@tonic-gate return (1); 1062*7c478bd9Sstevel@tonic-gate } 1063*7c478bd9Sstevel@tonic-gate /* 1064*7c478bd9Sstevel@tonic-gate * Not an issue if no such kids 1065*7c478bd9Sstevel@tonic-gate */ 1066*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_noinvolpm == 0) { 1067*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1068*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd != 0) { 1069*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = dip; 1070*7c478bd9Sstevel@tonic-gate do { 1071*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d) noinvol %d " 1072*7c478bd9Sstevel@tonic-gate "volpmd %d\n", pmf, PM_DEVICE(pdip), 1073*7c478bd9Sstevel@tonic-gate DEVI(pdip)->devi_pm_noinvolpm, 1074*7c478bd9Sstevel@tonic-gate DEVI(pdip)->devi_pm_volpmd)) 1075*7c478bd9Sstevel@tonic-gate pdip = ddi_get_parent(pdip); 1076*7c478bd9Sstevel@tonic-gate } while (pdip); 1077*7c478bd9Sstevel@tonic-gate } 1078*7c478bd9Sstevel@tonic-gate #endif 1079*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_volpmd == 0); 1080*7c478bd9Sstevel@tonic-gate return (0); 1081*7c478bd9Sstevel@tonic-gate } 1082*7c478bd9Sstevel@tonic-gate 1083*7c478bd9Sstevel@tonic-gate /* 1084*7c478bd9Sstevel@tonic-gate * Since we now maintain the counts correct at every node, we no longer 1085*7c478bd9Sstevel@tonic-gate * need to look up the tree. An ancestor cannot use up the free cycle 1086*7c478bd9Sstevel@tonic-gate * without the children getting their counts adjusted. 1087*7c478bd9Sstevel@tonic-gate */ 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1090*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd) 1091*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s@%s(%s#%d)\n", pmf, 1092*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, DEVI(dip)->devi_pm_volpmd, 1093*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 1094*7c478bd9Sstevel@tonic-gate #endif 1095*7c478bd9Sstevel@tonic-gate return (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd); 1096*7c478bd9Sstevel@tonic-gate } 1097*7c478bd9Sstevel@tonic-gate 1098*7c478bd9Sstevel@tonic-gate /* 1099*7c478bd9Sstevel@tonic-gate * This function performs the actual scanning of the device. 1100*7c478bd9Sstevel@tonic-gate * It attempts to power off the indicated device's components if they have 1101*7c478bd9Sstevel@tonic-gate * been idle and other restrictions are met. 1102*7c478bd9Sstevel@tonic-gate * pm_scan_dev calculates and returns when the next scan should happen for 1103*7c478bd9Sstevel@tonic-gate * this device. 1104*7c478bd9Sstevel@tonic-gate */ 1105*7c478bd9Sstevel@tonic-gate time_t 1106*7c478bd9Sstevel@tonic-gate pm_scan_dev(dev_info_t *dip) 1107*7c478bd9Sstevel@tonic-gate { 1108*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "scan_dev") 1109*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp; 1110*7c478bd9Sstevel@tonic-gate time_t *timestamp, idletime, now, thresh; 1111*7c478bd9Sstevel@tonic-gate time_t timeleft = 0; 1112*7c478bd9Sstevel@tonic-gate int i, nxtpwr, curpwr, pwrndx, unused; 1113*7c478bd9Sstevel@tonic-gate size_t size; 1114*7c478bd9Sstevel@tonic-gate pm_component_t *cp; 1115*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 1116*7c478bd9Sstevel@tonic-gate int circ; 1117*7c478bd9Sstevel@tonic-gate static int cur_threshold(dev_info_t *, int); 1118*7c478bd9Sstevel@tonic-gate static int pm_next_lower_power(pm_component_t *, int); 1119*7c478bd9Sstevel@tonic-gate 1120*7c478bd9Sstevel@tonic-gate /* 1121*7c478bd9Sstevel@tonic-gate * skip attaching device 1122*7c478bd9Sstevel@tonic-gate */ 1123*7c478bd9Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 1124*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) is attaching, timeleft(%lx)\n", 1125*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_min_scan)) 1126*7c478bd9Sstevel@tonic-gate return (pm_min_scan); 1127*7c478bd9Sstevel@tonic-gate } 1128*7c478bd9Sstevel@tonic-gate 1129*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 1130*7c478bd9Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 1131*7c478bd9Sstevel@tonic-gate ASSERT(scanp && PM_GET_PM_INFO(dip)); 1132*7c478bd9Sstevel@tonic-gate 1133*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip))) 1134*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): kuc is %d\n", pmf, PM_DEVICE(dip), 1135*7c478bd9Sstevel@tonic-gate PM_KUC(dip))) 1136*7c478bd9Sstevel@tonic-gate 1137*7c478bd9Sstevel@tonic-gate /* no scan under the following conditions */ 1138*7c478bd9Sstevel@tonic-gate if (pm_scans_disabled || !autopm_enabled || 1139*7c478bd9Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP) || 1140*7c478bd9Sstevel@tonic-gate (PM_KUC(dip) != 0) || 1141*7c478bd9Sstevel@tonic-gate PM_ISDIRECT(dip) || pm_noinvol(dip)) { 1142*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1143*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END, %s@%s(%s#%d)] no scan, " 1144*7c478bd9Sstevel@tonic-gate "scan_disabled(%d), apm_enabled(%d), kuc(%d), " 1145*7c478bd9Sstevel@tonic-gate "%s directpm, %s pm_noinvol\n", pmf, PM_DEVICE(dip), 1146*7c478bd9Sstevel@tonic-gate pm_scans_disabled, autopm_enabled, PM_KUC(dip), 1147*7c478bd9Sstevel@tonic-gate PM_ISDIRECT(dip) ? "is" : "is not", 1148*7c478bd9Sstevel@tonic-gate pm_noinvol(dip) ? "is" : "is not")) 1149*7c478bd9Sstevel@tonic-gate return (LONG_MAX); 1150*7c478bd9Sstevel@tonic-gate } 1151*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1152*7c478bd9Sstevel@tonic-gate 1153*7c478bd9Sstevel@tonic-gate if (!ndi_devi_tryenter(pdip, &circ)) { 1154*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) can't hold pdip", 1155*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(pdip))) 1156*7c478bd9Sstevel@tonic-gate return ((time_t)1); 1157*7c478bd9Sstevel@tonic-gate } 1158*7c478bd9Sstevel@tonic-gate now = gethrestime_sec(); 1159*7c478bd9Sstevel@tonic-gate size = PM_NUMCMPTS(dip) * sizeof (time_t); 1160*7c478bd9Sstevel@tonic-gate timestamp = kmem_alloc(size, KM_SLEEP); 1161*7c478bd9Sstevel@tonic-gate pm_get_timestamps(dip, timestamp); 1162*7c478bd9Sstevel@tonic-gate 1163*7c478bd9Sstevel@tonic-gate /* 1164*7c478bd9Sstevel@tonic-gate * Since we removed support for backwards compatible devices, 1165*7c478bd9Sstevel@tonic-gate * (see big comment at top of file) 1166*7c478bd9Sstevel@tonic-gate * it is no longer required to deal with component 0 last. 1167*7c478bd9Sstevel@tonic-gate */ 1168*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 1169*7c478bd9Sstevel@tonic-gate /* 1170*7c478bd9Sstevel@tonic-gate * If already off (an optimization, perhaps) 1171*7c478bd9Sstevel@tonic-gate */ 1172*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 1173*7c478bd9Sstevel@tonic-gate pwrndx = cp->pmc_cur_pwr; 1174*7c478bd9Sstevel@tonic-gate curpwr = (pwrndx == PM_LEVEL_UNKNOWN) ? 1175*7c478bd9Sstevel@tonic-gate PM_LEVEL_UNKNOWN : 1176*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[pwrndx]; 1177*7c478bd9Sstevel@tonic-gate 1178*7c478bd9Sstevel@tonic-gate if (pwrndx == 0) { 1179*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d off or " 1180*7c478bd9Sstevel@tonic-gate "lowest\n", pmf, PM_DEVICE(dip), i)) 1181*7c478bd9Sstevel@tonic-gate /* skip device if off or at its lowest */ 1182*7c478bd9Sstevel@tonic-gate continue; 1183*7c478bd9Sstevel@tonic-gate } 1184*7c478bd9Sstevel@tonic-gate 1185*7c478bd9Sstevel@tonic-gate thresh = cur_threshold(dip, i); /* comp i threshold */ 1186*7c478bd9Sstevel@tonic-gate if ((timestamp[i] == 0) || (cp->pmc_busycount > 0)) { 1187*7c478bd9Sstevel@tonic-gate /* were busy or newly became busy by another thread */ 1188*7c478bd9Sstevel@tonic-gate if (timeleft == 0) 1189*7c478bd9Sstevel@tonic-gate timeleft = max(thresh, pm_min_scan); 1190*7c478bd9Sstevel@tonic-gate else 1191*7c478bd9Sstevel@tonic-gate timeleft = min( 1192*7c478bd9Sstevel@tonic-gate timeleft, max(thresh, pm_min_scan)); 1193*7c478bd9Sstevel@tonic-gate continue; 1194*7c478bd9Sstevel@tonic-gate } 1195*7c478bd9Sstevel@tonic-gate 1196*7c478bd9Sstevel@tonic-gate idletime = now - timestamp[i]; /* idle time */ 1197*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d idle time %lx\n", 1198*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), i, idletime)) 1199*7c478bd9Sstevel@tonic-gate if (idletime >= thresh || PM_IS_PID(dip)) { 1200*7c478bd9Sstevel@tonic-gate nxtpwr = pm_next_lower_power(cp, pwrndx); 1201*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, %d->%d\n", 1202*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), i, curpwr, nxtpwr)) 1203*7c478bd9Sstevel@tonic-gate if (pm_set_power(dip, i, nxtpwr, PM_LEVEL_DOWNONLY, 1204*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_FAIL, 1, &unused) != DDI_SUCCESS && 1205*7c478bd9Sstevel@tonic-gate PM_CURPOWER(dip, i) != nxtpwr) { 1206*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, " 1207*7c478bd9Sstevel@tonic-gate "%d->%d Failed\n", pmf, PM_DEVICE(dip), 1208*7c478bd9Sstevel@tonic-gate i, curpwr, nxtpwr)) 1209*7c478bd9Sstevel@tonic-gate timeleft = pm_min_scan; 1210*7c478bd9Sstevel@tonic-gate continue; 1211*7c478bd9Sstevel@tonic-gate } else { 1212*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, " 1213*7c478bd9Sstevel@tonic-gate "%d->%d, GOOD curpwr %d\n", pmf, 1214*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), i, curpwr, nxtpwr, 1215*7c478bd9Sstevel@tonic-gate cur_power(cp))) 1216*7c478bd9Sstevel@tonic-gate 1217*7c478bd9Sstevel@tonic-gate if (nxtpwr == 0) /* component went off */ 1218*7c478bd9Sstevel@tonic-gate continue; 1219*7c478bd9Sstevel@tonic-gate 1220*7c478bd9Sstevel@tonic-gate /* 1221*7c478bd9Sstevel@tonic-gate * scan to next lower level 1222*7c478bd9Sstevel@tonic-gate */ 1223*7c478bd9Sstevel@tonic-gate if (timeleft == 0) 1224*7c478bd9Sstevel@tonic-gate timeleft = max( 1225*7c478bd9Sstevel@tonic-gate 1, cur_threshold(dip, i)); 1226*7c478bd9Sstevel@tonic-gate else 1227*7c478bd9Sstevel@tonic-gate timeleft = min(timeleft, 1228*7c478bd9Sstevel@tonic-gate max(1, cur_threshold(dip, i))); 1229*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, " 1230*7c478bd9Sstevel@tonic-gate "timeleft(%lx)\n", pmf, PM_DEVICE(dip), 1231*7c478bd9Sstevel@tonic-gate i, timeleft)) 1232*7c478bd9Sstevel@tonic-gate } 1233*7c478bd9Sstevel@tonic-gate } else { /* comp not idle long enough */ 1234*7c478bd9Sstevel@tonic-gate if (timeleft == 0) 1235*7c478bd9Sstevel@tonic-gate timeleft = thresh - idletime; 1236*7c478bd9Sstevel@tonic-gate else 1237*7c478bd9Sstevel@tonic-gate timeleft = min(timeleft, (thresh - idletime)); 1238*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, timeleft=" 1239*7c478bd9Sstevel@tonic-gate "%lx\n", pmf, PM_DEVICE(dip), i, timeleft)) 1240*7c478bd9Sstevel@tonic-gate } 1241*7c478bd9Sstevel@tonic-gate } 1242*7c478bd9Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 1243*7c478bd9Sstevel@tonic-gate kmem_free(timestamp, size); 1244*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] timeleft(%lx)\n", pmf, 1245*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), timeleft)) 1246*7c478bd9Sstevel@tonic-gate 1247*7c478bd9Sstevel@tonic-gate /* 1248*7c478bd9Sstevel@tonic-gate * if components are already at lowest level, timeleft is left 0 1249*7c478bd9Sstevel@tonic-gate */ 1250*7c478bd9Sstevel@tonic-gate return ((timeleft == 0) ? LONG_MAX : timeleft); 1251*7c478bd9Sstevel@tonic-gate } 1252*7c478bd9Sstevel@tonic-gate 1253*7c478bd9Sstevel@tonic-gate /* 1254*7c478bd9Sstevel@tonic-gate * pm_scan_stop - cancel scheduled pm_rescan, 1255*7c478bd9Sstevel@tonic-gate * wait for termination of dispatched pm_scan thread 1256*7c478bd9Sstevel@tonic-gate * and active pm_scan_dev thread. 1257*7c478bd9Sstevel@tonic-gate */ 1258*7c478bd9Sstevel@tonic-gate void 1259*7c478bd9Sstevel@tonic-gate pm_scan_stop(dev_info_t *dip) 1260*7c478bd9Sstevel@tonic-gate { 1261*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "scan_stop") 1262*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp; 1263*7c478bd9Sstevel@tonic-gate timeout_id_t scanid; 1264*7c478bd9Sstevel@tonic-gate 1265*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip))) 1266*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 1267*7c478bd9Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 1268*7c478bd9Sstevel@tonic-gate if (!scanp) { 1269*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] scan not initialized\n", 1270*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 1271*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1272*7c478bd9Sstevel@tonic-gate return; 1273*7c478bd9Sstevel@tonic-gate } 1274*7c478bd9Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_STOP; 1275*7c478bd9Sstevel@tonic-gate 1276*7c478bd9Sstevel@tonic-gate /* cancel scheduled scan taskq */ 1277*7c478bd9Sstevel@tonic-gate while (scanp->ps_scan_id) { 1278*7c478bd9Sstevel@tonic-gate scanid = scanp->ps_scan_id; 1279*7c478bd9Sstevel@tonic-gate scanp->ps_scan_id = 0; 1280*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1281*7c478bd9Sstevel@tonic-gate (void) untimeout(scanid); 1282*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 1283*7c478bd9Sstevel@tonic-gate } 1284*7c478bd9Sstevel@tonic-gate 1285*7c478bd9Sstevel@tonic-gate while (scanp->ps_scan_flags & (PM_SCANNING | PM_SCAN_DISPATCHED)) { 1286*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1287*7c478bd9Sstevel@tonic-gate delay(1); 1288*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 1289*7c478bd9Sstevel@tonic-gate } 1290*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1291*7c478bd9Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip))) 1292*7c478bd9Sstevel@tonic-gate } 1293*7c478bd9Sstevel@tonic-gate 1294*7c478bd9Sstevel@tonic-gate int 1295*7c478bd9Sstevel@tonic-gate pm_scan_stop_walk(dev_info_t *dip, void *arg) 1296*7c478bd9Sstevel@tonic-gate { 1297*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 1298*7c478bd9Sstevel@tonic-gate 1299*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_SCAN(dip)) 1300*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 1301*7c478bd9Sstevel@tonic-gate ASSERT(!PM_ISBC(dip)); 1302*7c478bd9Sstevel@tonic-gate pm_scan_stop(dip); 1303*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 1304*7c478bd9Sstevel@tonic-gate } 1305*7c478bd9Sstevel@tonic-gate 1306*7c478bd9Sstevel@tonic-gate /* 1307*7c478bd9Sstevel@tonic-gate * Converts a power level value to its index 1308*7c478bd9Sstevel@tonic-gate */ 1309*7c478bd9Sstevel@tonic-gate static int 1310*7c478bd9Sstevel@tonic-gate power_val_to_index(pm_component_t *cp, int val) 1311*7c478bd9Sstevel@tonic-gate { 1312*7c478bd9Sstevel@tonic-gate int limit, i, *ip; 1313*7c478bd9Sstevel@tonic-gate 1314*7c478bd9Sstevel@tonic-gate ASSERT(val != PM_LEVEL_UPONLY && val != PM_LEVEL_DOWNONLY && 1315*7c478bd9Sstevel@tonic-gate val != PM_LEVEL_EXACT); 1316*7c478bd9Sstevel@tonic-gate /* convert power value into index (i) */ 1317*7c478bd9Sstevel@tonic-gate limit = cp->pmc_comp.pmc_numlevels; 1318*7c478bd9Sstevel@tonic-gate ip = cp->pmc_comp.pmc_lvals; 1319*7c478bd9Sstevel@tonic-gate for (i = 0; i < limit; i++) 1320*7c478bd9Sstevel@tonic-gate if (val == *ip++) 1321*7c478bd9Sstevel@tonic-gate return (i); 1322*7c478bd9Sstevel@tonic-gate return (-1); 1323*7c478bd9Sstevel@tonic-gate } 1324*7c478bd9Sstevel@tonic-gate 1325*7c478bd9Sstevel@tonic-gate /* 1326*7c478bd9Sstevel@tonic-gate * Converts a numeric power level to a printable string 1327*7c478bd9Sstevel@tonic-gate */ 1328*7c478bd9Sstevel@tonic-gate static char * 1329*7c478bd9Sstevel@tonic-gate power_val_to_string(pm_component_t *cp, int val) 1330*7c478bd9Sstevel@tonic-gate { 1331*7c478bd9Sstevel@tonic-gate int index; 1332*7c478bd9Sstevel@tonic-gate 1333*7c478bd9Sstevel@tonic-gate if (val == PM_LEVEL_UPONLY) 1334*7c478bd9Sstevel@tonic-gate return ("<UPONLY>"); 1335*7c478bd9Sstevel@tonic-gate 1336*7c478bd9Sstevel@tonic-gate if (val == PM_LEVEL_UNKNOWN || 1337*7c478bd9Sstevel@tonic-gate (index = power_val_to_index(cp, val)) == -1) 1338*7c478bd9Sstevel@tonic-gate return ("<LEVEL_UNKNOWN>"); 1339*7c478bd9Sstevel@tonic-gate 1340*7c478bd9Sstevel@tonic-gate return (cp->pmc_comp.pmc_lnames[index]); 1341*7c478bd9Sstevel@tonic-gate } 1342*7c478bd9Sstevel@tonic-gate 1343*7c478bd9Sstevel@tonic-gate /* 1344*7c478bd9Sstevel@tonic-gate * Return true if this node has been claimed by a ppm. 1345*7c478bd9Sstevel@tonic-gate */ 1346*7c478bd9Sstevel@tonic-gate static int 1347*7c478bd9Sstevel@tonic-gate pm_ppm_claimed(dev_info_t *dip) 1348*7c478bd9Sstevel@tonic-gate { 1349*7c478bd9Sstevel@tonic-gate return (PPM(dip) != NULL); 1350*7c478bd9Sstevel@tonic-gate } 1351*7c478bd9Sstevel@tonic-gate 1352*7c478bd9Sstevel@tonic-gate /* 1353*7c478bd9Sstevel@tonic-gate * A node which was voluntarily power managed has just used up its "free cycle" 1354*7c478bd9Sstevel@tonic-gate * and need is volpmd field cleared, and the same done to all its descendents 1355*7c478bd9Sstevel@tonic-gate */ 1356*7c478bd9Sstevel@tonic-gate static void 1357*7c478bd9Sstevel@tonic-gate pm_clear_volpm_dip(dev_info_t *dip) 1358*7c478bd9Sstevel@tonic-gate { 1359*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "clear_volpm_dip") 1360*7c478bd9Sstevel@tonic-gate 1361*7c478bd9Sstevel@tonic-gate if (dip == NULL) 1362*7c478bd9Sstevel@tonic-gate return; 1363*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm from %s@%s(%s#%d)\n", pmf, 1364*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 1365*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0; 1366*7c478bd9Sstevel@tonic-gate for (dip = ddi_get_child(dip); dip; dip = ddi_get_next_sibling(dip)) { 1367*7c478bd9Sstevel@tonic-gate pm_clear_volpm_dip(dip); 1368*7c478bd9Sstevel@tonic-gate } 1369*7c478bd9Sstevel@tonic-gate } 1370*7c478bd9Sstevel@tonic-gate 1371*7c478bd9Sstevel@tonic-gate /* 1372*7c478bd9Sstevel@tonic-gate * A node which was voluntarily power managed has used up the "free cycles" 1373*7c478bd9Sstevel@tonic-gate * for the subtree that it is the root of. Scan through the list of detached 1374*7c478bd9Sstevel@tonic-gate * nodes and adjust the counts of any that are descendents of the node. 1375*7c478bd9Sstevel@tonic-gate */ 1376*7c478bd9Sstevel@tonic-gate static void 1377*7c478bd9Sstevel@tonic-gate pm_clear_volpm_list(dev_info_t *dip) 1378*7c478bd9Sstevel@tonic-gate { 1379*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "clear_volpm_list") 1380*7c478bd9Sstevel@tonic-gate char *pathbuf; 1381*7c478bd9Sstevel@tonic-gate size_t len; 1382*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip; 1383*7c478bd9Sstevel@tonic-gate 1384*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1385*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 1386*7c478bd9Sstevel@tonic-gate len = strlen(pathbuf); 1387*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm list %s\n", pmf, pathbuf)) 1388*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 1389*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 1390*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm: ni_path %s\n", pmf, 1391*7c478bd9Sstevel@tonic-gate ip->ni_path)) 1392*7c478bd9Sstevel@tonic-gate if (strncmp(pathbuf, ip->ni_path, len) == 0 && 1393*7c478bd9Sstevel@tonic-gate ip->ni_path[len] == '/') { 1394*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm: %s\n", pmf, 1395*7c478bd9Sstevel@tonic-gate ip->ni_path)) 1396*7c478bd9Sstevel@tonic-gate ip->ni_volpmd = 0; 1397*7c478bd9Sstevel@tonic-gate ip->ni_wasvolpmd = 0; 1398*7c478bd9Sstevel@tonic-gate } 1399*7c478bd9Sstevel@tonic-gate } 1400*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 1401*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 1402*7c478bd9Sstevel@tonic-gate } 1403*7c478bd9Sstevel@tonic-gate 1404*7c478bd9Sstevel@tonic-gate /* 1405*7c478bd9Sstevel@tonic-gate * Powers a device, suspending or resuming the driver if it is a backward 1406*7c478bd9Sstevel@tonic-gate * compatible device, calling into ppm to change power level. 1407*7c478bd9Sstevel@tonic-gate * Called with the component's power lock held. 1408*7c478bd9Sstevel@tonic-gate */ 1409*7c478bd9Sstevel@tonic-gate static int 1410*7c478bd9Sstevel@tonic-gate power_dev(dev_info_t *dip, int comp, int level, int old_level, 1411*7c478bd9Sstevel@tonic-gate pm_canblock_t canblock, pm_ppm_devlist_t **devlist) 1412*7c478bd9Sstevel@tonic-gate { 1413*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "power_dev") 1414*7c478bd9Sstevel@tonic-gate power_req_t power_req; 1415*7c478bd9Sstevel@tonic-gate int power_op_ret; /* DDI_SUCCESS or DDI_FAILURE */ 1416*7c478bd9Sstevel@tonic-gate int resume_needed = 0; 1417*7c478bd9Sstevel@tonic-gate int suspended = 0; 1418*7c478bd9Sstevel@tonic-gate int result; 1419*7c478bd9Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, comp); 1420*7c478bd9Sstevel@tonic-gate int bc = PM_ISBC(dip); 1421*7c478bd9Sstevel@tonic-gate int pm_all_components_off(dev_info_t *); 1422*7c478bd9Sstevel@tonic-gate int clearvolpmd = 0; 1423*7c478bd9Sstevel@tonic-gate char pathbuf[MAXNAMELEN]; 1424*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1425*7c478bd9Sstevel@tonic-gate char *ppmname, *ppmaddr; 1426*7c478bd9Sstevel@tonic-gate #endif 1427*7c478bd9Sstevel@tonic-gate /* 1428*7c478bd9Sstevel@tonic-gate * If this is comp 0 of a backwards compat device and we are 1429*7c478bd9Sstevel@tonic-gate * going to take the power away, we need to detach it with 1430*7c478bd9Sstevel@tonic-gate * DDI_PM_SUSPEND command. 1431*7c478bd9Sstevel@tonic-gate */ 1432*7c478bd9Sstevel@tonic-gate if (bc && comp == 0 && POWERING_OFF(old_level, level)) { 1433*7c478bd9Sstevel@tonic-gate if (devi_detach(dip, DDI_PM_SUSPEND) != DDI_SUCCESS) { 1434*7c478bd9Sstevel@tonic-gate /* We could not suspend before turning cmpt zero off */ 1435*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: could not suspend %s@%s(%s#%d)\n", 1436*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 1437*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1438*7c478bd9Sstevel@tonic-gate } else { 1439*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_SUSPENDED; 1440*7c478bd9Sstevel@tonic-gate suspended++; 1441*7c478bd9Sstevel@tonic-gate } 1442*7c478bd9Sstevel@tonic-gate } 1443*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_SET_POWER; 1444*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.who = dip; 1445*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.cmpt = comp; 1446*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.old_level = old_level; 1447*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.new_level = level; 1448*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.canblock = canblock; 1449*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.cookie = NULL; 1450*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1451*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { 1452*7c478bd9Sstevel@tonic-gate ppmname = PM_NAME(PPM(dip)); 1453*7c478bd9Sstevel@tonic-gate ppmaddr = PM_ADDR(PPM(dip)); 1454*7c478bd9Sstevel@tonic-gate 1455*7c478bd9Sstevel@tonic-gate } else { 1456*7c478bd9Sstevel@tonic-gate ppmname = "noppm"; 1457*7c478bd9Sstevel@tonic-gate ppmaddr = "0"; 1458*7c478bd9Sstevel@tonic-gate } 1459*7c478bd9Sstevel@tonic-gate PMD(PMD_PPM, ("%s: %s@%s(%s#%d):%s[%d] %s (%d) -> %s (%d) via %s@%s\n", 1460*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), cp->pmc_comp.pmc_name, comp, 1461*7c478bd9Sstevel@tonic-gate power_val_to_string(cp, old_level), old_level, 1462*7c478bd9Sstevel@tonic-gate power_val_to_string(cp, level), level, ppmname, ppmaddr)) 1463*7c478bd9Sstevel@tonic-gate #endif 1464*7c478bd9Sstevel@tonic-gate /* 1465*7c478bd9Sstevel@tonic-gate * If non-bc noinvolpm device is turning first comp on, or noinvolpm 1466*7c478bd9Sstevel@tonic-gate * bc device comp 0 is powering on, then we count it as a power cycle 1467*7c478bd9Sstevel@tonic-gate * against its voluntary count. 1468*7c478bd9Sstevel@tonic-gate */ 1469*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd && 1470*7c478bd9Sstevel@tonic-gate (!bc && pm_all_components_off(dip) && level != 0) || 1471*7c478bd9Sstevel@tonic-gate (bc && comp == 0 && POWERING_ON(old_level, level))) 1472*7c478bd9Sstevel@tonic-gate clearvolpmd = 1; 1473*7c478bd9Sstevel@tonic-gate if ((power_op_ret = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 1474*7c478bd9Sstevel@tonic-gate &power_req, &result)) == DDI_SUCCESS) { 1475*7c478bd9Sstevel@tonic-gate /* 1476*7c478bd9Sstevel@tonic-gate * Now do involuntary pm accounting; If we've just cycled power 1477*7c478bd9Sstevel@tonic-gate * on a voluntarily pm'd node, and by inference on its entire 1478*7c478bd9Sstevel@tonic-gate * subtree, we need to set the subtree (including those nodes 1479*7c478bd9Sstevel@tonic-gate * already detached) volpmd counts to 0, and subtract out the 1480*7c478bd9Sstevel@tonic-gate * value of the current node's volpmd count from the ancestors 1481*7c478bd9Sstevel@tonic-gate */ 1482*7c478bd9Sstevel@tonic-gate if (clearvolpmd) { 1483*7c478bd9Sstevel@tonic-gate int volpmd = DEVI(dip)->devi_pm_volpmd; 1484*7c478bd9Sstevel@tonic-gate pm_clear_volpm_dip(dip); 1485*7c478bd9Sstevel@tonic-gate pm_clear_volpm_list(dip); 1486*7c478bd9Sstevel@tonic-gate if (volpmd) { 1487*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 1488*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_POWER, 1489*7c478bd9Sstevel@tonic-gate volpmd, 0, pathbuf, dip); 1490*7c478bd9Sstevel@tonic-gate } 1491*7c478bd9Sstevel@tonic-gate } 1492*7c478bd9Sstevel@tonic-gate } else { 1493*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't set comp %d (%s) of %s@%s(%s#%d) " 1494*7c478bd9Sstevel@tonic-gate "to level %d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, 1495*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), level, power_val_to_string(cp, level))) 1496*7c478bd9Sstevel@tonic-gate } 1497*7c478bd9Sstevel@tonic-gate /* 1498*7c478bd9Sstevel@tonic-gate * If some other devices were also powered up (e.g. other cpus in 1499*7c478bd9Sstevel@tonic-gate * the same domain) return a pointer to that list 1500*7c478bd9Sstevel@tonic-gate */ 1501*7c478bd9Sstevel@tonic-gate if (devlist) { 1502*7c478bd9Sstevel@tonic-gate *devlist = (pm_ppm_devlist_t *) 1503*7c478bd9Sstevel@tonic-gate power_req.req.ppm_set_power_req.cookie; 1504*7c478bd9Sstevel@tonic-gate } 1505*7c478bd9Sstevel@tonic-gate /* 1506*7c478bd9Sstevel@tonic-gate * We will have to resume the device if the device is backwards compat 1507*7c478bd9Sstevel@tonic-gate * device and either of the following is true: 1508*7c478bd9Sstevel@tonic-gate * -This is comp 0 and we have successfully powered it up 1509*7c478bd9Sstevel@tonic-gate * -This is comp 0 and we have failed to power it down. Resume is 1510*7c478bd9Sstevel@tonic-gate * needed because we have suspended it above 1511*7c478bd9Sstevel@tonic-gate */ 1512*7c478bd9Sstevel@tonic-gate 1513*7c478bd9Sstevel@tonic-gate if (bc && comp == 0) { 1514*7c478bd9Sstevel@tonic-gate ASSERT(PM_ISDIRECT(dip) || DEVI_IS_DETACHING(dip)); 1515*7c478bd9Sstevel@tonic-gate if (power_op_ret == DDI_SUCCESS) { 1516*7c478bd9Sstevel@tonic-gate if (POWERING_ON(old_level, level)) { 1517*7c478bd9Sstevel@tonic-gate /* 1518*7c478bd9Sstevel@tonic-gate * It must be either suspended or resumed 1519*7c478bd9Sstevel@tonic-gate * via pm_power_has_changed path 1520*7c478bd9Sstevel@tonic-gate */ 1521*7c478bd9Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags & 1522*7c478bd9Sstevel@tonic-gate PMC_SUSPENDED) || 1523*7c478bd9Sstevel@tonic-gate (PM_CP(dip, comp)->pmc_flags & 1524*7c478bd9Sstevel@tonic-gate PM_PHC_WHILE_SET_POWER)); 1525*7c478bd9Sstevel@tonic-gate 1526*7c478bd9Sstevel@tonic-gate resume_needed = suspended; 1527*7c478bd9Sstevel@tonic-gate } 1528*7c478bd9Sstevel@tonic-gate } else { 1529*7c478bd9Sstevel@tonic-gate if (POWERING_OFF(old_level, level)) { 1530*7c478bd9Sstevel@tonic-gate /* 1531*7c478bd9Sstevel@tonic-gate * It must be either suspended or resumed 1532*7c478bd9Sstevel@tonic-gate * via pm_power_has_changed path 1533*7c478bd9Sstevel@tonic-gate */ 1534*7c478bd9Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags & 1535*7c478bd9Sstevel@tonic-gate PMC_SUSPENDED) || 1536*7c478bd9Sstevel@tonic-gate (PM_CP(dip, comp)->pmc_flags & 1537*7c478bd9Sstevel@tonic-gate PM_PHC_WHILE_SET_POWER)); 1538*7c478bd9Sstevel@tonic-gate 1539*7c478bd9Sstevel@tonic-gate resume_needed = suspended; 1540*7c478bd9Sstevel@tonic-gate } 1541*7c478bd9Sstevel@tonic-gate } 1542*7c478bd9Sstevel@tonic-gate } 1543*7c478bd9Sstevel@tonic-gate if (resume_needed) { 1544*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED); 1545*7c478bd9Sstevel@tonic-gate /* ppm is not interested in DDI_PM_RESUME */ 1546*7c478bd9Sstevel@tonic-gate if ((power_op_ret = devi_attach(dip, DDI_PM_RESUME)) == 1547*7c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 1548*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED; 1549*7c478bd9Sstevel@tonic-gate } else 1550*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!pm: Can't resume %s@%s(%s#%d)", 1551*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip)); 1552*7c478bd9Sstevel@tonic-gate } 1553*7c478bd9Sstevel@tonic-gate return (power_op_ret); 1554*7c478bd9Sstevel@tonic-gate } 1555*7c478bd9Sstevel@tonic-gate 1556*7c478bd9Sstevel@tonic-gate /* 1557*7c478bd9Sstevel@tonic-gate * Return true if we are the owner or a borrower of the devi lock. See 1558*7c478bd9Sstevel@tonic-gate * pm_lock_power_single() about borrowing the lock. 1559*7c478bd9Sstevel@tonic-gate */ 1560*7c478bd9Sstevel@tonic-gate static int 1561*7c478bd9Sstevel@tonic-gate pm_devi_lock_held(dev_info_t *dip) 1562*7c478bd9Sstevel@tonic-gate { 1563*7c478bd9Sstevel@tonic-gate lock_loan_t *cur; 1564*7c478bd9Sstevel@tonic-gate 1565*7c478bd9Sstevel@tonic-gate if (DEVI_BUSY_OWNED(dip)) 1566*7c478bd9Sstevel@tonic-gate return (1); 1567*7c478bd9Sstevel@tonic-gate 1568*7c478bd9Sstevel@tonic-gate /* return false if no locks borrowed */ 1569*7c478bd9Sstevel@tonic-gate if (lock_loan_head.pmlk_next == NULL) 1570*7c478bd9Sstevel@tonic-gate return (0); 1571*7c478bd9Sstevel@tonic-gate 1572*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 1573*7c478bd9Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */ 1574*7c478bd9Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next) 1575*7c478bd9Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 1576*7c478bd9Sstevel@tonic-gate break; 1577*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 1578*7c478bd9Sstevel@tonic-gate 1579*7c478bd9Sstevel@tonic-gate return (cur != NULL && cur->pmlk_lender == DEVI(dip)->devi_busy_thread); 1580*7c478bd9Sstevel@tonic-gate } 1581*7c478bd9Sstevel@tonic-gate 1582*7c478bd9Sstevel@tonic-gate /* 1583*7c478bd9Sstevel@tonic-gate * pm_set_power: adjusts power level of device. Assumes device is power 1584*7c478bd9Sstevel@tonic-gate * manageable & component exists. 1585*7c478bd9Sstevel@tonic-gate * 1586*7c478bd9Sstevel@tonic-gate * Cases which require us to bring up devices we keep up ("wekeepups") for 1587*7c478bd9Sstevel@tonic-gate * backwards compatible devices: 1588*7c478bd9Sstevel@tonic-gate * component 0 is off and we're bringing it up from 0 1589*7c478bd9Sstevel@tonic-gate * bring up wekeepup first 1590*7c478bd9Sstevel@tonic-gate * and recursively when component 0 is off and we bring some other 1591*7c478bd9Sstevel@tonic-gate * component up from 0 1592*7c478bd9Sstevel@tonic-gate * For devices which are not backward compatible, our dependency notion is much 1593*7c478bd9Sstevel@tonic-gate * simpler. Unless all components are off, then wekeeps must be on. 1594*7c478bd9Sstevel@tonic-gate * We don't treat component 0 differently. 1595*7c478bd9Sstevel@tonic-gate * Canblock tells how to deal with a direct pm'd device. 1596*7c478bd9Sstevel@tonic-gate * Scan arg tells us if we were called from scan, in which case we don't need 1597*7c478bd9Sstevel@tonic-gate * to go back to the root node and walk down to change power. 1598*7c478bd9Sstevel@tonic-gate */ 1599*7c478bd9Sstevel@tonic-gate int 1600*7c478bd9Sstevel@tonic-gate pm_set_power(dev_info_t *dip, int comp, int level, int direction, 1601*7c478bd9Sstevel@tonic-gate pm_canblock_t canblock, int scan, int *retp) 1602*7c478bd9Sstevel@tonic-gate { 1603*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "set_power") 1604*7c478bd9Sstevel@tonic-gate char *pathbuf; 1605*7c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t bpc; 1606*7c478bd9Sstevel@tonic-gate pm_sp_misc_t pspm; 1607*7c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 1608*7c478bd9Sstevel@tonic-gate int unused = DDI_SUCCESS; 1609*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 1610*7c478bd9Sstevel@tonic-gate 1611*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1612*7c478bd9Sstevel@tonic-gate int diverted = 0; 1613*7c478bd9Sstevel@tonic-gate 1614*7c478bd9Sstevel@tonic-gate /* 1615*7c478bd9Sstevel@tonic-gate * This prevents operations on the console from calling prom_printf and 1616*7c478bd9Sstevel@tonic-gate * either deadlocking or bringing up the console because of debug 1617*7c478bd9Sstevel@tonic-gate * output 1618*7c478bd9Sstevel@tonic-gate */ 1619*7c478bd9Sstevel@tonic-gate if (dip == cfb_dip) { 1620*7c478bd9Sstevel@tonic-gate diverted++; 1621*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 1622*7c478bd9Sstevel@tonic-gate pm_divertdebug++; 1623*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 1624*7c478bd9Sstevel@tonic-gate } 1625*7c478bd9Sstevel@tonic-gate #endif 1626*7c478bd9Sstevel@tonic-gate ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY || 1627*7c478bd9Sstevel@tonic-gate direction == PM_LEVEL_EXACT); 1628*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d, dir=%s, new=%d\n", 1629*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, pm_decode_direction(direction), level)) 1630*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1631*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 1632*7c478bd9Sstevel@tonic-gate bpc.bpc_dip = dip; 1633*7c478bd9Sstevel@tonic-gate bpc.bpc_path = pathbuf; 1634*7c478bd9Sstevel@tonic-gate bpc.bpc_comp = comp; 1635*7c478bd9Sstevel@tonic-gate bpc.bpc_olevel = PM_CURPOWER(dip, comp); 1636*7c478bd9Sstevel@tonic-gate bpc.bpc_nlevel = level; 1637*7c478bd9Sstevel@tonic-gate pspm.pspm_direction = direction; 1638*7c478bd9Sstevel@tonic-gate pspm.pspm_errnop = retp; 1639*7c478bd9Sstevel@tonic-gate pspm.pspm_canblock = canblock; 1640*7c478bd9Sstevel@tonic-gate pspm.pspm_scan = scan; 1641*7c478bd9Sstevel@tonic-gate bpc.bpc_private = &pspm; 1642*7c478bd9Sstevel@tonic-gate 1643*7c478bd9Sstevel@tonic-gate /* 1644*7c478bd9Sstevel@tonic-gate * If a config operation is being done (we've locked the parent) or 1645*7c478bd9Sstevel@tonic-gate * we already hold the power lock (we've locked the node) 1646*7c478bd9Sstevel@tonic-gate * then we can operate directly on the node because we have already 1647*7c478bd9Sstevel@tonic-gate * brought up all the ancestors, otherwise, we have to go back to the 1648*7c478bd9Sstevel@tonic-gate * top of the tree. 1649*7c478bd9Sstevel@tonic-gate */ 1650*7c478bd9Sstevel@tonic-gate if (pm_devi_lock_held(pdip) || pm_devi_lock_held(dip)) 1651*7c478bd9Sstevel@tonic-gate ret = pm_busop_set_power(dip, NULL, BUS_POWER_CHILD_PWRCHG, 1652*7c478bd9Sstevel@tonic-gate (void *)&bpc, (void *)&unused); 1653*7c478bd9Sstevel@tonic-gate else 1654*7c478bd9Sstevel@tonic-gate ret = pm_busop_bus_power(ddi_root_node(), NULL, 1655*7c478bd9Sstevel@tonic-gate BUS_POWER_CHILD_PWRCHG, (void *)&bpc, (void *)&unused); 1656*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1657*7c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS || *retp != DDI_SUCCESS) { 1658*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) can't change power, ret=%d, " 1659*7c478bd9Sstevel@tonic-gate "errno=%d\n", pmf, PM_DEVICE(dip), ret, *retp)) 1660*7c478bd9Sstevel@tonic-gate } 1661*7c478bd9Sstevel@tonic-gate if (diverted) { 1662*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 1663*7c478bd9Sstevel@tonic-gate pm_divertdebug--; 1664*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 1665*7c478bd9Sstevel@tonic-gate } 1666*7c478bd9Sstevel@tonic-gate #endif 1667*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 1668*7c478bd9Sstevel@tonic-gate return (ret); 1669*7c478bd9Sstevel@tonic-gate } 1670*7c478bd9Sstevel@tonic-gate 1671*7c478bd9Sstevel@tonic-gate 1672*7c478bd9Sstevel@tonic-gate static dev_info_t * 1673*7c478bd9Sstevel@tonic-gate find_dip(dev_info_t *dip, char *dev_name, int holddip) 1674*7c478bd9Sstevel@tonic-gate { 1675*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "find_dip") 1676*7c478bd9Sstevel@tonic-gate dev_info_t *cdip; 1677*7c478bd9Sstevel@tonic-gate char *child_dev, *addr; 1678*7c478bd9Sstevel@tonic-gate char *device; /* writeable copy of path */ 1679*7c478bd9Sstevel@tonic-gate int dev_len = strlen(dev_name)+1; 1680*7c478bd9Sstevel@tonic-gate int circ; 1681*7c478bd9Sstevel@tonic-gate 1682*7c478bd9Sstevel@tonic-gate device = kmem_zalloc(dev_len, KM_SLEEP); 1683*7c478bd9Sstevel@tonic-gate (void) strcpy(device, dev_name); 1684*7c478bd9Sstevel@tonic-gate addr = strchr(device, '@'); 1685*7c478bd9Sstevel@tonic-gate child_dev = strchr(device, '/'); 1686*7c478bd9Sstevel@tonic-gate if ((addr != NULL) && (child_dev == NULL || addr < child_dev)) { 1687*7c478bd9Sstevel@tonic-gate /* 1688*7c478bd9Sstevel@tonic-gate * We have device = "name@addr..." form 1689*7c478bd9Sstevel@tonic-gate */ 1690*7c478bd9Sstevel@tonic-gate *addr++ = '\0'; /* for strcmp (and skip '@') */ 1691*7c478bd9Sstevel@tonic-gate if (child_dev != NULL) 1692*7c478bd9Sstevel@tonic-gate *child_dev++ = '\0'; /* for strcmp (and skip '/') */ 1693*7c478bd9Sstevel@tonic-gate } else { 1694*7c478bd9Sstevel@tonic-gate /* 1695*7c478bd9Sstevel@tonic-gate * We have device = "name/..." or "name" 1696*7c478bd9Sstevel@tonic-gate */ 1697*7c478bd9Sstevel@tonic-gate addr = ""; 1698*7c478bd9Sstevel@tonic-gate if (child_dev != NULL) 1699*7c478bd9Sstevel@tonic-gate *child_dev++ = '\0'; /* for strcmp (and skip '/') */ 1700*7c478bd9Sstevel@tonic-gate } 1701*7c478bd9Sstevel@tonic-gate for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 1702*7c478bd9Sstevel@tonic-gate if (strcmp(ddi_node_name(dip), device) == 0) { 1703*7c478bd9Sstevel@tonic-gate /* If the driver isn't loaded, we prune the search */ 1704*7c478bd9Sstevel@tonic-gate if (i_ddi_node_state(dip) < DS_READY) { 1705*7c478bd9Sstevel@tonic-gate continue; 1706*7c478bd9Sstevel@tonic-gate } 1707*7c478bd9Sstevel@tonic-gate if (strcmp(ddi_get_name_addr(dip), addr) == 0) { 1708*7c478bd9Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: matched %s@%s" 1709*7c478bd9Sstevel@tonic-gate "(%s#%d)\n", pmf, PM_DEVICE(dip))) 1710*7c478bd9Sstevel@tonic-gate if (child_dev != NULL) { 1711*7c478bd9Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: %s@%s(%s#%d): " 1712*7c478bd9Sstevel@tonic-gate "held, call find_dip %s\n", pmf, 1713*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), child_dev)) 1714*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 1715*7c478bd9Sstevel@tonic-gate cdip = dip; 1716*7c478bd9Sstevel@tonic-gate dip = find_dip(ddi_get_child(dip), 1717*7c478bd9Sstevel@tonic-gate child_dev, holddip); 1718*7c478bd9Sstevel@tonic-gate ndi_devi_exit(cdip, circ); 1719*7c478bd9Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: %s@%s(%s#%d): " 1720*7c478bd9Sstevel@tonic-gate "release, find_dip rets %s\n", pmf, 1721*7c478bd9Sstevel@tonic-gate PM_DEVICE(cdip), child_dev)) 1722*7c478bd9Sstevel@tonic-gate } else { 1723*7c478bd9Sstevel@tonic-gate if (holddip) { 1724*7c478bd9Sstevel@tonic-gate e_ddi_hold_devi(dip); 1725*7c478bd9Sstevel@tonic-gate PMD(PMD_DHR | PMD_NAMETODIP, 1726*7c478bd9Sstevel@tonic-gate ("%s: held %s@%s(%s#%d), " 1727*7c478bd9Sstevel@tonic-gate "refcnt=%d\n", pmf, 1728*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), 1729*7c478bd9Sstevel@tonic-gate e_ddi_devi_holdcnt(dip))) 1730*7c478bd9Sstevel@tonic-gate } 1731*7c478bd9Sstevel@tonic-gate } 1732*7c478bd9Sstevel@tonic-gate kmem_free(device, dev_len); 1733*7c478bd9Sstevel@tonic-gate return (dip); 1734*7c478bd9Sstevel@tonic-gate } 1735*7c478bd9Sstevel@tonic-gate } 1736*7c478bd9Sstevel@tonic-gate } 1737*7c478bd9Sstevel@tonic-gate kmem_free(device, dev_len); 1738*7c478bd9Sstevel@tonic-gate return (dip); 1739*7c478bd9Sstevel@tonic-gate } 1740*7c478bd9Sstevel@tonic-gate 1741*7c478bd9Sstevel@tonic-gate /* 1742*7c478bd9Sstevel@tonic-gate * If holddip is set, then if a dip is found we return with the node held 1743*7c478bd9Sstevel@tonic-gate */ 1744*7c478bd9Sstevel@tonic-gate dev_info_t * 1745*7c478bd9Sstevel@tonic-gate pm_name_to_dip(char *pathname, int holddip) 1746*7c478bd9Sstevel@tonic-gate { 1747*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "name_to_dip") 1748*7c478bd9Sstevel@tonic-gate dev_info_t *dip = NULL; 1749*7c478bd9Sstevel@tonic-gate char dev_name[MAXNAMELEN]; 1750*7c478bd9Sstevel@tonic-gate dev_info_t *first_child; 1751*7c478bd9Sstevel@tonic-gate int circular; 1752*7c478bd9Sstevel@tonic-gate 1753*7c478bd9Sstevel@tonic-gate if (!pathname) 1754*7c478bd9Sstevel@tonic-gate return (NULL); 1755*7c478bd9Sstevel@tonic-gate 1756*7c478bd9Sstevel@tonic-gate (void) strncpy(dev_name, pathname, MAXNAMELEN); 1757*7c478bd9Sstevel@tonic-gate 1758*7c478bd9Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: devname: %s\n", pmf, dev_name)) 1759*7c478bd9Sstevel@tonic-gate 1760*7c478bd9Sstevel@tonic-gate /* 1761*7c478bd9Sstevel@tonic-gate * First we attempt to match the node in the tree. If we succeed 1762*7c478bd9Sstevel@tonic-gate * we hold the driver and look up the dip again. 1763*7c478bd9Sstevel@tonic-gate * No need to hold the root as that node is always held. 1764*7c478bd9Sstevel@tonic-gate */ 1765*7c478bd9Sstevel@tonic-gate if (dev_name[0] == '/') { 1766*7c478bd9Sstevel@tonic-gate ndi_devi_enter(ddi_root_node(), &circular); 1767*7c478bd9Sstevel@tonic-gate first_child = ddi_get_child(ddi_root_node()); 1768*7c478bd9Sstevel@tonic-gate dip = find_dip(first_child, dev_name + 1, holddip); 1769*7c478bd9Sstevel@tonic-gate ndi_devi_exit(ddi_root_node(), circular); 1770*7c478bd9Sstevel@tonic-gate 1771*7c478bd9Sstevel@tonic-gate } else { 1772*7c478bd9Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: physpath with unrooted " 1773*7c478bd9Sstevel@tonic-gate "search\n", pmf)) 1774*7c478bd9Sstevel@tonic-gate return (NULL); 1775*7c478bd9Sstevel@tonic-gate } 1776*7c478bd9Sstevel@tonic-gate 1777*7c478bd9Sstevel@tonic-gate ASSERT(!dip || 1778*7c478bd9Sstevel@tonic-gate (ddi_name_to_major(ddi_binding_name(dip)) != (major_t)-1)); 1779*7c478bd9Sstevel@tonic-gate 1780*7c478bd9Sstevel@tonic-gate return (dip); 1781*7c478bd9Sstevel@tonic-gate } 1782*7c478bd9Sstevel@tonic-gate 1783*7c478bd9Sstevel@tonic-gate /* 1784*7c478bd9Sstevel@tonic-gate * Search for a dependency and mark it unsatisfied 1785*7c478bd9Sstevel@tonic-gate */ 1786*7c478bd9Sstevel@tonic-gate static void 1787*7c478bd9Sstevel@tonic-gate pm_unsatisfy(char *keeper, char *kept) 1788*7c478bd9Sstevel@tonic-gate { 1789*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "unsatisfy") 1790*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 1791*7c478bd9Sstevel@tonic-gate 1792*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, keeper, kept)) 1793*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 1794*7c478bd9Sstevel@tonic-gate if (!dp->pdr_isprop) { 1795*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) == 0 && 1796*7c478bd9Sstevel@tonic-gate (dp->pdr_kept_count > 0) && 1797*7c478bd9Sstevel@tonic-gate strcmp(dp->pdr_kept_paths[0], kept) == 0) { 1798*7c478bd9Sstevel@tonic-gate if (dp->pdr_satisfied) { 1799*7c478bd9Sstevel@tonic-gate dp->pdr_satisfied = 0; 1800*7c478bd9Sstevel@tonic-gate pm_unresolved_deps++; 1801*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: clear satisfied, " 1802*7c478bd9Sstevel@tonic-gate "pm_unresolved_deps now %d\n", pmf, 1803*7c478bd9Sstevel@tonic-gate pm_unresolved_deps)) 1804*7c478bd9Sstevel@tonic-gate } 1805*7c478bd9Sstevel@tonic-gate } 1806*7c478bd9Sstevel@tonic-gate } 1807*7c478bd9Sstevel@tonic-gate } 1808*7c478bd9Sstevel@tonic-gate } 1809*7c478bd9Sstevel@tonic-gate 1810*7c478bd9Sstevel@tonic-gate /* 1811*7c478bd9Sstevel@tonic-gate * Device dip is being un power managed, it keeps up count other devices. 1812*7c478bd9Sstevel@tonic-gate * We need to release any hold we have on the kept devices, and also 1813*7c478bd9Sstevel@tonic-gate * mark the dependency no longer satisfied. 1814*7c478bd9Sstevel@tonic-gate */ 1815*7c478bd9Sstevel@tonic-gate static void 1816*7c478bd9Sstevel@tonic-gate pm_unkeeps(int count, char *keeper, char **keptpaths, int pwr) 1817*7c478bd9Sstevel@tonic-gate { 1818*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "unkeeps") 1819*7c478bd9Sstevel@tonic-gate int i, j; 1820*7c478bd9Sstevel@tonic-gate dev_info_t *kept; 1821*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 1822*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 1823*7c478bd9Sstevel@tonic-gate int keeper_on = 0, circ; 1824*7c478bd9Sstevel@tonic-gate 1825*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: count=%d, keeper=%s, keptpaths=%p\n", pmf, count, 1826*7c478bd9Sstevel@tonic-gate keeper, (void *)keptpaths)) 1827*7c478bd9Sstevel@tonic-gate /* 1828*7c478bd9Sstevel@tonic-gate * Try to grab keeper. Keeper may have gone away by now, 1829*7c478bd9Sstevel@tonic-gate * in this case, used the passed in value pwr 1830*7c478bd9Sstevel@tonic-gate */ 1831*7c478bd9Sstevel@tonic-gate dip = pm_name_to_dip(keeper, 1); 1832*7c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 1833*7c478bd9Sstevel@tonic-gate /* Release power hold */ 1834*7c478bd9Sstevel@tonic-gate kept = pm_name_to_dip(keptpaths[i], 1); 1835*7c478bd9Sstevel@tonic-gate if (kept) { 1836*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)[%d]\n", pmf, 1837*7c478bd9Sstevel@tonic-gate PM_DEVICE(kept), i)) 1838*7c478bd9Sstevel@tonic-gate /* 1839*7c478bd9Sstevel@tonic-gate * We need to check if we skipped a bringup here 1840*7c478bd9Sstevel@tonic-gate * because we could have failed the bringup 1841*7c478bd9Sstevel@tonic-gate * (ie DIRECT PM device) and have 1842*7c478bd9Sstevel@tonic-gate * not increment the count. 1843*7c478bd9Sstevel@tonic-gate */ 1844*7c478bd9Sstevel@tonic-gate if ((dip != NULL) && (PM_GET_PM_INFO(dip) != NULL)) { 1845*7c478bd9Sstevel@tonic-gate keeper_on = 0; 1846*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 1847*7c478bd9Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(dip); j++) { 1848*7c478bd9Sstevel@tonic-gate cp = &DEVI(dip)->devi_pm_components[j]; 1849*7c478bd9Sstevel@tonic-gate if (cur_power(cp)) { 1850*7c478bd9Sstevel@tonic-gate keeper_on++; 1851*7c478bd9Sstevel@tonic-gate break; 1852*7c478bd9Sstevel@tonic-gate } 1853*7c478bd9Sstevel@tonic-gate } 1854*7c478bd9Sstevel@tonic-gate if (keeper_on && (PM_SKBU(kept) == 0)) { 1855*7c478bd9Sstevel@tonic-gate pm_rele_power(kept); 1856*7c478bd9Sstevel@tonic-gate DEVI(kept)->devi_pm_flags 1857*7c478bd9Sstevel@tonic-gate &= ~PMC_SKIP_BRINGUP; 1858*7c478bd9Sstevel@tonic-gate } 1859*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 1860*7c478bd9Sstevel@tonic-gate } else if (pwr) { 1861*7c478bd9Sstevel@tonic-gate if (PM_SKBU(kept) == 0) { 1862*7c478bd9Sstevel@tonic-gate pm_rele_power(kept); 1863*7c478bd9Sstevel@tonic-gate DEVI(kept)->devi_pm_flags 1864*7c478bd9Sstevel@tonic-gate &= ~PMC_SKIP_BRINGUP; 1865*7c478bd9Sstevel@tonic-gate } 1866*7c478bd9Sstevel@tonic-gate } 1867*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 1868*7c478bd9Sstevel@tonic-gate } 1869*7c478bd9Sstevel@tonic-gate /* 1870*7c478bd9Sstevel@tonic-gate * mark this dependency not satisfied 1871*7c478bd9Sstevel@tonic-gate */ 1872*7c478bd9Sstevel@tonic-gate pm_unsatisfy(keeper, keptpaths[i]); 1873*7c478bd9Sstevel@tonic-gate } 1874*7c478bd9Sstevel@tonic-gate if (dip) 1875*7c478bd9Sstevel@tonic-gate ddi_release_devi(dip); 1876*7c478bd9Sstevel@tonic-gate } 1877*7c478bd9Sstevel@tonic-gate 1878*7c478bd9Sstevel@tonic-gate /* 1879*7c478bd9Sstevel@tonic-gate * Device kept is being un power managed, it is kept up by keeper. 1880*7c478bd9Sstevel@tonic-gate * We need to mark the dependency no longer satisfied. 1881*7c478bd9Sstevel@tonic-gate */ 1882*7c478bd9Sstevel@tonic-gate static void 1883*7c478bd9Sstevel@tonic-gate pm_unkepts(char *kept, char *keeper) 1884*7c478bd9Sstevel@tonic-gate { 1885*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "unkepts") 1886*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: kept=%s, keeper=%s\n", pmf, kept, keeper)) 1887*7c478bd9Sstevel@tonic-gate ASSERT(keeper != NULL); 1888*7c478bd9Sstevel@tonic-gate /* 1889*7c478bd9Sstevel@tonic-gate * mark this dependency not satisfied 1890*7c478bd9Sstevel@tonic-gate */ 1891*7c478bd9Sstevel@tonic-gate pm_unsatisfy(keeper, kept); 1892*7c478bd9Sstevel@tonic-gate } 1893*7c478bd9Sstevel@tonic-gate 1894*7c478bd9Sstevel@tonic-gate /* 1895*7c478bd9Sstevel@tonic-gate * Removes dependency information and hold on the kepts, if the path is a 1896*7c478bd9Sstevel@tonic-gate * path of a keeper. 1897*7c478bd9Sstevel@tonic-gate */ 1898*7c478bd9Sstevel@tonic-gate static void 1899*7c478bd9Sstevel@tonic-gate pm_free_keeper(char *path, int pwr) 1900*7c478bd9Sstevel@tonic-gate { 1901*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 1902*7c478bd9Sstevel@tonic-gate int i; 1903*7c478bd9Sstevel@tonic-gate size_t length; 1904*7c478bd9Sstevel@tonic-gate 1905*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 1906*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, path) != 0) 1907*7c478bd9Sstevel@tonic-gate continue; 1908*7c478bd9Sstevel@tonic-gate /* 1909*7c478bd9Sstevel@tonic-gate * Remove all our kept holds and the dependency records, 1910*7c478bd9Sstevel@tonic-gate * then free up the kept lists. 1911*7c478bd9Sstevel@tonic-gate */ 1912*7c478bd9Sstevel@tonic-gate pm_unkeeps(dp->pdr_kept_count, path, dp->pdr_kept_paths, pwr); 1913*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count) { 1914*7c478bd9Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 1915*7c478bd9Sstevel@tonic-gate length = strlen(dp->pdr_kept_paths[i]); 1916*7c478bd9Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths[i], length + 1); 1917*7c478bd9Sstevel@tonic-gate } 1918*7c478bd9Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, 1919*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count * sizeof (char **)); 1920*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths = NULL; 1921*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count = 0; 1922*7c478bd9Sstevel@tonic-gate } 1923*7c478bd9Sstevel@tonic-gate } 1924*7c478bd9Sstevel@tonic-gate } 1925*7c478bd9Sstevel@tonic-gate 1926*7c478bd9Sstevel@tonic-gate /* 1927*7c478bd9Sstevel@tonic-gate * Removes the device represented by path from the list of kepts, if the 1928*7c478bd9Sstevel@tonic-gate * path is a path of a kept 1929*7c478bd9Sstevel@tonic-gate */ 1930*7c478bd9Sstevel@tonic-gate static void 1931*7c478bd9Sstevel@tonic-gate pm_free_kept(char *path) 1932*7c478bd9Sstevel@tonic-gate { 1933*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 1934*7c478bd9Sstevel@tonic-gate int i; 1935*7c478bd9Sstevel@tonic-gate int j, count; 1936*7c478bd9Sstevel@tonic-gate size_t length; 1937*7c478bd9Sstevel@tonic-gate char **paths; 1938*7c478bd9Sstevel@tonic-gate 1939*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 1940*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count == 0) 1941*7c478bd9Sstevel@tonic-gate continue; 1942*7c478bd9Sstevel@tonic-gate count = dp->pdr_kept_count; 1943*7c478bd9Sstevel@tonic-gate /* Remove this device from the kept path lists */ 1944*7c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 1945*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], path) == 0) { 1946*7c478bd9Sstevel@tonic-gate pm_unkepts(path, dp->pdr_keeper); 1947*7c478bd9Sstevel@tonic-gate length = strlen(dp->pdr_kept_paths[i]) + 1; 1948*7c478bd9Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths[i], length); 1949*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths[i] = NULL; 1950*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count--; 1951*7c478bd9Sstevel@tonic-gate } 1952*7c478bd9Sstevel@tonic-gate } 1953*7c478bd9Sstevel@tonic-gate /* Compact the kept paths array */ 1954*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count) { 1955*7c478bd9Sstevel@tonic-gate length = dp->pdr_kept_count * sizeof (char **); 1956*7c478bd9Sstevel@tonic-gate paths = kmem_zalloc(length, KM_SLEEP); 1957*7c478bd9Sstevel@tonic-gate j = 0; 1958*7c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 1959*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_paths[i] != NULL) { 1960*7c478bd9Sstevel@tonic-gate paths[j] = dp->pdr_kept_paths[i]; 1961*7c478bd9Sstevel@tonic-gate j++; 1962*7c478bd9Sstevel@tonic-gate } 1963*7c478bd9Sstevel@tonic-gate } 1964*7c478bd9Sstevel@tonic-gate ASSERT(j == dp->pdr_kept_count); 1965*7c478bd9Sstevel@tonic-gate } 1966*7c478bd9Sstevel@tonic-gate /* Now free the old array and point to the new one */ 1967*7c478bd9Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, count * sizeof (char **)); 1968*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count) 1969*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths = paths; 1970*7c478bd9Sstevel@tonic-gate else 1971*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths = NULL; 1972*7c478bd9Sstevel@tonic-gate } 1973*7c478bd9Sstevel@tonic-gate } 1974*7c478bd9Sstevel@tonic-gate 1975*7c478bd9Sstevel@tonic-gate /* 1976*7c478bd9Sstevel@tonic-gate * Free the dependency information for a device. 1977*7c478bd9Sstevel@tonic-gate */ 1978*7c478bd9Sstevel@tonic-gate void 1979*7c478bd9Sstevel@tonic-gate pm_free_keeps(char *path, int pwr) 1980*7c478bd9Sstevel@tonic-gate { 1981*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "free_keeps") 1982*7c478bd9Sstevel@tonic-gate 1983*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 1984*7c478bd9Sstevel@tonic-gate int doprdeps = 0; 1985*7c478bd9Sstevel@tonic-gate void prdeps(char *); 1986*7c478bd9Sstevel@tonic-gate 1987*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s\n", pmf, path)) 1988*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) { 1989*7c478bd9Sstevel@tonic-gate doprdeps = 1; 1990*7c478bd9Sstevel@tonic-gate prdeps("pm_free_keeps before"); 1991*7c478bd9Sstevel@tonic-gate } 1992*7c478bd9Sstevel@tonic-gate #endif 1993*7c478bd9Sstevel@tonic-gate /* 1994*7c478bd9Sstevel@tonic-gate * First assume we are a keeper and remove all our kepts. 1995*7c478bd9Sstevel@tonic-gate */ 1996*7c478bd9Sstevel@tonic-gate pm_free_keeper(path, pwr); 1997*7c478bd9Sstevel@tonic-gate /* 1998*7c478bd9Sstevel@tonic-gate * Now assume we a kept device, and remove all our records. 1999*7c478bd9Sstevel@tonic-gate */ 2000*7c478bd9Sstevel@tonic-gate pm_free_kept(path); 2001*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 2002*7c478bd9Sstevel@tonic-gate if (doprdeps) { 2003*7c478bd9Sstevel@tonic-gate prdeps("pm_free_keeps after"); 2004*7c478bd9Sstevel@tonic-gate } 2005*7c478bd9Sstevel@tonic-gate #endif 2006*7c478bd9Sstevel@tonic-gate } 2007*7c478bd9Sstevel@tonic-gate 2008*7c478bd9Sstevel@tonic-gate static int 2009*7c478bd9Sstevel@tonic-gate pm_is_kept(char *path) 2010*7c478bd9Sstevel@tonic-gate { 2011*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 2012*7c478bd9Sstevel@tonic-gate int i; 2013*7c478bd9Sstevel@tonic-gate 2014*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 2015*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count == 0) 2016*7c478bd9Sstevel@tonic-gate continue; 2017*7c478bd9Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 2018*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], path) == 0) 2019*7c478bd9Sstevel@tonic-gate return (1); 2020*7c478bd9Sstevel@tonic-gate } 2021*7c478bd9Sstevel@tonic-gate } 2022*7c478bd9Sstevel@tonic-gate return (0); 2023*7c478bd9Sstevel@tonic-gate } 2024*7c478bd9Sstevel@tonic-gate 2025*7c478bd9Sstevel@tonic-gate static void 2026*7c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(dev_info_t *dip, int cnt) 2027*7c478bd9Sstevel@tonic-gate { 2028*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "hold_rele_power") 2029*7c478bd9Sstevel@tonic-gate int circ; 2030*7c478bd9Sstevel@tonic-gate 2031*7c478bd9Sstevel@tonic-gate if ((dip == NULL) || 2032*7c478bd9Sstevel@tonic-gate (PM_GET_PM_INFO(dip) == NULL) || PM_ISBC(dip)) 2033*7c478bd9Sstevel@tonic-gate return; 2034*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 2035*7c478bd9Sstevel@tonic-gate ASSERT(cnt >= 0 && PM_KUC(dip) >= 0 || cnt < 0 && PM_KUC(dip) > 0); 2036*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: kidsupcnt for %s@%s(%s#%d) %d->%d\n", pmf, 2037*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), PM_KUC(dip), (PM_KUC(dip) + cnt))) 2038*7c478bd9Sstevel@tonic-gate PM_KUC(dip) += cnt; 2039*7c478bd9Sstevel@tonic-gate ASSERT(PM_KUC(dip) >= 0); 2040*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 2041*7c478bd9Sstevel@tonic-gate if (cnt < 0 && PM_KUC(dip) == 0) 2042*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 2043*7c478bd9Sstevel@tonic-gate } 2044*7c478bd9Sstevel@tonic-gate 2045*7c478bd9Sstevel@tonic-gate #define MAX_PPM_HANDLERS 4 2046*7c478bd9Sstevel@tonic-gate 2047*7c478bd9Sstevel@tonic-gate kmutex_t ppm_lock; /* in case we ever do multi-threaded startup */ 2048*7c478bd9Sstevel@tonic-gate 2049*7c478bd9Sstevel@tonic-gate struct ppm_callbacks { 2050*7c478bd9Sstevel@tonic-gate int (*ppmc_func)(dev_info_t *); 2051*7c478bd9Sstevel@tonic-gate dev_info_t *ppmc_dip; 2052*7c478bd9Sstevel@tonic-gate } ppm_callbacks[MAX_PPM_HANDLERS + 1]; 2053*7c478bd9Sstevel@tonic-gate 2054*7c478bd9Sstevel@tonic-gate 2055*7c478bd9Sstevel@tonic-gate /* 2056*7c478bd9Sstevel@tonic-gate * This routine calls into all the registered ppms to notify them 2057*7c478bd9Sstevel@tonic-gate * that either all components of power-managed devices are at their 2058*7c478bd9Sstevel@tonic-gate * lowest levels or no longer all are at their lowest levels. 2059*7c478bd9Sstevel@tonic-gate */ 2060*7c478bd9Sstevel@tonic-gate static void 2061*7c478bd9Sstevel@tonic-gate pm_ppm_notify_all_lowest(dev_info_t *dip, int mode) 2062*7c478bd9Sstevel@tonic-gate { 2063*7c478bd9Sstevel@tonic-gate struct ppm_callbacks *ppmcp; 2064*7c478bd9Sstevel@tonic-gate power_req_t power_req; 2065*7c478bd9Sstevel@tonic-gate int result = 0; 2066*7c478bd9Sstevel@tonic-gate 2067*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_ALL_LOWEST; 2068*7c478bd9Sstevel@tonic-gate power_req.req.ppm_all_lowest_req.mode = mode; 2069*7c478bd9Sstevel@tonic-gate mutex_enter(&ppm_lock); 2070*7c478bd9Sstevel@tonic-gate for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) 2071*7c478bd9Sstevel@tonic-gate (void) pm_ctlops((dev_info_t *)ppmcp->ppmc_dip, dip, 2072*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 2073*7c478bd9Sstevel@tonic-gate mutex_exit(&ppm_lock); 2074*7c478bd9Sstevel@tonic-gate } 2075*7c478bd9Sstevel@tonic-gate 2076*7c478bd9Sstevel@tonic-gate static void 2077*7c478bd9Sstevel@tonic-gate pm_set_pm_info(dev_info_t *dip, void *value) 2078*7c478bd9Sstevel@tonic-gate { 2079*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_info = value; 2080*7c478bd9Sstevel@tonic-gate } 2081*7c478bd9Sstevel@tonic-gate 2082*7c478bd9Sstevel@tonic-gate pm_rsvp_t *pm_blocked_list; 2083*7c478bd9Sstevel@tonic-gate 2084*7c478bd9Sstevel@tonic-gate /* 2085*7c478bd9Sstevel@tonic-gate * Look up an entry in the blocked list by dip and component 2086*7c478bd9Sstevel@tonic-gate */ 2087*7c478bd9Sstevel@tonic-gate static pm_rsvp_t * 2088*7c478bd9Sstevel@tonic-gate pm_rsvp_lookup(dev_info_t *dip, int comp) 2089*7c478bd9Sstevel@tonic-gate { 2090*7c478bd9Sstevel@tonic-gate pm_rsvp_t *p; 2091*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 2092*7c478bd9Sstevel@tonic-gate for (p = pm_blocked_list; p; p = p->pr_next) 2093*7c478bd9Sstevel@tonic-gate if (p->pr_dip == dip && p->pr_comp == comp) { 2094*7c478bd9Sstevel@tonic-gate return (p); 2095*7c478bd9Sstevel@tonic-gate } 2096*7c478bd9Sstevel@tonic-gate return (NULL); 2097*7c478bd9Sstevel@tonic-gate } 2098*7c478bd9Sstevel@tonic-gate 2099*7c478bd9Sstevel@tonic-gate /* 2100*7c478bd9Sstevel@tonic-gate * Called when a device which is direct power managed (or the parent or 2101*7c478bd9Sstevel@tonic-gate * dependent of such a device) changes power, or when a pm clone is closed 2102*7c478bd9Sstevel@tonic-gate * that was direct power managing a device. This call results in pm_blocked() 2103*7c478bd9Sstevel@tonic-gate * (below) returning. 2104*7c478bd9Sstevel@tonic-gate */ 2105*7c478bd9Sstevel@tonic-gate void 2106*7c478bd9Sstevel@tonic-gate pm_proceed(dev_info_t *dip, int cmd, int comp, int newlevel) 2107*7c478bd9Sstevel@tonic-gate { 2108*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "proceed") 2109*7c478bd9Sstevel@tonic-gate pm_rsvp_t *found = NULL; 2110*7c478bd9Sstevel@tonic-gate pm_rsvp_t *p; 2111*7c478bd9Sstevel@tonic-gate 2112*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 2113*7c478bd9Sstevel@tonic-gate switch (cmd) { 2114*7c478bd9Sstevel@tonic-gate /* 2115*7c478bd9Sstevel@tonic-gate * we're giving up control, let any pending op continue 2116*7c478bd9Sstevel@tonic-gate */ 2117*7c478bd9Sstevel@tonic-gate case PMP_RELEASE: 2118*7c478bd9Sstevel@tonic-gate for (p = pm_blocked_list; p; p = p->pr_next) { 2119*7c478bd9Sstevel@tonic-gate if (dip == p->pr_dip) { 2120*7c478bd9Sstevel@tonic-gate p->pr_retval = PMP_RELEASE; 2121*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: RELEASE %s@%s(%s#%d)\n", 2122*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 2123*7c478bd9Sstevel@tonic-gate cv_signal(&p->pr_cv); 2124*7c478bd9Sstevel@tonic-gate } 2125*7c478bd9Sstevel@tonic-gate } 2126*7c478bd9Sstevel@tonic-gate break; 2127*7c478bd9Sstevel@tonic-gate 2128*7c478bd9Sstevel@tonic-gate /* 2129*7c478bd9Sstevel@tonic-gate * process has done PM_SET_CURRENT_POWER; let a matching request 2130*7c478bd9Sstevel@tonic-gate * succeed and a non-matching request for the same device fail 2131*7c478bd9Sstevel@tonic-gate */ 2132*7c478bd9Sstevel@tonic-gate case PMP_SETPOWER: 2133*7c478bd9Sstevel@tonic-gate found = pm_rsvp_lookup(dip, comp); 2134*7c478bd9Sstevel@tonic-gate if (!found) /* if driver not waiting */ 2135*7c478bd9Sstevel@tonic-gate break; 2136*7c478bd9Sstevel@tonic-gate /* 2137*7c478bd9Sstevel@tonic-gate * This cannot be pm_lower_power, since that can only happen 2138*7c478bd9Sstevel@tonic-gate * during detach or probe 2139*7c478bd9Sstevel@tonic-gate */ 2140*7c478bd9Sstevel@tonic-gate if (found->pr_newlevel <= newlevel) { 2141*7c478bd9Sstevel@tonic-gate found->pr_retval = PMP_SUCCEED; 2142*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: SUCCEED %s@%s(%s#%d)\n", pmf, 2143*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 2144*7c478bd9Sstevel@tonic-gate } else { 2145*7c478bd9Sstevel@tonic-gate found->pr_retval = PMP_FAIL; 2146*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: FAIL %s@%s(%s#%d)\n", pmf, 2147*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 2148*7c478bd9Sstevel@tonic-gate } 2149*7c478bd9Sstevel@tonic-gate cv_signal(&found->pr_cv); 2150*7c478bd9Sstevel@tonic-gate break; 2151*7c478bd9Sstevel@tonic-gate 2152*7c478bd9Sstevel@tonic-gate default: 2153*7c478bd9Sstevel@tonic-gate panic("pm_proceed unknown cmd %d", cmd); 2154*7c478bd9Sstevel@tonic-gate } 2155*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 2156*7c478bd9Sstevel@tonic-gate } 2157*7c478bd9Sstevel@tonic-gate 2158*7c478bd9Sstevel@tonic-gate /* 2159*7c478bd9Sstevel@tonic-gate * This routine dispatches new work to the dependency thread. Caller must 2160*7c478bd9Sstevel@tonic-gate * be prepared to block for memory if necessary. 2161*7c478bd9Sstevel@tonic-gate */ 2162*7c478bd9Sstevel@tonic-gate void 2163*7c478bd9Sstevel@tonic-gate pm_dispatch_to_dep_thread(int cmd, char *keeper, char *kept, int wait, 2164*7c478bd9Sstevel@tonic-gate int *res, int cached_pwr) 2165*7c478bd9Sstevel@tonic-gate { 2166*7c478bd9Sstevel@tonic-gate pm_dep_wk_t *new_work; 2167*7c478bd9Sstevel@tonic-gate 2168*7c478bd9Sstevel@tonic-gate new_work = kmem_zalloc(sizeof (pm_dep_wk_t), KM_SLEEP); 2169*7c478bd9Sstevel@tonic-gate new_work->pdw_type = cmd; 2170*7c478bd9Sstevel@tonic-gate new_work->pdw_wait = wait; 2171*7c478bd9Sstevel@tonic-gate new_work->pdw_done = 0; 2172*7c478bd9Sstevel@tonic-gate new_work->pdw_ret = 0; 2173*7c478bd9Sstevel@tonic-gate new_work->pdw_pwr = cached_pwr; 2174*7c478bd9Sstevel@tonic-gate cv_init(&new_work->pdw_cv, NULL, CV_DEFAULT, NULL); 2175*7c478bd9Sstevel@tonic-gate if (keeper != NULL) { 2176*7c478bd9Sstevel@tonic-gate new_work->pdw_keeper = kmem_zalloc(strlen(keeper) + 1, 2177*7c478bd9Sstevel@tonic-gate KM_SLEEP); 2178*7c478bd9Sstevel@tonic-gate (void) strcpy(new_work->pdw_keeper, keeper); 2179*7c478bd9Sstevel@tonic-gate } 2180*7c478bd9Sstevel@tonic-gate if (kept != NULL) { 2181*7c478bd9Sstevel@tonic-gate new_work->pdw_kept = kmem_zalloc(strlen(kept) + 1, KM_SLEEP); 2182*7c478bd9Sstevel@tonic-gate (void) strcpy(new_work->pdw_kept, kept); 2183*7c478bd9Sstevel@tonic-gate } 2184*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_dep_thread_lock); 2185*7c478bd9Sstevel@tonic-gate if (pm_dep_thread_workq == NULL) { 2186*7c478bd9Sstevel@tonic-gate pm_dep_thread_workq = new_work; 2187*7c478bd9Sstevel@tonic-gate pm_dep_thread_tail = new_work; 2188*7c478bd9Sstevel@tonic-gate new_work->pdw_next = NULL; 2189*7c478bd9Sstevel@tonic-gate } else { 2190*7c478bd9Sstevel@tonic-gate pm_dep_thread_tail->pdw_next = new_work; 2191*7c478bd9Sstevel@tonic-gate pm_dep_thread_tail = new_work; 2192*7c478bd9Sstevel@tonic-gate new_work->pdw_next = NULL; 2193*7c478bd9Sstevel@tonic-gate } 2194*7c478bd9Sstevel@tonic-gate cv_signal(&pm_dep_thread_cv); 2195*7c478bd9Sstevel@tonic-gate /* If caller asked for it, wait till it is done. */ 2196*7c478bd9Sstevel@tonic-gate if (wait) { 2197*7c478bd9Sstevel@tonic-gate while (!new_work->pdw_done) 2198*7c478bd9Sstevel@tonic-gate cv_wait(&new_work->pdw_cv, &pm_dep_thread_lock); 2199*7c478bd9Sstevel@tonic-gate /* 2200*7c478bd9Sstevel@tonic-gate * Pass return status, if any, back. 2201*7c478bd9Sstevel@tonic-gate */ 2202*7c478bd9Sstevel@tonic-gate if (res != NULL) 2203*7c478bd9Sstevel@tonic-gate *res = new_work->pdw_ret; 2204*7c478bd9Sstevel@tonic-gate /* 2205*7c478bd9Sstevel@tonic-gate * If we asked to wait, it is our job to free the request 2206*7c478bd9Sstevel@tonic-gate * structure. 2207*7c478bd9Sstevel@tonic-gate */ 2208*7c478bd9Sstevel@tonic-gate if (new_work->pdw_keeper) 2209*7c478bd9Sstevel@tonic-gate kmem_free(new_work->pdw_keeper, 2210*7c478bd9Sstevel@tonic-gate strlen(new_work->pdw_keeper) + 1); 2211*7c478bd9Sstevel@tonic-gate if (new_work->pdw_kept) 2212*7c478bd9Sstevel@tonic-gate kmem_free(new_work->pdw_kept, 2213*7c478bd9Sstevel@tonic-gate strlen(new_work->pdw_kept) + 1); 2214*7c478bd9Sstevel@tonic-gate kmem_free(new_work, sizeof (pm_dep_wk_t)); 2215*7c478bd9Sstevel@tonic-gate } 2216*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_dep_thread_lock); 2217*7c478bd9Sstevel@tonic-gate } 2218*7c478bd9Sstevel@tonic-gate 2219*7c478bd9Sstevel@tonic-gate /* 2220*7c478bd9Sstevel@tonic-gate * Release the pm resource for this device. 2221*7c478bd9Sstevel@tonic-gate */ 2222*7c478bd9Sstevel@tonic-gate void 2223*7c478bd9Sstevel@tonic-gate pm_rem_info(dev_info_t *dip) 2224*7c478bd9Sstevel@tonic-gate { 2225*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "rem_info") 2226*7c478bd9Sstevel@tonic-gate int i, count = 0; 2227*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 2228*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 2229*7c478bd9Sstevel@tonic-gate char *pathbuf; 2230*7c478bd9Sstevel@tonic-gate int work_type = PM_DEP_WK_DETACH; 2231*7c478bd9Sstevel@tonic-gate 2232*7c478bd9Sstevel@tonic-gate ASSERT(info); 2233*7c478bd9Sstevel@tonic-gate 2234*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 2235*7c478bd9Sstevel@tonic-gate if (PM_ISDIRECT(dip)) { 2236*7c478bd9Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_DIRECT; 2237*7c478bd9Sstevel@tonic-gate ASSERT(info->pmi_clone); 2238*7c478bd9Sstevel@tonic-gate info->pmi_clone = 0; 2239*7c478bd9Sstevel@tonic-gate pm_proceed(dip, PMP_RELEASE, -1, -1); 2240*7c478bd9Sstevel@tonic-gate } 2241*7c478bd9Sstevel@tonic-gate ASSERT(!PM_GET_PM_SCAN(dip)); 2242*7c478bd9Sstevel@tonic-gate 2243*7c478bd9Sstevel@tonic-gate /* 2244*7c478bd9Sstevel@tonic-gate * Now adjust parent's kidsupcnt. BC nodes we check only comp 0, 2245*7c478bd9Sstevel@tonic-gate * Others we check all components. BC node that has already 2246*7c478bd9Sstevel@tonic-gate * called pm_destroy_components() has zero component count. 2247*7c478bd9Sstevel@tonic-gate * Parents that get notification are not adjusted because their 2248*7c478bd9Sstevel@tonic-gate * kidsupcnt is always 0 (or 1 during configuration). 2249*7c478bd9Sstevel@tonic-gate */ 2250*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d) has %d components\n", pmf, 2251*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), PM_NUMCMPTS(dip))) 2252*7c478bd9Sstevel@tonic-gate 2253*7c478bd9Sstevel@tonic-gate /* node is detached, so we can examine power without locking */ 2254*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip)) { 2255*7c478bd9Sstevel@tonic-gate count = (PM_CURPOWER(dip, 0) != 0); 2256*7c478bd9Sstevel@tonic-gate } else { 2257*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 2258*7c478bd9Sstevel@tonic-gate count += (PM_CURPOWER(dip, i) != 0); 2259*7c478bd9Sstevel@tonic-gate } 2260*7c478bd9Sstevel@tonic-gate 2261*7c478bd9Sstevel@tonic-gate if (PM_NUMCMPTS(dip) && pdip && !PM_WANTS_NOTIFICATION(pdip)) 2262*7c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(pdip, -count); 2263*7c478bd9Sstevel@tonic-gate 2264*7c478bd9Sstevel@tonic-gate /* Schedule a request to clean up dependency records */ 2265*7c478bd9Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 2266*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 2267*7c478bd9Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, pathbuf, 2268*7c478bd9Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, (count > 0)); 2269*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 2270*7c478bd9Sstevel@tonic-gate 2271*7c478bd9Sstevel@tonic-gate /* 2272*7c478bd9Sstevel@tonic-gate * Adjust the pm_comps_notlowest count since this device is 2273*7c478bd9Sstevel@tonic-gate * not being power-managed anymore. 2274*7c478bd9Sstevel@tonic-gate */ 2275*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 2276*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, i) != 0) 2277*7c478bd9Sstevel@tonic-gate PM_DECR_NOTLOWEST(dip); 2278*7c478bd9Sstevel@tonic-gate } 2279*7c478bd9Sstevel@tonic-gate /* 2280*7c478bd9Sstevel@tonic-gate * Once we clear the info pointer, it looks like it is not power 2281*7c478bd9Sstevel@tonic-gate * managed to everybody else. 2282*7c478bd9Sstevel@tonic-gate */ 2283*7c478bd9Sstevel@tonic-gate pm_set_pm_info(dip, NULL); 2284*7c478bd9Sstevel@tonic-gate kmem_free(info, sizeof (pm_info_t)); 2285*7c478bd9Sstevel@tonic-gate } 2286*7c478bd9Sstevel@tonic-gate 2287*7c478bd9Sstevel@tonic-gate int 2288*7c478bd9Sstevel@tonic-gate pm_get_norm_pwrs(dev_info_t *dip, int **valuep, size_t *length) 2289*7c478bd9Sstevel@tonic-gate { 2290*7c478bd9Sstevel@tonic-gate int components = PM_NUMCMPTS(dip); 2291*7c478bd9Sstevel@tonic-gate int *bufp; 2292*7c478bd9Sstevel@tonic-gate size_t size; 2293*7c478bd9Sstevel@tonic-gate int i; 2294*7c478bd9Sstevel@tonic-gate 2295*7c478bd9Sstevel@tonic-gate if (components <= 0) { 2296*7c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!pm: %s@%s(%s#%d) has no components, " 2297*7c478bd9Sstevel@tonic-gate "can't get normal power values\n", PM_DEVICE(dip)); 2298*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2299*7c478bd9Sstevel@tonic-gate } else { 2300*7c478bd9Sstevel@tonic-gate size = components * sizeof (int); 2301*7c478bd9Sstevel@tonic-gate bufp = kmem_alloc(size, KM_SLEEP); 2302*7c478bd9Sstevel@tonic-gate for (i = 0; i < components; i++) { 2303*7c478bd9Sstevel@tonic-gate bufp[i] = pm_get_normal_power(dip, i); 2304*7c478bd9Sstevel@tonic-gate } 2305*7c478bd9Sstevel@tonic-gate } 2306*7c478bd9Sstevel@tonic-gate *length = size; 2307*7c478bd9Sstevel@tonic-gate *valuep = bufp; 2308*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2309*7c478bd9Sstevel@tonic-gate } 2310*7c478bd9Sstevel@tonic-gate 2311*7c478bd9Sstevel@tonic-gate static int 2312*7c478bd9Sstevel@tonic-gate pm_reset_timestamps(dev_info_t *dip, void *arg) 2313*7c478bd9Sstevel@tonic-gate { 2314*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 2315*7c478bd9Sstevel@tonic-gate 2316*7c478bd9Sstevel@tonic-gate int components; 2317*7c478bd9Sstevel@tonic-gate int i; 2318*7c478bd9Sstevel@tonic-gate 2319*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) 2320*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 2321*7c478bd9Sstevel@tonic-gate components = PM_NUMCMPTS(dip); 2322*7c478bd9Sstevel@tonic-gate ASSERT(components > 0); 2323*7c478bd9Sstevel@tonic-gate PM_LOCK_BUSY(dip); 2324*7c478bd9Sstevel@tonic-gate for (i = 0; i < components; i++) { 2325*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 2326*7c478bd9Sstevel@tonic-gate /* 2327*7c478bd9Sstevel@tonic-gate * If the component was not marked as busy, 2328*7c478bd9Sstevel@tonic-gate * reset its timestamp to now. 2329*7c478bd9Sstevel@tonic-gate */ 2330*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 2331*7c478bd9Sstevel@tonic-gate if (cp->pmc_timestamp) 2332*7c478bd9Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 2333*7c478bd9Sstevel@tonic-gate } 2334*7c478bd9Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 2335*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 2336*7c478bd9Sstevel@tonic-gate } 2337*7c478bd9Sstevel@tonic-gate 2338*7c478bd9Sstevel@tonic-gate /* 2339*7c478bd9Sstevel@tonic-gate * Convert a power level to an index into the levels array (or 2340*7c478bd9Sstevel@tonic-gate * just PM_LEVEL_UNKNOWN in that special case). 2341*7c478bd9Sstevel@tonic-gate */ 2342*7c478bd9Sstevel@tonic-gate static int 2343*7c478bd9Sstevel@tonic-gate pm_level_to_index(dev_info_t *dip, pm_component_t *cp, int level) 2344*7c478bd9Sstevel@tonic-gate { 2345*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "level_to_index") 2346*7c478bd9Sstevel@tonic-gate int i; 2347*7c478bd9Sstevel@tonic-gate int limit = cp->pmc_comp.pmc_numlevels; 2348*7c478bd9Sstevel@tonic-gate int *ip = cp->pmc_comp.pmc_lvals; 2349*7c478bd9Sstevel@tonic-gate 2350*7c478bd9Sstevel@tonic-gate if (level == PM_LEVEL_UNKNOWN) 2351*7c478bd9Sstevel@tonic-gate return (level); 2352*7c478bd9Sstevel@tonic-gate 2353*7c478bd9Sstevel@tonic-gate for (i = 0; i < limit; i++) { 2354*7c478bd9Sstevel@tonic-gate if (level == *ip++) { 2355*7c478bd9Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d)[%d] to %x\n", 2356*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), 2357*7c478bd9Sstevel@tonic-gate (int)(cp - DEVI(dip)->devi_pm_components), level)) 2358*7c478bd9Sstevel@tonic-gate return (i); 2359*7c478bd9Sstevel@tonic-gate } 2360*7c478bd9Sstevel@tonic-gate } 2361*7c478bd9Sstevel@tonic-gate panic("pm_level_to_index: level %d not found for device " 2362*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)", level, PM_DEVICE(dip)); 2363*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 2364*7c478bd9Sstevel@tonic-gate } 2365*7c478bd9Sstevel@tonic-gate 2366*7c478bd9Sstevel@tonic-gate /* 2367*7c478bd9Sstevel@tonic-gate * Internal function to set current power level 2368*7c478bd9Sstevel@tonic-gate */ 2369*7c478bd9Sstevel@tonic-gate static void 2370*7c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dev_info_t *dip, pm_component_t *cp, int level) 2371*7c478bd9Sstevel@tonic-gate { 2372*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "set_cur_pwr") 2373*7c478bd9Sstevel@tonic-gate int curpwr = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ? 2374*7c478bd9Sstevel@tonic-gate cp->pmc_phc_pwr : cp->pmc_cur_pwr); 2375*7c478bd9Sstevel@tonic-gate 2376*7c478bd9Sstevel@tonic-gate /* 2377*7c478bd9Sstevel@tonic-gate * Nothing to adjust if current & new levels are the same. 2378*7c478bd9Sstevel@tonic-gate */ 2379*7c478bd9Sstevel@tonic-gate if (curpwr != PM_LEVEL_UNKNOWN && 2380*7c478bd9Sstevel@tonic-gate level == cp->pmc_comp.pmc_lvals[curpwr]) 2381*7c478bd9Sstevel@tonic-gate return; 2382*7c478bd9Sstevel@tonic-gate 2383*7c478bd9Sstevel@tonic-gate /* 2384*7c478bd9Sstevel@tonic-gate * Keep the count for comps doing transition to/from lowest 2385*7c478bd9Sstevel@tonic-gate * level. 2386*7c478bd9Sstevel@tonic-gate */ 2387*7c478bd9Sstevel@tonic-gate if (curpwr == 0) { 2388*7c478bd9Sstevel@tonic-gate PM_INCR_NOTLOWEST(dip); 2389*7c478bd9Sstevel@tonic-gate } else if (level == cp->pmc_comp.pmc_lvals[0]) { 2390*7c478bd9Sstevel@tonic-gate PM_DECR_NOTLOWEST(dip); 2391*7c478bd9Sstevel@tonic-gate } 2392*7c478bd9Sstevel@tonic-gate cp->pmc_phc_pwr = PM_LEVEL_UNKNOWN; 2393*7c478bd9Sstevel@tonic-gate cp->pmc_cur_pwr = pm_level_to_index(dip, cp, level); 2394*7c478bd9Sstevel@tonic-gate } 2395*7c478bd9Sstevel@tonic-gate 2396*7c478bd9Sstevel@tonic-gate /* 2397*7c478bd9Sstevel@tonic-gate * This is the default method of setting the power of a device if no ppm 2398*7c478bd9Sstevel@tonic-gate * driver has claimed it. 2399*7c478bd9Sstevel@tonic-gate */ 2400*7c478bd9Sstevel@tonic-gate int 2401*7c478bd9Sstevel@tonic-gate pm_power(dev_info_t *dip, int comp, int level) 2402*7c478bd9Sstevel@tonic-gate { 2403*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "power") 2404*7c478bd9Sstevel@tonic-gate struct dev_ops *ops; 2405*7c478bd9Sstevel@tonic-gate int (*fn)(dev_info_t *, int, int); 2406*7c478bd9Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, comp); 2407*7c478bd9Sstevel@tonic-gate int retval; 2408*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 2409*7c478bd9Sstevel@tonic-gate static int pm_phc_impl(dev_info_t *, int, int, int); 2410*7c478bd9Sstevel@tonic-gate 2411*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf, 2412*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), comp, level)) 2413*7c478bd9Sstevel@tonic-gate if (!(ops = ddi_get_driver(dip))) { 2414*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) has no ops\n", pmf, 2415*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 2416*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2417*7c478bd9Sstevel@tonic-gate } 2418*7c478bd9Sstevel@tonic-gate if ((ops->devo_rev < 2) || !(fn = ops->devo_power)) { 2419*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s%s\n", pmf, 2420*7c478bd9Sstevel@tonic-gate (ops->devo_rev < 2 ? " wrong devo_rev" : ""), 2421*7c478bd9Sstevel@tonic-gate (!fn ? " devo_power NULL" : ""))) 2422*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2423*7c478bd9Sstevel@tonic-gate } 2424*7c478bd9Sstevel@tonic-gate cp->pmc_flags |= PM_POWER_OP; 2425*7c478bd9Sstevel@tonic-gate retval = (*fn)(dip, comp, level); 2426*7c478bd9Sstevel@tonic-gate cp->pmc_flags &= ~PM_POWER_OP; 2427*7c478bd9Sstevel@tonic-gate if (retval == DDI_SUCCESS) { 2428*7c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level); 2429*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2430*7c478bd9Sstevel@tonic-gate } 2431*7c478bd9Sstevel@tonic-gate 2432*7c478bd9Sstevel@tonic-gate /* 2433*7c478bd9Sstevel@tonic-gate * If pm_power_has_changed() detected a deadlock with pm_power() it 2434*7c478bd9Sstevel@tonic-gate * updated only the power level of the component. If our attempt to 2435*7c478bd9Sstevel@tonic-gate * set the device new to a power level above has failed we sync the 2436*7c478bd9Sstevel@tonic-gate * total power state via phc code now. 2437*7c478bd9Sstevel@tonic-gate */ 2438*7c478bd9Sstevel@tonic-gate if (cp->pmc_flags & PM_PHC_WHILE_SET_POWER) { 2439*7c478bd9Sstevel@tonic-gate int phc_lvl = 2440*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]; 2441*7c478bd9Sstevel@tonic-gate 2442*7c478bd9Sstevel@tonic-gate ASSERT(info); 2443*7c478bd9Sstevel@tonic-gate (void) pm_phc_impl(dip, comp, phc_lvl, 0); 2444*7c478bd9Sstevel@tonic-gate PMD(PMD_PHC, ("%s: phc %s@%s(%s#%d) comp=%d level=%d\n", 2445*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, phc_lvl)) 2446*7c478bd9Sstevel@tonic-gate } 2447*7c478bd9Sstevel@tonic-gate 2448*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't set comp=%d (%s) of %s@%s(%s#%d) to " 2449*7c478bd9Sstevel@tonic-gate "level=%d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, PM_DEVICE(dip), 2450*7c478bd9Sstevel@tonic-gate level, power_val_to_string(cp, level))); 2451*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2452*7c478bd9Sstevel@tonic-gate } 2453*7c478bd9Sstevel@tonic-gate 2454*7c478bd9Sstevel@tonic-gate int 2455*7c478bd9Sstevel@tonic-gate pm_unmanage(dev_info_t *dip) 2456*7c478bd9Sstevel@tonic-gate { 2457*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "unmanage") 2458*7c478bd9Sstevel@tonic-gate power_req_t power_req; 2459*7c478bd9Sstevel@tonic-gate int result, retval = 0; 2460*7c478bd9Sstevel@tonic-gate 2461*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 2462*7c478bd9Sstevel@tonic-gate PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, 2463*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 2464*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNMANAGE; 2465*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 2466*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) 2467*7c478bd9Sstevel@tonic-gate retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 2468*7c478bd9Sstevel@tonic-gate &power_req, &result); 2469*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 2470*7c478bd9Sstevel@tonic-gate else 2471*7c478bd9Sstevel@tonic-gate retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 2472*7c478bd9Sstevel@tonic-gate &power_req, &result); 2473*7c478bd9Sstevel@tonic-gate #endif 2474*7c478bd9Sstevel@tonic-gate ASSERT(retval == DDI_SUCCESS); 2475*7c478bd9Sstevel@tonic-gate pm_rem_info(dip); 2476*7c478bd9Sstevel@tonic-gate return (retval); 2477*7c478bd9Sstevel@tonic-gate } 2478*7c478bd9Sstevel@tonic-gate 2479*7c478bd9Sstevel@tonic-gate int 2480*7c478bd9Sstevel@tonic-gate pm_raise_power(dev_info_t *dip, int comp, int level) 2481*7c478bd9Sstevel@tonic-gate { 2482*7c478bd9Sstevel@tonic-gate if (level < 0) 2483*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2484*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) || 2485*7c478bd9Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) 2486*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2487*7c478bd9Sstevel@tonic-gate 2488*7c478bd9Sstevel@tonic-gate return (dev_is_needed(dip, comp, level, PM_LEVEL_UPONLY)); 2489*7c478bd9Sstevel@tonic-gate } 2490*7c478bd9Sstevel@tonic-gate 2491*7c478bd9Sstevel@tonic-gate int 2492*7c478bd9Sstevel@tonic-gate pm_lower_power(dev_info_t *dip, int comp, int level) 2493*7c478bd9Sstevel@tonic-gate { 2494*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_lower_power") 2495*7c478bd9Sstevel@tonic-gate 2496*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) || 2497*7c478bd9Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) { 2498*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) " 2499*7c478bd9Sstevel@tonic-gate "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 2500*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2501*7c478bd9Sstevel@tonic-gate } 2502*7c478bd9Sstevel@tonic-gate 2503*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) { 2504*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) not detaching\n", 2505*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 2506*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2507*7c478bd9Sstevel@tonic-gate } 2508*7c478bd9Sstevel@tonic-gate 2509*7c478bd9Sstevel@tonic-gate /* 2510*7c478bd9Sstevel@tonic-gate * If we don't care about saving power, or we're treating this node 2511*7c478bd9Sstevel@tonic-gate * specially, then this is a no-op 2512*7c478bd9Sstevel@tonic-gate */ 2513*7c478bd9Sstevel@tonic-gate if (!autopm_enabled || pm_noinvol(dip)) { 2514*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) %s%s\n", pmf, PM_DEVICE(dip), 2515*7c478bd9Sstevel@tonic-gate !autopm_enabled ? "!autopm_enabled " : "", 2516*7c478bd9Sstevel@tonic-gate pm_noinvol(dip) ? "pm_noinvol()" : "")) 2517*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2518*7c478bd9Sstevel@tonic-gate } 2519*7c478bd9Sstevel@tonic-gate 2520*7c478bd9Sstevel@tonic-gate if (dev_is_needed(dip, comp, level, PM_LEVEL_DOWNONLY) != DDI_SUCCESS) { 2521*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) dev_is_needed failed\n", pmf, 2522*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 2523*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2524*7c478bd9Sstevel@tonic-gate } 2525*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2526*7c478bd9Sstevel@tonic-gate } 2527*7c478bd9Sstevel@tonic-gate 2528*7c478bd9Sstevel@tonic-gate /* 2529*7c478bd9Sstevel@tonic-gate * Find the entries struct for a given dip in the blocked list, return it locked 2530*7c478bd9Sstevel@tonic-gate */ 2531*7c478bd9Sstevel@tonic-gate static psce_t * 2532*7c478bd9Sstevel@tonic-gate pm_psc_dip_to_direct(dev_info_t *dip, pscc_t **psccp) 2533*7c478bd9Sstevel@tonic-gate { 2534*7c478bd9Sstevel@tonic-gate pscc_t *p; 2535*7c478bd9Sstevel@tonic-gate psce_t *psce; 2536*7c478bd9Sstevel@tonic-gate 2537*7c478bd9Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_READER); 2538*7c478bd9Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = p->pscc_next) { 2539*7c478bd9Sstevel@tonic-gate if (p->pscc_dip == dip) { 2540*7c478bd9Sstevel@tonic-gate *psccp = p; 2541*7c478bd9Sstevel@tonic-gate psce = p->pscc_entries; 2542*7c478bd9Sstevel@tonic-gate mutex_enter(&psce->psce_lock); 2543*7c478bd9Sstevel@tonic-gate ASSERT(psce); 2544*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 2545*7c478bd9Sstevel@tonic-gate return (psce); 2546*7c478bd9Sstevel@tonic-gate } 2547*7c478bd9Sstevel@tonic-gate } 2548*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 2549*7c478bd9Sstevel@tonic-gate panic("sunpm: no entry for dip %p in direct list", (void *)dip); 2550*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 2551*7c478bd9Sstevel@tonic-gate } 2552*7c478bd9Sstevel@tonic-gate 2553*7c478bd9Sstevel@tonic-gate /* 2554*7c478bd9Sstevel@tonic-gate * Write an entry indicating a power level change (to be passed to a process 2555*7c478bd9Sstevel@tonic-gate * later) in the given psce. 2556*7c478bd9Sstevel@tonic-gate * If we were called in the path that brings up the console fb in the 2557*7c478bd9Sstevel@tonic-gate * case of entering the prom, we don't want to sleep. If the alloc fails, then 2558*7c478bd9Sstevel@tonic-gate * we create a record that has a size of -1, a physaddr of NULL, and that 2559*7c478bd9Sstevel@tonic-gate * has the overflow flag set. 2560*7c478bd9Sstevel@tonic-gate */ 2561*7c478bd9Sstevel@tonic-gate static int 2562*7c478bd9Sstevel@tonic-gate psc_entry(ushort_t event, psce_t *psce, dev_info_t *dip, int comp, int new, 2563*7c478bd9Sstevel@tonic-gate int old, int which, pm_canblock_t canblock) 2564*7c478bd9Sstevel@tonic-gate { 2565*7c478bd9Sstevel@tonic-gate char buf[MAXNAMELEN]; 2566*7c478bd9Sstevel@tonic-gate pm_state_change_t *p; 2567*7c478bd9Sstevel@tonic-gate size_t size; 2568*7c478bd9Sstevel@tonic-gate caddr_t physpath = NULL; 2569*7c478bd9Sstevel@tonic-gate int overrun = 0; 2570*7c478bd9Sstevel@tonic-gate 2571*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&psce->psce_lock)); 2572*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, buf); 2573*7c478bd9Sstevel@tonic-gate size = strlen(buf) + 1; 2574*7c478bd9Sstevel@tonic-gate p = psce->psce_in; 2575*7c478bd9Sstevel@tonic-gate if (canblock == PM_CANBLOCK_BYPASS) { 2576*7c478bd9Sstevel@tonic-gate physpath = kmem_alloc(size, KM_NOSLEEP); 2577*7c478bd9Sstevel@tonic-gate if (physpath == NULL) { 2578*7c478bd9Sstevel@tonic-gate /* 2579*7c478bd9Sstevel@tonic-gate * mark current entry as overrun 2580*7c478bd9Sstevel@tonic-gate */ 2581*7c478bd9Sstevel@tonic-gate p->flags |= PSC_EVENT_LOST; 2582*7c478bd9Sstevel@tonic-gate size = (size_t)-1; 2583*7c478bd9Sstevel@tonic-gate } 2584*7c478bd9Sstevel@tonic-gate } else 2585*7c478bd9Sstevel@tonic-gate physpath = kmem_alloc(size, KM_SLEEP); 2586*7c478bd9Sstevel@tonic-gate if (p->size) { /* overflow; mark the next entry */ 2587*7c478bd9Sstevel@tonic-gate if (p->size != (size_t)-1) 2588*7c478bd9Sstevel@tonic-gate kmem_free(p->physpath, p->size); 2589*7c478bd9Sstevel@tonic-gate ASSERT(psce->psce_out == p); 2590*7c478bd9Sstevel@tonic-gate if (p == psce->psce_last) { 2591*7c478bd9Sstevel@tonic-gate psce->psce_first->flags |= PSC_EVENT_LOST; 2592*7c478bd9Sstevel@tonic-gate psce->psce_out = psce->psce_first; 2593*7c478bd9Sstevel@tonic-gate } else { 2594*7c478bd9Sstevel@tonic-gate (p + 1)->flags |= PSC_EVENT_LOST; 2595*7c478bd9Sstevel@tonic-gate psce->psce_out = (p + 1); 2596*7c478bd9Sstevel@tonic-gate } 2597*7c478bd9Sstevel@tonic-gate overrun++; 2598*7c478bd9Sstevel@tonic-gate } else if (physpath == NULL) { /* alloc failed, mark this entry */ 2599*7c478bd9Sstevel@tonic-gate p->flags |= PSC_EVENT_LOST; 2600*7c478bd9Sstevel@tonic-gate p->size = 0; 2601*7c478bd9Sstevel@tonic-gate p->physpath = NULL; 2602*7c478bd9Sstevel@tonic-gate } 2603*7c478bd9Sstevel@tonic-gate if (which == PSC_INTEREST) { 2604*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); 2605*7c478bd9Sstevel@tonic-gate if (pm_comps_notlowest == 0) 2606*7c478bd9Sstevel@tonic-gate p->flags |= PSC_ALL_LOWEST; 2607*7c478bd9Sstevel@tonic-gate else 2608*7c478bd9Sstevel@tonic-gate p->flags &= ~PSC_ALL_LOWEST; 2609*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); 2610*7c478bd9Sstevel@tonic-gate } 2611*7c478bd9Sstevel@tonic-gate p->event = event; 2612*7c478bd9Sstevel@tonic-gate p->timestamp = gethrestime_sec(); 2613*7c478bd9Sstevel@tonic-gate p->component = comp; 2614*7c478bd9Sstevel@tonic-gate p->old_level = old; 2615*7c478bd9Sstevel@tonic-gate p->new_level = new; 2616*7c478bd9Sstevel@tonic-gate p->physpath = physpath; 2617*7c478bd9Sstevel@tonic-gate p->size = size; 2618*7c478bd9Sstevel@tonic-gate if (physpath != NULL) 2619*7c478bd9Sstevel@tonic-gate (void) strcpy(p->physpath, buf); 2620*7c478bd9Sstevel@tonic-gate if (p == psce->psce_last) 2621*7c478bd9Sstevel@tonic-gate psce->psce_in = psce->psce_first; 2622*7c478bd9Sstevel@tonic-gate else 2623*7c478bd9Sstevel@tonic-gate psce->psce_in = ++p; 2624*7c478bd9Sstevel@tonic-gate mutex_exit(&psce->psce_lock); 2625*7c478bd9Sstevel@tonic-gate return (overrun); 2626*7c478bd9Sstevel@tonic-gate } 2627*7c478bd9Sstevel@tonic-gate 2628*7c478bd9Sstevel@tonic-gate /* 2629*7c478bd9Sstevel@tonic-gate * Find the next entry on the interest list. We keep a pointer to the item we 2630*7c478bd9Sstevel@tonic-gate * last returned in the user's cooke. Returns a locked entries struct. 2631*7c478bd9Sstevel@tonic-gate */ 2632*7c478bd9Sstevel@tonic-gate static psce_t * 2633*7c478bd9Sstevel@tonic-gate psc_interest(void **cookie, pscc_t **psccp) 2634*7c478bd9Sstevel@tonic-gate { 2635*7c478bd9Sstevel@tonic-gate pscc_t *pscc; 2636*7c478bd9Sstevel@tonic-gate pscc_t **cookiep = (pscc_t **)cookie; 2637*7c478bd9Sstevel@tonic-gate 2638*7c478bd9Sstevel@tonic-gate if (*cookiep == NULL) 2639*7c478bd9Sstevel@tonic-gate pscc = pm_pscc_interest; 2640*7c478bd9Sstevel@tonic-gate else 2641*7c478bd9Sstevel@tonic-gate pscc = (*cookiep)->pscc_next; 2642*7c478bd9Sstevel@tonic-gate if (pscc) { 2643*7c478bd9Sstevel@tonic-gate *cookiep = pscc; 2644*7c478bd9Sstevel@tonic-gate *psccp = pscc; 2645*7c478bd9Sstevel@tonic-gate mutex_enter(&pscc->pscc_entries->psce_lock); 2646*7c478bd9Sstevel@tonic-gate return (pscc->pscc_entries); 2647*7c478bd9Sstevel@tonic-gate } else { 2648*7c478bd9Sstevel@tonic-gate return (NULL); 2649*7c478bd9Sstevel@tonic-gate } 2650*7c478bd9Sstevel@tonic-gate } 2651*7c478bd9Sstevel@tonic-gate 2652*7c478bd9Sstevel@tonic-gate /* 2653*7c478bd9Sstevel@tonic-gate * Create an entry for a process to pick up indicating a power level change. 2654*7c478bd9Sstevel@tonic-gate */ 2655*7c478bd9Sstevel@tonic-gate static void 2656*7c478bd9Sstevel@tonic-gate pm_enqueue_notify(ushort_t cmd, dev_info_t *dip, int comp, 2657*7c478bd9Sstevel@tonic-gate int newlevel, int oldlevel, pm_canblock_t canblock) 2658*7c478bd9Sstevel@tonic-gate { 2659*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "enqueue_notify") 2660*7c478bd9Sstevel@tonic-gate pscc_t *pscc; 2661*7c478bd9Sstevel@tonic-gate psce_t *psce; 2662*7c478bd9Sstevel@tonic-gate void *cookie = NULL; 2663*7c478bd9Sstevel@tonic-gate int overrun; 2664*7c478bd9Sstevel@tonic-gate 2665*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 2666*7c478bd9Sstevel@tonic-gate switch (cmd) { 2667*7c478bd9Sstevel@tonic-gate case PSC_PENDING_CHANGE: /* only for controlling process */ 2668*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: PENDING %s@%s(%s#%d), comp %d, %d -> %d\n", 2669*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, oldlevel, newlevel)) 2670*7c478bd9Sstevel@tonic-gate psce = pm_psc_dip_to_direct(dip, &pscc); 2671*7c478bd9Sstevel@tonic-gate ASSERT(psce); 2672*7c478bd9Sstevel@tonic-gate PMD(PMD_IOCTL, ("%s: PENDING: %s@%s(%s#%d) pm_poll_cnt[%d] " 2673*7c478bd9Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone, 2674*7c478bd9Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone])) 2675*7c478bd9Sstevel@tonic-gate overrun = psc_entry(cmd, psce, dip, comp, newlevel, oldlevel, 2676*7c478bd9Sstevel@tonic-gate PSC_DIRECT, canblock); 2677*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone)) 2678*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_clone_lock); 2679*7c478bd9Sstevel@tonic-gate if (!overrun) 2680*7c478bd9Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]++; 2681*7c478bd9Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]); 2682*7c478bd9Sstevel@tonic-gate pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN)); 2683*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_clone_lock); 2684*7c478bd9Sstevel@tonic-gate break; 2685*7c478bd9Sstevel@tonic-gate case PSC_HAS_CHANGED: 2686*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: HAS %s@%s(%s#%d), comp %d, %d -> %d\n", 2687*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, oldlevel, newlevel)) 2688*7c478bd9Sstevel@tonic-gate if (PM_ISDIRECT(dip) && canblock != PM_CANBLOCK_BYPASS) { 2689*7c478bd9Sstevel@tonic-gate psce = pm_psc_dip_to_direct(dip, &pscc); 2690*7c478bd9Sstevel@tonic-gate PMD(PMD_IOCTL, ("%s: HAS: %s@%s(%s#%d) pm_poll_cnt[%d] " 2691*7c478bd9Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone, 2692*7c478bd9Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone])) 2693*7c478bd9Sstevel@tonic-gate overrun = psc_entry(cmd, psce, dip, comp, newlevel, 2694*7c478bd9Sstevel@tonic-gate oldlevel, PSC_DIRECT, canblock); 2695*7c478bd9Sstevel@tonic-gate PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone)) 2696*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_clone_lock); 2697*7c478bd9Sstevel@tonic-gate if (!overrun) 2698*7c478bd9Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]++; 2699*7c478bd9Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]); 2700*7c478bd9Sstevel@tonic-gate pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN)); 2701*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_clone_lock); 2702*7c478bd9Sstevel@tonic-gate } 2703*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_clone_lock); 2704*7c478bd9Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_READER); 2705*7c478bd9Sstevel@tonic-gate while ((psce = psc_interest(&cookie, &pscc)) != NULL) { 2706*7c478bd9Sstevel@tonic-gate (void) psc_entry(cmd, psce, dip, comp, newlevel, 2707*7c478bd9Sstevel@tonic-gate oldlevel, PSC_INTEREST, canblock); 2708*7c478bd9Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]); 2709*7c478bd9Sstevel@tonic-gate } 2710*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock); 2711*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_clone_lock); 2712*7c478bd9Sstevel@tonic-gate break; 2713*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 2714*7c478bd9Sstevel@tonic-gate default: 2715*7c478bd9Sstevel@tonic-gate ASSERT(0); 2716*7c478bd9Sstevel@tonic-gate #endif 2717*7c478bd9Sstevel@tonic-gate } 2718*7c478bd9Sstevel@tonic-gate } 2719*7c478bd9Sstevel@tonic-gate 2720*7c478bd9Sstevel@tonic-gate static void 2721*7c478bd9Sstevel@tonic-gate pm_enqueue_notify_others(pm_ppm_devlist_t **listp, pm_canblock_t canblock) 2722*7c478bd9Sstevel@tonic-gate { 2723*7c478bd9Sstevel@tonic-gate if (listp) { 2724*7c478bd9Sstevel@tonic-gate pm_ppm_devlist_t *p, *next = NULL; 2725*7c478bd9Sstevel@tonic-gate 2726*7c478bd9Sstevel@tonic-gate for (p = *listp; p; p = next) { 2727*7c478bd9Sstevel@tonic-gate next = p->ppd_next; 2728*7c478bd9Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, p->ppd_who, 2729*7c478bd9Sstevel@tonic-gate p->ppd_cmpt, p->ppd_new_level, p->ppd_old_level, 2730*7c478bd9Sstevel@tonic-gate canblock); 2731*7c478bd9Sstevel@tonic-gate kmem_free(p, sizeof (pm_ppm_devlist_t)); 2732*7c478bd9Sstevel@tonic-gate } 2733*7c478bd9Sstevel@tonic-gate *listp = NULL; 2734*7c478bd9Sstevel@tonic-gate } 2735*7c478bd9Sstevel@tonic-gate } 2736*7c478bd9Sstevel@tonic-gate 2737*7c478bd9Sstevel@tonic-gate /* 2738*7c478bd9Sstevel@tonic-gate * Try to get the power locks of the parent node and target (child) 2739*7c478bd9Sstevel@tonic-gate * node. Return true if successful (with both locks held) or false 2740*7c478bd9Sstevel@tonic-gate * (with no locks held). 2741*7c478bd9Sstevel@tonic-gate */ 2742*7c478bd9Sstevel@tonic-gate static int 2743*7c478bd9Sstevel@tonic-gate pm_try_parent_child_locks(dev_info_t *pdip, 2744*7c478bd9Sstevel@tonic-gate dev_info_t *dip, int *pcircp, int *circp) 2745*7c478bd9Sstevel@tonic-gate { 2746*7c478bd9Sstevel@tonic-gate if (ndi_devi_tryenter(pdip, pcircp)) 2747*7c478bd9Sstevel@tonic-gate if (PM_TRY_LOCK_POWER(dip, circp)) { 2748*7c478bd9Sstevel@tonic-gate return (1); 2749*7c478bd9Sstevel@tonic-gate } else { 2750*7c478bd9Sstevel@tonic-gate ndi_devi_exit(pdip, *pcircp); 2751*7c478bd9Sstevel@tonic-gate } 2752*7c478bd9Sstevel@tonic-gate return (0); 2753*7c478bd9Sstevel@tonic-gate } 2754*7c478bd9Sstevel@tonic-gate 2755*7c478bd9Sstevel@tonic-gate /* 2756*7c478bd9Sstevel@tonic-gate * Determine if the power lock owner is blocked by current thread. 2757*7c478bd9Sstevel@tonic-gate * returns : 2758*7c478bd9Sstevel@tonic-gate * 1 - If the thread owning the effective power lock (the first lock on 2759*7c478bd9Sstevel@tonic-gate * which a thread blocks when it does PM_LOCK_POWER) is blocked by 2760*7c478bd9Sstevel@tonic-gate * a mutex held by the current thread. 2761*7c478bd9Sstevel@tonic-gate * 2762*7c478bd9Sstevel@tonic-gate * 0 - otherwise 2763*7c478bd9Sstevel@tonic-gate * 2764*7c478bd9Sstevel@tonic-gate * Note : This function is called by pm_power_has_changed to determine whether 2765*7c478bd9Sstevel@tonic-gate * it is executing in parallel with pm_set_power. 2766*7c478bd9Sstevel@tonic-gate */ 2767*7c478bd9Sstevel@tonic-gate static int 2768*7c478bd9Sstevel@tonic-gate pm_blocked_by_us(dev_info_t *dip) 2769*7c478bd9Sstevel@tonic-gate { 2770*7c478bd9Sstevel@tonic-gate power_req_t power_req; 2771*7c478bd9Sstevel@tonic-gate kthread_t *owner; 2772*7c478bd9Sstevel@tonic-gate int result; 2773*7c478bd9Sstevel@tonic-gate kmutex_t *mp; 2774*7c478bd9Sstevel@tonic-gate dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm; 2775*7c478bd9Sstevel@tonic-gate 2776*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_LOCK_OWNER; 2777*7c478bd9Sstevel@tonic-gate power_req.req.ppm_power_lock_owner_req.who = dip; 2778*7c478bd9Sstevel@tonic-gate if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, &result) != 2779*7c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 2780*7c478bd9Sstevel@tonic-gate /* 2781*7c478bd9Sstevel@tonic-gate * It is assumed that if the device is claimed by ppm, ppm 2782*7c478bd9Sstevel@tonic-gate * will always implement this request type and it'll always 2783*7c478bd9Sstevel@tonic-gate * return success. We panic here, if it fails. 2784*7c478bd9Sstevel@tonic-gate */ 2785*7c478bd9Sstevel@tonic-gate panic("pm: Can't determine power lock owner of %s@%s(%s#%d)\n", 2786*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip)); 2787*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 2788*7c478bd9Sstevel@tonic-gate } 2789*7c478bd9Sstevel@tonic-gate 2790*7c478bd9Sstevel@tonic-gate if ((owner = power_req.req.ppm_power_lock_owner_req.owner) != NULL && 2791*7c478bd9Sstevel@tonic-gate owner->t_state == TS_SLEEP && 2792*7c478bd9Sstevel@tonic-gate owner->t_sobj_ops && 2793*7c478bd9Sstevel@tonic-gate SOBJ_TYPE(owner->t_sobj_ops) == SOBJ_MUTEX && 2794*7c478bd9Sstevel@tonic-gate (mp = (kmutex_t *)owner->t_wchan) && 2795*7c478bd9Sstevel@tonic-gate mutex_owner(mp) == curthread) 2796*7c478bd9Sstevel@tonic-gate return (1); 2797*7c478bd9Sstevel@tonic-gate 2798*7c478bd9Sstevel@tonic-gate return (0); 2799*7c478bd9Sstevel@tonic-gate } 2800*7c478bd9Sstevel@tonic-gate 2801*7c478bd9Sstevel@tonic-gate /* 2802*7c478bd9Sstevel@tonic-gate * Notify parent which wants to hear about a child's power changes. 2803*7c478bd9Sstevel@tonic-gate */ 2804*7c478bd9Sstevel@tonic-gate static void 2805*7c478bd9Sstevel@tonic-gate pm_notify_parent(dev_info_t *dip, 2806*7c478bd9Sstevel@tonic-gate dev_info_t *pdip, int comp, int old_level, int level) 2807*7c478bd9Sstevel@tonic-gate { 2808*7c478bd9Sstevel@tonic-gate pm_bp_has_changed_t bphc; 2809*7c478bd9Sstevel@tonic-gate pm_sp_misc_t pspm; 2810*7c478bd9Sstevel@tonic-gate char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2811*7c478bd9Sstevel@tonic-gate int result = DDI_SUCCESS; 2812*7c478bd9Sstevel@tonic-gate 2813*7c478bd9Sstevel@tonic-gate bphc.bphc_dip = dip; 2814*7c478bd9Sstevel@tonic-gate bphc.bphc_path = ddi_pathname(dip, pathbuf); 2815*7c478bd9Sstevel@tonic-gate bphc.bphc_comp = comp; 2816*7c478bd9Sstevel@tonic-gate bphc.bphc_olevel = old_level; 2817*7c478bd9Sstevel@tonic-gate bphc.bphc_nlevel = level; 2818*7c478bd9Sstevel@tonic-gate pspm.pspm_canblock = PM_CANBLOCK_BLOCK; 2819*7c478bd9Sstevel@tonic-gate pspm.pspm_scan = 0; 2820*7c478bd9Sstevel@tonic-gate bphc.bphc_private = &pspm; 2821*7c478bd9Sstevel@tonic-gate (void) (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL, 2822*7c478bd9Sstevel@tonic-gate BUS_POWER_HAS_CHANGED, (void *)&bphc, (void *)&result); 2823*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 2824*7c478bd9Sstevel@tonic-gate } 2825*7c478bd9Sstevel@tonic-gate 2826*7c478bd9Sstevel@tonic-gate /* 2827*7c478bd9Sstevel@tonic-gate * Check if we need to resume a BC device, and make the attach call as required. 2828*7c478bd9Sstevel@tonic-gate */ 2829*7c478bd9Sstevel@tonic-gate static int 2830*7c478bd9Sstevel@tonic-gate pm_check_and_resume(dev_info_t *dip, int comp, int old_level, int level) 2831*7c478bd9Sstevel@tonic-gate { 2832*7c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 2833*7c478bd9Sstevel@tonic-gate 2834*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip) && comp == 0 && old_level == 0 && level != 0) { 2835*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED); 2836*7c478bd9Sstevel@tonic-gate /* ppm is not interested in DDI_PM_RESUME */ 2837*7c478bd9Sstevel@tonic-gate if ((ret = devi_attach(dip, DDI_PM_RESUME)) != DDI_SUCCESS) 2838*7c478bd9Sstevel@tonic-gate /* XXX Should we mark it resumed, */ 2839*7c478bd9Sstevel@tonic-gate /* even though it failed? */ 2840*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!pm: Can't resume %s@%s", 2841*7c478bd9Sstevel@tonic-gate PM_NAME(dip), PM_ADDR(dip)); 2842*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED; 2843*7c478bd9Sstevel@tonic-gate } 2844*7c478bd9Sstevel@tonic-gate 2845*7c478bd9Sstevel@tonic-gate return (ret); 2846*7c478bd9Sstevel@tonic-gate } 2847*7c478bd9Sstevel@tonic-gate 2848*7c478bd9Sstevel@tonic-gate /* 2849*7c478bd9Sstevel@tonic-gate * Tests outside the lock to see if we should bother to enqueue an entry 2850*7c478bd9Sstevel@tonic-gate * for any watching process. If yes, then caller will take the lock and 2851*7c478bd9Sstevel@tonic-gate * do the full protocol 2852*7c478bd9Sstevel@tonic-gate */ 2853*7c478bd9Sstevel@tonic-gate static int 2854*7c478bd9Sstevel@tonic-gate pm_watchers() 2855*7c478bd9Sstevel@tonic-gate { 2856*7c478bd9Sstevel@tonic-gate if (pm_processes_stopped) 2857*7c478bd9Sstevel@tonic-gate return (0); 2858*7c478bd9Sstevel@tonic-gate return (pm_pscc_direct || pm_pscc_interest); 2859*7c478bd9Sstevel@tonic-gate } 2860*7c478bd9Sstevel@tonic-gate 2861*7c478bd9Sstevel@tonic-gate /* 2862*7c478bd9Sstevel@tonic-gate * A driver is reporting that the power of one of its device's components 2863*7c478bd9Sstevel@tonic-gate * has changed. Update the power state accordingly. 2864*7c478bd9Sstevel@tonic-gate */ 2865*7c478bd9Sstevel@tonic-gate int 2866*7c478bd9Sstevel@tonic-gate pm_power_has_changed(dev_info_t *dip, int comp, int level) 2867*7c478bd9Sstevel@tonic-gate { 2868*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_power_has_changed") 2869*7c478bd9Sstevel@tonic-gate int ret; 2870*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 2871*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 2872*7c478bd9Sstevel@tonic-gate int blocked, circ, pcirc, old_level; 2873*7c478bd9Sstevel@tonic-gate static int pm_phc_impl(dev_info_t *, int, int, int); 2874*7c478bd9Sstevel@tonic-gate 2875*7c478bd9Sstevel@tonic-gate if (level < 0) { 2876*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d): bad level=%d\n", pmf, 2877*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), level)) 2878*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2879*7c478bd9Sstevel@tonic-gate } 2880*7c478bd9Sstevel@tonic-gate 2881*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP | PMD_DEP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf, 2882*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), comp, level)) 2883*7c478bd9Sstevel@tonic-gate 2884*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, &cp) || 2885*7c478bd9Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) 2886*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2887*7c478bd9Sstevel@tonic-gate 2888*7c478bd9Sstevel@tonic-gate /* 2889*7c478bd9Sstevel@tonic-gate * A driver thread calling pm_power_has_changed and another thread 2890*7c478bd9Sstevel@tonic-gate * calling pm_set_power can deadlock. The problem is not resolvable 2891*7c478bd9Sstevel@tonic-gate * by changing lock order, so we use pm_blocked_by_us() to detect 2892*7c478bd9Sstevel@tonic-gate * this specific deadlock. If we can't get the lock immediately 2893*7c478bd9Sstevel@tonic-gate * and we are deadlocked, just update the component's level, do 2894*7c478bd9Sstevel@tonic-gate * notifications, and return. We intend to update the total power 2895*7c478bd9Sstevel@tonic-gate * state later (if the other thread fails to set power to the 2896*7c478bd9Sstevel@tonic-gate * desired level). If we were called because of a power change on a 2897*7c478bd9Sstevel@tonic-gate * component that isn't involved in a set_power op, update all state 2898*7c478bd9Sstevel@tonic-gate * immediately. 2899*7c478bd9Sstevel@tonic-gate */ 2900*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, comp); 2901*7c478bd9Sstevel@tonic-gate while (!pm_try_parent_child_locks(pdip, dip, &pcirc, &circ)) { 2902*7c478bd9Sstevel@tonic-gate if (((blocked = pm_blocked_by_us(dip)) != 0) && 2903*7c478bd9Sstevel@tonic-gate (cp->pmc_flags & PM_POWER_OP)) { 2904*7c478bd9Sstevel@tonic-gate if (pm_watchers()) { 2905*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 2906*7c478bd9Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, 2907*7c478bd9Sstevel@tonic-gate level, cur_power(cp), PM_CANBLOCK_BLOCK); 2908*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 2909*7c478bd9Sstevel@tonic-gate } 2910*7c478bd9Sstevel@tonic-gate if (pdip && PM_WANTS_NOTIFICATION(pdip)) 2911*7c478bd9Sstevel@tonic-gate pm_notify_parent(dip, 2912*7c478bd9Sstevel@tonic-gate pdip, comp, cur_power(cp), level); 2913*7c478bd9Sstevel@tonic-gate (void) pm_check_and_resume(dip, 2914*7c478bd9Sstevel@tonic-gate comp, cur_power(cp), level); 2915*7c478bd9Sstevel@tonic-gate 2916*7c478bd9Sstevel@tonic-gate /* 2917*7c478bd9Sstevel@tonic-gate * Stash the old power index, update curpwr, and flag 2918*7c478bd9Sstevel@tonic-gate * that the total power state needs to be synched. 2919*7c478bd9Sstevel@tonic-gate */ 2920*7c478bd9Sstevel@tonic-gate cp->pmc_flags |= PM_PHC_WHILE_SET_POWER; 2921*7c478bd9Sstevel@tonic-gate /* 2922*7c478bd9Sstevel@tonic-gate * Several pm_power_has_changed calls could arrive 2923*7c478bd9Sstevel@tonic-gate * while the set power path remains blocked. Keep the 2924*7c478bd9Sstevel@tonic-gate * oldest old power and the newest new power of any 2925*7c478bd9Sstevel@tonic-gate * sequence of phc calls which arrive during deadlock. 2926*7c478bd9Sstevel@tonic-gate */ 2927*7c478bd9Sstevel@tonic-gate if (cp->pmc_phc_pwr == PM_LEVEL_UNKNOWN) 2928*7c478bd9Sstevel@tonic-gate cp->pmc_phc_pwr = cp->pmc_cur_pwr; 2929*7c478bd9Sstevel@tonic-gate cp->pmc_cur_pwr = 2930*7c478bd9Sstevel@tonic-gate pm_level_to_index(dip, cp, level); 2931*7c478bd9Sstevel@tonic-gate PMD(PMD_PHC, ("%s: deadlock for %s@%s(%s#%d), comp=%d, " 2932*7c478bd9Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 2933*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2934*7c478bd9Sstevel@tonic-gate } else 2935*7c478bd9Sstevel@tonic-gate if (blocked) { /* blocked, but different cmpt? */ 2936*7c478bd9Sstevel@tonic-gate if (!ndi_devi_tryenter(pdip, &pcirc)) { 2937*7c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, 2938*7c478bd9Sstevel@tonic-gate "!pm: parent kuc not updated due " 2939*7c478bd9Sstevel@tonic-gate "to possible deadlock.\n"); 2940*7c478bd9Sstevel@tonic-gate return (pm_phc_impl(dip, 2941*7c478bd9Sstevel@tonic-gate comp, level, 1)); 2942*7c478bd9Sstevel@tonic-gate } 2943*7c478bd9Sstevel@tonic-gate old_level = cur_power(cp); 2944*7c478bd9Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip) && 2945*7c478bd9Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0) && 2946*7c478bd9Sstevel@tonic-gate POWERING_ON(old_level, level)) 2947*7c478bd9Sstevel@tonic-gate pm_hold_power(pdip); 2948*7c478bd9Sstevel@tonic-gate ret = pm_phc_impl(dip, comp, level, 1); 2949*7c478bd9Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 2950*7c478bd9Sstevel@tonic-gate if ((!PM_ISBC(dip) || 2951*7c478bd9Sstevel@tonic-gate comp == 0) && level == 0 && 2952*7c478bd9Sstevel@tonic-gate old_level != PM_LEVEL_UNKNOWN) 2953*7c478bd9Sstevel@tonic-gate pm_rele_power(pdip); 2954*7c478bd9Sstevel@tonic-gate } 2955*7c478bd9Sstevel@tonic-gate ndi_devi_exit(pdip, pcirc); 2956*7c478bd9Sstevel@tonic-gate /* child lock not held: deadlock */ 2957*7c478bd9Sstevel@tonic-gate return (ret); 2958*7c478bd9Sstevel@tonic-gate } 2959*7c478bd9Sstevel@tonic-gate delay(1); 2960*7c478bd9Sstevel@tonic-gate PMD(PMD_PHC, ("%s: try lock again\n", pmf)) 2961*7c478bd9Sstevel@tonic-gate } 2962*7c478bd9Sstevel@tonic-gate 2963*7c478bd9Sstevel@tonic-gate /* non-deadlock case */ 2964*7c478bd9Sstevel@tonic-gate old_level = cur_power(cp); 2965*7c478bd9Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip) && 2966*7c478bd9Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0) && POWERING_ON(old_level, level)) 2967*7c478bd9Sstevel@tonic-gate pm_hold_power(pdip); 2968*7c478bd9Sstevel@tonic-gate ret = pm_phc_impl(dip, comp, level, 1); 2969*7c478bd9Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 2970*7c478bd9Sstevel@tonic-gate if ((!PM_ISBC(dip) || comp == 0) && level == 0 && 2971*7c478bd9Sstevel@tonic-gate old_level != PM_LEVEL_UNKNOWN) 2972*7c478bd9Sstevel@tonic-gate pm_rele_power(pdip); 2973*7c478bd9Sstevel@tonic-gate } 2974*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 2975*7c478bd9Sstevel@tonic-gate ndi_devi_exit(pdip, pcirc); 2976*7c478bd9Sstevel@tonic-gate return (ret); 2977*7c478bd9Sstevel@tonic-gate } 2978*7c478bd9Sstevel@tonic-gate 2979*7c478bd9Sstevel@tonic-gate /* 2980*7c478bd9Sstevel@tonic-gate * Account for power changes to a component of the the console frame buffer. 2981*7c478bd9Sstevel@tonic-gate * If lowering power from full (or "unkown", which is treatd as full) 2982*7c478bd9Sstevel@tonic-gate * we will increment the "components off" count of the fb device. 2983*7c478bd9Sstevel@tonic-gate * Subsequent lowering of the same component doesn't affect the count. If 2984*7c478bd9Sstevel@tonic-gate * raising a component back to full power, we will decrement the count. 2985*7c478bd9Sstevel@tonic-gate * 2986*7c478bd9Sstevel@tonic-gate * Return: the increment value for pm_cfb_comps_off (-1, 0, or 1) 2987*7c478bd9Sstevel@tonic-gate */ 2988*7c478bd9Sstevel@tonic-gate static int 2989*7c478bd9Sstevel@tonic-gate calc_cfb_comps_incr(dev_info_t *dip, int cmpt, int old, int new) 2990*7c478bd9Sstevel@tonic-gate { 2991*7c478bd9Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, cmpt); 2992*7c478bd9Sstevel@tonic-gate int on = (old == PM_LEVEL_UNKNOWN || old == cp->pmc_norm_pwr); 2993*7c478bd9Sstevel@tonic-gate int want_normal = (new == cp->pmc_norm_pwr); 2994*7c478bd9Sstevel@tonic-gate int incr = 0; 2995*7c478bd9Sstevel@tonic-gate 2996*7c478bd9Sstevel@tonic-gate if (on && !want_normal) 2997*7c478bd9Sstevel@tonic-gate incr = 1; 2998*7c478bd9Sstevel@tonic-gate else if (!on && want_normal) 2999*7c478bd9Sstevel@tonic-gate incr = -1; 3000*7c478bd9Sstevel@tonic-gate return (incr); 3001*7c478bd9Sstevel@tonic-gate } 3002*7c478bd9Sstevel@tonic-gate 3003*7c478bd9Sstevel@tonic-gate /* 3004*7c478bd9Sstevel@tonic-gate * Adjust the count of console frame buffer components < full power. 3005*7c478bd9Sstevel@tonic-gate */ 3006*7c478bd9Sstevel@tonic-gate static void 3007*7c478bd9Sstevel@tonic-gate update_comps_off(int incr, dev_info_t *dip) 3008*7c478bd9Sstevel@tonic-gate { 3009*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 3010*7c478bd9Sstevel@tonic-gate pm_cfb_comps_off += incr; 3011*7c478bd9Sstevel@tonic-gate ASSERT(pm_cfb_comps_off <= PM_NUMCMPTS(dip)); 3012*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 3013*7c478bd9Sstevel@tonic-gate } 3014*7c478bd9Sstevel@tonic-gate 3015*7c478bd9Sstevel@tonic-gate /* 3016*7c478bd9Sstevel@tonic-gate * Update the power state in the framework (via the ppm). The 'notify' 3017*7c478bd9Sstevel@tonic-gate * argument tells whether to notify watchers. Power lock is already held. 3018*7c478bd9Sstevel@tonic-gate */ 3019*7c478bd9Sstevel@tonic-gate static int 3020*7c478bd9Sstevel@tonic-gate pm_phc_impl(dev_info_t *dip, int comp, int level, int notify) 3021*7c478bd9Sstevel@tonic-gate { 3022*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "phc_impl") 3023*7c478bd9Sstevel@tonic-gate power_req_t power_req; 3024*7c478bd9Sstevel@tonic-gate int i, dodeps = 0; 3025*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 3026*7c478bd9Sstevel@tonic-gate int result; 3027*7c478bd9Sstevel@tonic-gate int old_level; 3028*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 3029*7c478bd9Sstevel@tonic-gate int incr = 0; 3030*7c478bd9Sstevel@tonic-gate dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm; 3031*7c478bd9Sstevel@tonic-gate int work_type = 0; 3032*7c478bd9Sstevel@tonic-gate char *pathbuf; 3033*7c478bd9Sstevel@tonic-gate 3034*7c478bd9Sstevel@tonic-gate /* Must use "official" power level for this test. */ 3035*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, comp); 3036*7c478bd9Sstevel@tonic-gate old_level = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ? 3037*7c478bd9Sstevel@tonic-gate cp->pmc_phc_pwr : cp->pmc_cur_pwr); 3038*7c478bd9Sstevel@tonic-gate if (old_level != PM_LEVEL_UNKNOWN) 3039*7c478bd9Sstevel@tonic-gate old_level = cp->pmc_comp.pmc_lvals[old_level]; 3040*7c478bd9Sstevel@tonic-gate 3041*7c478bd9Sstevel@tonic-gate if (level == old_level) { 3042*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d is already at " 3043*7c478bd9Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 3044*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3045*7c478bd9Sstevel@tonic-gate } 3046*7c478bd9Sstevel@tonic-gate 3047*7c478bd9Sstevel@tonic-gate /* 3048*7c478bd9Sstevel@tonic-gate * Tell ppm about this. 3049*7c478bd9Sstevel@tonic-gate */ 3050*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY; 3051*7c478bd9Sstevel@tonic-gate power_req.req.ppm_notify_level_req.who = dip; 3052*7c478bd9Sstevel@tonic-gate power_req.req.ppm_notify_level_req.cmpt = comp; 3053*7c478bd9Sstevel@tonic-gate power_req.req.ppm_notify_level_req.new_level = level; 3054*7c478bd9Sstevel@tonic-gate power_req.req.ppm_notify_level_req.old_level = old_level; 3055*7c478bd9Sstevel@tonic-gate if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, 3056*7c478bd9Sstevel@tonic-gate &result) == DDI_FAILURE) { 3057*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: pm_ctlops %s@%s(%s#%d) to %d failed\n", 3058*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), level)) 3059*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3060*7c478bd9Sstevel@tonic-gate } 3061*7c478bd9Sstevel@tonic-gate 3062*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 3063*7c478bd9Sstevel@tonic-gate incr = calc_cfb_comps_incr(dip, comp, old_level, level); 3064*7c478bd9Sstevel@tonic-gate 3065*7c478bd9Sstevel@tonic-gate if (incr) { 3066*7c478bd9Sstevel@tonic-gate update_comps_off(incr, dip); 3067*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) comp=%d %d->%d " 3068*7c478bd9Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip), 3069*7c478bd9Sstevel@tonic-gate comp, old_level, level, pm_cfb_comps_off)) 3070*7c478bd9Sstevel@tonic-gate } 3071*7c478bd9Sstevel@tonic-gate } 3072*7c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level); 3073*7c478bd9Sstevel@tonic-gate result = DDI_SUCCESS; 3074*7c478bd9Sstevel@tonic-gate 3075*7c478bd9Sstevel@tonic-gate if (notify) { 3076*7c478bd9Sstevel@tonic-gate if (pdip && PM_WANTS_NOTIFICATION(pdip)) 3077*7c478bd9Sstevel@tonic-gate pm_notify_parent(dip, pdip, comp, old_level, level); 3078*7c478bd9Sstevel@tonic-gate (void) pm_check_and_resume(dip, comp, old_level, level); 3079*7c478bd9Sstevel@tonic-gate } 3080*7c478bd9Sstevel@tonic-gate 3081*7c478bd9Sstevel@tonic-gate /* 3082*7c478bd9Sstevel@tonic-gate * Decrement the dependency kidsup count if we turn a device 3083*7c478bd9Sstevel@tonic-gate * off. 3084*7c478bd9Sstevel@tonic-gate */ 3085*7c478bd9Sstevel@tonic-gate if (POWERING_OFF(old_level, level)) { 3086*7c478bd9Sstevel@tonic-gate dodeps = 1; 3087*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3088*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 3089*7c478bd9Sstevel@tonic-gate if (cur_power(cp)) { 3090*7c478bd9Sstevel@tonic-gate dodeps = 0; 3091*7c478bd9Sstevel@tonic-gate break; 3092*7c478bd9Sstevel@tonic-gate } 3093*7c478bd9Sstevel@tonic-gate } 3094*7c478bd9Sstevel@tonic-gate if (dodeps) 3095*7c478bd9Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_OFF; 3096*7c478bd9Sstevel@tonic-gate } 3097*7c478bd9Sstevel@tonic-gate 3098*7c478bd9Sstevel@tonic-gate /* 3099*7c478bd9Sstevel@tonic-gate * Increment if we turn it on. Check to see 3100*7c478bd9Sstevel@tonic-gate * if other comps are already on, if so, 3101*7c478bd9Sstevel@tonic-gate * dont increment. 3102*7c478bd9Sstevel@tonic-gate */ 3103*7c478bd9Sstevel@tonic-gate if (POWERING_ON(old_level, level)) { 3104*7c478bd9Sstevel@tonic-gate dodeps = 1; 3105*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3106*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 3107*7c478bd9Sstevel@tonic-gate if (comp == i) 3108*7c478bd9Sstevel@tonic-gate continue; 3109*7c478bd9Sstevel@tonic-gate /* -1 also treated as 0 in this case */ 3110*7c478bd9Sstevel@tonic-gate if (cur_power(cp) > 0) { 3111*7c478bd9Sstevel@tonic-gate dodeps = 0; 3112*7c478bd9Sstevel@tonic-gate break; 3113*7c478bd9Sstevel@tonic-gate } 3114*7c478bd9Sstevel@tonic-gate } 3115*7c478bd9Sstevel@tonic-gate if (dodeps) 3116*7c478bd9Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_ON; 3117*7c478bd9Sstevel@tonic-gate } 3118*7c478bd9Sstevel@tonic-gate 3119*7c478bd9Sstevel@tonic-gate if (dodeps) { 3120*7c478bd9Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 3121*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 3122*7c478bd9Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, NULL, 3123*7c478bd9Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0); 3124*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 3125*7c478bd9Sstevel@tonic-gate } 3126*7c478bd9Sstevel@tonic-gate 3127*7c478bd9Sstevel@tonic-gate if (notify && (level != old_level) && pm_watchers()) { 3128*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 3129*7c478bd9Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, level, old_level, 3130*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_BLOCK); 3131*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 3132*7c478bd9Sstevel@tonic-gate } 3133*7c478bd9Sstevel@tonic-gate 3134*7c478bd9Sstevel@tonic-gate PMD(PMD_RESCAN, ("%s: %s@%s(%s#%d): pm_rescan\n", pmf, PM_DEVICE(dip))) 3135*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 3136*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3137*7c478bd9Sstevel@tonic-gate } 3138*7c478bd9Sstevel@tonic-gate 3139*7c478bd9Sstevel@tonic-gate /* 3140*7c478bd9Sstevel@tonic-gate * This function is called at startup time to notify pm of the existence 3141*7c478bd9Sstevel@tonic-gate * of any platform power managers for this platform. As a result of 3142*7c478bd9Sstevel@tonic-gate * this registration, each function provided will be called each time 3143*7c478bd9Sstevel@tonic-gate * a device node is attached, until one returns true, and it must claim the 3144*7c478bd9Sstevel@tonic-gate * device node (by returning non-zero) if it wants to be involved in the 3145*7c478bd9Sstevel@tonic-gate * node's power management. If it does claim the node, then it will 3146*7c478bd9Sstevel@tonic-gate * subsequently be notified of attach and detach events. 3147*7c478bd9Sstevel@tonic-gate * 3148*7c478bd9Sstevel@tonic-gate */ 3149*7c478bd9Sstevel@tonic-gate 3150*7c478bd9Sstevel@tonic-gate int 3151*7c478bd9Sstevel@tonic-gate pm_register_ppm(int (*func)(dev_info_t *), dev_info_t *dip) 3152*7c478bd9Sstevel@tonic-gate { 3153*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "register_ppm") 3154*7c478bd9Sstevel@tonic-gate struct ppm_callbacks *ppmcp; 3155*7c478bd9Sstevel@tonic-gate pm_component_t *cp; 3156*7c478bd9Sstevel@tonic-gate int i, pwr, result, circ; 3157*7c478bd9Sstevel@tonic-gate power_req_t power_req; 3158*7c478bd9Sstevel@tonic-gate struct ppm_notify_level_req *p = &power_req.req.ppm_notify_level_req; 3159*7c478bd9Sstevel@tonic-gate void pm_ppm_claim(dev_info_t *); 3160*7c478bd9Sstevel@tonic-gate 3161*7c478bd9Sstevel@tonic-gate mutex_enter(&ppm_lock); 3162*7c478bd9Sstevel@tonic-gate ppmcp = ppm_callbacks; 3163*7c478bd9Sstevel@tonic-gate for (i = 0; i < MAX_PPM_HANDLERS; i++, ppmcp++) { 3164*7c478bd9Sstevel@tonic-gate if (ppmcp->ppmc_func == NULL) { 3165*7c478bd9Sstevel@tonic-gate ppmcp->ppmc_func = func; 3166*7c478bd9Sstevel@tonic-gate ppmcp->ppmc_dip = dip; 3167*7c478bd9Sstevel@tonic-gate break; 3168*7c478bd9Sstevel@tonic-gate } 3169*7c478bd9Sstevel@tonic-gate } 3170*7c478bd9Sstevel@tonic-gate mutex_exit(&ppm_lock); 3171*7c478bd9Sstevel@tonic-gate 3172*7c478bd9Sstevel@tonic-gate if (i >= MAX_PPM_HANDLERS) 3173*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3174*7c478bd9Sstevel@tonic-gate while ((dip = ddi_get_parent(dip)) != NULL) { 3175*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) 3176*7c478bd9Sstevel@tonic-gate continue; 3177*7c478bd9Sstevel@tonic-gate pm_ppm_claim(dip); 3178*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { 3179*7c478bd9Sstevel@tonic-gate /* 3180*7c478bd9Sstevel@tonic-gate * Tell ppm about this. 3181*7c478bd9Sstevel@tonic-gate */ 3182*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY; 3183*7c478bd9Sstevel@tonic-gate p->old_level = PM_LEVEL_UNKNOWN; 3184*7c478bd9Sstevel@tonic-gate p->who = dip; 3185*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 3186*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3187*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 3188*7c478bd9Sstevel@tonic-gate pwr = cp->pmc_cur_pwr; 3189*7c478bd9Sstevel@tonic-gate if (pwr != PM_LEVEL_UNKNOWN) { 3190*7c478bd9Sstevel@tonic-gate p->cmpt = i; 3191*7c478bd9Sstevel@tonic-gate p->new_level = cur_power(cp); 3192*7c478bd9Sstevel@tonic-gate p->old_level = PM_LEVEL_UNKNOWN; 3193*7c478bd9Sstevel@tonic-gate if (pm_ctlops(PPM(dip), dip, 3194*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, 3195*7c478bd9Sstevel@tonic-gate &result) == DDI_FAILURE) { 3196*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: pc " 3197*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) to %d " 3198*7c478bd9Sstevel@tonic-gate "fails\n", pmf, 3199*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), pwr)) 3200*7c478bd9Sstevel@tonic-gate } 3201*7c478bd9Sstevel@tonic-gate } 3202*7c478bd9Sstevel@tonic-gate } 3203*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 3204*7c478bd9Sstevel@tonic-gate } 3205*7c478bd9Sstevel@tonic-gate } 3206*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3207*7c478bd9Sstevel@tonic-gate } 3208*7c478bd9Sstevel@tonic-gate 3209*7c478bd9Sstevel@tonic-gate /* 3210*7c478bd9Sstevel@tonic-gate * Call the ppm's that have registered and adjust the devinfo struct as 3211*7c478bd9Sstevel@tonic-gate * appropriate. First one to claim it gets it. The sets of devices claimed 3212*7c478bd9Sstevel@tonic-gate * by each ppm are assumed to be disjoint. 3213*7c478bd9Sstevel@tonic-gate */ 3214*7c478bd9Sstevel@tonic-gate void 3215*7c478bd9Sstevel@tonic-gate pm_ppm_claim(dev_info_t *dip) 3216*7c478bd9Sstevel@tonic-gate { 3217*7c478bd9Sstevel@tonic-gate struct ppm_callbacks *ppmcp; 3218*7c478bd9Sstevel@tonic-gate 3219*7c478bd9Sstevel@tonic-gate if (PPM(dip)) { 3220*7c478bd9Sstevel@tonic-gate return; 3221*7c478bd9Sstevel@tonic-gate } 3222*7c478bd9Sstevel@tonic-gate mutex_enter(&ppm_lock); 3223*7c478bd9Sstevel@tonic-gate for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) { 3224*7c478bd9Sstevel@tonic-gate if ((*ppmcp->ppmc_func)(dip)) { 3225*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_ppm = 3226*7c478bd9Sstevel@tonic-gate (struct dev_info *)ppmcp->ppmc_dip; 3227*7c478bd9Sstevel@tonic-gate mutex_exit(&ppm_lock); 3228*7c478bd9Sstevel@tonic-gate return; 3229*7c478bd9Sstevel@tonic-gate } 3230*7c478bd9Sstevel@tonic-gate } 3231*7c478bd9Sstevel@tonic-gate mutex_exit(&ppm_lock); 3232*7c478bd9Sstevel@tonic-gate } 3233*7c478bd9Sstevel@tonic-gate 3234*7c478bd9Sstevel@tonic-gate /* 3235*7c478bd9Sstevel@tonic-gate * Node is being detached so stop autopm until we see if it succeeds, in which 3236*7c478bd9Sstevel@tonic-gate * case pm_stop will be called. For backwards compatible devices we bring the 3237*7c478bd9Sstevel@tonic-gate * device up to full power on the assumption the detach will succeed. 3238*7c478bd9Sstevel@tonic-gate */ 3239*7c478bd9Sstevel@tonic-gate void 3240*7c478bd9Sstevel@tonic-gate pm_detaching(dev_info_t *dip) 3241*7c478bd9Sstevel@tonic-gate { 3242*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "detaching") 3243*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 3244*7c478bd9Sstevel@tonic-gate int iscons; 3245*7c478bd9Sstevel@tonic-gate 3246*7c478bd9Sstevel@tonic-gate PMD(PMD_REMDEV, ("%s: %s@%s(%s#%d), %d comps\n", pmf, PM_DEVICE(dip), 3247*7c478bd9Sstevel@tonic-gate PM_NUMCMPTS(dip))) 3248*7c478bd9Sstevel@tonic-gate if (info == NULL) 3249*7c478bd9Sstevel@tonic-gate return; 3250*7c478bd9Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip)); 3251*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 3252*7c478bd9Sstevel@tonic-gate info->pmi_dev_pm_state |= PM_DETACHING; 3253*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 3254*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) 3255*7c478bd9Sstevel@tonic-gate pm_scan_stop(dip); 3256*7c478bd9Sstevel@tonic-gate 3257*7c478bd9Sstevel@tonic-gate /* 3258*7c478bd9Sstevel@tonic-gate * console and old-style devices get brought up when detaching. 3259*7c478bd9Sstevel@tonic-gate */ 3260*7c478bd9Sstevel@tonic-gate iscons = PM_IS_CFB(dip); 3261*7c478bd9Sstevel@tonic-gate if (iscons || PM_ISBC(dip)) { 3262*7c478bd9Sstevel@tonic-gate (void) pm_all_to_normal(dip, PM_CANBLOCK_BYPASS); 3263*7c478bd9Sstevel@tonic-gate if (iscons) { 3264*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 3265*7c478bd9Sstevel@tonic-gate while (cfb_inuse) { 3266*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 3267*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: delay; cfb_inuse\n", pmf)) 3268*7c478bd9Sstevel@tonic-gate delay(1); 3269*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 3270*7c478bd9Sstevel@tonic-gate } 3271*7c478bd9Sstevel@tonic-gate ASSERT(cfb_dip_detaching == NULL); 3272*7c478bd9Sstevel@tonic-gate ASSERT(cfb_dip); 3273*7c478bd9Sstevel@tonic-gate cfb_dip_detaching = cfb_dip; /* case detach fails */ 3274*7c478bd9Sstevel@tonic-gate cfb_dip = NULL; 3275*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 3276*7c478bd9Sstevel@tonic-gate } 3277*7c478bd9Sstevel@tonic-gate } 3278*7c478bd9Sstevel@tonic-gate } 3279*7c478bd9Sstevel@tonic-gate 3280*7c478bd9Sstevel@tonic-gate /* 3281*7c478bd9Sstevel@tonic-gate * Node failed to detach. If it used to be autopm'd, make it so again. 3282*7c478bd9Sstevel@tonic-gate */ 3283*7c478bd9Sstevel@tonic-gate void 3284*7c478bd9Sstevel@tonic-gate pm_detach_failed(dev_info_t *dip) 3285*7c478bd9Sstevel@tonic-gate { 3286*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "detach_failed") 3287*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 3288*7c478bd9Sstevel@tonic-gate int pm_all_at_normal(dev_info_t *); 3289*7c478bd9Sstevel@tonic-gate 3290*7c478bd9Sstevel@tonic-gate if (info == NULL) 3291*7c478bd9Sstevel@tonic-gate return; 3292*7c478bd9Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip)); 3293*7c478bd9Sstevel@tonic-gate if (info->pmi_dev_pm_state & PM_DETACHING) { 3294*7c478bd9Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_DETACHING; 3295*7c478bd9Sstevel@tonic-gate if (info->pmi_dev_pm_state & PM_ALLNORM_DEFERRED) { 3296*7c478bd9Sstevel@tonic-gate /* Make sure the operation is still needed */ 3297*7c478bd9Sstevel@tonic-gate if (!pm_all_at_normal(dip)) { 3298*7c478bd9Sstevel@tonic-gate if (pm_all_to_normal(dip, 3299*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_FAIL) != DDI_SUCCESS) { 3300*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: could not bring " 3301*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) to normal\n", pmf, 3302*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 3303*7c478bd9Sstevel@tonic-gate } 3304*7c478bd9Sstevel@tonic-gate } 3305*7c478bd9Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_ALLNORM_DEFERRED; 3306*7c478bd9Sstevel@tonic-gate } 3307*7c478bd9Sstevel@tonic-gate } 3308*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) { 3309*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_scan_lock); 3310*7c478bd9Sstevel@tonic-gate if (autopm_enabled) 3311*7c478bd9Sstevel@tonic-gate pm_scan_init(dip); 3312*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 3313*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 3314*7c478bd9Sstevel@tonic-gate } 3315*7c478bd9Sstevel@tonic-gate } 3316*7c478bd9Sstevel@tonic-gate 3317*7c478bd9Sstevel@tonic-gate /* generic Backwards Compatible component */ 3318*7c478bd9Sstevel@tonic-gate static char *bc_names[] = {"off", "on"}; 3319*7c478bd9Sstevel@tonic-gate 3320*7c478bd9Sstevel@tonic-gate static pm_comp_t bc_comp = {"unknown", 2, NULL, NULL, &bc_names[0]}; 3321*7c478bd9Sstevel@tonic-gate 3322*7c478bd9Sstevel@tonic-gate static void 3323*7c478bd9Sstevel@tonic-gate e_pm_default_levels(dev_info_t *dip, pm_component_t *cp, int norm) 3324*7c478bd9Sstevel@tonic-gate { 3325*7c478bd9Sstevel@tonic-gate pm_comp_t *pmc; 3326*7c478bd9Sstevel@tonic-gate pmc = &cp->pmc_comp; 3327*7c478bd9Sstevel@tonic-gate pmc->pmc_numlevels = 2; 3328*7c478bd9Sstevel@tonic-gate pmc->pmc_lvals[0] = 0; 3329*7c478bd9Sstevel@tonic-gate pmc->pmc_lvals[1] = norm; 3330*7c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dip, cp, norm); 3331*7c478bd9Sstevel@tonic-gate } 3332*7c478bd9Sstevel@tonic-gate 3333*7c478bd9Sstevel@tonic-gate static void 3334*7c478bd9Sstevel@tonic-gate e_pm_default_components(dev_info_t *dip, int cmpts) 3335*7c478bd9Sstevel@tonic-gate { 3336*7c478bd9Sstevel@tonic-gate int i; 3337*7c478bd9Sstevel@tonic-gate pm_component_t *p = DEVI(dip)->devi_pm_components; 3338*7c478bd9Sstevel@tonic-gate 3339*7c478bd9Sstevel@tonic-gate p = DEVI(dip)->devi_pm_components; 3340*7c478bd9Sstevel@tonic-gate for (i = 0; i < cmpts; i++, p++) { 3341*7c478bd9Sstevel@tonic-gate p->pmc_comp = bc_comp; /* struct assignment */ 3342*7c478bd9Sstevel@tonic-gate p->pmc_comp.pmc_lvals = kmem_zalloc(2 * sizeof (int), 3343*7c478bd9Sstevel@tonic-gate KM_SLEEP); 3344*7c478bd9Sstevel@tonic-gate p->pmc_comp.pmc_thresh = kmem_alloc(2 * sizeof (int), 3345*7c478bd9Sstevel@tonic-gate KM_SLEEP); 3346*7c478bd9Sstevel@tonic-gate p->pmc_comp.pmc_numlevels = 2; 3347*7c478bd9Sstevel@tonic-gate p->pmc_comp.pmc_thresh[0] = INT_MAX; 3348*7c478bd9Sstevel@tonic-gate p->pmc_comp.pmc_thresh[1] = INT_MAX; 3349*7c478bd9Sstevel@tonic-gate } 3350*7c478bd9Sstevel@tonic-gate } 3351*7c478bd9Sstevel@tonic-gate 3352*7c478bd9Sstevel@tonic-gate /* 3353*7c478bd9Sstevel@tonic-gate * Called from functions that require components to exist already to allow 3354*7c478bd9Sstevel@tonic-gate * for their creation by parsing the pm-components property. 3355*7c478bd9Sstevel@tonic-gate * Device will not be power managed as a result of this call 3356*7c478bd9Sstevel@tonic-gate * No locking needed because we're single threaded by the ndi_devi_enter 3357*7c478bd9Sstevel@tonic-gate * done while attaching, and the device isn't visible until after it has 3358*7c478bd9Sstevel@tonic-gate * attached 3359*7c478bd9Sstevel@tonic-gate */ 3360*7c478bd9Sstevel@tonic-gate int 3361*7c478bd9Sstevel@tonic-gate pm_premanage(dev_info_t *dip, int style) 3362*7c478bd9Sstevel@tonic-gate { 3363*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "premanage") 3364*7c478bd9Sstevel@tonic-gate pm_comp_t *pcp, *compp; 3365*7c478bd9Sstevel@tonic-gate int cmpts, i, norm, error; 3366*7c478bd9Sstevel@tonic-gate pm_component_t *p = DEVI(dip)->devi_pm_components; 3367*7c478bd9Sstevel@tonic-gate pm_comp_t *pm_autoconfig(dev_info_t *, int *); 3368*7c478bd9Sstevel@tonic-gate 3369*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3370*7c478bd9Sstevel@tonic-gate /* 3371*7c478bd9Sstevel@tonic-gate * If this dip has already been processed, don't mess with it 3372*7c478bd9Sstevel@tonic-gate */ 3373*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE) 3374*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3375*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_FAILED) { 3376*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3377*7c478bd9Sstevel@tonic-gate } 3378*7c478bd9Sstevel@tonic-gate /* 3379*7c478bd9Sstevel@tonic-gate * Look up pm-components property and create components accordingly 3380*7c478bd9Sstevel@tonic-gate * If that fails, fall back to backwards compatibility 3381*7c478bd9Sstevel@tonic-gate */ 3382*7c478bd9Sstevel@tonic-gate if ((compp = pm_autoconfig(dip, &error)) == NULL) { 3383*7c478bd9Sstevel@tonic-gate /* 3384*7c478bd9Sstevel@tonic-gate * If error is set, the property existed but was not well formed 3385*7c478bd9Sstevel@tonic-gate */ 3386*7c478bd9Sstevel@tonic-gate if (error || (style == PM_STYLE_NEW)) { 3387*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_FAILED; 3388*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3389*7c478bd9Sstevel@tonic-gate } 3390*7c478bd9Sstevel@tonic-gate /* 3391*7c478bd9Sstevel@tonic-gate * If they don't have the pm-components property, then we 3392*7c478bd9Sstevel@tonic-gate * want the old "no pm until PM_SET_DEVICE_THRESHOLDS ioctl" 3393*7c478bd9Sstevel@tonic-gate * behavior driver must have called pm_create_components, and 3394*7c478bd9Sstevel@tonic-gate * we need to flesh out dummy components 3395*7c478bd9Sstevel@tonic-gate */ 3396*7c478bd9Sstevel@tonic-gate if ((cmpts = PM_NUMCMPTS(dip)) == 0) { 3397*7c478bd9Sstevel@tonic-gate /* 3398*7c478bd9Sstevel@tonic-gate * Not really failure, but we don't want the 3399*7c478bd9Sstevel@tonic-gate * caller to treat it as success 3400*7c478bd9Sstevel@tonic-gate */ 3401*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3402*7c478bd9Sstevel@tonic-gate } 3403*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_BC; 3404*7c478bd9Sstevel@tonic-gate e_pm_default_components(dip, cmpts); 3405*7c478bd9Sstevel@tonic-gate for (i = 0; i < cmpts; i++) { 3406*7c478bd9Sstevel@tonic-gate /* 3407*7c478bd9Sstevel@tonic-gate * if normal power not set yet, we don't really know 3408*7c478bd9Sstevel@tonic-gate * what *ANY* of the power values are. If normal 3409*7c478bd9Sstevel@tonic-gate * power is set, then we assume for this backwards 3410*7c478bd9Sstevel@tonic-gate * compatible case that the values are 0, normal power. 3411*7c478bd9Sstevel@tonic-gate */ 3412*7c478bd9Sstevel@tonic-gate norm = pm_get_normal_power(dip, i); 3413*7c478bd9Sstevel@tonic-gate if (norm == (uint_t)-1) { 3414*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d)[%d]\n", pmf, 3415*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), i)) 3416*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3417*7c478bd9Sstevel@tonic-gate } 3418*7c478bd9Sstevel@tonic-gate /* 3419*7c478bd9Sstevel@tonic-gate * Components of BC devices start at their normal power, 3420*7c478bd9Sstevel@tonic-gate * so count them to be not at their lowest power. 3421*7c478bd9Sstevel@tonic-gate */ 3422*7c478bd9Sstevel@tonic-gate PM_INCR_NOTLOWEST(dip); 3423*7c478bd9Sstevel@tonic-gate e_pm_default_levels(dip, PM_CP(dip, i), norm); 3424*7c478bd9Sstevel@tonic-gate } 3425*7c478bd9Sstevel@tonic-gate } else { 3426*7c478bd9Sstevel@tonic-gate /* 3427*7c478bd9Sstevel@tonic-gate * e_pm_create_components was called from pm_autoconfig(), it 3428*7c478bd9Sstevel@tonic-gate * creates components with no descriptions (or known levels) 3429*7c478bd9Sstevel@tonic-gate */ 3430*7c478bd9Sstevel@tonic-gate cmpts = PM_NUMCMPTS(dip); 3431*7c478bd9Sstevel@tonic-gate ASSERT(cmpts != 0); 3432*7c478bd9Sstevel@tonic-gate pcp = compp; 3433*7c478bd9Sstevel@tonic-gate p = DEVI(dip)->devi_pm_components; 3434*7c478bd9Sstevel@tonic-gate for (i = 0; i < cmpts; i++, p++) { 3435*7c478bd9Sstevel@tonic-gate p->pmc_comp = *pcp++; /* struct assignment */ 3436*7c478bd9Sstevel@tonic-gate ASSERT(PM_CP(dip, i)->pmc_cur_pwr == 0); 3437*7c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN); 3438*7c478bd9Sstevel@tonic-gate } 3439*7c478bd9Sstevel@tonic-gate pm_set_device_threshold(dip, pm_system_idle_threshold, 3440*7c478bd9Sstevel@tonic-gate PMC_DEF_THRESH); 3441*7c478bd9Sstevel@tonic-gate kmem_free(compp, cmpts * sizeof (pm_comp_t)); 3442*7c478bd9Sstevel@tonic-gate } 3443*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3444*7c478bd9Sstevel@tonic-gate } 3445*7c478bd9Sstevel@tonic-gate 3446*7c478bd9Sstevel@tonic-gate /* 3447*7c478bd9Sstevel@tonic-gate * Called from during or after the device's attach to let us know it is ready 3448*7c478bd9Sstevel@tonic-gate * to play autopm. Look up the pm model and manage the device accordingly. 3449*7c478bd9Sstevel@tonic-gate * Returns system call errno value. 3450*7c478bd9Sstevel@tonic-gate * If DDI_ATTACH and DDI_DETACH were in same namespace, this would be 3451*7c478bd9Sstevel@tonic-gate * a little cleaner 3452*7c478bd9Sstevel@tonic-gate * 3453*7c478bd9Sstevel@tonic-gate * Called with dip lock held, return with dip lock unheld. 3454*7c478bd9Sstevel@tonic-gate */ 3455*7c478bd9Sstevel@tonic-gate 3456*7c478bd9Sstevel@tonic-gate int 3457*7c478bd9Sstevel@tonic-gate e_pm_manage(dev_info_t *dip, int style) 3458*7c478bd9Sstevel@tonic-gate { 3459*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "e_manage") 3460*7c478bd9Sstevel@tonic-gate pm_info_t *info; 3461*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 3462*7c478bd9Sstevel@tonic-gate int pm_thresh_specd(dev_info_t *); 3463*7c478bd9Sstevel@tonic-gate int count; 3464*7c478bd9Sstevel@tonic-gate char *pathbuf; 3465*7c478bd9Sstevel@tonic-gate 3466*7c478bd9Sstevel@tonic-gate if (pm_premanage(dip, style) != DDI_SUCCESS) { 3467*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3468*7c478bd9Sstevel@tonic-gate } 3469*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3470*7c478bd9Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip) == NULL); 3471*7c478bd9Sstevel@tonic-gate info = kmem_zalloc(sizeof (pm_info_t), KM_SLEEP); 3472*7c478bd9Sstevel@tonic-gate 3473*7c478bd9Sstevel@tonic-gate /* 3474*7c478bd9Sstevel@tonic-gate * Now set up parent's kidsupcnt. BC nodes are assumed to start 3475*7c478bd9Sstevel@tonic-gate * out at their normal power, so they are "up", others start out 3476*7c478bd9Sstevel@tonic-gate * unknown, which is effectively "up". Parent which want notification 3477*7c478bd9Sstevel@tonic-gate * get kidsupcnt of 0 always. 3478*7c478bd9Sstevel@tonic-gate */ 3479*7c478bd9Sstevel@tonic-gate count = (PM_ISBC(dip)) ? 1 : PM_NUMCMPTS(dip); 3480*7c478bd9Sstevel@tonic-gate if (count && pdip && !PM_WANTS_NOTIFICATION(pdip)) 3481*7c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(pdip, count); 3482*7c478bd9Sstevel@tonic-gate 3483*7c478bd9Sstevel@tonic-gate pm_set_pm_info(dip, info); 3484*7c478bd9Sstevel@tonic-gate /* 3485*7c478bd9Sstevel@tonic-gate * Apply any recorded thresholds 3486*7c478bd9Sstevel@tonic-gate */ 3487*7c478bd9Sstevel@tonic-gate (void) pm_thresh_specd(dip); 3488*7c478bd9Sstevel@tonic-gate 3489*7c478bd9Sstevel@tonic-gate /* 3490*7c478bd9Sstevel@tonic-gate * Do dependency processing. 3491*7c478bd9Sstevel@tonic-gate */ 3492*7c478bd9Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 3493*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 3494*7c478bd9Sstevel@tonic-gate pm_dispatch_to_dep_thread(PM_DEP_WK_ATTACH, pathbuf, pathbuf, 3495*7c478bd9Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0); 3496*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 3497*7c478bd9Sstevel@tonic-gate 3498*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) { 3499*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_scan_lock); 3500*7c478bd9Sstevel@tonic-gate if (autopm_enabled) { 3501*7c478bd9Sstevel@tonic-gate pm_scan_init(dip); 3502*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 3503*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 3504*7c478bd9Sstevel@tonic-gate } else { 3505*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 3506*7c478bd9Sstevel@tonic-gate } 3507*7c478bd9Sstevel@tonic-gate } 3508*7c478bd9Sstevel@tonic-gate return (0); 3509*7c478bd9Sstevel@tonic-gate } 3510*7c478bd9Sstevel@tonic-gate 3511*7c478bd9Sstevel@tonic-gate /* 3512*7c478bd9Sstevel@tonic-gate * This is the obsolete exported interface for a driver to find out its 3513*7c478bd9Sstevel@tonic-gate * "normal" (max) power. 3514*7c478bd9Sstevel@tonic-gate * We only get components destroyed while no power management is 3515*7c478bd9Sstevel@tonic-gate * going on (and the device is detached), so we don't need a mutex here 3516*7c478bd9Sstevel@tonic-gate */ 3517*7c478bd9Sstevel@tonic-gate int 3518*7c478bd9Sstevel@tonic-gate pm_get_normal_power(dev_info_t *dip, int comp) 3519*7c478bd9Sstevel@tonic-gate { 3520*7c478bd9Sstevel@tonic-gate 3521*7c478bd9Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip)) { 3522*7c478bd9Sstevel@tonic-gate return (PM_CP(dip, comp)->pmc_norm_pwr); 3523*7c478bd9Sstevel@tonic-gate } 3524*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3525*7c478bd9Sstevel@tonic-gate } 3526*7c478bd9Sstevel@tonic-gate 3527*7c478bd9Sstevel@tonic-gate /* 3528*7c478bd9Sstevel@tonic-gate * Fetches the current power level. Return DDI_SUCCESS or DDI_FAILURE. 3529*7c478bd9Sstevel@tonic-gate */ 3530*7c478bd9Sstevel@tonic-gate int 3531*7c478bd9Sstevel@tonic-gate pm_get_current_power(dev_info_t *dip, int comp, int *levelp) 3532*7c478bd9Sstevel@tonic-gate { 3533*7c478bd9Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip)) { 3534*7c478bd9Sstevel@tonic-gate *levelp = PM_CURPOWER(dip, comp); 3535*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3536*7c478bd9Sstevel@tonic-gate } 3537*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3538*7c478bd9Sstevel@tonic-gate } 3539*7c478bd9Sstevel@tonic-gate 3540*7c478bd9Sstevel@tonic-gate /* 3541*7c478bd9Sstevel@tonic-gate * Returns current threshold of indicated component 3542*7c478bd9Sstevel@tonic-gate */ 3543*7c478bd9Sstevel@tonic-gate static int 3544*7c478bd9Sstevel@tonic-gate cur_threshold(dev_info_t *dip, int comp) 3545*7c478bd9Sstevel@tonic-gate { 3546*7c478bd9Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, comp); 3547*7c478bd9Sstevel@tonic-gate int pwr; 3548*7c478bd9Sstevel@tonic-gate 3549*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip)) { 3550*7c478bd9Sstevel@tonic-gate /* 3551*7c478bd9Sstevel@tonic-gate * backwards compatible nodes only have one threshold 3552*7c478bd9Sstevel@tonic-gate */ 3553*7c478bd9Sstevel@tonic-gate return (cp->pmc_comp.pmc_thresh[1]); 3554*7c478bd9Sstevel@tonic-gate } 3555*7c478bd9Sstevel@tonic-gate pwr = cp->pmc_cur_pwr; 3556*7c478bd9Sstevel@tonic-gate if (pwr == PM_LEVEL_UNKNOWN) { 3557*7c478bd9Sstevel@tonic-gate int thresh; 3558*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) 3559*7c478bd9Sstevel@tonic-gate thresh = pm_default_nexus_threshold; 3560*7c478bd9Sstevel@tonic-gate else 3561*7c478bd9Sstevel@tonic-gate thresh = pm_system_idle_threshold; 3562*7c478bd9Sstevel@tonic-gate return (thresh); 3563*7c478bd9Sstevel@tonic-gate } 3564*7c478bd9Sstevel@tonic-gate ASSERT(cp->pmc_comp.pmc_thresh); 3565*7c478bd9Sstevel@tonic-gate return (cp->pmc_comp.pmc_thresh[pwr]); 3566*7c478bd9Sstevel@tonic-gate } 3567*7c478bd9Sstevel@tonic-gate 3568*7c478bd9Sstevel@tonic-gate /* 3569*7c478bd9Sstevel@tonic-gate * Compute next lower component power level given power index. 3570*7c478bd9Sstevel@tonic-gate */ 3571*7c478bd9Sstevel@tonic-gate static int 3572*7c478bd9Sstevel@tonic-gate pm_next_lower_power(pm_component_t *cp, int pwrndx) 3573*7c478bd9Sstevel@tonic-gate { 3574*7c478bd9Sstevel@tonic-gate int nxt_pwr; 3575*7c478bd9Sstevel@tonic-gate 3576*7c478bd9Sstevel@tonic-gate if (pwrndx == PM_LEVEL_UNKNOWN) { 3577*7c478bd9Sstevel@tonic-gate nxt_pwr = cp->pmc_comp.pmc_lvals[0]; 3578*7c478bd9Sstevel@tonic-gate } else { 3579*7c478bd9Sstevel@tonic-gate pwrndx--; 3580*7c478bd9Sstevel@tonic-gate ASSERT(pwrndx >= 0); 3581*7c478bd9Sstevel@tonic-gate nxt_pwr = cp->pmc_comp.pmc_lvals[pwrndx]; 3582*7c478bd9Sstevel@tonic-gate } 3583*7c478bd9Sstevel@tonic-gate return (nxt_pwr); 3584*7c478bd9Sstevel@tonic-gate } 3585*7c478bd9Sstevel@tonic-gate 3586*7c478bd9Sstevel@tonic-gate /* 3587*7c478bd9Sstevel@tonic-gate * Bring all components of device to normal power 3588*7c478bd9Sstevel@tonic-gate */ 3589*7c478bd9Sstevel@tonic-gate int 3590*7c478bd9Sstevel@tonic-gate pm_all_to_normal(dev_info_t *dip, pm_canblock_t canblock) 3591*7c478bd9Sstevel@tonic-gate { 3592*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "all_to_normal") 3593*7c478bd9Sstevel@tonic-gate int *normal; 3594*7c478bd9Sstevel@tonic-gate int i, ncomps, result; 3595*7c478bd9Sstevel@tonic-gate size_t size; 3596*7c478bd9Sstevel@tonic-gate int changefailed = 0; 3597*7c478bd9Sstevel@tonic-gate 3598*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3599*7c478bd9Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip)); 3600*7c478bd9Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) { 3601*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get norm pwrs for " 3602*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3603*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3604*7c478bd9Sstevel@tonic-gate } 3605*7c478bd9Sstevel@tonic-gate ncomps = PM_NUMCMPTS(dip); 3606*7c478bd9Sstevel@tonic-gate for (i = 0; i < ncomps; i++) { 3607*7c478bd9Sstevel@tonic-gate if (pm_set_power(dip, i, normal[i], 3608*7c478bd9Sstevel@tonic-gate PM_LEVEL_UPONLY, canblock, 0, &result) != DDI_SUCCESS) { 3609*7c478bd9Sstevel@tonic-gate changefailed++; 3610*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM | PMD_FAIL, ("%s: failed to set " 3611*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)[%d] to %d, errno %d\n", pmf, 3612*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), i, normal[i], result)) 3613*7c478bd9Sstevel@tonic-gate } 3614*7c478bd9Sstevel@tonic-gate } 3615*7c478bd9Sstevel@tonic-gate kmem_free(normal, size); 3616*7c478bd9Sstevel@tonic-gate if (changefailed) { 3617*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) " 3618*7c478bd9Sstevel@tonic-gate "to full power\n", pmf, changefailed, PM_DEVICE(dip))) 3619*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3620*7c478bd9Sstevel@tonic-gate } 3621*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3622*7c478bd9Sstevel@tonic-gate } 3623*7c478bd9Sstevel@tonic-gate 3624*7c478bd9Sstevel@tonic-gate /* 3625*7c478bd9Sstevel@tonic-gate * Returns true if all components of device are at normal power 3626*7c478bd9Sstevel@tonic-gate */ 3627*7c478bd9Sstevel@tonic-gate int 3628*7c478bd9Sstevel@tonic-gate pm_all_at_normal(dev_info_t *dip) 3629*7c478bd9Sstevel@tonic-gate { 3630*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "all_at_normal") 3631*7c478bd9Sstevel@tonic-gate int *normal; 3632*7c478bd9Sstevel@tonic-gate int i; 3633*7c478bd9Sstevel@tonic-gate size_t size; 3634*7c478bd9Sstevel@tonic-gate 3635*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3636*7c478bd9Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) { 3637*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get normal power\n", pmf)) 3638*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3639*7c478bd9Sstevel@tonic-gate } 3640*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3641*7c478bd9Sstevel@tonic-gate int current = PM_CURPOWER(dip, i); 3642*7c478bd9Sstevel@tonic-gate if (normal[i] > current) { 3643*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d) comp=%d, " 3644*7c478bd9Sstevel@tonic-gate "norm=%d, cur=%d\n", pmf, PM_DEVICE(dip), i, 3645*7c478bd9Sstevel@tonic-gate normal[i], current)) 3646*7c478bd9Sstevel@tonic-gate break; 3647*7c478bd9Sstevel@tonic-gate } 3648*7c478bd9Sstevel@tonic-gate } 3649*7c478bd9Sstevel@tonic-gate kmem_free(normal, size); 3650*7c478bd9Sstevel@tonic-gate if (i != PM_NUMCMPTS(dip)) { 3651*7c478bd9Sstevel@tonic-gate return (0); 3652*7c478bd9Sstevel@tonic-gate } 3653*7c478bd9Sstevel@tonic-gate return (1); 3654*7c478bd9Sstevel@tonic-gate } 3655*7c478bd9Sstevel@tonic-gate 3656*7c478bd9Sstevel@tonic-gate static void 3657*7c478bd9Sstevel@tonic-gate bring_wekeeps_up(char *keeper) 3658*7c478bd9Sstevel@tonic-gate { 3659*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "bring_wekeeps_up") 3660*7c478bd9Sstevel@tonic-gate int i; 3661*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 3662*7c478bd9Sstevel@tonic-gate pm_info_t *wku_info; 3663*7c478bd9Sstevel@tonic-gate char *kept_path; 3664*7c478bd9Sstevel@tonic-gate dev_info_t *kept; 3665*7c478bd9Sstevel@tonic-gate static void bring_pmdep_up(dev_info_t *, int); 3666*7c478bd9Sstevel@tonic-gate 3667*7c478bd9Sstevel@tonic-gate if (panicstr) { 3668*7c478bd9Sstevel@tonic-gate return; 3669*7c478bd9Sstevel@tonic-gate } 3670*7c478bd9Sstevel@tonic-gate /* 3671*7c478bd9Sstevel@tonic-gate * We process the request even if the keeper detaches because 3672*7c478bd9Sstevel@tonic-gate * detach processing expects this to increment kidsupcnt of kept. 3673*7c478bd9Sstevel@tonic-gate */ 3674*7c478bd9Sstevel@tonic-gate PMD(PMD_BRING, ("%s: keeper= %s\n", pmf, keeper)) 3675*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 3676*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0) 3677*7c478bd9Sstevel@tonic-gate continue; 3678*7c478bd9Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 3679*7c478bd9Sstevel@tonic-gate kept_path = dp->pdr_kept_paths[i]; 3680*7c478bd9Sstevel@tonic-gate if (kept_path == NULL) 3681*7c478bd9Sstevel@tonic-gate continue; 3682*7c478bd9Sstevel@tonic-gate ASSERT(kept_path[0] != '\0'); 3683*7c478bd9Sstevel@tonic-gate if ((kept = pm_name_to_dip(kept_path, 1)) == NULL) 3684*7c478bd9Sstevel@tonic-gate continue; 3685*7c478bd9Sstevel@tonic-gate wku_info = PM_GET_PM_INFO(kept); 3686*7c478bd9Sstevel@tonic-gate if (wku_info == NULL) { 3687*7c478bd9Sstevel@tonic-gate if (kept) 3688*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 3689*7c478bd9Sstevel@tonic-gate continue; 3690*7c478bd9Sstevel@tonic-gate } 3691*7c478bd9Sstevel@tonic-gate /* 3692*7c478bd9Sstevel@tonic-gate * Don't mess with it if it is being detached, it isn't 3693*7c478bd9Sstevel@tonic-gate * safe to call its power entry point 3694*7c478bd9Sstevel@tonic-gate */ 3695*7c478bd9Sstevel@tonic-gate if (wku_info->pmi_dev_pm_state & PM_DETACHING) { 3696*7c478bd9Sstevel@tonic-gate if (kept) 3697*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 3698*7c478bd9Sstevel@tonic-gate continue; 3699*7c478bd9Sstevel@tonic-gate } 3700*7c478bd9Sstevel@tonic-gate bring_pmdep_up(kept, 1); 3701*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 3702*7c478bd9Sstevel@tonic-gate } 3703*7c478bd9Sstevel@tonic-gate } 3704*7c478bd9Sstevel@tonic-gate } 3705*7c478bd9Sstevel@tonic-gate 3706*7c478bd9Sstevel@tonic-gate /* 3707*7c478bd9Sstevel@tonic-gate * Bring up the 'kept' device passed as argument 3708*7c478bd9Sstevel@tonic-gate */ 3709*7c478bd9Sstevel@tonic-gate static void 3710*7c478bd9Sstevel@tonic-gate bring_pmdep_up(dev_info_t *kept_dip, int hold) 3711*7c478bd9Sstevel@tonic-gate { 3712*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "bring_pmdep_up") 3713*7c478bd9Sstevel@tonic-gate int is_all_at_normal = 0; 3714*7c478bd9Sstevel@tonic-gate 3715*7c478bd9Sstevel@tonic-gate /* 3716*7c478bd9Sstevel@tonic-gate * If the kept device has been unmanaged, do nothing. 3717*7c478bd9Sstevel@tonic-gate */ 3718*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(kept_dip)) 3719*7c478bd9Sstevel@tonic-gate return; 3720*7c478bd9Sstevel@tonic-gate 3721*7c478bd9Sstevel@tonic-gate /* Just ignore DIRECT PM device till they are released. */ 3722*7c478bd9Sstevel@tonic-gate if (!pm_processes_stopped && PM_ISDIRECT(kept_dip) && 3723*7c478bd9Sstevel@tonic-gate !(is_all_at_normal = pm_all_at_normal(kept_dip))) { 3724*7c478bd9Sstevel@tonic-gate PMD(PMD_BRING, ("%s: can't bring up PM_DIRECT %s@%s(%s#%d) " 3725*7c478bd9Sstevel@tonic-gate "controlling process did something else\n", pmf, 3726*7c478bd9Sstevel@tonic-gate PM_DEVICE(kept_dip))) 3727*7c478bd9Sstevel@tonic-gate DEVI(kept_dip)->devi_pm_flags |= PMC_SKIP_BRINGUP; 3728*7c478bd9Sstevel@tonic-gate return; 3729*7c478bd9Sstevel@tonic-gate } 3730*7c478bd9Sstevel@tonic-gate /* if we got here the keeper had a transition from OFF->ON */ 3731*7c478bd9Sstevel@tonic-gate if (hold) 3732*7c478bd9Sstevel@tonic-gate pm_hold_power(kept_dip); 3733*7c478bd9Sstevel@tonic-gate 3734*7c478bd9Sstevel@tonic-gate if (!is_all_at_normal) 3735*7c478bd9Sstevel@tonic-gate (void) pm_all_to_normal(kept_dip, PM_CANBLOCK_FAIL); 3736*7c478bd9Sstevel@tonic-gate } 3737*7c478bd9Sstevel@tonic-gate 3738*7c478bd9Sstevel@tonic-gate /* 3739*7c478bd9Sstevel@tonic-gate * A bunch of stuff that belongs only to the next routine (or two) 3740*7c478bd9Sstevel@tonic-gate */ 3741*7c478bd9Sstevel@tonic-gate 3742*7c478bd9Sstevel@tonic-gate static const char namestr[] = "NAME="; 3743*7c478bd9Sstevel@tonic-gate static const int nameln = sizeof (namestr) - 1; 3744*7c478bd9Sstevel@tonic-gate static const char pmcompstr[] = "pm-components"; 3745*7c478bd9Sstevel@tonic-gate 3746*7c478bd9Sstevel@tonic-gate struct pm_comp_pkg { 3747*7c478bd9Sstevel@tonic-gate pm_comp_t *comp; 3748*7c478bd9Sstevel@tonic-gate struct pm_comp_pkg *next; 3749*7c478bd9Sstevel@tonic-gate }; 3750*7c478bd9Sstevel@tonic-gate 3751*7c478bd9Sstevel@tonic-gate #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 3752*7c478bd9Sstevel@tonic-gate 3753*7c478bd9Sstevel@tonic-gate #define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \ 3754*7c478bd9Sstevel@tonic-gate ((ch) >= 'A' && (ch) <= 'F')) 3755*7c478bd9Sstevel@tonic-gate 3756*7c478bd9Sstevel@tonic-gate /* 3757*7c478bd9Sstevel@tonic-gate * Rather than duplicate this code ... 3758*7c478bd9Sstevel@tonic-gate * (this code excerpted from the function that follows it) 3759*7c478bd9Sstevel@tonic-gate */ 3760*7c478bd9Sstevel@tonic-gate #define FINISH_COMP { \ 3761*7c478bd9Sstevel@tonic-gate ASSERT(compp); \ 3762*7c478bd9Sstevel@tonic-gate compp->pmc_lnames_sz = size; \ 3763*7c478bd9Sstevel@tonic-gate tp = compp->pmc_lname_buf = kmem_alloc(size, KM_SLEEP); \ 3764*7c478bd9Sstevel@tonic-gate compp->pmc_numlevels = level; \ 3765*7c478bd9Sstevel@tonic-gate compp->pmc_lnames = kmem_alloc(level * sizeof (char *), KM_SLEEP); \ 3766*7c478bd9Sstevel@tonic-gate compp->pmc_lvals = kmem_alloc(level * sizeof (int), KM_SLEEP); \ 3767*7c478bd9Sstevel@tonic-gate compp->pmc_thresh = kmem_alloc(level * sizeof (int), KM_SLEEP); \ 3768*7c478bd9Sstevel@tonic-gate /* copy string out of prop array into buffer */ \ 3769*7c478bd9Sstevel@tonic-gate for (j = 0; j < level; j++) { \ 3770*7c478bd9Sstevel@tonic-gate compp->pmc_thresh[j] = INT_MAX; /* only [0] sticks */ \ 3771*7c478bd9Sstevel@tonic-gate compp->pmc_lvals[j] = lvals[j]; \ 3772*7c478bd9Sstevel@tonic-gate (void) strcpy(tp, lnames[j]); \ 3773*7c478bd9Sstevel@tonic-gate compp->pmc_lnames[j] = tp; \ 3774*7c478bd9Sstevel@tonic-gate tp += lszs[j]; \ 3775*7c478bd9Sstevel@tonic-gate } \ 3776*7c478bd9Sstevel@tonic-gate ASSERT(tp > compp->pmc_lname_buf && tp <= \ 3777*7c478bd9Sstevel@tonic-gate compp->pmc_lname_buf + compp->pmc_lnames_sz); \ 3778*7c478bd9Sstevel@tonic-gate } 3779*7c478bd9Sstevel@tonic-gate 3780*7c478bd9Sstevel@tonic-gate /* 3781*7c478bd9Sstevel@tonic-gate * Create (empty) component data structures. 3782*7c478bd9Sstevel@tonic-gate */ 3783*7c478bd9Sstevel@tonic-gate static void 3784*7c478bd9Sstevel@tonic-gate e_pm_create_components(dev_info_t *dip, int num_components) 3785*7c478bd9Sstevel@tonic-gate { 3786*7c478bd9Sstevel@tonic-gate struct pm_component *compp, *ocompp; 3787*7c478bd9Sstevel@tonic-gate int i, size = 0; 3788*7c478bd9Sstevel@tonic-gate 3789*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3790*7c478bd9Sstevel@tonic-gate ASSERT(!DEVI(dip)->devi_pm_components); 3791*7c478bd9Sstevel@tonic-gate ASSERT(!(DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE)); 3792*7c478bd9Sstevel@tonic-gate size = sizeof (struct pm_component) * num_components; 3793*7c478bd9Sstevel@tonic-gate 3794*7c478bd9Sstevel@tonic-gate compp = kmem_zalloc(size, KM_SLEEP); 3795*7c478bd9Sstevel@tonic-gate ocompp = compp; 3796*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_comp_size = size; 3797*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_num_components = num_components; 3798*7c478bd9Sstevel@tonic-gate PM_LOCK_BUSY(dip); 3799*7c478bd9Sstevel@tonic-gate for (i = 0; i < num_components; i++) { 3800*7c478bd9Sstevel@tonic-gate compp->pmc_timestamp = gethrestime_sec(); 3801*7c478bd9Sstevel@tonic-gate compp->pmc_norm_pwr = (uint_t)-1; 3802*7c478bd9Sstevel@tonic-gate compp++; 3803*7c478bd9Sstevel@tonic-gate } 3804*7c478bd9Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 3805*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_components = ocompp; 3806*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_DONE; 3807*7c478bd9Sstevel@tonic-gate } 3808*7c478bd9Sstevel@tonic-gate 3809*7c478bd9Sstevel@tonic-gate /* 3810*7c478bd9Sstevel@tonic-gate * Parse hex or decimal value from char string 3811*7c478bd9Sstevel@tonic-gate */ 3812*7c478bd9Sstevel@tonic-gate static char * 3813*7c478bd9Sstevel@tonic-gate pm_parsenum(char *cp, int *valp) 3814*7c478bd9Sstevel@tonic-gate { 3815*7c478bd9Sstevel@tonic-gate int ch, offset; 3816*7c478bd9Sstevel@tonic-gate char numbuf[256]; 3817*7c478bd9Sstevel@tonic-gate char *np = numbuf; 3818*7c478bd9Sstevel@tonic-gate int value = 0; 3819*7c478bd9Sstevel@tonic-gate 3820*7c478bd9Sstevel@tonic-gate ch = *cp++; 3821*7c478bd9Sstevel@tonic-gate if (isdigit(ch)) { 3822*7c478bd9Sstevel@tonic-gate if (ch == '0') { 3823*7c478bd9Sstevel@tonic-gate if ((ch = *cp++) == 'x' || ch == 'X') { 3824*7c478bd9Sstevel@tonic-gate ch = *cp++; 3825*7c478bd9Sstevel@tonic-gate while (isxdigit(ch)) { 3826*7c478bd9Sstevel@tonic-gate *np++ = (char)ch; 3827*7c478bd9Sstevel@tonic-gate ch = *cp++; 3828*7c478bd9Sstevel@tonic-gate } 3829*7c478bd9Sstevel@tonic-gate *np = 0; 3830*7c478bd9Sstevel@tonic-gate cp--; 3831*7c478bd9Sstevel@tonic-gate goto hexval; 3832*7c478bd9Sstevel@tonic-gate } else { 3833*7c478bd9Sstevel@tonic-gate goto digit; 3834*7c478bd9Sstevel@tonic-gate } 3835*7c478bd9Sstevel@tonic-gate } else { 3836*7c478bd9Sstevel@tonic-gate digit: 3837*7c478bd9Sstevel@tonic-gate while (isdigit(ch)) { 3838*7c478bd9Sstevel@tonic-gate *np++ = (char)ch; 3839*7c478bd9Sstevel@tonic-gate ch = *cp++; 3840*7c478bd9Sstevel@tonic-gate } 3841*7c478bd9Sstevel@tonic-gate *np = 0; 3842*7c478bd9Sstevel@tonic-gate cp--; 3843*7c478bd9Sstevel@tonic-gate goto decval; 3844*7c478bd9Sstevel@tonic-gate } 3845*7c478bd9Sstevel@tonic-gate } else 3846*7c478bd9Sstevel@tonic-gate return (NULL); 3847*7c478bd9Sstevel@tonic-gate 3848*7c478bd9Sstevel@tonic-gate hexval: 3849*7c478bd9Sstevel@tonic-gate for (np = numbuf; *np; np++) { 3850*7c478bd9Sstevel@tonic-gate if (*np >= 'a' && *np <= 'f') 3851*7c478bd9Sstevel@tonic-gate offset = 'a' - 10; 3852*7c478bd9Sstevel@tonic-gate else if (*np >= 'A' && *np <= 'F') 3853*7c478bd9Sstevel@tonic-gate offset = 'A' - 10; 3854*7c478bd9Sstevel@tonic-gate else if (*np >= '0' && *np <= '9') 3855*7c478bd9Sstevel@tonic-gate offset = '0'; 3856*7c478bd9Sstevel@tonic-gate value *= 16; 3857*7c478bd9Sstevel@tonic-gate value += *np - offset; 3858*7c478bd9Sstevel@tonic-gate } 3859*7c478bd9Sstevel@tonic-gate *valp = value; 3860*7c478bd9Sstevel@tonic-gate return (cp); 3861*7c478bd9Sstevel@tonic-gate 3862*7c478bd9Sstevel@tonic-gate decval: 3863*7c478bd9Sstevel@tonic-gate offset = '0'; 3864*7c478bd9Sstevel@tonic-gate for (np = numbuf; *np; np++) { 3865*7c478bd9Sstevel@tonic-gate value *= 10; 3866*7c478bd9Sstevel@tonic-gate value += *np - offset; 3867*7c478bd9Sstevel@tonic-gate } 3868*7c478bd9Sstevel@tonic-gate *valp = value; 3869*7c478bd9Sstevel@tonic-gate return (cp); 3870*7c478bd9Sstevel@tonic-gate } 3871*7c478bd9Sstevel@tonic-gate 3872*7c478bd9Sstevel@tonic-gate /* 3873*7c478bd9Sstevel@tonic-gate * Set max (previously documented as "normal") power. 3874*7c478bd9Sstevel@tonic-gate */ 3875*7c478bd9Sstevel@tonic-gate static void 3876*7c478bd9Sstevel@tonic-gate e_pm_set_max_power(dev_info_t *dip, int component_number, int level) 3877*7c478bd9Sstevel@tonic-gate { 3878*7c478bd9Sstevel@tonic-gate PM_CP(dip, component_number)->pmc_norm_pwr = level; 3879*7c478bd9Sstevel@tonic-gate } 3880*7c478bd9Sstevel@tonic-gate 3881*7c478bd9Sstevel@tonic-gate /* 3882*7c478bd9Sstevel@tonic-gate * Internal routine for destroying components 3883*7c478bd9Sstevel@tonic-gate * It is called even when there might not be any, so it must be forgiving. 3884*7c478bd9Sstevel@tonic-gate */ 3885*7c478bd9Sstevel@tonic-gate static void 3886*7c478bd9Sstevel@tonic-gate e_pm_destroy_components(dev_info_t *dip) 3887*7c478bd9Sstevel@tonic-gate { 3888*7c478bd9Sstevel@tonic-gate int i; 3889*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 3890*7c478bd9Sstevel@tonic-gate 3891*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3892*7c478bd9Sstevel@tonic-gate if (PM_NUMCMPTS(dip) == 0) 3893*7c478bd9Sstevel@tonic-gate return; 3894*7c478bd9Sstevel@tonic-gate cp = DEVI(dip)->devi_pm_components; 3895*7c478bd9Sstevel@tonic-gate ASSERT(cp); 3896*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++, cp++) { 3897*7c478bd9Sstevel@tonic-gate int nlevels = cp->pmc_comp.pmc_numlevels; 3898*7c478bd9Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lvals, nlevels * sizeof (int)); 3899*7c478bd9Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_thresh, nlevels * sizeof (int)); 3900*7c478bd9Sstevel@tonic-gate /* 3901*7c478bd9Sstevel@tonic-gate * For BC nodes, the rest is static in bc_comp, so skip it 3902*7c478bd9Sstevel@tonic-gate */ 3903*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip)) 3904*7c478bd9Sstevel@tonic-gate continue; 3905*7c478bd9Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_name, cp->pmc_comp.pmc_name_sz); 3906*7c478bd9Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lnames, nlevels * sizeof (char *)); 3907*7c478bd9Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lname_buf, 3908*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_lnames_sz); 3909*7c478bd9Sstevel@tonic-gate } 3910*7c478bd9Sstevel@tonic-gate kmem_free(DEVI(dip)->devi_pm_components, DEVI(dip)->devi_pm_comp_size); 3911*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_components = NULL; 3912*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_num_components = 0; 3913*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= 3914*7c478bd9Sstevel@tonic-gate ~(PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED); 3915*7c478bd9Sstevel@tonic-gate } 3916*7c478bd9Sstevel@tonic-gate 3917*7c478bd9Sstevel@tonic-gate /* 3918*7c478bd9Sstevel@tonic-gate * Read the pm-components property (if there is one) and use it to set up 3919*7c478bd9Sstevel@tonic-gate * components. Returns a pointer to an array of component structures if 3920*7c478bd9Sstevel@tonic-gate * pm-components found and successfully parsed, else returns NULL. 3921*7c478bd9Sstevel@tonic-gate * Sets error return *errp to true to indicate a failure (as opposed to no 3922*7c478bd9Sstevel@tonic-gate * property being present). 3923*7c478bd9Sstevel@tonic-gate */ 3924*7c478bd9Sstevel@tonic-gate pm_comp_t * 3925*7c478bd9Sstevel@tonic-gate pm_autoconfig(dev_info_t *dip, int *errp) 3926*7c478bd9Sstevel@tonic-gate { 3927*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "autoconfig") 3928*7c478bd9Sstevel@tonic-gate uint_t nelems; 3929*7c478bd9Sstevel@tonic-gate char **pp; 3930*7c478bd9Sstevel@tonic-gate pm_comp_t *compp = NULL; 3931*7c478bd9Sstevel@tonic-gate int i, j, level, components = 0; 3932*7c478bd9Sstevel@tonic-gate size_t size = 0; 3933*7c478bd9Sstevel@tonic-gate struct pm_comp_pkg *p, *ptail; 3934*7c478bd9Sstevel@tonic-gate struct pm_comp_pkg *phead = NULL; 3935*7c478bd9Sstevel@tonic-gate int *lvals = NULL; 3936*7c478bd9Sstevel@tonic-gate int *lszs = NULL; 3937*7c478bd9Sstevel@tonic-gate int *np = NULL; 3938*7c478bd9Sstevel@tonic-gate int npi = 0; 3939*7c478bd9Sstevel@tonic-gate char **lnames = NULL; 3940*7c478bd9Sstevel@tonic-gate char *cp, *tp; 3941*7c478bd9Sstevel@tonic-gate pm_comp_t *ret = NULL; 3942*7c478bd9Sstevel@tonic-gate 3943*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3944*7c478bd9Sstevel@tonic-gate *errp = 0; /* assume success */ 3945*7c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 3946*7c478bd9Sstevel@tonic-gate (char *)pmcompstr, &pp, &nelems) != DDI_PROP_SUCCESS) { 3947*7c478bd9Sstevel@tonic-gate return (NULL); 3948*7c478bd9Sstevel@tonic-gate } 3949*7c478bd9Sstevel@tonic-gate 3950*7c478bd9Sstevel@tonic-gate if (nelems < 3) { /* need at least one name and two levels */ 3951*7c478bd9Sstevel@tonic-gate goto errout; 3952*7c478bd9Sstevel@tonic-gate } 3953*7c478bd9Sstevel@tonic-gate 3954*7c478bd9Sstevel@tonic-gate /* 3955*7c478bd9Sstevel@tonic-gate * pm_create_components is no longer allowed 3956*7c478bd9Sstevel@tonic-gate */ 3957*7c478bd9Sstevel@tonic-gate if (PM_NUMCMPTS(dip) != 0) { 3958*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) has %d comps\n", 3959*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_NUMCMPTS(dip))) 3960*7c478bd9Sstevel@tonic-gate goto errout; 3961*7c478bd9Sstevel@tonic-gate } 3962*7c478bd9Sstevel@tonic-gate 3963*7c478bd9Sstevel@tonic-gate lvals = kmem_alloc(nelems * sizeof (int), KM_SLEEP); 3964*7c478bd9Sstevel@tonic-gate lszs = kmem_alloc(nelems * sizeof (int), KM_SLEEP); 3965*7c478bd9Sstevel@tonic-gate lnames = kmem_alloc(nelems * sizeof (char *), KM_SLEEP); 3966*7c478bd9Sstevel@tonic-gate np = kmem_alloc(nelems * sizeof (int), KM_SLEEP); 3967*7c478bd9Sstevel@tonic-gate 3968*7c478bd9Sstevel@tonic-gate level = 0; 3969*7c478bd9Sstevel@tonic-gate phead = NULL; 3970*7c478bd9Sstevel@tonic-gate for (i = 0; i < nelems; i++) { 3971*7c478bd9Sstevel@tonic-gate cp = pp[i]; 3972*7c478bd9Sstevel@tonic-gate if (!isdigit(*cp)) { /* must be name */ 3973*7c478bd9Sstevel@tonic-gate if (strncmp(cp, namestr, nameln) != 0) { 3974*7c478bd9Sstevel@tonic-gate goto errout; 3975*7c478bd9Sstevel@tonic-gate } 3976*7c478bd9Sstevel@tonic-gate if (i != 0) { 3977*7c478bd9Sstevel@tonic-gate if (level == 0) { /* no level spec'd */ 3978*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: no level spec'd\n", 3979*7c478bd9Sstevel@tonic-gate pmf)) 3980*7c478bd9Sstevel@tonic-gate goto errout; 3981*7c478bd9Sstevel@tonic-gate } 3982*7c478bd9Sstevel@tonic-gate np[npi++] = lvals[level - 1]; 3983*7c478bd9Sstevel@tonic-gate /* finish up previous component levels */ 3984*7c478bd9Sstevel@tonic-gate FINISH_COMP; 3985*7c478bd9Sstevel@tonic-gate } 3986*7c478bd9Sstevel@tonic-gate cp += nameln; 3987*7c478bd9Sstevel@tonic-gate if (!*cp) { 3988*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: nsa\n", pmf)) 3989*7c478bd9Sstevel@tonic-gate goto errout; 3990*7c478bd9Sstevel@tonic-gate } 3991*7c478bd9Sstevel@tonic-gate p = kmem_zalloc(sizeof (*phead), KM_SLEEP); 3992*7c478bd9Sstevel@tonic-gate if (phead == NULL) { 3993*7c478bd9Sstevel@tonic-gate phead = ptail = p; 3994*7c478bd9Sstevel@tonic-gate } else { 3995*7c478bd9Sstevel@tonic-gate ptail->next = p; 3996*7c478bd9Sstevel@tonic-gate ptail = p; 3997*7c478bd9Sstevel@tonic-gate } 3998*7c478bd9Sstevel@tonic-gate compp = p->comp = kmem_zalloc(sizeof (pm_comp_t), 3999*7c478bd9Sstevel@tonic-gate KM_SLEEP); 4000*7c478bd9Sstevel@tonic-gate compp->pmc_name_sz = strlen(cp) + 1; 4001*7c478bd9Sstevel@tonic-gate compp->pmc_name = kmem_zalloc(compp->pmc_name_sz, 4002*7c478bd9Sstevel@tonic-gate KM_SLEEP); 4003*7c478bd9Sstevel@tonic-gate (void) strncpy(compp->pmc_name, cp, compp->pmc_name_sz); 4004*7c478bd9Sstevel@tonic-gate components++; 4005*7c478bd9Sstevel@tonic-gate level = 0; 4006*7c478bd9Sstevel@tonic-gate } else { /* better be power level <num>=<name> */ 4007*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4008*7c478bd9Sstevel@tonic-gate tp = cp; 4009*7c478bd9Sstevel@tonic-gate #endif 4010*7c478bd9Sstevel@tonic-gate if (i == 0 || 4011*7c478bd9Sstevel@tonic-gate (cp = pm_parsenum(cp, &lvals[level])) == NULL) { 4012*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: parsenum(%s)\n", pmf, tp)) 4013*7c478bd9Sstevel@tonic-gate goto errout; 4014*7c478bd9Sstevel@tonic-gate } 4015*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4016*7c478bd9Sstevel@tonic-gate tp = cp; 4017*7c478bd9Sstevel@tonic-gate #endif 4018*7c478bd9Sstevel@tonic-gate if (*cp++ != '=' || !*cp) { 4019*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: ex =, got %s\n", pmf, tp)) 4020*7c478bd9Sstevel@tonic-gate goto errout; 4021*7c478bd9Sstevel@tonic-gate } 4022*7c478bd9Sstevel@tonic-gate 4023*7c478bd9Sstevel@tonic-gate lszs[level] = strlen(cp) + 1; 4024*7c478bd9Sstevel@tonic-gate size += lszs[level]; 4025*7c478bd9Sstevel@tonic-gate lnames[level] = cp; /* points into prop string */ 4026*7c478bd9Sstevel@tonic-gate level++; 4027*7c478bd9Sstevel@tonic-gate } 4028*7c478bd9Sstevel@tonic-gate } 4029*7c478bd9Sstevel@tonic-gate np[npi++] = lvals[level - 1]; 4030*7c478bd9Sstevel@tonic-gate if (level == 0) { /* ended with a name */ 4031*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: ewn\n", pmf)) 4032*7c478bd9Sstevel@tonic-gate goto errout; 4033*7c478bd9Sstevel@tonic-gate } 4034*7c478bd9Sstevel@tonic-gate FINISH_COMP; 4035*7c478bd9Sstevel@tonic-gate 4036*7c478bd9Sstevel@tonic-gate 4037*7c478bd9Sstevel@tonic-gate /* 4038*7c478bd9Sstevel@tonic-gate * Now we have a list of components--we have to return instead an 4039*7c478bd9Sstevel@tonic-gate * array of them, but we can just copy the top level and leave 4040*7c478bd9Sstevel@tonic-gate * the rest as is 4041*7c478bd9Sstevel@tonic-gate */ 4042*7c478bd9Sstevel@tonic-gate (void) e_pm_create_components(dip, components); 4043*7c478bd9Sstevel@tonic-gate for (i = 0; i < components; i++) 4044*7c478bd9Sstevel@tonic-gate e_pm_set_max_power(dip, i, np[i]); 4045*7c478bd9Sstevel@tonic-gate 4046*7c478bd9Sstevel@tonic-gate ret = kmem_zalloc(components * sizeof (pm_comp_t), KM_SLEEP); 4047*7c478bd9Sstevel@tonic-gate for (i = 0, p = phead; i < components; i++) { 4048*7c478bd9Sstevel@tonic-gate ASSERT(p); 4049*7c478bd9Sstevel@tonic-gate /* 4050*7c478bd9Sstevel@tonic-gate * Now sanity-check values: levels must be monotonically 4051*7c478bd9Sstevel@tonic-gate * increasing 4052*7c478bd9Sstevel@tonic-gate */ 4053*7c478bd9Sstevel@tonic-gate if (p->comp->pmc_numlevels < 2) { 4054*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) only %d " 4055*7c478bd9Sstevel@tonic-gate "levels\n", pmf, 4056*7c478bd9Sstevel@tonic-gate p->comp->pmc_name, PM_DEVICE(dip), 4057*7c478bd9Sstevel@tonic-gate p->comp->pmc_numlevels)) 4058*7c478bd9Sstevel@tonic-gate goto errout; 4059*7c478bd9Sstevel@tonic-gate } 4060*7c478bd9Sstevel@tonic-gate for (j = 0; j < p->comp->pmc_numlevels; j++) { 4061*7c478bd9Sstevel@tonic-gate if ((p->comp->pmc_lvals[j] < 0) || ((j > 0) && 4062*7c478bd9Sstevel@tonic-gate (p->comp->pmc_lvals[j] <= 4063*7c478bd9Sstevel@tonic-gate p->comp->pmc_lvals[j - 1]))) { 4064*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) " 4065*7c478bd9Sstevel@tonic-gate "not mono. incr, %d follows %d\n", pmf, 4066*7c478bd9Sstevel@tonic-gate p->comp->pmc_name, PM_DEVICE(dip), 4067*7c478bd9Sstevel@tonic-gate p->comp->pmc_lvals[j], 4068*7c478bd9Sstevel@tonic-gate p->comp->pmc_lvals[j - 1])) 4069*7c478bd9Sstevel@tonic-gate goto errout; 4070*7c478bd9Sstevel@tonic-gate } 4071*7c478bd9Sstevel@tonic-gate } 4072*7c478bd9Sstevel@tonic-gate ret[i] = *p->comp; /* struct assignment */ 4073*7c478bd9Sstevel@tonic-gate for (j = 0; j < i; j++) { 4074*7c478bd9Sstevel@tonic-gate /* 4075*7c478bd9Sstevel@tonic-gate * Test for unique component names 4076*7c478bd9Sstevel@tonic-gate */ 4077*7c478bd9Sstevel@tonic-gate if (strcmp(ret[j].pmc_name, ret[i].pmc_name) == 0) { 4078*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s of %s@%s(%s#%d) not " 4079*7c478bd9Sstevel@tonic-gate "unique\n", pmf, ret[j].pmc_name, 4080*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 4081*7c478bd9Sstevel@tonic-gate goto errout; 4082*7c478bd9Sstevel@tonic-gate } 4083*7c478bd9Sstevel@tonic-gate } 4084*7c478bd9Sstevel@tonic-gate ptail = p; 4085*7c478bd9Sstevel@tonic-gate p = p->next; 4086*7c478bd9Sstevel@tonic-gate phead = p; /* errout depends on phead making sense */ 4087*7c478bd9Sstevel@tonic-gate kmem_free(ptail->comp, sizeof (*ptail->comp)); 4088*7c478bd9Sstevel@tonic-gate kmem_free(ptail, sizeof (*ptail)); 4089*7c478bd9Sstevel@tonic-gate } 4090*7c478bd9Sstevel@tonic-gate out: 4091*7c478bd9Sstevel@tonic-gate ddi_prop_free(pp); 4092*7c478bd9Sstevel@tonic-gate if (lvals) 4093*7c478bd9Sstevel@tonic-gate kmem_free(lvals, nelems * sizeof (int)); 4094*7c478bd9Sstevel@tonic-gate if (lszs) 4095*7c478bd9Sstevel@tonic-gate kmem_free(lszs, nelems * sizeof (int)); 4096*7c478bd9Sstevel@tonic-gate if (lnames) 4097*7c478bd9Sstevel@tonic-gate kmem_free(lnames, nelems * sizeof (char *)); 4098*7c478bd9Sstevel@tonic-gate if (np) 4099*7c478bd9Sstevel@tonic-gate kmem_free(np, nelems * sizeof (int)); 4100*7c478bd9Sstevel@tonic-gate return (ret); 4101*7c478bd9Sstevel@tonic-gate 4102*7c478bd9Sstevel@tonic-gate errout: 4103*7c478bd9Sstevel@tonic-gate e_pm_destroy_components(dip); 4104*7c478bd9Sstevel@tonic-gate *errp = 1; /* signal failure */ 4105*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "!pm: %s property ", pmcompstr); 4106*7c478bd9Sstevel@tonic-gate for (i = 0; i < nelems - 1; i++) 4107*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "!'%s', ", pp[i]); 4108*7c478bd9Sstevel@tonic-gate if (nelems != 0) 4109*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "!'%s'", pp[nelems - 1]); 4110*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "! for %s@%s(%s#%d) is ill-formed.\n", PM_DEVICE(dip)); 4111*7c478bd9Sstevel@tonic-gate for (p = phead; p; ) { 4112*7c478bd9Sstevel@tonic-gate pm_comp_t *pp; 4113*7c478bd9Sstevel@tonic-gate int n; 4114*7c478bd9Sstevel@tonic-gate 4115*7c478bd9Sstevel@tonic-gate ptail = p; 4116*7c478bd9Sstevel@tonic-gate /* 4117*7c478bd9Sstevel@tonic-gate * Free component data structures 4118*7c478bd9Sstevel@tonic-gate */ 4119*7c478bd9Sstevel@tonic-gate pp = p->comp; 4120*7c478bd9Sstevel@tonic-gate n = pp->pmc_numlevels; 4121*7c478bd9Sstevel@tonic-gate if (pp->pmc_name_sz) { 4122*7c478bd9Sstevel@tonic-gate kmem_free(pp->pmc_name, pp->pmc_name_sz); 4123*7c478bd9Sstevel@tonic-gate } 4124*7c478bd9Sstevel@tonic-gate if (pp->pmc_lnames_sz) { 4125*7c478bd9Sstevel@tonic-gate kmem_free(pp->pmc_lname_buf, pp->pmc_lnames_sz); 4126*7c478bd9Sstevel@tonic-gate } 4127*7c478bd9Sstevel@tonic-gate if (pp->pmc_lnames) { 4128*7c478bd9Sstevel@tonic-gate kmem_free(pp->pmc_lnames, n * (sizeof (char *))); 4129*7c478bd9Sstevel@tonic-gate } 4130*7c478bd9Sstevel@tonic-gate if (pp->pmc_thresh) { 4131*7c478bd9Sstevel@tonic-gate kmem_free(pp->pmc_thresh, n * (sizeof (int))); 4132*7c478bd9Sstevel@tonic-gate } 4133*7c478bd9Sstevel@tonic-gate if (pp->pmc_lvals) { 4134*7c478bd9Sstevel@tonic-gate kmem_free(pp->pmc_lvals, n * (sizeof (int))); 4135*7c478bd9Sstevel@tonic-gate } 4136*7c478bd9Sstevel@tonic-gate p = ptail->next; 4137*7c478bd9Sstevel@tonic-gate kmem_free(ptail, sizeof (*ptail)); 4138*7c478bd9Sstevel@tonic-gate } 4139*7c478bd9Sstevel@tonic-gate if (ret != NULL) 4140*7c478bd9Sstevel@tonic-gate kmem_free(ret, components * sizeof (pm_comp_t)); 4141*7c478bd9Sstevel@tonic-gate ret = NULL; 4142*7c478bd9Sstevel@tonic-gate goto out; 4143*7c478bd9Sstevel@tonic-gate } 4144*7c478bd9Sstevel@tonic-gate 4145*7c478bd9Sstevel@tonic-gate /* 4146*7c478bd9Sstevel@tonic-gate * Set threshold values for a devices components by dividing the target 4147*7c478bd9Sstevel@tonic-gate * threshold (base) by the number of transitions and assign each transition 4148*7c478bd9Sstevel@tonic-gate * that threshold. This will get the entire device down in the target time if 4149*7c478bd9Sstevel@tonic-gate * all components are idle and even if there are dependencies among components. 4150*7c478bd9Sstevel@tonic-gate * 4151*7c478bd9Sstevel@tonic-gate * Devices may well get powered all the way down before the target time, but 4152*7c478bd9Sstevel@tonic-gate * at least the EPA will be happy. 4153*7c478bd9Sstevel@tonic-gate */ 4154*7c478bd9Sstevel@tonic-gate void 4155*7c478bd9Sstevel@tonic-gate pm_set_device_threshold(dev_info_t *dip, int base, int flag) 4156*7c478bd9Sstevel@tonic-gate { 4157*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "set_device_threshold") 4158*7c478bd9Sstevel@tonic-gate int target_threshold = (base * 95) / 100; 4159*7c478bd9Sstevel@tonic-gate int level, comp; /* loop counters */ 4160*7c478bd9Sstevel@tonic-gate int transitions = 0; 4161*7c478bd9Sstevel@tonic-gate int ncomp = PM_NUMCMPTS(dip); 4162*7c478bd9Sstevel@tonic-gate int thresh; 4163*7c478bd9Sstevel@tonic-gate int remainder; 4164*7c478bd9Sstevel@tonic-gate pm_comp_t *pmc; 4165*7c478bd9Sstevel@tonic-gate int i, circ; 4166*7c478bd9Sstevel@tonic-gate 4167*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 4168*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 4169*7c478bd9Sstevel@tonic-gate /* 4170*7c478bd9Sstevel@tonic-gate * First we handle the easy one. If we're setting the default 4171*7c478bd9Sstevel@tonic-gate * threshold for a node with children, then we set it to the 4172*7c478bd9Sstevel@tonic-gate * default nexus threshold (currently 0) and mark it as default 4173*7c478bd9Sstevel@tonic-gate * nexus threshold instead 4174*7c478bd9Sstevel@tonic-gate */ 4175*7c478bd9Sstevel@tonic-gate if (PM_IS_NEXUS(dip)) { 4176*7c478bd9Sstevel@tonic-gate if (flag == PMC_DEF_THRESH) { 4177*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: [%s@%s(%s#%d) NEXDEF]\n", pmf, 4178*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 4179*7c478bd9Sstevel@tonic-gate thresh = pm_default_nexus_threshold; 4180*7c478bd9Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4181*7c478bd9Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4182*7c478bd9Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; 4183*7c478bd9Sstevel@tonic-gate level++) { 4184*7c478bd9Sstevel@tonic-gate pmc->pmc_thresh[level] = thresh; 4185*7c478bd9Sstevel@tonic-gate } 4186*7c478bd9Sstevel@tonic-gate } 4187*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_dev_thresh = 4188*7c478bd9Sstevel@tonic-gate pm_default_nexus_threshold; 4189*7c478bd9Sstevel@tonic-gate /* 4190*7c478bd9Sstevel@tonic-gate * If the nexus node is being reconfigured back to 4191*7c478bd9Sstevel@tonic-gate * the default threshold, adjust the notlowest count. 4192*7c478bd9Sstevel@tonic-gate */ 4193*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & 4194*7c478bd9Sstevel@tonic-gate (PMC_DEV_THRESH|PMC_COMP_THRESH)) { 4195*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 4196*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 4197*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == 0) 4198*7c478bd9Sstevel@tonic-gate continue; 4199*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); 4200*7c478bd9Sstevel@tonic-gate ASSERT(pm_comps_notlowest); 4201*7c478bd9Sstevel@tonic-gate pm_comps_notlowest--; 4202*7c478bd9Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr " 4203*7c478bd9Sstevel@tonic-gate "notlowest to %d\n", pmf, 4204*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), pm_comps_notlowest)) 4205*7c478bd9Sstevel@tonic-gate if (pm_comps_notlowest == 0) 4206*7c478bd9Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, 4207*7c478bd9Sstevel@tonic-gate PM_ALL_LOWEST); 4208*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); 4209*7c478bd9Sstevel@tonic-gate } 4210*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 4211*7c478bd9Sstevel@tonic-gate } 4212*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE; 4213*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NEXDEF_THRESH; 4214*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 4215*7c478bd9Sstevel@tonic-gate return; 4216*7c478bd9Sstevel@tonic-gate } else if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) { 4217*7c478bd9Sstevel@tonic-gate /* 4218*7c478bd9Sstevel@tonic-gate * If the nexus node is being configured for a 4219*7c478bd9Sstevel@tonic-gate * non-default threshold, include that node in 4220*7c478bd9Sstevel@tonic-gate * the notlowest accounting. 4221*7c478bd9Sstevel@tonic-gate */ 4222*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 4223*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 4224*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == 0) 4225*7c478bd9Sstevel@tonic-gate continue; 4226*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); 4227*7c478bd9Sstevel@tonic-gate if (pm_comps_notlowest == 0) 4228*7c478bd9Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, 4229*7c478bd9Sstevel@tonic-gate PM_NOT_ALL_LOWEST); 4230*7c478bd9Sstevel@tonic-gate pm_comps_notlowest++; 4231*7c478bd9Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr " 4232*7c478bd9Sstevel@tonic-gate "notlowest to %d\n", pmf, 4233*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), pm_comps_notlowest)) 4234*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); 4235*7c478bd9Sstevel@tonic-gate } 4236*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 4237*7c478bd9Sstevel@tonic-gate } 4238*7c478bd9Sstevel@tonic-gate } 4239*7c478bd9Sstevel@tonic-gate /* 4240*7c478bd9Sstevel@tonic-gate * Compute the total number of transitions for all components 4241*7c478bd9Sstevel@tonic-gate * of the device. Distribute the threshold evenly over them 4242*7c478bd9Sstevel@tonic-gate */ 4243*7c478bd9Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4244*7c478bd9Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4245*7c478bd9Sstevel@tonic-gate ASSERT(pmc->pmc_numlevels > 1); 4246*7c478bd9Sstevel@tonic-gate transitions += pmc->pmc_numlevels - 1; 4247*7c478bd9Sstevel@tonic-gate } 4248*7c478bd9Sstevel@tonic-gate ASSERT(transitions); 4249*7c478bd9Sstevel@tonic-gate thresh = target_threshold / transitions; 4250*7c478bd9Sstevel@tonic-gate 4251*7c478bd9Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4252*7c478bd9Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4253*7c478bd9Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) { 4254*7c478bd9Sstevel@tonic-gate pmc->pmc_thresh[level] = thresh; 4255*7c478bd9Sstevel@tonic-gate } 4256*7c478bd9Sstevel@tonic-gate } 4257*7c478bd9Sstevel@tonic-gate 4258*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4259*7c478bd9Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4260*7c478bd9Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4261*7c478bd9Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) { 4262*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: thresh before %s@%s(%s#%d) " 4263*7c478bd9Sstevel@tonic-gate "comp=%d, level=%d, %d\n", pmf, PM_DEVICE(dip), 4264*7c478bd9Sstevel@tonic-gate comp, level, pmc->pmc_thresh[level])) 4265*7c478bd9Sstevel@tonic-gate } 4266*7c478bd9Sstevel@tonic-gate } 4267*7c478bd9Sstevel@tonic-gate #endif 4268*7c478bd9Sstevel@tonic-gate /* 4269*7c478bd9Sstevel@tonic-gate * Distribute any remainder till they are all gone 4270*7c478bd9Sstevel@tonic-gate */ 4271*7c478bd9Sstevel@tonic-gate remainder = target_threshold - thresh * transitions; 4272*7c478bd9Sstevel@tonic-gate level = 1; 4273*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4274*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: remainder=%d target_threshold=%d thresh=%d " 4275*7c478bd9Sstevel@tonic-gate "trans=%d\n", pmf, remainder, target_threshold, thresh, 4276*7c478bd9Sstevel@tonic-gate transitions)) 4277*7c478bd9Sstevel@tonic-gate #endif 4278*7c478bd9Sstevel@tonic-gate while (remainder > 0) { 4279*7c478bd9Sstevel@tonic-gate comp = 0; 4280*7c478bd9Sstevel@tonic-gate while (remainder && (comp < ncomp)) { 4281*7c478bd9Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4282*7c478bd9Sstevel@tonic-gate if (level < pmc->pmc_numlevels) { 4283*7c478bd9Sstevel@tonic-gate pmc->pmc_thresh[level] += 1; 4284*7c478bd9Sstevel@tonic-gate remainder--; 4285*7c478bd9Sstevel@tonic-gate } 4286*7c478bd9Sstevel@tonic-gate comp++; 4287*7c478bd9Sstevel@tonic-gate } 4288*7c478bd9Sstevel@tonic-gate level++; 4289*7c478bd9Sstevel@tonic-gate } 4290*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4291*7c478bd9Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4292*7c478bd9Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4293*7c478bd9Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) { 4294*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: thresh after %s@%s(%s#%d) " 4295*7c478bd9Sstevel@tonic-gate "comp=%d level=%d, %d\n", pmf, PM_DEVICE(dip), 4296*7c478bd9Sstevel@tonic-gate comp, level, pmc->pmc_thresh[level])) 4297*7c478bd9Sstevel@tonic-gate } 4298*7c478bd9Sstevel@tonic-gate } 4299*7c478bd9Sstevel@tonic-gate #endif 4300*7c478bd9Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip)); 4301*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_dev_thresh = base; 4302*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE; 4303*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= flag; 4304*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 4305*7c478bd9Sstevel@tonic-gate } 4306*7c478bd9Sstevel@tonic-gate 4307*7c478bd9Sstevel@tonic-gate /* 4308*7c478bd9Sstevel@tonic-gate * Called when there is no old-style platform power management driver 4309*7c478bd9Sstevel@tonic-gate */ 4310*7c478bd9Sstevel@tonic-gate static int 4311*7c478bd9Sstevel@tonic-gate ddi_no_platform_power(power_req_t *req) 4312*7c478bd9Sstevel@tonic-gate { 4313*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(req)) 4314*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4315*7c478bd9Sstevel@tonic-gate } 4316*7c478bd9Sstevel@tonic-gate 4317*7c478bd9Sstevel@tonic-gate /* 4318*7c478bd9Sstevel@tonic-gate * This function calls the entry point supplied by the platform-specific 4319*7c478bd9Sstevel@tonic-gate * pm driver to bring the device component 'pm_cmpt' to power level 'pm_level'. 4320*7c478bd9Sstevel@tonic-gate * The use of global for getting the function name from platform-specific 4321*7c478bd9Sstevel@tonic-gate * pm driver is not ideal, but it is simple and efficient. 4322*7c478bd9Sstevel@tonic-gate * The previous property lookup was being done in the idle loop on swift 4323*7c478bd9Sstevel@tonic-gate * systems without pmc chips and hurt deskbench performance as well as 4324*7c478bd9Sstevel@tonic-gate * violating scheduler locking rules 4325*7c478bd9Sstevel@tonic-gate */ 4326*7c478bd9Sstevel@tonic-gate int (*pm_platform_power)(power_req_t *) = ddi_no_platform_power; 4327*7c478bd9Sstevel@tonic-gate 4328*7c478bd9Sstevel@tonic-gate /* 4329*7c478bd9Sstevel@tonic-gate * Old obsolete interface for a device to request a power change (but only 4330*7c478bd9Sstevel@tonic-gate * an increase in power) 4331*7c478bd9Sstevel@tonic-gate */ 4332*7c478bd9Sstevel@tonic-gate int 4333*7c478bd9Sstevel@tonic-gate ddi_dev_is_needed(dev_info_t *dip, int cmpt, int level) 4334*7c478bd9Sstevel@tonic-gate { 4335*7c478bd9Sstevel@tonic-gate return (pm_raise_power(dip, cmpt, level)); 4336*7c478bd9Sstevel@tonic-gate } 4337*7c478bd9Sstevel@tonic-gate 4338*7c478bd9Sstevel@tonic-gate /* 4339*7c478bd9Sstevel@tonic-gate * The old obsolete interface to platform power management. Only used by 4340*7c478bd9Sstevel@tonic-gate * Gypsy platform and APM on X86. 4341*7c478bd9Sstevel@tonic-gate */ 4342*7c478bd9Sstevel@tonic-gate int 4343*7c478bd9Sstevel@tonic-gate ddi_power(dev_info_t *dip, int pm_cmpt, int pm_level) 4344*7c478bd9Sstevel@tonic-gate { 4345*7c478bd9Sstevel@tonic-gate power_req_t request; 4346*7c478bd9Sstevel@tonic-gate 4347*7c478bd9Sstevel@tonic-gate request.request_type = PMR_SET_POWER; 4348*7c478bd9Sstevel@tonic-gate request.req.set_power_req.who = dip; 4349*7c478bd9Sstevel@tonic-gate request.req.set_power_req.cmpt = pm_cmpt; 4350*7c478bd9Sstevel@tonic-gate request.req.set_power_req.level = pm_level; 4351*7c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL)); 4352*7c478bd9Sstevel@tonic-gate } 4353*7c478bd9Sstevel@tonic-gate 4354*7c478bd9Sstevel@tonic-gate /* 4355*7c478bd9Sstevel@tonic-gate * A driver can invoke this from its detach routine when DDI_SUSPEND is 4356*7c478bd9Sstevel@tonic-gate * passed. Returns true if subsequent processing could result in power being 4357*7c478bd9Sstevel@tonic-gate * removed from the device. The arg is not currently used because it is 4358*7c478bd9Sstevel@tonic-gate * implicit in the operation of cpr/DR. 4359*7c478bd9Sstevel@tonic-gate */ 4360*7c478bd9Sstevel@tonic-gate int 4361*7c478bd9Sstevel@tonic-gate ddi_removing_power(dev_info_t *dip) 4362*7c478bd9Sstevel@tonic-gate { 4363*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(dip)) 4364*7c478bd9Sstevel@tonic-gate return (pm_powering_down); 4365*7c478bd9Sstevel@tonic-gate } 4366*7c478bd9Sstevel@tonic-gate 4367*7c478bd9Sstevel@tonic-gate /* 4368*7c478bd9Sstevel@tonic-gate * Returns true if a device indicates that its parent handles suspend/resume 4369*7c478bd9Sstevel@tonic-gate * processing for it. 4370*7c478bd9Sstevel@tonic-gate */ 4371*7c478bd9Sstevel@tonic-gate int 4372*7c478bd9Sstevel@tonic-gate e_ddi_parental_suspend_resume(dev_info_t *dip) 4373*7c478bd9Sstevel@tonic-gate { 4374*7c478bd9Sstevel@tonic-gate return (DEVI(dip)->devi_pm_flags & PMC_PARENTAL_SR); 4375*7c478bd9Sstevel@tonic-gate } 4376*7c478bd9Sstevel@tonic-gate 4377*7c478bd9Sstevel@tonic-gate /* 4378*7c478bd9Sstevel@tonic-gate * Called for devices which indicate that their parent does suspend/resume 4379*7c478bd9Sstevel@tonic-gate * handling for them 4380*7c478bd9Sstevel@tonic-gate */ 4381*7c478bd9Sstevel@tonic-gate int 4382*7c478bd9Sstevel@tonic-gate e_ddi_suspend(dev_info_t *dip, ddi_detach_cmd_t cmd) 4383*7c478bd9Sstevel@tonic-gate { 4384*7c478bd9Sstevel@tonic-gate power_req_t request; 4385*7c478bd9Sstevel@tonic-gate request.request_type = PMR_SUSPEND; 4386*7c478bd9Sstevel@tonic-gate request.req.suspend_req.who = dip; 4387*7c478bd9Sstevel@tonic-gate request.req.suspend_req.cmd = cmd; 4388*7c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL)); 4389*7c478bd9Sstevel@tonic-gate } 4390*7c478bd9Sstevel@tonic-gate 4391*7c478bd9Sstevel@tonic-gate /* 4392*7c478bd9Sstevel@tonic-gate * Called for devices which indicate that their parent does suspend/resume 4393*7c478bd9Sstevel@tonic-gate * handling for them 4394*7c478bd9Sstevel@tonic-gate */ 4395*7c478bd9Sstevel@tonic-gate int 4396*7c478bd9Sstevel@tonic-gate e_ddi_resume(dev_info_t *dip, ddi_attach_cmd_t cmd) 4397*7c478bd9Sstevel@tonic-gate { 4398*7c478bd9Sstevel@tonic-gate power_req_t request; 4399*7c478bd9Sstevel@tonic-gate request.request_type = PMR_RESUME; 4400*7c478bd9Sstevel@tonic-gate request.req.resume_req.who = dip; 4401*7c478bd9Sstevel@tonic-gate request.req.resume_req.cmd = cmd; 4402*7c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL)); 4403*7c478bd9Sstevel@tonic-gate } 4404*7c478bd9Sstevel@tonic-gate 4405*7c478bd9Sstevel@tonic-gate /* 4406*7c478bd9Sstevel@tonic-gate * Old obsolete exported interface for drivers to create components. 4407*7c478bd9Sstevel@tonic-gate * This is now handled by exporting the pm-components property. 4408*7c478bd9Sstevel@tonic-gate */ 4409*7c478bd9Sstevel@tonic-gate int 4410*7c478bd9Sstevel@tonic-gate pm_create_components(dev_info_t *dip, int num_components) 4411*7c478bd9Sstevel@tonic-gate { 4412*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_create_components") 4413*7c478bd9Sstevel@tonic-gate 4414*7c478bd9Sstevel@tonic-gate if (num_components < 1) 4415*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4416*7c478bd9Sstevel@tonic-gate 4417*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) { 4418*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4419*7c478bd9Sstevel@tonic-gate } 4420*7c478bd9Sstevel@tonic-gate 4421*7c478bd9Sstevel@tonic-gate /* don't need to lock dip because attach is single threaded */ 4422*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_components) { 4423*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) already has %d\n", pmf, 4424*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), PM_NUMCMPTS(dip))) 4425*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4426*7c478bd9Sstevel@tonic-gate } 4427*7c478bd9Sstevel@tonic-gate e_pm_create_components(dip, num_components); 4428*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_BC; 4429*7c478bd9Sstevel@tonic-gate e_pm_default_components(dip, num_components); 4430*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4431*7c478bd9Sstevel@tonic-gate } 4432*7c478bd9Sstevel@tonic-gate 4433*7c478bd9Sstevel@tonic-gate /* 4434*7c478bd9Sstevel@tonic-gate * Obsolete interface previously called by drivers to destroy their components 4435*7c478bd9Sstevel@tonic-gate * at detach time. This is now done automatically. However, we need to keep 4436*7c478bd9Sstevel@tonic-gate * this for the old drivers. 4437*7c478bd9Sstevel@tonic-gate */ 4438*7c478bd9Sstevel@tonic-gate void 4439*7c478bd9Sstevel@tonic-gate pm_destroy_components(dev_info_t *dip) 4440*7c478bd9Sstevel@tonic-gate { 4441*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_destroy_components") 4442*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 4443*7c478bd9Sstevel@tonic-gate 4444*7c478bd9Sstevel@tonic-gate PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, 4445*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 4446*7c478bd9Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip)); 4447*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4448*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) 4449*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!driver exporting pm-components property " 4450*7c478bd9Sstevel@tonic-gate "(%s@%s) calls pm_destroy_components", PM_NAME(dip), 4451*7c478bd9Sstevel@tonic-gate PM_ADDR(dip)); 4452*7c478bd9Sstevel@tonic-gate #endif 4453*7c478bd9Sstevel@tonic-gate /* 4454*7c478bd9Sstevel@tonic-gate * We ignore this unless this is an old-style driver, except for 4455*7c478bd9Sstevel@tonic-gate * printing the message above 4456*7c478bd9Sstevel@tonic-gate */ 4457*7c478bd9Sstevel@tonic-gate if (PM_NUMCMPTS(dip) == 0 || !PM_ISBC(dip)) { 4458*7c478bd9Sstevel@tonic-gate PMD(PMD_REMDEV, ("%s: ignore %s@%s(%s#%d)\n", pmf, 4459*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 4460*7c478bd9Sstevel@tonic-gate return; 4461*7c478bd9Sstevel@tonic-gate } 4462*7c478bd9Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip)); 4463*7c478bd9Sstevel@tonic-gate 4464*7c478bd9Sstevel@tonic-gate /* 4465*7c478bd9Sstevel@tonic-gate * pm_unmanage will clear info pointer later, after dealing with 4466*7c478bd9Sstevel@tonic-gate * dependencies 4467*7c478bd9Sstevel@tonic-gate */ 4468*7c478bd9Sstevel@tonic-gate ASSERT(!PM_GET_PM_SCAN(dip)); /* better be gone already */ 4469*7c478bd9Sstevel@tonic-gate /* 4470*7c478bd9Sstevel@tonic-gate * Now adjust parent's kidsupcnt. We check only comp 0. 4471*7c478bd9Sstevel@tonic-gate * Parents that get notification are not adjusted because their 4472*7c478bd9Sstevel@tonic-gate * kidsupcnt is always 0 (or 1 during probe and attach). 4473*7c478bd9Sstevel@tonic-gate */ 4474*7c478bd9Sstevel@tonic-gate if ((PM_CURPOWER(dip, 0) != 0) && pdip && !PM_WANTS_NOTIFICATION(pdip)) 4475*7c478bd9Sstevel@tonic-gate pm_rele_power(pdip); 4476*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4477*7c478bd9Sstevel@tonic-gate else { 4478*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: kuc stays %s@%s(%s#%d) comps gone\n", 4479*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 4480*7c478bd9Sstevel@tonic-gate } 4481*7c478bd9Sstevel@tonic-gate #endif 4482*7c478bd9Sstevel@tonic-gate e_pm_destroy_components(dip); 4483*7c478bd9Sstevel@tonic-gate /* 4484*7c478bd9Sstevel@tonic-gate * Forget we ever knew anything about the components of this device 4485*7c478bd9Sstevel@tonic-gate */ 4486*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= 4487*7c478bd9Sstevel@tonic-gate ~(PMC_BC | PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED); 4488*7c478bd9Sstevel@tonic-gate } 4489*7c478bd9Sstevel@tonic-gate 4490*7c478bd9Sstevel@tonic-gate /* 4491*7c478bd9Sstevel@tonic-gate * Exported interface for a driver to set a component busy. 4492*7c478bd9Sstevel@tonic-gate */ 4493*7c478bd9Sstevel@tonic-gate int 4494*7c478bd9Sstevel@tonic-gate pm_busy_component(dev_info_t *dip, int cmpt) 4495*7c478bd9Sstevel@tonic-gate { 4496*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 4497*7c478bd9Sstevel@tonic-gate 4498*7c478bd9Sstevel@tonic-gate ASSERT(dip != NULL); 4499*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp)) 4500*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4501*7c478bd9Sstevel@tonic-gate PM_LOCK_BUSY(dip); 4502*7c478bd9Sstevel@tonic-gate cp->pmc_busycount++; 4503*7c478bd9Sstevel@tonic-gate cp->pmc_timestamp = 0; 4504*7c478bd9Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 4505*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4506*7c478bd9Sstevel@tonic-gate } 4507*7c478bd9Sstevel@tonic-gate 4508*7c478bd9Sstevel@tonic-gate /* 4509*7c478bd9Sstevel@tonic-gate * Exported interface for a driver to set a component idle. 4510*7c478bd9Sstevel@tonic-gate */ 4511*7c478bd9Sstevel@tonic-gate int 4512*7c478bd9Sstevel@tonic-gate pm_idle_component(dev_info_t *dip, int cmpt) 4513*7c478bd9Sstevel@tonic-gate { 4514*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_idle_component") 4515*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 4516*7c478bd9Sstevel@tonic-gate pm_scan_t *scanp = PM_GET_PM_SCAN(dip); 4517*7c478bd9Sstevel@tonic-gate 4518*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp)) 4519*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4520*7c478bd9Sstevel@tonic-gate 4521*7c478bd9Sstevel@tonic-gate PM_LOCK_BUSY(dip); 4522*7c478bd9Sstevel@tonic-gate if (cp->pmc_busycount) { 4523*7c478bd9Sstevel@tonic-gate if (--(cp->pmc_busycount) == 0) 4524*7c478bd9Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 4525*7c478bd9Sstevel@tonic-gate } else { 4526*7c478bd9Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 4527*7c478bd9Sstevel@tonic-gate } 4528*7c478bd9Sstevel@tonic-gate 4529*7c478bd9Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 4530*7c478bd9Sstevel@tonic-gate 4531*7c478bd9Sstevel@tonic-gate /* 4532*7c478bd9Sstevel@tonic-gate * if device becomes idle during idle down period, try scan it down 4533*7c478bd9Sstevel@tonic-gate */ 4534*7c478bd9Sstevel@tonic-gate if (scanp && PM_IS_PID(dip)) { 4535*7c478bd9Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d) idle.\n", pmf, 4536*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 4537*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 4538*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4539*7c478bd9Sstevel@tonic-gate } 4540*7c478bd9Sstevel@tonic-gate 4541*7c478bd9Sstevel@tonic-gate /* 4542*7c478bd9Sstevel@tonic-gate * handle scan not running with nexus threshold == 0 4543*7c478bd9Sstevel@tonic-gate */ 4544*7c478bd9Sstevel@tonic-gate 4545*7c478bd9Sstevel@tonic-gate if (PM_IS_NEXUS(dip) && (cp->pmc_busycount == 0)) { 4546*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 4547*7c478bd9Sstevel@tonic-gate } 4548*7c478bd9Sstevel@tonic-gate 4549*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4550*7c478bd9Sstevel@tonic-gate } 4551*7c478bd9Sstevel@tonic-gate 4552*7c478bd9Sstevel@tonic-gate /* 4553*7c478bd9Sstevel@tonic-gate * This is the old obsolete interface called by drivers to set their normal 4554*7c478bd9Sstevel@tonic-gate * power. Thus we can't fix its behavior or return a value. 4555*7c478bd9Sstevel@tonic-gate * This functionality is replaced by the pm-component property. 4556*7c478bd9Sstevel@tonic-gate * We'll only get components destroyed while no power management is 4557*7c478bd9Sstevel@tonic-gate * going on (and the device is detached), so we don't need a mutex here 4558*7c478bd9Sstevel@tonic-gate */ 4559*7c478bd9Sstevel@tonic-gate void 4560*7c478bd9Sstevel@tonic-gate pm_set_normal_power(dev_info_t *dip, int comp, int level) 4561*7c478bd9Sstevel@tonic-gate { 4562*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "set_normal_power") 4563*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4564*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) 4565*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!call to pm_set_normal_power() by %s@%s " 4566*7c478bd9Sstevel@tonic-gate "(driver exporting pm-components property) ignored", 4567*7c478bd9Sstevel@tonic-gate PM_NAME(dip), PM_ADDR(dip)); 4568*7c478bd9Sstevel@tonic-gate #endif 4569*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip)) { 4570*7c478bd9Sstevel@tonic-gate PMD(PMD_NORM, ("%s: %s@%s(%s#%d) set normal power comp=%d, " 4571*7c478bd9Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 4572*7c478bd9Sstevel@tonic-gate e_pm_set_max_power(dip, comp, level); 4573*7c478bd9Sstevel@tonic-gate e_pm_default_levels(dip, PM_CP(dip, comp), level); 4574*7c478bd9Sstevel@tonic-gate } 4575*7c478bd9Sstevel@tonic-gate } 4576*7c478bd9Sstevel@tonic-gate 4577*7c478bd9Sstevel@tonic-gate /* 4578*7c478bd9Sstevel@tonic-gate * Called on a successfully detached driver to free pm resources 4579*7c478bd9Sstevel@tonic-gate */ 4580*7c478bd9Sstevel@tonic-gate static void 4581*7c478bd9Sstevel@tonic-gate pm_stop(dev_info_t *dip) 4582*7c478bd9Sstevel@tonic-gate { 4583*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "stop") 4584*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 4585*7c478bd9Sstevel@tonic-gate 4586*7c478bd9Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 4587*7c478bd9Sstevel@tonic-gate /* stopping scan, destroy scan data structure */ 4588*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) { 4589*7c478bd9Sstevel@tonic-gate pm_scan_stop(dip); 4590*7c478bd9Sstevel@tonic-gate pm_scan_fini(dip); 4591*7c478bd9Sstevel@tonic-gate } 4592*7c478bd9Sstevel@tonic-gate 4593*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) != NULL) { 4594*7c478bd9Sstevel@tonic-gate if (pm_unmanage(dip) == DDI_SUCCESS) { 4595*7c478bd9Sstevel@tonic-gate /* 4596*7c478bd9Sstevel@tonic-gate * Old style driver may have called 4597*7c478bd9Sstevel@tonic-gate * pm_destroy_components already, but just in case ... 4598*7c478bd9Sstevel@tonic-gate */ 4599*7c478bd9Sstevel@tonic-gate e_pm_destroy_components(dip); 4600*7c478bd9Sstevel@tonic-gate } else { 4601*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't pm_unmanage %s@%s(%s#%d)\n", 4602*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 4603*7c478bd9Sstevel@tonic-gate } 4604*7c478bd9Sstevel@tonic-gate } else { 4605*7c478bd9Sstevel@tonic-gate if (PM_NUMCMPTS(dip)) 4606*7c478bd9Sstevel@tonic-gate e_pm_destroy_components(dip); 4607*7c478bd9Sstevel@tonic-gate else { 4608*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NOPMKID) { 4609*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_NOPMKID; 4610*7c478bd9Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 4611*7c478bd9Sstevel@tonic-gate pm_rele_power(pdip); 4612*7c478bd9Sstevel@tonic-gate } else if (pdip && MDI_VHCI(pdip)) { 4613*7c478bd9Sstevel@tonic-gate (void) mdi_power(pdip, 4614*7c478bd9Sstevel@tonic-gate MDI_PM_RELE_POWER, 4615*7c478bd9Sstevel@tonic-gate (void *)dip, NULL, 0); 4616*7c478bd9Sstevel@tonic-gate } 4617*7c478bd9Sstevel@tonic-gate } 4618*7c478bd9Sstevel@tonic-gate } 4619*7c478bd9Sstevel@tonic-gate } 4620*7c478bd9Sstevel@tonic-gate } 4621*7c478bd9Sstevel@tonic-gate 4622*7c478bd9Sstevel@tonic-gate /* 4623*7c478bd9Sstevel@tonic-gate * The node is the subject of a reparse pm props ioctl. Throw away the old 4624*7c478bd9Sstevel@tonic-gate * info and start over. 4625*7c478bd9Sstevel@tonic-gate */ 4626*7c478bd9Sstevel@tonic-gate int 4627*7c478bd9Sstevel@tonic-gate e_new_pm_props(dev_info_t *dip) 4628*7c478bd9Sstevel@tonic-gate { 4629*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) != NULL) { 4630*7c478bd9Sstevel@tonic-gate pm_stop(dip); 4631*7c478bd9Sstevel@tonic-gate 4632*7c478bd9Sstevel@tonic-gate if (e_pm_manage(dip, PM_STYLE_NEW) != DDI_SUCCESS) { 4633*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4634*7c478bd9Sstevel@tonic-gate } 4635*7c478bd9Sstevel@tonic-gate } 4636*7c478bd9Sstevel@tonic-gate e_pm_props(dip); 4637*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4638*7c478bd9Sstevel@tonic-gate } 4639*7c478bd9Sstevel@tonic-gate 4640*7c478bd9Sstevel@tonic-gate /* 4641*7c478bd9Sstevel@tonic-gate * Device has been attached, so process its pm properties 4642*7c478bd9Sstevel@tonic-gate */ 4643*7c478bd9Sstevel@tonic-gate void 4644*7c478bd9Sstevel@tonic-gate e_pm_props(dev_info_t *dip) 4645*7c478bd9Sstevel@tonic-gate { 4646*7c478bd9Sstevel@tonic-gate char *pp; 4647*7c478bd9Sstevel@tonic-gate int len; 4648*7c478bd9Sstevel@tonic-gate int flags = 0; 4649*7c478bd9Sstevel@tonic-gate int propflag = DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP; 4650*7c478bd9Sstevel@tonic-gate 4651*7c478bd9Sstevel@tonic-gate /* 4652*7c478bd9Sstevel@tonic-gate * It doesn't matter if we do this more than once, we should always 4653*7c478bd9Sstevel@tonic-gate * get the same answers, and if not, then the last one in is the 4654*7c478bd9Sstevel@tonic-gate * best one. 4655*7c478bd9Sstevel@tonic-gate */ 4656*7c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-hardware-state", 4657*7c478bd9Sstevel@tonic-gate (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) { 4658*7c478bd9Sstevel@tonic-gate if (strcmp(pp, "needs-suspend-resume") == 0) { 4659*7c478bd9Sstevel@tonic-gate flags = PMC_NEEDS_SR; 4660*7c478bd9Sstevel@tonic-gate } else if (strcmp(pp, "no-suspend-resume") == 0) { 4661*7c478bd9Sstevel@tonic-gate flags = PMC_NO_SR; 4662*7c478bd9Sstevel@tonic-gate } else if (strcmp(pp, "parental-suspend-resume") == 0) { 4663*7c478bd9Sstevel@tonic-gate flags = PMC_PARENTAL_SR; 4664*7c478bd9Sstevel@tonic-gate } else { 4665*7c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "!device %s@%s has unrecognized " 4666*7c478bd9Sstevel@tonic-gate "%s property value '%s'", PM_NAME(dip), 4667*7c478bd9Sstevel@tonic-gate PM_ADDR(dip), "pm-hardware-state", pp); 4668*7c478bd9Sstevel@tonic-gate } 4669*7c478bd9Sstevel@tonic-gate kmem_free(pp, len); 4670*7c478bd9Sstevel@tonic-gate } 4671*7c478bd9Sstevel@tonic-gate /* 4672*7c478bd9Sstevel@tonic-gate * This next segment (PMC_WANTS_NOTIFY) is in 4673*7c478bd9Sstevel@tonic-gate * support of nexus drivers which will want to be involved in 4674*7c478bd9Sstevel@tonic-gate * (or at least notified of) their child node's power level transitions. 4675*7c478bd9Sstevel@tonic-gate * "pm-want-child-notification?" is defined by the parent. 4676*7c478bd9Sstevel@tonic-gate */ 4677*7c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag, 4678*7c478bd9Sstevel@tonic-gate "pm-want-child-notification?") && PM_HAS_BUS_POWER(dip)) 4679*7c478bd9Sstevel@tonic-gate flags |= PMC_WANTS_NOTIFY; 4680*7c478bd9Sstevel@tonic-gate ASSERT(PM_HAS_BUS_POWER(dip) || !ddi_prop_exists(DDI_DEV_T_ANY, 4681*7c478bd9Sstevel@tonic-gate dip, propflag, "pm-want-child-notification?")); 4682*7c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag, 4683*7c478bd9Sstevel@tonic-gate "no-involuntary-power-cycles")) 4684*7c478bd9Sstevel@tonic-gate flags |= PMC_NO_INVOL; 4685*7c478bd9Sstevel@tonic-gate /* devfs single threads us */ 4686*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= flags; 4687*7c478bd9Sstevel@tonic-gate } 4688*7c478bd9Sstevel@tonic-gate 4689*7c478bd9Sstevel@tonic-gate /* 4690*7c478bd9Sstevel@tonic-gate * This is the DDI_CTLOPS_POWER handler that is used when there is no ppm 4691*7c478bd9Sstevel@tonic-gate * driver which has claimed a node. 4692*7c478bd9Sstevel@tonic-gate * Sets old_power in arg struct. 4693*7c478bd9Sstevel@tonic-gate */ 4694*7c478bd9Sstevel@tonic-gate static int 4695*7c478bd9Sstevel@tonic-gate pm_default_ctlops(dev_info_t *dip, dev_info_t *rdip, 4696*7c478bd9Sstevel@tonic-gate ddi_ctl_enum_t ctlop, void *arg, void *result) 4697*7c478bd9Sstevel@tonic-gate { 4698*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(dip)) 4699*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "ctlops") 4700*7c478bd9Sstevel@tonic-gate power_req_t *reqp = (power_req_t *)arg; 4701*7c478bd9Sstevel@tonic-gate int retval; 4702*7c478bd9Sstevel@tonic-gate dev_info_t *target_dip; 4703*7c478bd9Sstevel@tonic-gate int new_level, old_level, cmpt; 4704*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4705*7c478bd9Sstevel@tonic-gate char *format; 4706*7c478bd9Sstevel@tonic-gate #endif 4707*7c478bd9Sstevel@tonic-gate 4708*7c478bd9Sstevel@tonic-gate /* 4709*7c478bd9Sstevel@tonic-gate * The interface for doing the actual power level changes is now 4710*7c478bd9Sstevel@tonic-gate * through the DDI_CTLOPS_POWER bus_ctl, so that we can plug in 4711*7c478bd9Sstevel@tonic-gate * different platform-specific power control drivers. 4712*7c478bd9Sstevel@tonic-gate * 4713*7c478bd9Sstevel@tonic-gate * This driver implements the "default" version of this interface. 4714*7c478bd9Sstevel@tonic-gate * If no ppm driver has been installed then this interface is called 4715*7c478bd9Sstevel@tonic-gate * instead. 4716*7c478bd9Sstevel@tonic-gate */ 4717*7c478bd9Sstevel@tonic-gate ASSERT(dip == NULL); 4718*7c478bd9Sstevel@tonic-gate switch (ctlop) { 4719*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_POWER: 4720*7c478bd9Sstevel@tonic-gate switch (reqp->request_type) { 4721*7c478bd9Sstevel@tonic-gate case PMR_PPM_SET_POWER: 4722*7c478bd9Sstevel@tonic-gate { 4723*7c478bd9Sstevel@tonic-gate target_dip = reqp->req.ppm_set_power_req.who; 4724*7c478bd9Sstevel@tonic-gate ASSERT(target_dip == rdip); 4725*7c478bd9Sstevel@tonic-gate new_level = reqp->req.ppm_set_power_req.new_level; 4726*7c478bd9Sstevel@tonic-gate cmpt = reqp->req.ppm_set_power_req.cmpt; 4727*7c478bd9Sstevel@tonic-gate /* pass back old power for the PM_LEVEL_UNKNOWN case */ 4728*7c478bd9Sstevel@tonic-gate old_level = PM_CURPOWER(target_dip, cmpt); 4729*7c478bd9Sstevel@tonic-gate reqp->req.ppm_set_power_req.old_level = old_level; 4730*7c478bd9Sstevel@tonic-gate retval = pm_power(target_dip, cmpt, new_level); 4731*7c478bd9Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PPM_SET_POWER %s@%s(%s#%d)[%d] %d->" 4732*7c478bd9Sstevel@tonic-gate "%d %s\n", pmf, PM_DEVICE(target_dip), cmpt, 4733*7c478bd9Sstevel@tonic-gate old_level, new_level, (retval == DDI_SUCCESS ? 4734*7c478bd9Sstevel@tonic-gate "chd" : "no chg"))) 4735*7c478bd9Sstevel@tonic-gate return (retval); 4736*7c478bd9Sstevel@tonic-gate } 4737*7c478bd9Sstevel@tonic-gate 4738*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_DETACH: 4739*7c478bd9Sstevel@tonic-gate case PMR_PPM_POST_DETACH: 4740*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_ATTACH: 4741*7c478bd9Sstevel@tonic-gate case PMR_PPM_POST_ATTACH: 4742*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_PROBE: 4743*7c478bd9Sstevel@tonic-gate case PMR_PPM_POST_PROBE: 4744*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_RESUME: 4745*7c478bd9Sstevel@tonic-gate case PMR_PPM_INIT_CHILD: 4746*7c478bd9Sstevel@tonic-gate case PMR_PPM_UNINIT_CHILD: 4747*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 4748*7c478bd9Sstevel@tonic-gate switch (reqp->request_type) { 4749*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_DETACH: 4750*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_DETACH " 4751*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4752*7c478bd9Sstevel@tonic-gate break; 4753*7c478bd9Sstevel@tonic-gate case PMR_PPM_POST_DETACH: 4754*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_POST_DETACH " 4755*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4756*7c478bd9Sstevel@tonic-gate break; 4757*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_ATTACH: 4758*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_ATTACH " 4759*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4760*7c478bd9Sstevel@tonic-gate break; 4761*7c478bd9Sstevel@tonic-gate case PMR_PPM_POST_ATTACH: 4762*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_POST_ATTACH " 4763*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4764*7c478bd9Sstevel@tonic-gate break; 4765*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_PROBE: 4766*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_PROBE " 4767*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4768*7c478bd9Sstevel@tonic-gate break; 4769*7c478bd9Sstevel@tonic-gate case PMR_PPM_POST_PROBE: 4770*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_POST_PROBE " 4771*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4772*7c478bd9Sstevel@tonic-gate break; 4773*7c478bd9Sstevel@tonic-gate case PMR_PPM_PRE_RESUME: 4774*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_RESUME " 4775*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4776*7c478bd9Sstevel@tonic-gate break; 4777*7c478bd9Sstevel@tonic-gate case PMR_PPM_INIT_CHILD: 4778*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_INIT_CHILD " 4779*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4780*7c478bd9Sstevel@tonic-gate break; 4781*7c478bd9Sstevel@tonic-gate case PMR_PPM_UNINIT_CHILD: 4782*7c478bd9Sstevel@tonic-gate format = "%s: PMR_PPM_UNINIT_CHILD " 4783*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4784*7c478bd9Sstevel@tonic-gate break; 4785*7c478bd9Sstevel@tonic-gate default: 4786*7c478bd9Sstevel@tonic-gate break; 4787*7c478bd9Sstevel@tonic-gate } 4788*7c478bd9Sstevel@tonic-gate PMD(PMD_PPM, (format, pmf, PM_DEVICE(rdip), 4789*7c478bd9Sstevel@tonic-gate reqp->req.ppm_config_req.result)) 4790*7c478bd9Sstevel@tonic-gate #endif 4791*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4792*7c478bd9Sstevel@tonic-gate 4793*7c478bd9Sstevel@tonic-gate case PMR_PPM_POWER_CHANGE_NOTIFY: 4794*7c478bd9Sstevel@tonic-gate /* 4795*7c478bd9Sstevel@tonic-gate * Nothing for us to do 4796*7c478bd9Sstevel@tonic-gate */ 4797*7c478bd9Sstevel@tonic-gate ASSERT(reqp->req.ppm_notify_level_req.who == rdip); 4798*7c478bd9Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PMR_PPM_POWER_CHANGE_NOTIFY " 4799*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)[%d] %d->%d\n", pmf, 4800*7c478bd9Sstevel@tonic-gate PM_DEVICE(reqp->req.ppm_notify_level_req.who), 4801*7c478bd9Sstevel@tonic-gate reqp->req.ppm_notify_level_req.cmpt, 4802*7c478bd9Sstevel@tonic-gate PM_CURPOWER(reqp->req.ppm_notify_level_req.who, 4803*7c478bd9Sstevel@tonic-gate reqp->req.ppm_notify_level_req.cmpt), 4804*7c478bd9Sstevel@tonic-gate reqp->req.ppm_notify_level_req.new_level)) 4805*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4806*7c478bd9Sstevel@tonic-gate 4807*7c478bd9Sstevel@tonic-gate case PMR_PPM_UNMANAGE: 4808*7c478bd9Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PMR_PPM_UNMANAGE %s@%s(%s#%d)\n", 4809*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(rdip))) 4810*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4811*7c478bd9Sstevel@tonic-gate 4812*7c478bd9Sstevel@tonic-gate case PMR_PPM_LOCK_POWER: 4813*7c478bd9Sstevel@tonic-gate pm_lock_power_single(reqp->req.ppm_lock_power_req.who, 4814*7c478bd9Sstevel@tonic-gate reqp->req.ppm_lock_power_req.circp); 4815*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4816*7c478bd9Sstevel@tonic-gate 4817*7c478bd9Sstevel@tonic-gate case PMR_PPM_UNLOCK_POWER: 4818*7c478bd9Sstevel@tonic-gate pm_unlock_power_single( 4819*7c478bd9Sstevel@tonic-gate reqp->req.ppm_unlock_power_req.who, 4820*7c478bd9Sstevel@tonic-gate reqp->req.ppm_unlock_power_req.circ); 4821*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4822*7c478bd9Sstevel@tonic-gate 4823*7c478bd9Sstevel@tonic-gate case PMR_PPM_TRY_LOCK_POWER: 4824*7c478bd9Sstevel@tonic-gate *(int *)result = pm_try_locking_power_single( 4825*7c478bd9Sstevel@tonic-gate reqp->req.ppm_lock_power_req.who, 4826*7c478bd9Sstevel@tonic-gate reqp->req.ppm_lock_power_req.circp); 4827*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4828*7c478bd9Sstevel@tonic-gate 4829*7c478bd9Sstevel@tonic-gate case PMR_PPM_POWER_LOCK_OWNER: 4830*7c478bd9Sstevel@tonic-gate target_dip = reqp->req.ppm_power_lock_owner_req.who; 4831*7c478bd9Sstevel@tonic-gate ASSERT(target_dip == rdip); 4832*7c478bd9Sstevel@tonic-gate reqp->req.ppm_power_lock_owner_req.owner = 4833*7c478bd9Sstevel@tonic-gate DEVI(rdip)->devi_busy_thread; 4834*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4835*7c478bd9Sstevel@tonic-gate default: 4836*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: default!\n", pmf)) 4837*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4838*7c478bd9Sstevel@tonic-gate } 4839*7c478bd9Sstevel@tonic-gate 4840*7c478bd9Sstevel@tonic-gate default: 4841*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: unknown\n", pmf)) 4842*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4843*7c478bd9Sstevel@tonic-gate } 4844*7c478bd9Sstevel@tonic-gate } 4845*7c478bd9Sstevel@tonic-gate 4846*7c478bd9Sstevel@tonic-gate /* 4847*7c478bd9Sstevel@tonic-gate * We overload the bus_ctl ops here--perhaps we ought to have a distinct 4848*7c478bd9Sstevel@tonic-gate * power_ops struct for this functionality instead? 4849*7c478bd9Sstevel@tonic-gate * However, we only ever do this on a ppm driver. 4850*7c478bd9Sstevel@tonic-gate */ 4851*7c478bd9Sstevel@tonic-gate int 4852*7c478bd9Sstevel@tonic-gate pm_ctlops(dev_info_t *d, dev_info_t *r, ddi_ctl_enum_t op, void *a, void *v) 4853*7c478bd9Sstevel@tonic-gate { 4854*7c478bd9Sstevel@tonic-gate int (*fp)(); 4855*7c478bd9Sstevel@tonic-gate 4856*7c478bd9Sstevel@tonic-gate /* if no ppm handler, call the default routine */ 4857*7c478bd9Sstevel@tonic-gate if (d == NULL) { 4858*7c478bd9Sstevel@tonic-gate return (pm_default_ctlops(d, r, op, a, v)); 4859*7c478bd9Sstevel@tonic-gate } 4860*7c478bd9Sstevel@tonic-gate if (!d || !r) 4861*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4862*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(d)->devi_ops && DEVI(d)->devi_ops->devo_bus_ops && 4863*7c478bd9Sstevel@tonic-gate DEVI(d)->devi_ops->devo_bus_ops->bus_ctl); 4864*7c478bd9Sstevel@tonic-gate 4865*7c478bd9Sstevel@tonic-gate fp = DEVI(d)->devi_ops->devo_bus_ops->bus_ctl; 4866*7c478bd9Sstevel@tonic-gate return ((*fp)(d, r, op, a, v)); 4867*7c478bd9Sstevel@tonic-gate } 4868*7c478bd9Sstevel@tonic-gate 4869*7c478bd9Sstevel@tonic-gate /* 4870*7c478bd9Sstevel@tonic-gate * Called on a node when attach completes or the driver makes its first pm 4871*7c478bd9Sstevel@tonic-gate * call (whichever comes first). 4872*7c478bd9Sstevel@tonic-gate * In the attach case, device may not be power manageable at all. 4873*7c478bd9Sstevel@tonic-gate * Don't need to lock the dip because we're single threaded by the devfs code 4874*7c478bd9Sstevel@tonic-gate */ 4875*7c478bd9Sstevel@tonic-gate static int 4876*7c478bd9Sstevel@tonic-gate pm_start(dev_info_t *dip) 4877*7c478bd9Sstevel@tonic-gate { 4878*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "start") 4879*7c478bd9Sstevel@tonic-gate int ret; 4880*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 4881*7c478bd9Sstevel@tonic-gate int e_pm_manage(dev_info_t *, int); 4882*7c478bd9Sstevel@tonic-gate void pm_noinvol_specd(dev_info_t *dip); 4883*7c478bd9Sstevel@tonic-gate 4884*7c478bd9Sstevel@tonic-gate e_pm_props(dip); 4885*7c478bd9Sstevel@tonic-gate pm_noinvol_specd(dip); 4886*7c478bd9Sstevel@tonic-gate /* 4887*7c478bd9Sstevel@tonic-gate * If this dip has already been processed, don't mess with it 4888*7c478bd9Sstevel@tonic-gate * (but decrement the speculative count we did above, as whatever 4889*7c478bd9Sstevel@tonic-gate * code put it under pm already will have dealt with it) 4890*7c478bd9Sstevel@tonic-gate */ 4891*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip)) { 4892*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: pm already done for %s@%s(%s#%d)\n", 4893*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 4894*7c478bd9Sstevel@tonic-gate return (0); 4895*7c478bd9Sstevel@tonic-gate } 4896*7c478bd9Sstevel@tonic-gate ret = e_pm_manage(dip, PM_STYLE_UNKNOWN); 4897*7c478bd9Sstevel@tonic-gate 4898*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) { 4899*7c478bd9Sstevel@tonic-gate /* 4900*7c478bd9Sstevel@tonic-gate * keep the kidsupcount increment as is 4901*7c478bd9Sstevel@tonic-gate */ 4902*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NOPMKID; 4903*7c478bd9Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 4904*7c478bd9Sstevel@tonic-gate pm_hold_power(pdip); 4905*7c478bd9Sstevel@tonic-gate } else if (pdip && MDI_VHCI(pdip)) { 4906*7c478bd9Sstevel@tonic-gate (void) mdi_power(pdip, MDI_PM_HOLD_POWER, 4907*7c478bd9Sstevel@tonic-gate (void *)dip, NULL, 0); 4908*7c478bd9Sstevel@tonic-gate } 4909*7c478bd9Sstevel@tonic-gate 4910*7c478bd9Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: pm of %s@%s(%s#%d) failed, parent " 4911*7c478bd9Sstevel@tonic-gate "left up\n", pmf, PM_DEVICE(dip))) 4912*7c478bd9Sstevel@tonic-gate } 4913*7c478bd9Sstevel@tonic-gate 4914*7c478bd9Sstevel@tonic-gate return (ret); 4915*7c478bd9Sstevel@tonic-gate } 4916*7c478bd9Sstevel@tonic-gate 4917*7c478bd9Sstevel@tonic-gate /* 4918*7c478bd9Sstevel@tonic-gate * Keep a list of recorded thresholds. For now we just keep a list and 4919*7c478bd9Sstevel@tonic-gate * search it linearly. We don't expect too many entries. Can always hash it 4920*7c478bd9Sstevel@tonic-gate * later if we need to. 4921*7c478bd9Sstevel@tonic-gate */ 4922*7c478bd9Sstevel@tonic-gate void 4923*7c478bd9Sstevel@tonic-gate pm_record_thresh(pm_thresh_rec_t *rp) 4924*7c478bd9Sstevel@tonic-gate { 4925*7c478bd9Sstevel@tonic-gate pm_thresh_rec_t *pptr, *ptr; 4926*7c478bd9Sstevel@tonic-gate 4927*7c478bd9Sstevel@tonic-gate ASSERT(*rp->ptr_physpath); 4928*7c478bd9Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER); 4929*7c478bd9Sstevel@tonic-gate for (pptr = NULL, ptr = pm_thresh_head; 4930*7c478bd9Sstevel@tonic-gate ptr; pptr = ptr, ptr = ptr->ptr_next) { 4931*7c478bd9Sstevel@tonic-gate if (strcmp(rp->ptr_physpath, ptr->ptr_physpath) == 0) { 4932*7c478bd9Sstevel@tonic-gate /* replace this one */ 4933*7c478bd9Sstevel@tonic-gate rp->ptr_next = ptr->ptr_next; 4934*7c478bd9Sstevel@tonic-gate if (pptr) { 4935*7c478bd9Sstevel@tonic-gate pptr->ptr_next = rp; 4936*7c478bd9Sstevel@tonic-gate } else { 4937*7c478bd9Sstevel@tonic-gate pm_thresh_head = rp; 4938*7c478bd9Sstevel@tonic-gate } 4939*7c478bd9Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 4940*7c478bd9Sstevel@tonic-gate kmem_free(ptr, ptr->ptr_size); 4941*7c478bd9Sstevel@tonic-gate return; 4942*7c478bd9Sstevel@tonic-gate } 4943*7c478bd9Sstevel@tonic-gate continue; 4944*7c478bd9Sstevel@tonic-gate } 4945*7c478bd9Sstevel@tonic-gate /* 4946*7c478bd9Sstevel@tonic-gate * There was not a match in the list, insert this one in front 4947*7c478bd9Sstevel@tonic-gate */ 4948*7c478bd9Sstevel@tonic-gate if (pm_thresh_head) { 4949*7c478bd9Sstevel@tonic-gate rp->ptr_next = pm_thresh_head; 4950*7c478bd9Sstevel@tonic-gate pm_thresh_head = rp; 4951*7c478bd9Sstevel@tonic-gate } else { 4952*7c478bd9Sstevel@tonic-gate rp->ptr_next = NULL; 4953*7c478bd9Sstevel@tonic-gate pm_thresh_head = rp; 4954*7c478bd9Sstevel@tonic-gate } 4955*7c478bd9Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 4956*7c478bd9Sstevel@tonic-gate } 4957*7c478bd9Sstevel@tonic-gate 4958*7c478bd9Sstevel@tonic-gate /* 4959*7c478bd9Sstevel@tonic-gate * Create a new dependency record and hang a new dependency entry off of it 4960*7c478bd9Sstevel@tonic-gate */ 4961*7c478bd9Sstevel@tonic-gate pm_pdr_t * 4962*7c478bd9Sstevel@tonic-gate newpdr(char *kept, char *keeps, int isprop) 4963*7c478bd9Sstevel@tonic-gate { 4964*7c478bd9Sstevel@tonic-gate size_t size = strlen(kept) + strlen(keeps) + 2 + sizeof (pm_pdr_t); 4965*7c478bd9Sstevel@tonic-gate pm_pdr_t *p = kmem_zalloc(size, KM_SLEEP); 4966*7c478bd9Sstevel@tonic-gate p->pdr_size = size; 4967*7c478bd9Sstevel@tonic-gate p->pdr_isprop = isprop; 4968*7c478bd9Sstevel@tonic-gate p->pdr_kept_paths = NULL; 4969*7c478bd9Sstevel@tonic-gate p->pdr_kept_count = 0; 4970*7c478bd9Sstevel@tonic-gate p->pdr_kept = (char *)((intptr_t)p + sizeof (pm_pdr_t)); 4971*7c478bd9Sstevel@tonic-gate (void) strcpy(p->pdr_kept, kept); 4972*7c478bd9Sstevel@tonic-gate p->pdr_keeper = (char *)((intptr_t)p->pdr_kept + strlen(kept) + 1); 4973*7c478bd9Sstevel@tonic-gate (void) strcpy(p->pdr_keeper, keeps); 4974*7c478bd9Sstevel@tonic-gate ASSERT((intptr_t)p->pdr_keeper + strlen(p->pdr_keeper) + 1 <= 4975*7c478bd9Sstevel@tonic-gate (intptr_t)p + size); 4976*7c478bd9Sstevel@tonic-gate ASSERT((intptr_t)p->pdr_kept + strlen(p->pdr_kept) + 1 <= 4977*7c478bd9Sstevel@tonic-gate (intptr_t)p + size); 4978*7c478bd9Sstevel@tonic-gate return (p); 4979*7c478bd9Sstevel@tonic-gate } 4980*7c478bd9Sstevel@tonic-gate 4981*7c478bd9Sstevel@tonic-gate /* 4982*7c478bd9Sstevel@tonic-gate * Keep a list of recorded dependencies. We only keep the 4983*7c478bd9Sstevel@tonic-gate * keeper -> kept list for simplification. At this point We do not 4984*7c478bd9Sstevel@tonic-gate * care about whether the devices are attached or not yet, 4985*7c478bd9Sstevel@tonic-gate * this would be done in pm_keeper() and pm_kept(). 4986*7c478bd9Sstevel@tonic-gate * If a PM_RESET_PM happens, then we tear down and forget the dependencies, 4987*7c478bd9Sstevel@tonic-gate * and it is up to the user to issue the ioctl again if they want it 4988*7c478bd9Sstevel@tonic-gate * (e.g. pmconfig) 4989*7c478bd9Sstevel@tonic-gate * Returns true if dependency already exists in the list. 4990*7c478bd9Sstevel@tonic-gate */ 4991*7c478bd9Sstevel@tonic-gate int 4992*7c478bd9Sstevel@tonic-gate pm_record_keeper(char *kept, char *keeper, int isprop) 4993*7c478bd9Sstevel@tonic-gate { 4994*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "record_keeper") 4995*7c478bd9Sstevel@tonic-gate pm_pdr_t *npdr, *ppdr, *pdr; 4996*7c478bd9Sstevel@tonic-gate 4997*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s, %s\n", pmf, kept, keeper)) 4998*7c478bd9Sstevel@tonic-gate ASSERT(kept && keeper); 4999*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5000*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5001*7c478bd9Sstevel@tonic-gate prdeps("pm_record_keeper entry"); 5002*7c478bd9Sstevel@tonic-gate #endif 5003*7c478bd9Sstevel@tonic-gate for (ppdr = NULL, pdr = pm_dep_head; pdr; 5004*7c478bd9Sstevel@tonic-gate ppdr = pdr, pdr = pdr->pdr_next) { 5005*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: check %s, %s\n", pmf, pdr->pdr_kept, 5006*7c478bd9Sstevel@tonic-gate pdr->pdr_keeper)) 5007*7c478bd9Sstevel@tonic-gate if (strcmp(kept, pdr->pdr_kept) == 0 && 5008*7c478bd9Sstevel@tonic-gate strcmp(keeper, pdr->pdr_keeper) == 0) { 5009*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: match\n", pmf)) 5010*7c478bd9Sstevel@tonic-gate return (1); 5011*7c478bd9Sstevel@tonic-gate } 5012*7c478bd9Sstevel@tonic-gate } 5013*7c478bd9Sstevel@tonic-gate /* 5014*7c478bd9Sstevel@tonic-gate * We did not find any match, so we have to make an entry 5015*7c478bd9Sstevel@tonic-gate */ 5016*7c478bd9Sstevel@tonic-gate npdr = newpdr(kept, keeper, isprop); 5017*7c478bd9Sstevel@tonic-gate if (ppdr) { 5018*7c478bd9Sstevel@tonic-gate ASSERT(ppdr->pdr_next == NULL); 5019*7c478bd9Sstevel@tonic-gate ppdr->pdr_next = npdr; 5020*7c478bd9Sstevel@tonic-gate } else { 5021*7c478bd9Sstevel@tonic-gate ASSERT(pm_dep_head == NULL); 5022*7c478bd9Sstevel@tonic-gate pm_dep_head = npdr; 5023*7c478bd9Sstevel@tonic-gate } 5024*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5025*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5026*7c478bd9Sstevel@tonic-gate prdeps("pm_record_keeper after new record"); 5027*7c478bd9Sstevel@tonic-gate #endif 5028*7c478bd9Sstevel@tonic-gate if (!isprop) 5029*7c478bd9Sstevel@tonic-gate pm_unresolved_deps++; 5030*7c478bd9Sstevel@tonic-gate else 5031*7c478bd9Sstevel@tonic-gate pm_prop_deps++; 5032*7c478bd9Sstevel@tonic-gate return (0); 5033*7c478bd9Sstevel@tonic-gate } 5034*7c478bd9Sstevel@tonic-gate 5035*7c478bd9Sstevel@tonic-gate /* 5036*7c478bd9Sstevel@tonic-gate * Look up this device in the set of devices we've seen ioctls for 5037*7c478bd9Sstevel@tonic-gate * to see if we are holding a threshold spec for it. If so, make it so. 5038*7c478bd9Sstevel@tonic-gate * At ioctl time, we were given the physical path of the device. 5039*7c478bd9Sstevel@tonic-gate */ 5040*7c478bd9Sstevel@tonic-gate int 5041*7c478bd9Sstevel@tonic-gate pm_thresh_specd(dev_info_t *dip) 5042*7c478bd9Sstevel@tonic-gate { 5043*7c478bd9Sstevel@tonic-gate void pm_apply_recorded_thresh(dev_info_t *, pm_thresh_rec_t *); 5044*7c478bd9Sstevel@tonic-gate char *path = 0; 5045*7c478bd9Sstevel@tonic-gate char pathbuf[MAXNAMELEN]; 5046*7c478bd9Sstevel@tonic-gate pm_thresh_rec_t *rp; 5047*7c478bd9Sstevel@tonic-gate 5048*7c478bd9Sstevel@tonic-gate path = ddi_pathname(dip, pathbuf); 5049*7c478bd9Sstevel@tonic-gate 5050*7c478bd9Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_READER); 5051*7c478bd9Sstevel@tonic-gate for (rp = pm_thresh_head; rp; rp = rp->ptr_next) { 5052*7c478bd9Sstevel@tonic-gate if (strcmp(rp->ptr_physpath, path) != 0) 5053*7c478bd9Sstevel@tonic-gate continue; 5054*7c478bd9Sstevel@tonic-gate pm_apply_recorded_thresh(dip, rp); 5055*7c478bd9Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 5056*7c478bd9Sstevel@tonic-gate return (1); 5057*7c478bd9Sstevel@tonic-gate } 5058*7c478bd9Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 5059*7c478bd9Sstevel@tonic-gate return (0); 5060*7c478bd9Sstevel@tonic-gate } 5061*7c478bd9Sstevel@tonic-gate 5062*7c478bd9Sstevel@tonic-gate static int 5063*7c478bd9Sstevel@tonic-gate pm_set_keeping(dev_info_t *keeper, dev_info_t *kept) 5064*7c478bd9Sstevel@tonic-gate { 5065*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "set_keeping") 5066*7c478bd9Sstevel@tonic-gate pm_info_t *kept_info; 5067*7c478bd9Sstevel@tonic-gate int j, up = 0, circ; 5068*7c478bd9Sstevel@tonic-gate void prdeps(char *); 5069*7c478bd9Sstevel@tonic-gate 5070*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), kept=%s@%s(%s#%d)\n", pmf, 5071*7c478bd9Sstevel@tonic-gate PM_DEVICE(keeper), PM_DEVICE(kept))) 5072*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5073*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5074*7c478bd9Sstevel@tonic-gate prdeps("Before PAD\n"); 5075*7c478bd9Sstevel@tonic-gate #endif 5076*7c478bd9Sstevel@tonic-gate ASSERT(keeper != kept); 5077*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(keeper) == NULL) { 5078*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "!device %s@%s(%s#%d) keeps up device " 5079*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d), but the latter is not power managed", 5080*7c478bd9Sstevel@tonic-gate PM_DEVICE(keeper), PM_DEVICE(kept)); 5081*7c478bd9Sstevel@tonic-gate PMD((PMD_FAIL | PMD_KEEPS), ("%s: keeper %s@%s(%s#%d) is not" 5082*7c478bd9Sstevel@tonic-gate "power managed\n", pmf, PM_DEVICE(keeper))) 5083*7c478bd9Sstevel@tonic-gate return (0); 5084*7c478bd9Sstevel@tonic-gate } 5085*7c478bd9Sstevel@tonic-gate kept_info = PM_GET_PM_INFO(kept); 5086*7c478bd9Sstevel@tonic-gate ASSERT(kept_info); 5087*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(keeper, &circ); 5088*7c478bd9Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(keeper); j++) { 5089*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(keeper, j)) { 5090*7c478bd9Sstevel@tonic-gate up++; 5091*7c478bd9Sstevel@tonic-gate break; 5092*7c478bd9Sstevel@tonic-gate } 5093*7c478bd9Sstevel@tonic-gate } 5094*7c478bd9Sstevel@tonic-gate if (up) { 5095*7c478bd9Sstevel@tonic-gate /* Bringup and maintain a hold on the kept */ 5096*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: place a hold on kept %s@%s(%s#%d)\n", pmf, 5097*7c478bd9Sstevel@tonic-gate PM_DEVICE(kept))) 5098*7c478bd9Sstevel@tonic-gate bring_pmdep_up(kept, 1); 5099*7c478bd9Sstevel@tonic-gate } 5100*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(keeper, circ); 5101*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5102*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5103*7c478bd9Sstevel@tonic-gate prdeps("After PAD\n"); 5104*7c478bd9Sstevel@tonic-gate #endif 5105*7c478bd9Sstevel@tonic-gate return (1); 5106*7c478bd9Sstevel@tonic-gate } 5107*7c478bd9Sstevel@tonic-gate 5108*7c478bd9Sstevel@tonic-gate /* 5109*7c478bd9Sstevel@tonic-gate * Should this device keep up another device? 5110*7c478bd9Sstevel@tonic-gate * Look up this device in the set of devices we've seen ioctls for 5111*7c478bd9Sstevel@tonic-gate * to see if we are holding a dependency spec for it. If so, make it so. 5112*7c478bd9Sstevel@tonic-gate * Because we require the kept device to be attached already in order to 5113*7c478bd9Sstevel@tonic-gate * make the list entry (and hold it), we only need to look for keepers. 5114*7c478bd9Sstevel@tonic-gate * At ioctl time, we were given the physical path of the device. 5115*7c478bd9Sstevel@tonic-gate */ 5116*7c478bd9Sstevel@tonic-gate int 5117*7c478bd9Sstevel@tonic-gate pm_keeper(char *keeper) 5118*7c478bd9Sstevel@tonic-gate { 5119*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "keeper") 5120*7c478bd9Sstevel@tonic-gate int pm_apply_recorded_dep(dev_info_t *, pm_pdr_t *); 5121*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 5122*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 5123*7c478bd9Sstevel@tonic-gate dev_info_t *kept = NULL; 5124*7c478bd9Sstevel@tonic-gate int ret = 0; 5125*7c478bd9Sstevel@tonic-gate int i; 5126*7c478bd9Sstevel@tonic-gate 5127*7c478bd9Sstevel@tonic-gate if (!pm_unresolved_deps && !pm_prop_deps) 5128*7c478bd9Sstevel@tonic-gate return (0); 5129*7c478bd9Sstevel@tonic-gate ASSERT(keeper != NULL); 5130*7c478bd9Sstevel@tonic-gate dip = pm_name_to_dip(keeper, 1); 5131*7c478bd9Sstevel@tonic-gate if (dip == NULL) 5132*7c478bd9Sstevel@tonic-gate return (0); 5133*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s\n", pmf, keeper)) 5134*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 5135*7c478bd9Sstevel@tonic-gate if (!dp->pdr_isprop) { 5136*7c478bd9Sstevel@tonic-gate if (!pm_unresolved_deps) 5137*7c478bd9Sstevel@tonic-gate continue; 5138*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper %s\n", pmf, dp->pdr_keeper)) 5139*7c478bd9Sstevel@tonic-gate if (dp->pdr_satisfied) { 5140*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: satisfied\n", pmf)) 5141*7c478bd9Sstevel@tonic-gate continue; 5142*7c478bd9Sstevel@tonic-gate } 5143*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) == 0) { 5144*7c478bd9Sstevel@tonic-gate ret += pm_apply_recorded_dep(dip, dp); 5145*7c478bd9Sstevel@tonic-gate } 5146*7c478bd9Sstevel@tonic-gate } else { 5147*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0) 5148*7c478bd9Sstevel@tonic-gate continue; 5149*7c478bd9Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 5150*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_paths[i] == NULL) 5151*7c478bd9Sstevel@tonic-gate continue; 5152*7c478bd9Sstevel@tonic-gate kept = pm_name_to_dip(dp->pdr_kept_paths[i], 1); 5153*7c478bd9Sstevel@tonic-gate if (kept == NULL) 5154*7c478bd9Sstevel@tonic-gate continue; 5155*7c478bd9Sstevel@tonic-gate ASSERT(ddi_prop_exists(DDI_DEV_T_ANY, kept, 5156*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, dp->pdr_kept)); 5157*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), " 5158*7c478bd9Sstevel@tonic-gate "kept=%s@%s(%s#%d) keptcnt=%d\n", 5159*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_DEVICE(kept), 5160*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count)) 5161*7c478bd9Sstevel@tonic-gate if (kept != dip) { 5162*7c478bd9Sstevel@tonic-gate ret += pm_set_keeping(dip, kept); 5163*7c478bd9Sstevel@tonic-gate } 5164*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 5165*7c478bd9Sstevel@tonic-gate } 5166*7c478bd9Sstevel@tonic-gate 5167*7c478bd9Sstevel@tonic-gate } 5168*7c478bd9Sstevel@tonic-gate } 5169*7c478bd9Sstevel@tonic-gate ddi_release_devi(dip); 5170*7c478bd9Sstevel@tonic-gate return (ret); 5171*7c478bd9Sstevel@tonic-gate } 5172*7c478bd9Sstevel@tonic-gate 5173*7c478bd9Sstevel@tonic-gate /* 5174*7c478bd9Sstevel@tonic-gate * Should this device be kept up by another device? 5175*7c478bd9Sstevel@tonic-gate * Look up all dependency recorded from PM_ADD_DEPENDENT and 5176*7c478bd9Sstevel@tonic-gate * PM_ADD_DEPENDENT_PROPERTY ioctls. Record down on the keeper's 5177*7c478bd9Sstevel@tonic-gate * kept device lists. 5178*7c478bd9Sstevel@tonic-gate */ 5179*7c478bd9Sstevel@tonic-gate static int 5180*7c478bd9Sstevel@tonic-gate pm_kept(char *keptp) 5181*7c478bd9Sstevel@tonic-gate { 5182*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "kept") 5183*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 5184*7c478bd9Sstevel@tonic-gate int found = 0; 5185*7c478bd9Sstevel@tonic-gate int ret = 0; 5186*7c478bd9Sstevel@tonic-gate dev_info_t *keeper; 5187*7c478bd9Sstevel@tonic-gate dev_info_t *kept; 5188*7c478bd9Sstevel@tonic-gate size_t length; 5189*7c478bd9Sstevel@tonic-gate int i; 5190*7c478bd9Sstevel@tonic-gate char **paths; 5191*7c478bd9Sstevel@tonic-gate char *path; 5192*7c478bd9Sstevel@tonic-gate 5193*7c478bd9Sstevel@tonic-gate ASSERT(keptp != NULL); 5194*7c478bd9Sstevel@tonic-gate kept = pm_name_to_dip(keptp, 1); 5195*7c478bd9Sstevel@tonic-gate if (kept == NULL) 5196*7c478bd9Sstevel@tonic-gate return (0); 5197*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(kept))) 5198*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 5199*7c478bd9Sstevel@tonic-gate if (dp->pdr_isprop) { 5200*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: property %s\n", pmf, dp->pdr_kept)) 5201*7c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, kept, 5202*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, dp->pdr_kept)) { 5203*7c478bd9Sstevel@tonic-gate /* 5204*7c478bd9Sstevel@tonic-gate * Dont allow self dependency. 5205*7c478bd9Sstevel@tonic-gate */ 5206*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keptp) == 0) 5207*7c478bd9Sstevel@tonic-gate continue; 5208*7c478bd9Sstevel@tonic-gate keeper = pm_name_to_dip(dp->pdr_keeper, 1); 5209*7c478bd9Sstevel@tonic-gate if (keeper == NULL) 5210*7c478bd9Sstevel@tonic-gate continue; 5211*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: adding to kepts path list " 5212*7c478bd9Sstevel@tonic-gate "%p\n", pmf, (void *)kept)) 5213*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5214*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5215*7c478bd9Sstevel@tonic-gate prdeps("Before Adding from pm_kept\n"); 5216*7c478bd9Sstevel@tonic-gate #endif 5217*7c478bd9Sstevel@tonic-gate /* 5218*7c478bd9Sstevel@tonic-gate * Add ourselves to the dip list. 5219*7c478bd9Sstevel@tonic-gate */ 5220*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count == 0) { 5221*7c478bd9Sstevel@tonic-gate length = strlen(keptp) + 1; 5222*7c478bd9Sstevel@tonic-gate path = 5223*7c478bd9Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP); 5224*7c478bd9Sstevel@tonic-gate paths = kmem_alloc(sizeof (char **), 5225*7c478bd9Sstevel@tonic-gate KM_SLEEP); 5226*7c478bd9Sstevel@tonic-gate (void) strcpy(path, keptp); 5227*7c478bd9Sstevel@tonic-gate paths[0] = path; 5228*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths = paths; 5229*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count++; 5230*7c478bd9Sstevel@tonic-gate } else { 5231*7c478bd9Sstevel@tonic-gate /* Check to see if already on list */ 5232*7c478bd9Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; 5233*7c478bd9Sstevel@tonic-gate i++) { 5234*7c478bd9Sstevel@tonic-gate if (strcmp(keptp, 5235*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths[i]) 5236*7c478bd9Sstevel@tonic-gate == 0) { 5237*7c478bd9Sstevel@tonic-gate found++; 5238*7c478bd9Sstevel@tonic-gate break; 5239*7c478bd9Sstevel@tonic-gate } 5240*7c478bd9Sstevel@tonic-gate } 5241*7c478bd9Sstevel@tonic-gate if (found) { 5242*7c478bd9Sstevel@tonic-gate ddi_release_devi(keeper); 5243*7c478bd9Sstevel@tonic-gate continue; 5244*7c478bd9Sstevel@tonic-gate } 5245*7c478bd9Sstevel@tonic-gate length = dp->pdr_kept_count * 5246*7c478bd9Sstevel@tonic-gate sizeof (char **); 5247*7c478bd9Sstevel@tonic-gate paths = kmem_alloc( 5248*7c478bd9Sstevel@tonic-gate length + sizeof (char **), 5249*7c478bd9Sstevel@tonic-gate KM_SLEEP); 5250*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count) { 5251*7c478bd9Sstevel@tonic-gate bcopy(dp->pdr_kept_paths, 5252*7c478bd9Sstevel@tonic-gate paths, length); 5253*7c478bd9Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, 5254*7c478bd9Sstevel@tonic-gate length); 5255*7c478bd9Sstevel@tonic-gate } 5256*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths = paths; 5257*7c478bd9Sstevel@tonic-gate length = strlen(keptp) + 1; 5258*7c478bd9Sstevel@tonic-gate path = 5259*7c478bd9Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP); 5260*7c478bd9Sstevel@tonic-gate (void) strcpy(path, keptp); 5261*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths[i] = path; 5262*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count++; 5263*7c478bd9Sstevel@tonic-gate } 5264*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5265*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5266*7c478bd9Sstevel@tonic-gate prdeps("After from pm_kept\n"); 5267*7c478bd9Sstevel@tonic-gate #endif 5268*7c478bd9Sstevel@tonic-gate if (keeper) { 5269*7c478bd9Sstevel@tonic-gate ret += pm_set_keeping(keeper, kept); 5270*7c478bd9Sstevel@tonic-gate ddi_release_devi(keeper); 5271*7c478bd9Sstevel@tonic-gate } 5272*7c478bd9Sstevel@tonic-gate } 5273*7c478bd9Sstevel@tonic-gate } else { 5274*7c478bd9Sstevel@tonic-gate /* 5275*7c478bd9Sstevel@tonic-gate * pm_keeper would be called later to do 5276*7c478bd9Sstevel@tonic-gate * the actual pm_set_keeping. 5277*7c478bd9Sstevel@tonic-gate */ 5278*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: adding to kepts path list %p\n", 5279*7c478bd9Sstevel@tonic-gate pmf, (void *)kept)) 5280*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5281*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5282*7c478bd9Sstevel@tonic-gate prdeps("Before Adding from pm_kept\n"); 5283*7c478bd9Sstevel@tonic-gate #endif 5284*7c478bd9Sstevel@tonic-gate if (strcmp(keptp, dp->pdr_kept) == 0) { 5285*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_paths == NULL) { 5286*7c478bd9Sstevel@tonic-gate length = strlen(keptp) + 1; 5287*7c478bd9Sstevel@tonic-gate path = 5288*7c478bd9Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP); 5289*7c478bd9Sstevel@tonic-gate paths = kmem_alloc(sizeof (char **), 5290*7c478bd9Sstevel@tonic-gate KM_SLEEP); 5291*7c478bd9Sstevel@tonic-gate (void) strcpy(path, keptp); 5292*7c478bd9Sstevel@tonic-gate paths[0] = path; 5293*7c478bd9Sstevel@tonic-gate dp->pdr_kept_paths = paths; 5294*7c478bd9Sstevel@tonic-gate dp->pdr_kept_count++; 5295*7c478bd9Sstevel@tonic-gate } 5296*7c478bd9Sstevel@tonic-gate } 5297*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5298*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5299*7c478bd9Sstevel@tonic-gate prdeps("After from pm_kept\n"); 5300*7c478bd9Sstevel@tonic-gate #endif 5301*7c478bd9Sstevel@tonic-gate } 5302*7c478bd9Sstevel@tonic-gate } 5303*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 5304*7c478bd9Sstevel@tonic-gate return (ret); 5305*7c478bd9Sstevel@tonic-gate } 5306*7c478bd9Sstevel@tonic-gate 5307*7c478bd9Sstevel@tonic-gate /* 5308*7c478bd9Sstevel@tonic-gate * Apply a recorded dependency. dp specifies the dependency, and 5309*7c478bd9Sstevel@tonic-gate * keeper is already known to be the device that keeps up the other (kept) one. 5310*7c478bd9Sstevel@tonic-gate * We have to the whole tree for the "kept" device, then apply 5311*7c478bd9Sstevel@tonic-gate * the dependency (which may already be applied). 5312*7c478bd9Sstevel@tonic-gate */ 5313*7c478bd9Sstevel@tonic-gate int 5314*7c478bd9Sstevel@tonic-gate pm_apply_recorded_dep(dev_info_t *keeper, pm_pdr_t *dp) 5315*7c478bd9Sstevel@tonic-gate { 5316*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "apply_recorded_dep") 5317*7c478bd9Sstevel@tonic-gate dev_info_t *kept = NULL; 5318*7c478bd9Sstevel@tonic-gate int ret = 0; 5319*7c478bd9Sstevel@tonic-gate char *keptp = NULL; 5320*7c478bd9Sstevel@tonic-gate 5321*7c478bd9Sstevel@tonic-gate /* 5322*7c478bd9Sstevel@tonic-gate * Device to Device dependency can only be 1 to 1. 5323*7c478bd9Sstevel@tonic-gate */ 5324*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_paths == NULL) 5325*7c478bd9Sstevel@tonic-gate return (0); 5326*7c478bd9Sstevel@tonic-gate keptp = dp->pdr_kept_paths[0]; 5327*7c478bd9Sstevel@tonic-gate if (keptp == NULL) 5328*7c478bd9Sstevel@tonic-gate return (0); 5329*7c478bd9Sstevel@tonic-gate ASSERT(*keptp != '\0'); 5330*7c478bd9Sstevel@tonic-gate kept = pm_name_to_dip(keptp, 1); 5331*7c478bd9Sstevel@tonic-gate if (kept == NULL) 5332*7c478bd9Sstevel@tonic-gate return (0); 5333*7c478bd9Sstevel@tonic-gate if (kept) { 5334*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, 5335*7c478bd9Sstevel@tonic-gate dp->pdr_keeper, keptp)) 5336*7c478bd9Sstevel@tonic-gate if (pm_set_keeping(keeper, kept)) { 5337*7c478bd9Sstevel@tonic-gate ASSERT(dp->pdr_satisfied == 0); 5338*7c478bd9Sstevel@tonic-gate dp->pdr_satisfied = 1; 5339*7c478bd9Sstevel@tonic-gate ASSERT(pm_unresolved_deps); 5340*7c478bd9Sstevel@tonic-gate pm_unresolved_deps--; 5341*7c478bd9Sstevel@tonic-gate ret++; 5342*7c478bd9Sstevel@tonic-gate } 5343*7c478bd9Sstevel@tonic-gate } 5344*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 5345*7c478bd9Sstevel@tonic-gate 5346*7c478bd9Sstevel@tonic-gate return (ret); 5347*7c478bd9Sstevel@tonic-gate } 5348*7c478bd9Sstevel@tonic-gate 5349*7c478bd9Sstevel@tonic-gate /* 5350*7c478bd9Sstevel@tonic-gate * Called from common/io/pm.c 5351*7c478bd9Sstevel@tonic-gate */ 5352*7c478bd9Sstevel@tonic-gate int 5353*7c478bd9Sstevel@tonic-gate pm_cur_power(pm_component_t *cp) 5354*7c478bd9Sstevel@tonic-gate { 5355*7c478bd9Sstevel@tonic-gate return (cur_power(cp)); 5356*7c478bd9Sstevel@tonic-gate } 5357*7c478bd9Sstevel@tonic-gate 5358*7c478bd9Sstevel@tonic-gate /* 5359*7c478bd9Sstevel@tonic-gate * External interface to sanity-check a power level. 5360*7c478bd9Sstevel@tonic-gate */ 5361*7c478bd9Sstevel@tonic-gate int 5362*7c478bd9Sstevel@tonic-gate pm_valid_power(dev_info_t *dip, int comp, int level) 5363*7c478bd9Sstevel@tonic-gate { 5364*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "valid_power") 5365*7c478bd9Sstevel@tonic-gate 5366*7c478bd9Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip) && level >= 0) 5367*7c478bd9Sstevel@tonic-gate return (e_pm_valid_power(dip, comp, level)); 5368*7c478bd9Sstevel@tonic-gate else { 5369*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: comp=%d, ncomp=%d, level=%d\n", 5370*7c478bd9Sstevel@tonic-gate pmf, comp, PM_NUMCMPTS(dip), level)) 5371*7c478bd9Sstevel@tonic-gate return (0); 5372*7c478bd9Sstevel@tonic-gate } 5373*7c478bd9Sstevel@tonic-gate } 5374*7c478bd9Sstevel@tonic-gate 5375*7c478bd9Sstevel@tonic-gate /* 5376*7c478bd9Sstevel@tonic-gate * Called when a device that is direct power managed needs to change state. 5377*7c478bd9Sstevel@tonic-gate * This routine arranges to block the request until the process managing 5378*7c478bd9Sstevel@tonic-gate * the device makes the change (or some other incompatible change) or 5379*7c478bd9Sstevel@tonic-gate * the process closes /dev/pm. 5380*7c478bd9Sstevel@tonic-gate */ 5381*7c478bd9Sstevel@tonic-gate static int 5382*7c478bd9Sstevel@tonic-gate pm_block(dev_info_t *dip, int comp, int newpower, int oldpower) 5383*7c478bd9Sstevel@tonic-gate { 5384*7c478bd9Sstevel@tonic-gate pm_rsvp_t *new = kmem_zalloc(sizeof (*new), KM_SLEEP); 5385*7c478bd9Sstevel@tonic-gate int ret = 0; 5386*7c478bd9Sstevel@tonic-gate void pm_dequeue_blocked(pm_rsvp_t *); 5387*7c478bd9Sstevel@tonic-gate void pm_enqueue_blocked(pm_rsvp_t *); 5388*7c478bd9Sstevel@tonic-gate 5389*7c478bd9Sstevel@tonic-gate ASSERT(!pm_processes_stopped); 5390*7c478bd9Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip)); 5391*7c478bd9Sstevel@tonic-gate new->pr_dip = dip; 5392*7c478bd9Sstevel@tonic-gate new->pr_comp = comp; 5393*7c478bd9Sstevel@tonic-gate new->pr_newlevel = newpower; 5394*7c478bd9Sstevel@tonic-gate new->pr_oldlevel = oldpower; 5395*7c478bd9Sstevel@tonic-gate cv_init(&new->pr_cv, NULL, CV_DEFAULT, NULL); 5396*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 5397*7c478bd9Sstevel@tonic-gate pm_enqueue_blocked(new); 5398*7c478bd9Sstevel@tonic-gate pm_enqueue_notify(PSC_PENDING_CHANGE, dip, comp, newpower, oldpower, 5399*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_BLOCK); 5400*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5401*7c478bd9Sstevel@tonic-gate /* 5402*7c478bd9Sstevel@tonic-gate * truss may make the cv_wait_sig return prematurely 5403*7c478bd9Sstevel@tonic-gate */ 5404*7c478bd9Sstevel@tonic-gate while (ret == 0) { 5405*7c478bd9Sstevel@tonic-gate /* 5406*7c478bd9Sstevel@tonic-gate * Normally there will be no user context involved, but if 5407*7c478bd9Sstevel@tonic-gate * there is (e.g. we are here via an ioctl call to a driver) 5408*7c478bd9Sstevel@tonic-gate * then we should allow the process to abort the request, 5409*7c478bd9Sstevel@tonic-gate * or we get an unkillable process if the same thread does 5410*7c478bd9Sstevel@tonic-gate * PM_DIRECT_PM and pm_raise_power 5411*7c478bd9Sstevel@tonic-gate */ 5412*7c478bd9Sstevel@tonic-gate if (cv_wait_sig(&new->pr_cv, &pm_rsvp_lock) == 0) { 5413*7c478bd9Sstevel@tonic-gate ret = PMP_FAIL; 5414*7c478bd9Sstevel@tonic-gate } else { 5415*7c478bd9Sstevel@tonic-gate ret = new->pr_retval; 5416*7c478bd9Sstevel@tonic-gate } 5417*7c478bd9Sstevel@tonic-gate } 5418*7c478bd9Sstevel@tonic-gate pm_dequeue_blocked(new); 5419*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 5420*7c478bd9Sstevel@tonic-gate cv_destroy(&new->pr_cv); 5421*7c478bd9Sstevel@tonic-gate kmem_free(new, sizeof (*new)); 5422*7c478bd9Sstevel@tonic-gate return (ret); 5423*7c478bd9Sstevel@tonic-gate } 5424*7c478bd9Sstevel@tonic-gate 5425*7c478bd9Sstevel@tonic-gate /* 5426*7c478bd9Sstevel@tonic-gate * Returns true if the process is interested in power level changes (has issued 5427*7c478bd9Sstevel@tonic-gate * PM_GET_STATE_CHANGE ioctl). 5428*7c478bd9Sstevel@tonic-gate */ 5429*7c478bd9Sstevel@tonic-gate int 5430*7c478bd9Sstevel@tonic-gate pm_interest_registered(int clone) 5431*7c478bd9Sstevel@tonic-gate { 5432*7c478bd9Sstevel@tonic-gate ASSERT(clone >= 0 && clone < PM_MAX_CLONE - 1); 5433*7c478bd9Sstevel@tonic-gate return (pm_interest[clone]); 5434*7c478bd9Sstevel@tonic-gate } 5435*7c478bd9Sstevel@tonic-gate 5436*7c478bd9Sstevel@tonic-gate /* 5437*7c478bd9Sstevel@tonic-gate * Process with clone has just done PM_DIRECT_PM on dip, or has asked to 5438*7c478bd9Sstevel@tonic-gate * watch all state transitions (dip == NULL). Set up data 5439*7c478bd9Sstevel@tonic-gate * structs to communicate with process about state changes. 5440*7c478bd9Sstevel@tonic-gate */ 5441*7c478bd9Sstevel@tonic-gate void 5442*7c478bd9Sstevel@tonic-gate pm_register_watcher(int clone, dev_info_t *dip) 5443*7c478bd9Sstevel@tonic-gate { 5444*7c478bd9Sstevel@tonic-gate pscc_t *p; 5445*7c478bd9Sstevel@tonic-gate psce_t *psce; 5446*7c478bd9Sstevel@tonic-gate static void pm_enqueue_pscc(pscc_t *, pscc_t **); 5447*7c478bd9Sstevel@tonic-gate 5448*7c478bd9Sstevel@tonic-gate /* 5449*7c478bd9Sstevel@tonic-gate * We definitely need a control struct, then we have to search to see 5450*7c478bd9Sstevel@tonic-gate * there is already an entries struct (in the dip != NULL case). 5451*7c478bd9Sstevel@tonic-gate */ 5452*7c478bd9Sstevel@tonic-gate pscc_t *pscc = kmem_zalloc(sizeof (*pscc), KM_SLEEP); 5453*7c478bd9Sstevel@tonic-gate pscc->pscc_clone = clone; 5454*7c478bd9Sstevel@tonic-gate pscc->pscc_dip = dip; 5455*7c478bd9Sstevel@tonic-gate 5456*7c478bd9Sstevel@tonic-gate if (dip) { 5457*7c478bd9Sstevel@tonic-gate int found = 0; 5458*7c478bd9Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_WRITER); 5459*7c478bd9Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = p->pscc_next) { 5460*7c478bd9Sstevel@tonic-gate /* 5461*7c478bd9Sstevel@tonic-gate * Already an entry for this clone, so just use it 5462*7c478bd9Sstevel@tonic-gate * for the new one (for the case where a single 5463*7c478bd9Sstevel@tonic-gate * process is watching multiple devices) 5464*7c478bd9Sstevel@tonic-gate */ 5465*7c478bd9Sstevel@tonic-gate if (p->pscc_clone == clone) { 5466*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_dip != dip); 5467*7c478bd9Sstevel@tonic-gate pscc->pscc_entries = p->pscc_entries; 5468*7c478bd9Sstevel@tonic-gate pscc->pscc_entries->psce_references++; 5469*7c478bd9Sstevel@tonic-gate found++; 5470*7c478bd9Sstevel@tonic-gate } 5471*7c478bd9Sstevel@tonic-gate } 5472*7c478bd9Sstevel@tonic-gate if (!found) { /* create a new one */ 5473*7c478bd9Sstevel@tonic-gate psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP); 5474*7c478bd9Sstevel@tonic-gate mutex_init(&psce->psce_lock, NULL, MUTEX_DEFAULT, NULL); 5475*7c478bd9Sstevel@tonic-gate psce->psce_first = 5476*7c478bd9Sstevel@tonic-gate kmem_zalloc(sizeof (pm_state_change_t) * PSCCOUNT, 5477*7c478bd9Sstevel@tonic-gate KM_SLEEP); 5478*7c478bd9Sstevel@tonic-gate psce->psce_in = psce->psce_out = psce->psce_first; 5479*7c478bd9Sstevel@tonic-gate psce->psce_last = &psce->psce_first[PSCCOUNT - 1]; 5480*7c478bd9Sstevel@tonic-gate psce->psce_references = 1; 5481*7c478bd9Sstevel@tonic-gate pscc->pscc_entries = psce; 5482*7c478bd9Sstevel@tonic-gate } 5483*7c478bd9Sstevel@tonic-gate pm_enqueue_pscc(pscc, &pm_pscc_direct); 5484*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 5485*7c478bd9Sstevel@tonic-gate } else { 5486*7c478bd9Sstevel@tonic-gate ASSERT(!pm_interest_registered(clone)); 5487*7c478bd9Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_WRITER); 5488*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5489*7c478bd9Sstevel@tonic-gate for (p = pm_pscc_interest; p; p = p->pscc_next) { 5490*7c478bd9Sstevel@tonic-gate /* 5491*7c478bd9Sstevel@tonic-gate * Should not be an entry for this clone! 5492*7c478bd9Sstevel@tonic-gate */ 5493*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_clone != clone); 5494*7c478bd9Sstevel@tonic-gate } 5495*7c478bd9Sstevel@tonic-gate #endif 5496*7c478bd9Sstevel@tonic-gate psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP); 5497*7c478bd9Sstevel@tonic-gate psce->psce_first = kmem_zalloc(sizeof (pm_state_change_t) * 5498*7c478bd9Sstevel@tonic-gate PSCCOUNT, KM_SLEEP); 5499*7c478bd9Sstevel@tonic-gate psce->psce_in = psce->psce_out = psce->psce_first; 5500*7c478bd9Sstevel@tonic-gate psce->psce_last = &psce->psce_first[PSCCOUNT - 1]; 5501*7c478bd9Sstevel@tonic-gate psce->psce_references = 1; 5502*7c478bd9Sstevel@tonic-gate pscc->pscc_entries = psce; 5503*7c478bd9Sstevel@tonic-gate pm_enqueue_pscc(pscc, &pm_pscc_interest); 5504*7c478bd9Sstevel@tonic-gate pm_interest[clone] = 1; 5505*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock); 5506*7c478bd9Sstevel@tonic-gate } 5507*7c478bd9Sstevel@tonic-gate } 5508*7c478bd9Sstevel@tonic-gate 5509*7c478bd9Sstevel@tonic-gate /* 5510*7c478bd9Sstevel@tonic-gate * Remove the given entry from the blocked list 5511*7c478bd9Sstevel@tonic-gate */ 5512*7c478bd9Sstevel@tonic-gate void 5513*7c478bd9Sstevel@tonic-gate pm_dequeue_blocked(pm_rsvp_t *p) 5514*7c478bd9Sstevel@tonic-gate { 5515*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 5516*7c478bd9Sstevel@tonic-gate if (pm_blocked_list == p) { 5517*7c478bd9Sstevel@tonic-gate ASSERT(p->pr_prev == NULL); 5518*7c478bd9Sstevel@tonic-gate if (p->pr_next != NULL) 5519*7c478bd9Sstevel@tonic-gate p->pr_next->pr_prev = NULL; 5520*7c478bd9Sstevel@tonic-gate pm_blocked_list = p->pr_next; 5521*7c478bd9Sstevel@tonic-gate } else { 5522*7c478bd9Sstevel@tonic-gate ASSERT(p->pr_prev != NULL); 5523*7c478bd9Sstevel@tonic-gate p->pr_prev->pr_next = p->pr_next; 5524*7c478bd9Sstevel@tonic-gate if (p->pr_next != NULL) 5525*7c478bd9Sstevel@tonic-gate p->pr_next->pr_prev = p->pr_prev; 5526*7c478bd9Sstevel@tonic-gate } 5527*7c478bd9Sstevel@tonic-gate } 5528*7c478bd9Sstevel@tonic-gate 5529*7c478bd9Sstevel@tonic-gate /* 5530*7c478bd9Sstevel@tonic-gate * Remove the given control struct from the given list 5531*7c478bd9Sstevel@tonic-gate */ 5532*7c478bd9Sstevel@tonic-gate static void 5533*7c478bd9Sstevel@tonic-gate pm_dequeue_pscc(pscc_t *p, pscc_t **list) 5534*7c478bd9Sstevel@tonic-gate { 5535*7c478bd9Sstevel@tonic-gate if (*list == p) { 5536*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL); 5537*7c478bd9Sstevel@tonic-gate if (p->pscc_next != NULL) 5538*7c478bd9Sstevel@tonic-gate p->pscc_next->pscc_prev = NULL; 5539*7c478bd9Sstevel@tonic-gate *list = p->pscc_next; 5540*7c478bd9Sstevel@tonic-gate } else { 5541*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_prev != NULL); 5542*7c478bd9Sstevel@tonic-gate p->pscc_prev->pscc_next = p->pscc_next; 5543*7c478bd9Sstevel@tonic-gate if (p->pscc_next != NULL) 5544*7c478bd9Sstevel@tonic-gate p->pscc_next->pscc_prev = p->pscc_prev; 5545*7c478bd9Sstevel@tonic-gate } 5546*7c478bd9Sstevel@tonic-gate } 5547*7c478bd9Sstevel@tonic-gate 5548*7c478bd9Sstevel@tonic-gate /* 5549*7c478bd9Sstevel@tonic-gate * Stick the control struct specified on the front of the list 5550*7c478bd9Sstevel@tonic-gate */ 5551*7c478bd9Sstevel@tonic-gate static void 5552*7c478bd9Sstevel@tonic-gate pm_enqueue_pscc(pscc_t *p, pscc_t **list) 5553*7c478bd9Sstevel@tonic-gate { 5554*7c478bd9Sstevel@tonic-gate pscc_t *h; /* entry at head of list */ 5555*7c478bd9Sstevel@tonic-gate if ((h = *list) == NULL) { 5556*7c478bd9Sstevel@tonic-gate *list = p; 5557*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_next == NULL); 5558*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL); 5559*7c478bd9Sstevel@tonic-gate } else { 5560*7c478bd9Sstevel@tonic-gate p->pscc_next = h; 5561*7c478bd9Sstevel@tonic-gate ASSERT(h->pscc_prev == NULL); 5562*7c478bd9Sstevel@tonic-gate h->pscc_prev = p; 5563*7c478bd9Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL); 5564*7c478bd9Sstevel@tonic-gate *list = p; 5565*7c478bd9Sstevel@tonic-gate } 5566*7c478bd9Sstevel@tonic-gate } 5567*7c478bd9Sstevel@tonic-gate 5568*7c478bd9Sstevel@tonic-gate /* 5569*7c478bd9Sstevel@tonic-gate * If dip is NULL, process is closing "clone" clean up all its registrations. 5570*7c478bd9Sstevel@tonic-gate * Otherwise only clean up those for dip because process is just giving up 5571*7c478bd9Sstevel@tonic-gate * control of a direct device. 5572*7c478bd9Sstevel@tonic-gate */ 5573*7c478bd9Sstevel@tonic-gate void 5574*7c478bd9Sstevel@tonic-gate pm_deregister_watcher(int clone, dev_info_t *dip) 5575*7c478bd9Sstevel@tonic-gate { 5576*7c478bd9Sstevel@tonic-gate pscc_t *p, *pn; 5577*7c478bd9Sstevel@tonic-gate psce_t *psce; 5578*7c478bd9Sstevel@tonic-gate int found = 0; 5579*7c478bd9Sstevel@tonic-gate 5580*7c478bd9Sstevel@tonic-gate if (dip == NULL) { 5581*7c478bd9Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_WRITER); 5582*7c478bd9Sstevel@tonic-gate for (p = pm_pscc_interest; p; p = pn) { 5583*7c478bd9Sstevel@tonic-gate pn = p->pscc_next; 5584*7c478bd9Sstevel@tonic-gate if (p->pscc_clone == clone) { 5585*7c478bd9Sstevel@tonic-gate pm_dequeue_pscc(p, &pm_pscc_interest); 5586*7c478bd9Sstevel@tonic-gate psce = p->pscc_entries; 5587*7c478bd9Sstevel@tonic-gate ASSERT(psce->psce_references == 1); 5588*7c478bd9Sstevel@tonic-gate mutex_destroy(&psce->psce_lock); 5589*7c478bd9Sstevel@tonic-gate kmem_free(psce->psce_first, 5590*7c478bd9Sstevel@tonic-gate sizeof (pm_state_change_t) * PSCCOUNT); 5591*7c478bd9Sstevel@tonic-gate kmem_free(psce, sizeof (*psce)); 5592*7c478bd9Sstevel@tonic-gate kmem_free(p, sizeof (*p)); 5593*7c478bd9Sstevel@tonic-gate } 5594*7c478bd9Sstevel@tonic-gate } 5595*7c478bd9Sstevel@tonic-gate pm_interest[clone] = 0; 5596*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock); 5597*7c478bd9Sstevel@tonic-gate } 5598*7c478bd9Sstevel@tonic-gate found = 0; 5599*7c478bd9Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_WRITER); 5600*7c478bd9Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = pn) { 5601*7c478bd9Sstevel@tonic-gate pn = p->pscc_next; 5602*7c478bd9Sstevel@tonic-gate if ((dip && p->pscc_dip == dip) || 5603*7c478bd9Sstevel@tonic-gate (dip == NULL && clone == p->pscc_clone)) { 5604*7c478bd9Sstevel@tonic-gate ASSERT(clone == p->pscc_clone); 5605*7c478bd9Sstevel@tonic-gate found++; 5606*7c478bd9Sstevel@tonic-gate /* 5607*7c478bd9Sstevel@tonic-gate * Remove from control list 5608*7c478bd9Sstevel@tonic-gate */ 5609*7c478bd9Sstevel@tonic-gate pm_dequeue_pscc(p, &pm_pscc_direct); 5610*7c478bd9Sstevel@tonic-gate /* 5611*7c478bd9Sstevel@tonic-gate * If we're the last reference, free the 5612*7c478bd9Sstevel@tonic-gate * entries struct. 5613*7c478bd9Sstevel@tonic-gate */ 5614*7c478bd9Sstevel@tonic-gate psce = p->pscc_entries; 5615*7c478bd9Sstevel@tonic-gate ASSERT(psce); 5616*7c478bd9Sstevel@tonic-gate if (psce->psce_references == 1) { 5617*7c478bd9Sstevel@tonic-gate kmem_free(psce->psce_first, 5618*7c478bd9Sstevel@tonic-gate PSCCOUNT * sizeof (pm_state_change_t)); 5619*7c478bd9Sstevel@tonic-gate kmem_free(psce, sizeof (*psce)); 5620*7c478bd9Sstevel@tonic-gate } else { 5621*7c478bd9Sstevel@tonic-gate psce->psce_references--; 5622*7c478bd9Sstevel@tonic-gate } 5623*7c478bd9Sstevel@tonic-gate kmem_free(p, sizeof (*p)); 5624*7c478bd9Sstevel@tonic-gate } 5625*7c478bd9Sstevel@tonic-gate } 5626*7c478bd9Sstevel@tonic-gate ASSERT(dip == NULL || found); 5627*7c478bd9Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 5628*7c478bd9Sstevel@tonic-gate } 5629*7c478bd9Sstevel@tonic-gate 5630*7c478bd9Sstevel@tonic-gate /* 5631*7c478bd9Sstevel@tonic-gate * Search the indicated list for an entry that matches clone, and return a 5632*7c478bd9Sstevel@tonic-gate * pointer to it. To be interesting, the entry must have something ready to 5633*7c478bd9Sstevel@tonic-gate * be passed up to the controlling process. 5634*7c478bd9Sstevel@tonic-gate * The returned entry will be locked upon return from this call. 5635*7c478bd9Sstevel@tonic-gate */ 5636*7c478bd9Sstevel@tonic-gate static psce_t * 5637*7c478bd9Sstevel@tonic-gate pm_psc_find_clone(int clone, pscc_t **list, krwlock_t *lock) 5638*7c478bd9Sstevel@tonic-gate { 5639*7c478bd9Sstevel@tonic-gate pscc_t *p; 5640*7c478bd9Sstevel@tonic-gate psce_t *psce; 5641*7c478bd9Sstevel@tonic-gate rw_enter(lock, RW_READER); 5642*7c478bd9Sstevel@tonic-gate for (p = *list; p; p = p->pscc_next) { 5643*7c478bd9Sstevel@tonic-gate if (clone == p->pscc_clone) { 5644*7c478bd9Sstevel@tonic-gate psce = p->pscc_entries; 5645*7c478bd9Sstevel@tonic-gate mutex_enter(&psce->psce_lock); 5646*7c478bd9Sstevel@tonic-gate if (psce->psce_out->size) { 5647*7c478bd9Sstevel@tonic-gate rw_exit(lock); 5648*7c478bd9Sstevel@tonic-gate return (psce); 5649*7c478bd9Sstevel@tonic-gate } else { 5650*7c478bd9Sstevel@tonic-gate mutex_exit(&psce->psce_lock); 5651*7c478bd9Sstevel@tonic-gate } 5652*7c478bd9Sstevel@tonic-gate } 5653*7c478bd9Sstevel@tonic-gate } 5654*7c478bd9Sstevel@tonic-gate rw_exit(lock); 5655*7c478bd9Sstevel@tonic-gate return (NULL); 5656*7c478bd9Sstevel@tonic-gate } 5657*7c478bd9Sstevel@tonic-gate 5658*7c478bd9Sstevel@tonic-gate /* 5659*7c478bd9Sstevel@tonic-gate * Find an entry for a particular clone in the direct list. 5660*7c478bd9Sstevel@tonic-gate */ 5661*7c478bd9Sstevel@tonic-gate psce_t * 5662*7c478bd9Sstevel@tonic-gate pm_psc_clone_to_direct(int clone) 5663*7c478bd9Sstevel@tonic-gate { 5664*7c478bd9Sstevel@tonic-gate static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *); 5665*7c478bd9Sstevel@tonic-gate return (pm_psc_find_clone(clone, &pm_pscc_direct, 5666*7c478bd9Sstevel@tonic-gate &pm_pscc_direct_rwlock)); 5667*7c478bd9Sstevel@tonic-gate } 5668*7c478bd9Sstevel@tonic-gate 5669*7c478bd9Sstevel@tonic-gate /* 5670*7c478bd9Sstevel@tonic-gate * Find an entry for a particular clone in the interest list. 5671*7c478bd9Sstevel@tonic-gate */ 5672*7c478bd9Sstevel@tonic-gate psce_t * 5673*7c478bd9Sstevel@tonic-gate pm_psc_clone_to_interest(int clone) 5674*7c478bd9Sstevel@tonic-gate { 5675*7c478bd9Sstevel@tonic-gate static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *); 5676*7c478bd9Sstevel@tonic-gate return (pm_psc_find_clone(clone, &pm_pscc_interest, 5677*7c478bd9Sstevel@tonic-gate &pm_pscc_interest_rwlock)); 5678*7c478bd9Sstevel@tonic-gate } 5679*7c478bd9Sstevel@tonic-gate 5680*7c478bd9Sstevel@tonic-gate /* 5681*7c478bd9Sstevel@tonic-gate * Put the given entry at the head of the blocked list 5682*7c478bd9Sstevel@tonic-gate */ 5683*7c478bd9Sstevel@tonic-gate void 5684*7c478bd9Sstevel@tonic-gate pm_enqueue_blocked(pm_rsvp_t *p) 5685*7c478bd9Sstevel@tonic-gate { 5686*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 5687*7c478bd9Sstevel@tonic-gate ASSERT(p->pr_next == NULL); 5688*7c478bd9Sstevel@tonic-gate ASSERT(p->pr_prev == NULL); 5689*7c478bd9Sstevel@tonic-gate if (pm_blocked_list != NULL) { 5690*7c478bd9Sstevel@tonic-gate p->pr_next = pm_blocked_list; 5691*7c478bd9Sstevel@tonic-gate ASSERT(pm_blocked_list->pr_prev == NULL); 5692*7c478bd9Sstevel@tonic-gate pm_blocked_list->pr_prev = p; 5693*7c478bd9Sstevel@tonic-gate pm_blocked_list = p; 5694*7c478bd9Sstevel@tonic-gate } else { 5695*7c478bd9Sstevel@tonic-gate pm_blocked_list = p; 5696*7c478bd9Sstevel@tonic-gate } 5697*7c478bd9Sstevel@tonic-gate } 5698*7c478bd9Sstevel@tonic-gate 5699*7c478bd9Sstevel@tonic-gate /* 5700*7c478bd9Sstevel@tonic-gate * Sets every power managed device back to its default threshold 5701*7c478bd9Sstevel@tonic-gate */ 5702*7c478bd9Sstevel@tonic-gate void 5703*7c478bd9Sstevel@tonic-gate pm_all_to_default_thresholds(void) 5704*7c478bd9Sstevel@tonic-gate { 5705*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_set_dev_thr_walk, 5706*7c478bd9Sstevel@tonic-gate (void *) &pm_system_idle_threshold); 5707*7c478bd9Sstevel@tonic-gate } 5708*7c478bd9Sstevel@tonic-gate 5709*7c478bd9Sstevel@tonic-gate static int 5710*7c478bd9Sstevel@tonic-gate pm_set_dev_thr_walk(dev_info_t *dip, void *arg) 5711*7c478bd9Sstevel@tonic-gate { 5712*7c478bd9Sstevel@tonic-gate int thr = (int)(*(int *)arg); 5713*7c478bd9Sstevel@tonic-gate 5714*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) 5715*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 5716*7c478bd9Sstevel@tonic-gate pm_set_device_threshold(dip, thr, PMC_DEF_THRESH); 5717*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 5718*7c478bd9Sstevel@tonic-gate } 5719*7c478bd9Sstevel@tonic-gate 5720*7c478bd9Sstevel@tonic-gate /* 5721*7c478bd9Sstevel@tonic-gate * Returns the current threshold value (in seconds) for the indicated component 5722*7c478bd9Sstevel@tonic-gate */ 5723*7c478bd9Sstevel@tonic-gate int 5724*7c478bd9Sstevel@tonic-gate pm_current_threshold(dev_info_t *dip, int comp, int *threshp) 5725*7c478bd9Sstevel@tonic-gate { 5726*7c478bd9Sstevel@tonic-gate if (comp < 0 || comp >= PM_NUMCMPTS(dip)) { 5727*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5728*7c478bd9Sstevel@tonic-gate } else { 5729*7c478bd9Sstevel@tonic-gate *threshp = cur_threshold(dip, comp); 5730*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5731*7c478bd9Sstevel@tonic-gate } 5732*7c478bd9Sstevel@tonic-gate } 5733*7c478bd9Sstevel@tonic-gate 5734*7c478bd9Sstevel@tonic-gate /* 5735*7c478bd9Sstevel@tonic-gate * To be called when changing the power level of a component of a device. 5736*7c478bd9Sstevel@tonic-gate * On some platforms, changing power on one device may require that power 5737*7c478bd9Sstevel@tonic-gate * be changed on other, related devices in the same transaction. Thus, we 5738*7c478bd9Sstevel@tonic-gate * always pass this request to the platform power manager so that all the 5739*7c478bd9Sstevel@tonic-gate * affected devices will be locked. 5740*7c478bd9Sstevel@tonic-gate */ 5741*7c478bd9Sstevel@tonic-gate void 5742*7c478bd9Sstevel@tonic-gate pm_lock_power(dev_info_t *dip, int *circp) 5743*7c478bd9Sstevel@tonic-gate { 5744*7c478bd9Sstevel@tonic-gate power_req_t power_req; 5745*7c478bd9Sstevel@tonic-gate int result; 5746*7c478bd9Sstevel@tonic-gate 5747*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_LOCK_POWER; 5748*7c478bd9Sstevel@tonic-gate power_req.req.ppm_lock_power_req.who = dip; 5749*7c478bd9Sstevel@tonic-gate power_req.req.ppm_lock_power_req.circp = circp; 5750*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result); 5751*7c478bd9Sstevel@tonic-gate } 5752*7c478bd9Sstevel@tonic-gate 5753*7c478bd9Sstevel@tonic-gate /* 5754*7c478bd9Sstevel@tonic-gate * Release the lock (or locks) acquired to change the power of a device. 5755*7c478bd9Sstevel@tonic-gate * See comments for pm_lock_power. 5756*7c478bd9Sstevel@tonic-gate */ 5757*7c478bd9Sstevel@tonic-gate void 5758*7c478bd9Sstevel@tonic-gate pm_unlock_power(dev_info_t *dip, int circ) 5759*7c478bd9Sstevel@tonic-gate { 5760*7c478bd9Sstevel@tonic-gate power_req_t power_req; 5761*7c478bd9Sstevel@tonic-gate int result; 5762*7c478bd9Sstevel@tonic-gate 5763*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNLOCK_POWER; 5764*7c478bd9Sstevel@tonic-gate power_req.req.ppm_unlock_power_req.who = dip; 5765*7c478bd9Sstevel@tonic-gate power_req.req.ppm_unlock_power_req.circ = circ; 5766*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result); 5767*7c478bd9Sstevel@tonic-gate } 5768*7c478bd9Sstevel@tonic-gate 5769*7c478bd9Sstevel@tonic-gate 5770*7c478bd9Sstevel@tonic-gate /* 5771*7c478bd9Sstevel@tonic-gate * Attempt (without blocking) to acquire the lock(s) needed to change the 5772*7c478bd9Sstevel@tonic-gate * power of a component of a device. See comments for pm_lock_power. 5773*7c478bd9Sstevel@tonic-gate * 5774*7c478bd9Sstevel@tonic-gate * Return: 1 if lock(s) acquired, 0 if not. 5775*7c478bd9Sstevel@tonic-gate */ 5776*7c478bd9Sstevel@tonic-gate int 5777*7c478bd9Sstevel@tonic-gate pm_try_locking_power(dev_info_t *dip, int *circp) 5778*7c478bd9Sstevel@tonic-gate { 5779*7c478bd9Sstevel@tonic-gate power_req_t power_req; 5780*7c478bd9Sstevel@tonic-gate int result; 5781*7c478bd9Sstevel@tonic-gate 5782*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_TRY_LOCK_POWER; 5783*7c478bd9Sstevel@tonic-gate power_req.req.ppm_lock_power_req.who = dip; 5784*7c478bd9Sstevel@tonic-gate power_req.req.ppm_lock_power_req.circp = circp; 5785*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result); 5786*7c478bd9Sstevel@tonic-gate return (result); 5787*7c478bd9Sstevel@tonic-gate } 5788*7c478bd9Sstevel@tonic-gate 5789*7c478bd9Sstevel@tonic-gate 5790*7c478bd9Sstevel@tonic-gate /* 5791*7c478bd9Sstevel@tonic-gate * Lock power state of a device. 5792*7c478bd9Sstevel@tonic-gate * 5793*7c478bd9Sstevel@tonic-gate * The implementation handles a special case where another thread may have 5794*7c478bd9Sstevel@tonic-gate * acquired the lock and created/launched this thread to do the work. If 5795*7c478bd9Sstevel@tonic-gate * the lock cannot be acquired immediately, we check to see if this thread 5796*7c478bd9Sstevel@tonic-gate * is registered as a borrower of the lock. If so, we may proceed without 5797*7c478bd9Sstevel@tonic-gate * the lock. This assumes that the lending thread blocks on the completion 5798*7c478bd9Sstevel@tonic-gate * of this thread. 5799*7c478bd9Sstevel@tonic-gate * 5800*7c478bd9Sstevel@tonic-gate * Note 1: for use by ppm only. 5801*7c478bd9Sstevel@tonic-gate * 5802*7c478bd9Sstevel@tonic-gate * Note 2: On failing to get the lock immediately, we search lock_loan list 5803*7c478bd9Sstevel@tonic-gate * for curthread (as borrower of the lock). On a hit, we check that the 5804*7c478bd9Sstevel@tonic-gate * lending thread already owns the lock we want. It is safe to compare 5805*7c478bd9Sstevel@tonic-gate * devi_busy_thread and thread id of the lender because in the == case (the 5806*7c478bd9Sstevel@tonic-gate * only one we care about) we know that the owner is blocked. Similarly, 5807*7c478bd9Sstevel@tonic-gate * If we find that curthread isn't registered as a lock borrower, it is safe 5808*7c478bd9Sstevel@tonic-gate * to use the blocking call (ndi_devi_enter) because we know that if we 5809*7c478bd9Sstevel@tonic-gate * weren't already listed as a borrower (upstream on the call stack) we won't 5810*7c478bd9Sstevel@tonic-gate * become one. 5811*7c478bd9Sstevel@tonic-gate */ 5812*7c478bd9Sstevel@tonic-gate void 5813*7c478bd9Sstevel@tonic-gate pm_lock_power_single(dev_info_t *dip, int *circp) 5814*7c478bd9Sstevel@tonic-gate { 5815*7c478bd9Sstevel@tonic-gate lock_loan_t *cur; 5816*7c478bd9Sstevel@tonic-gate 5817*7c478bd9Sstevel@tonic-gate /* if the lock is available, we are done. */ 5818*7c478bd9Sstevel@tonic-gate if (ndi_devi_tryenter(dip, circp)) 5819*7c478bd9Sstevel@tonic-gate return; 5820*7c478bd9Sstevel@tonic-gate 5821*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 5822*7c478bd9Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */ 5823*7c478bd9Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next) 5824*7c478bd9Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 5825*7c478bd9Sstevel@tonic-gate break; 5826*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 5827*7c478bd9Sstevel@tonic-gate 5828*7c478bd9Sstevel@tonic-gate /* if this thread not already registered, it is safe to block */ 5829*7c478bd9Sstevel@tonic-gate if (cur == NULL) 5830*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, circp); 5831*7c478bd9Sstevel@tonic-gate else { 5832*7c478bd9Sstevel@tonic-gate /* registered: does lender own the lock we want? */ 5833*7c478bd9Sstevel@tonic-gate if (cur->pmlk_lender == DEVI(dip)->devi_busy_thread) { 5834*7c478bd9Sstevel@tonic-gate ASSERT(cur->pmlk_dip == NULL || cur->pmlk_dip == dip); 5835*7c478bd9Sstevel@tonic-gate cur->pmlk_dip = dip; 5836*7c478bd9Sstevel@tonic-gate } else /* no: just block for it */ 5837*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, circp); 5838*7c478bd9Sstevel@tonic-gate 5839*7c478bd9Sstevel@tonic-gate } 5840*7c478bd9Sstevel@tonic-gate } 5841*7c478bd9Sstevel@tonic-gate 5842*7c478bd9Sstevel@tonic-gate /* 5843*7c478bd9Sstevel@tonic-gate * Drop the lock on the device's power state. See comment for 5844*7c478bd9Sstevel@tonic-gate * pm_lock_power_single() for special implementation considerations. 5845*7c478bd9Sstevel@tonic-gate * 5846*7c478bd9Sstevel@tonic-gate * Note: for use by ppm only. 5847*7c478bd9Sstevel@tonic-gate */ 5848*7c478bd9Sstevel@tonic-gate void 5849*7c478bd9Sstevel@tonic-gate pm_unlock_power_single(dev_info_t *dip, int circ) 5850*7c478bd9Sstevel@tonic-gate { 5851*7c478bd9Sstevel@tonic-gate lock_loan_t *cur; 5852*7c478bd9Sstevel@tonic-gate 5853*7c478bd9Sstevel@tonic-gate /* optimization: mutex not needed to check empty list */ 5854*7c478bd9Sstevel@tonic-gate if (lock_loan_head.pmlk_next == NULL) { 5855*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 5856*7c478bd9Sstevel@tonic-gate return; 5857*7c478bd9Sstevel@tonic-gate } 5858*7c478bd9Sstevel@tonic-gate 5859*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 5860*7c478bd9Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */ 5861*7c478bd9Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next) 5862*7c478bd9Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 5863*7c478bd9Sstevel@tonic-gate break; 5864*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 5865*7c478bd9Sstevel@tonic-gate 5866*7c478bd9Sstevel@tonic-gate if (cur == NULL || cur->pmlk_dip != dip) 5867*7c478bd9Sstevel@tonic-gate /* we acquired the lock directly, so return it */ 5868*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ); 5869*7c478bd9Sstevel@tonic-gate } 5870*7c478bd9Sstevel@tonic-gate 5871*7c478bd9Sstevel@tonic-gate /* 5872*7c478bd9Sstevel@tonic-gate * Try to take the lock for changing the power level of a component. 5873*7c478bd9Sstevel@tonic-gate * 5874*7c478bd9Sstevel@tonic-gate * Note: for use by ppm only. 5875*7c478bd9Sstevel@tonic-gate */ 5876*7c478bd9Sstevel@tonic-gate int 5877*7c478bd9Sstevel@tonic-gate pm_try_locking_power_single(dev_info_t *dip, int *circp) 5878*7c478bd9Sstevel@tonic-gate { 5879*7c478bd9Sstevel@tonic-gate return (ndi_devi_tryenter(dip, circp)); 5880*7c478bd9Sstevel@tonic-gate } 5881*7c478bd9Sstevel@tonic-gate 5882*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 5883*7c478bd9Sstevel@tonic-gate /* 5884*7c478bd9Sstevel@tonic-gate * The following are used only to print out data structures for debugging 5885*7c478bd9Sstevel@tonic-gate */ 5886*7c478bd9Sstevel@tonic-gate void 5887*7c478bd9Sstevel@tonic-gate prdeps(char *msg) 5888*7c478bd9Sstevel@tonic-gate { 5889*7c478bd9Sstevel@tonic-gate 5890*7c478bd9Sstevel@tonic-gate pm_pdr_t *rp; 5891*7c478bd9Sstevel@tonic-gate int i; 5892*7c478bd9Sstevel@tonic-gate 5893*7c478bd9Sstevel@tonic-gate pm_log("pm_dep_head %s %p\n", msg, (void *)pm_dep_head); 5894*7c478bd9Sstevel@tonic-gate for (rp = pm_dep_head; rp; rp = rp->pdr_next) { 5895*7c478bd9Sstevel@tonic-gate pm_log("%p: %s keeper %s, kept %s, kept count %d, next %p\n", 5896*7c478bd9Sstevel@tonic-gate (void *)rp, (rp->pdr_isprop ? "property" : "device"), 5897*7c478bd9Sstevel@tonic-gate rp->pdr_keeper, rp->pdr_kept, rp->pdr_kept_count, 5898*7c478bd9Sstevel@tonic-gate (void *)rp->pdr_next); 5899*7c478bd9Sstevel@tonic-gate if (rp->pdr_kept_count != 0) { 5900*7c478bd9Sstevel@tonic-gate pm_log("kept list = "); 5901*7c478bd9Sstevel@tonic-gate i = 0; 5902*7c478bd9Sstevel@tonic-gate while (i < rp->pdr_kept_count) { 5903*7c478bd9Sstevel@tonic-gate pm_log("%s ", rp->pdr_kept_paths[i]); 5904*7c478bd9Sstevel@tonic-gate i++; 5905*7c478bd9Sstevel@tonic-gate } 5906*7c478bd9Sstevel@tonic-gate pm_log("\n"); 5907*7c478bd9Sstevel@tonic-gate } 5908*7c478bd9Sstevel@tonic-gate } 5909*7c478bd9Sstevel@tonic-gate } 5910*7c478bd9Sstevel@tonic-gate 5911*7c478bd9Sstevel@tonic-gate void 5912*7c478bd9Sstevel@tonic-gate pr_noinvol(char *hdr) 5913*7c478bd9Sstevel@tonic-gate { 5914*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip; 5915*7c478bd9Sstevel@tonic-gate 5916*7c478bd9Sstevel@tonic-gate pm_log("%s\n", hdr); 5917*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 5918*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) 5919*7c478bd9Sstevel@tonic-gate pm_log("\tmaj %d, flags %x, noinvolpm %d %s\n", 5920*7c478bd9Sstevel@tonic-gate ip->ni_major, ip->ni_flags, ip->ni_noinvolpm, ip->ni_path); 5921*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 5922*7c478bd9Sstevel@tonic-gate } 5923*7c478bd9Sstevel@tonic-gate #endif 5924*7c478bd9Sstevel@tonic-gate 5925*7c478bd9Sstevel@tonic-gate /* 5926*7c478bd9Sstevel@tonic-gate * Attempt to apply the thresholds indicated by rp to the node specified by 5927*7c478bd9Sstevel@tonic-gate * dip. 5928*7c478bd9Sstevel@tonic-gate */ 5929*7c478bd9Sstevel@tonic-gate void 5930*7c478bd9Sstevel@tonic-gate pm_apply_recorded_thresh(dev_info_t *dip, pm_thresh_rec_t *rp) 5931*7c478bd9Sstevel@tonic-gate { 5932*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "apply_recorded_thresh") 5933*7c478bd9Sstevel@tonic-gate int i, j; 5934*7c478bd9Sstevel@tonic-gate int comps = PM_NUMCMPTS(dip); 5935*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 5936*7c478bd9Sstevel@tonic-gate pm_pte_t *ep; 5937*7c478bd9Sstevel@tonic-gate int pm_valid_thresh(dev_info_t *, pm_thresh_rec_t *); 5938*7c478bd9Sstevel@tonic-gate 5939*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: part: %s@%s(%s#%d), rp %p, %s\n", pmf, 5940*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), (void *)rp, rp->ptr_physpath)) 5941*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 5942*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip) || !pm_valid_thresh(dip, rp)) { 5943*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_GET_PM_INFO %p\n", 5944*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), (void*)PM_GET_PM_INFO(dip))) 5945*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_ISBC %d\n", 5946*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_ISBC(dip))) 5947*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) pm_valid_thresh %d\n", 5948*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_valid_thresh(dip, rp))) 5949*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5950*7c478bd9Sstevel@tonic-gate return; 5951*7c478bd9Sstevel@tonic-gate } 5952*7c478bd9Sstevel@tonic-gate 5953*7c478bd9Sstevel@tonic-gate ep = rp->ptr_entries; 5954*7c478bd9Sstevel@tonic-gate /* 5955*7c478bd9Sstevel@tonic-gate * Here we do the special case of a device threshold 5956*7c478bd9Sstevel@tonic-gate */ 5957*7c478bd9Sstevel@tonic-gate if (rp->ptr_numcomps == 0) { /* PM_SET_DEVICE_THRESHOLD product */ 5958*7c478bd9Sstevel@tonic-gate ASSERT(ep && ep->pte_numthresh == 1); 5959*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: set dev thr %s@%s(%s#%d) to 0x%x\n", 5960*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), ep->pte_thresh[0])) 5961*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5962*7c478bd9Sstevel@tonic-gate pm_set_device_threshold(dip, ep->pte_thresh[0], PMC_DEV_THRESH); 5963*7c478bd9Sstevel@tonic-gate if (autopm_enabled) 5964*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 5965*7c478bd9Sstevel@tonic-gate return; 5966*7c478bd9Sstevel@tonic-gate } 5967*7c478bd9Sstevel@tonic-gate for (i = 0; i < comps; i++) { 5968*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 5969*7c478bd9Sstevel@tonic-gate for (j = 0; j < ep->pte_numthresh; j++) { 5970*7c478bd9Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: set thr %d for %s@%s(%s#%d)[%d] " 5971*7c478bd9Sstevel@tonic-gate "to %x\n", pmf, j, PM_DEVICE(dip), 5972*7c478bd9Sstevel@tonic-gate i, ep->pte_thresh[j])) 5973*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_thresh[j + 1] = ep->pte_thresh[j]; 5974*7c478bd9Sstevel@tonic-gate } 5975*7c478bd9Sstevel@tonic-gate ep++; 5976*7c478bd9Sstevel@tonic-gate } 5977*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE; 5978*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMP_THRESH; 5979*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5980*7c478bd9Sstevel@tonic-gate 5981*7c478bd9Sstevel@tonic-gate if (autopm_enabled) 5982*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 5983*7c478bd9Sstevel@tonic-gate } 5984*7c478bd9Sstevel@tonic-gate 5985*7c478bd9Sstevel@tonic-gate /* 5986*7c478bd9Sstevel@tonic-gate * Returns true if the threshold specified by rp could be applied to dip 5987*7c478bd9Sstevel@tonic-gate * (that is, the number of components and transitions are the same) 5988*7c478bd9Sstevel@tonic-gate */ 5989*7c478bd9Sstevel@tonic-gate int 5990*7c478bd9Sstevel@tonic-gate pm_valid_thresh(dev_info_t *dip, pm_thresh_rec_t *rp) 5991*7c478bd9Sstevel@tonic-gate { 5992*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "valid_thresh") 5993*7c478bd9Sstevel@tonic-gate int comps, i; 5994*7c478bd9Sstevel@tonic-gate pm_component_t *cp; 5995*7c478bd9Sstevel@tonic-gate pm_pte_t *ep; 5996*7c478bd9Sstevel@tonic-gate 5997*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) { 5998*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s: no pm_info or BC\n", pmf, 5999*7c478bd9Sstevel@tonic-gate rp->ptr_physpath)) 6000*7c478bd9Sstevel@tonic-gate return (0); 6001*7c478bd9Sstevel@tonic-gate } 6002*7c478bd9Sstevel@tonic-gate /* 6003*7c478bd9Sstevel@tonic-gate * Special case: we represent the PM_SET_DEVICE_THRESHOLD case by 6004*7c478bd9Sstevel@tonic-gate * an entry with numcomps == 0, (since we don't know how many 6005*7c478bd9Sstevel@tonic-gate * components there are in advance). This is always a valid 6006*7c478bd9Sstevel@tonic-gate * spec. 6007*7c478bd9Sstevel@tonic-gate */ 6008*7c478bd9Sstevel@tonic-gate if (rp->ptr_numcomps == 0) { 6009*7c478bd9Sstevel@tonic-gate ASSERT(rp->ptr_entries && rp->ptr_entries->pte_numthresh == 1); 6010*7c478bd9Sstevel@tonic-gate return (1); 6011*7c478bd9Sstevel@tonic-gate } 6012*7c478bd9Sstevel@tonic-gate if (rp->ptr_numcomps != (comps = PM_NUMCMPTS(dip))) { 6013*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp # mm (dip %d cmd %d) for %s\n", 6014*7c478bd9Sstevel@tonic-gate pmf, PM_NUMCMPTS(dip), rp->ptr_numcomps, rp->ptr_physpath)) 6015*7c478bd9Sstevel@tonic-gate return (0); 6016*7c478bd9Sstevel@tonic-gate } 6017*7c478bd9Sstevel@tonic-gate ep = rp->ptr_entries; 6018*7c478bd9Sstevel@tonic-gate for (i = 0; i < comps; i++) { 6019*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 6020*7c478bd9Sstevel@tonic-gate if ((ep + i)->pte_numthresh != 6021*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_numlevels - 1) { 6022*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s[%d]: thresh=%d, record=%d\n", 6023*7c478bd9Sstevel@tonic-gate pmf, rp->ptr_physpath, i, 6024*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_numlevels - 1, 6025*7c478bd9Sstevel@tonic-gate (ep + i)->pte_numthresh)) 6026*7c478bd9Sstevel@tonic-gate return (0); 6027*7c478bd9Sstevel@tonic-gate } 6028*7c478bd9Sstevel@tonic-gate } 6029*7c478bd9Sstevel@tonic-gate return (1); 6030*7c478bd9Sstevel@tonic-gate } 6031*7c478bd9Sstevel@tonic-gate 6032*7c478bd9Sstevel@tonic-gate /* 6033*7c478bd9Sstevel@tonic-gate * Remove any recorded threshold for device physpath 6034*7c478bd9Sstevel@tonic-gate * We know there will be at most one. 6035*7c478bd9Sstevel@tonic-gate */ 6036*7c478bd9Sstevel@tonic-gate void 6037*7c478bd9Sstevel@tonic-gate pm_unrecord_threshold(char *physpath) 6038*7c478bd9Sstevel@tonic-gate { 6039*7c478bd9Sstevel@tonic-gate pm_thresh_rec_t *pptr, *ptr; 6040*7c478bd9Sstevel@tonic-gate 6041*7c478bd9Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER); 6042*7c478bd9Sstevel@tonic-gate for (pptr = NULL, ptr = pm_thresh_head; ptr; ptr = ptr->ptr_next) { 6043*7c478bd9Sstevel@tonic-gate if (strcmp(physpath, ptr->ptr_physpath) == 0) { 6044*7c478bd9Sstevel@tonic-gate if (pptr) { 6045*7c478bd9Sstevel@tonic-gate pptr->ptr_next = ptr->ptr_next; 6046*7c478bd9Sstevel@tonic-gate } else { 6047*7c478bd9Sstevel@tonic-gate ASSERT(pm_thresh_head == ptr); 6048*7c478bd9Sstevel@tonic-gate pm_thresh_head = ptr->ptr_next; 6049*7c478bd9Sstevel@tonic-gate } 6050*7c478bd9Sstevel@tonic-gate kmem_free(ptr, ptr->ptr_size); 6051*7c478bd9Sstevel@tonic-gate break; 6052*7c478bd9Sstevel@tonic-gate } 6053*7c478bd9Sstevel@tonic-gate pptr = ptr; 6054*7c478bd9Sstevel@tonic-gate } 6055*7c478bd9Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 6056*7c478bd9Sstevel@tonic-gate } 6057*7c478bd9Sstevel@tonic-gate 6058*7c478bd9Sstevel@tonic-gate /* 6059*7c478bd9Sstevel@tonic-gate * Discard all recorded thresholds. We are returning to the default pm state. 6060*7c478bd9Sstevel@tonic-gate */ 6061*7c478bd9Sstevel@tonic-gate void 6062*7c478bd9Sstevel@tonic-gate pm_discard_thresholds(void) 6063*7c478bd9Sstevel@tonic-gate { 6064*7c478bd9Sstevel@tonic-gate pm_thresh_rec_t *rp; 6065*7c478bd9Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER); 6066*7c478bd9Sstevel@tonic-gate while (pm_thresh_head) { 6067*7c478bd9Sstevel@tonic-gate rp = pm_thresh_head; 6068*7c478bd9Sstevel@tonic-gate pm_thresh_head = rp->ptr_next; 6069*7c478bd9Sstevel@tonic-gate kmem_free(rp, rp->ptr_size); 6070*7c478bd9Sstevel@tonic-gate } 6071*7c478bd9Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 6072*7c478bd9Sstevel@tonic-gate } 6073*7c478bd9Sstevel@tonic-gate 6074*7c478bd9Sstevel@tonic-gate /* 6075*7c478bd9Sstevel@tonic-gate * Discard all recorded dependencies. We are returning to the default pm state. 6076*7c478bd9Sstevel@tonic-gate */ 6077*7c478bd9Sstevel@tonic-gate void 6078*7c478bd9Sstevel@tonic-gate pm_discard_dependencies(void) 6079*7c478bd9Sstevel@tonic-gate { 6080*7c478bd9Sstevel@tonic-gate pm_pdr_t *rp; 6081*7c478bd9Sstevel@tonic-gate int i; 6082*7c478bd9Sstevel@tonic-gate size_t length; 6083*7c478bd9Sstevel@tonic-gate 6084*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6085*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_DEP) 6086*7c478bd9Sstevel@tonic-gate prdeps("Before discard\n"); 6087*7c478bd9Sstevel@tonic-gate #endif 6088*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_discard_dep_walk, NULL); 6089*7c478bd9Sstevel@tonic-gate 6090*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6091*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_DEP) 6092*7c478bd9Sstevel@tonic-gate prdeps("After discard\n"); 6093*7c478bd9Sstevel@tonic-gate #endif 6094*7c478bd9Sstevel@tonic-gate while (pm_dep_head) { 6095*7c478bd9Sstevel@tonic-gate rp = pm_dep_head; 6096*7c478bd9Sstevel@tonic-gate if (!rp->pdr_isprop) { 6097*7c478bd9Sstevel@tonic-gate ASSERT(rp->pdr_satisfied == 0); 6098*7c478bd9Sstevel@tonic-gate ASSERT(pm_unresolved_deps); 6099*7c478bd9Sstevel@tonic-gate pm_unresolved_deps--; 6100*7c478bd9Sstevel@tonic-gate } else { 6101*7c478bd9Sstevel@tonic-gate ASSERT(pm_prop_deps); 6102*7c478bd9Sstevel@tonic-gate pm_prop_deps--; 6103*7c478bd9Sstevel@tonic-gate } 6104*7c478bd9Sstevel@tonic-gate pm_dep_head = rp->pdr_next; 6105*7c478bd9Sstevel@tonic-gate if (rp->pdr_kept_count) { 6106*7c478bd9Sstevel@tonic-gate for (i = 0; i < rp->pdr_kept_count; i++) { 6107*7c478bd9Sstevel@tonic-gate length = strlen(rp->pdr_kept_paths[i]) + 1; 6108*7c478bd9Sstevel@tonic-gate kmem_free(rp->pdr_kept_paths[i], length); 6109*7c478bd9Sstevel@tonic-gate } 6110*7c478bd9Sstevel@tonic-gate kmem_free(rp->pdr_kept_paths, 6111*7c478bd9Sstevel@tonic-gate rp->pdr_kept_count * sizeof (char **)); 6112*7c478bd9Sstevel@tonic-gate } 6113*7c478bd9Sstevel@tonic-gate kmem_free(rp, rp->pdr_size); 6114*7c478bd9Sstevel@tonic-gate } 6115*7c478bd9Sstevel@tonic-gate } 6116*7c478bd9Sstevel@tonic-gate 6117*7c478bd9Sstevel@tonic-gate 6118*7c478bd9Sstevel@tonic-gate static int 6119*7c478bd9Sstevel@tonic-gate pm_discard_dep_walk(dev_info_t *dip, void *arg) 6120*7c478bd9Sstevel@tonic-gate { 6121*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 6122*7c478bd9Sstevel@tonic-gate char *pathbuf; 6123*7c478bd9Sstevel@tonic-gate 6124*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) 6125*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6126*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 6127*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 6128*7c478bd9Sstevel@tonic-gate pm_free_keeper(pathbuf, 0); 6129*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 6130*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6131*7c478bd9Sstevel@tonic-gate } 6132*7c478bd9Sstevel@tonic-gate 6133*7c478bd9Sstevel@tonic-gate static int 6134*7c478bd9Sstevel@tonic-gate pm_kept_walk(dev_info_t *dip, void *arg) 6135*7c478bd9Sstevel@tonic-gate { 6136*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 6137*7c478bd9Sstevel@tonic-gate char *pathbuf; 6138*7c478bd9Sstevel@tonic-gate 6139*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 6140*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 6141*7c478bd9Sstevel@tonic-gate (void) pm_kept(pathbuf); 6142*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 6143*7c478bd9Sstevel@tonic-gate 6144*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6145*7c478bd9Sstevel@tonic-gate } 6146*7c478bd9Sstevel@tonic-gate 6147*7c478bd9Sstevel@tonic-gate static int 6148*7c478bd9Sstevel@tonic-gate pm_keeper_walk(dev_info_t *dip, void *arg) 6149*7c478bd9Sstevel@tonic-gate { 6150*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 6151*7c478bd9Sstevel@tonic-gate char *pathbuf; 6152*7c478bd9Sstevel@tonic-gate 6153*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 6154*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 6155*7c478bd9Sstevel@tonic-gate (void) pm_keeper(pathbuf); 6156*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 6157*7c478bd9Sstevel@tonic-gate 6158*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6159*7c478bd9Sstevel@tonic-gate } 6160*7c478bd9Sstevel@tonic-gate 6161*7c478bd9Sstevel@tonic-gate static char * 6162*7c478bd9Sstevel@tonic-gate pdw_type_decode(int type) 6163*7c478bd9Sstevel@tonic-gate { 6164*7c478bd9Sstevel@tonic-gate switch (type) { 6165*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_POWER_ON: 6166*7c478bd9Sstevel@tonic-gate return ("power on"); 6167*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_POWER_OFF: 6168*7c478bd9Sstevel@tonic-gate return ("power off"); 6169*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_DETACH: 6170*7c478bd9Sstevel@tonic-gate return ("detach"); 6171*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_REMOVE_DEP: 6172*7c478bd9Sstevel@tonic-gate return ("remove dep"); 6173*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_BRINGUP_SELF: 6174*7c478bd9Sstevel@tonic-gate return ("bringup self"); 6175*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER: 6176*7c478bd9Sstevel@tonic-gate return ("add dependent"); 6177*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER_PROP: 6178*7c478bd9Sstevel@tonic-gate return ("add dependent property"); 6179*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_KEPT: 6180*7c478bd9Sstevel@tonic-gate return ("kept"); 6181*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_KEEPER: 6182*7c478bd9Sstevel@tonic-gate return ("keeper"); 6183*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_ATTACH: 6184*7c478bd9Sstevel@tonic-gate return ("attach"); 6185*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_CHECK_KEPT: 6186*7c478bd9Sstevel@tonic-gate return ("check kept"); 6187*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_CPR_SUSPEND: 6188*7c478bd9Sstevel@tonic-gate return ("suspend"); 6189*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_CPR_RESUME: 6190*7c478bd9Sstevel@tonic-gate return ("resume"); 6191*7c478bd9Sstevel@tonic-gate default: 6192*7c478bd9Sstevel@tonic-gate return ("unknown"); 6193*7c478bd9Sstevel@tonic-gate } 6194*7c478bd9Sstevel@tonic-gate 6195*7c478bd9Sstevel@tonic-gate } 6196*7c478bd9Sstevel@tonic-gate 6197*7c478bd9Sstevel@tonic-gate static void 6198*7c478bd9Sstevel@tonic-gate pm_rele_dep(char *keeper) 6199*7c478bd9Sstevel@tonic-gate { 6200*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "rele_dep") 6201*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 6202*7c478bd9Sstevel@tonic-gate char *kept_path = NULL; 6203*7c478bd9Sstevel@tonic-gate dev_info_t *kept = NULL; 6204*7c478bd9Sstevel@tonic-gate int count = 0; 6205*7c478bd9Sstevel@tonic-gate 6206*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 6207*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0) 6208*7c478bd9Sstevel@tonic-gate continue; 6209*7c478bd9Sstevel@tonic-gate for (count = 0; count < dp->pdr_kept_count; count++) { 6210*7c478bd9Sstevel@tonic-gate kept_path = dp->pdr_kept_paths[count]; 6211*7c478bd9Sstevel@tonic-gate if (kept_path == NULL) 6212*7c478bd9Sstevel@tonic-gate continue; 6213*7c478bd9Sstevel@tonic-gate kept = pm_name_to_dip(kept_path, 1); 6214*7c478bd9Sstevel@tonic-gate if (kept) { 6215*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: release kept=%s@%s(%s#%d) " 6216*7c478bd9Sstevel@tonic-gate "of keeper=%s\n", pmf, PM_DEVICE(kept), 6217*7c478bd9Sstevel@tonic-gate keeper)) 6218*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(kept)->devi_pm_kidsupcnt > 0); 6219*7c478bd9Sstevel@tonic-gate pm_rele_power(kept); 6220*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 6221*7c478bd9Sstevel@tonic-gate } 6222*7c478bd9Sstevel@tonic-gate } 6223*7c478bd9Sstevel@tonic-gate } 6224*7c478bd9Sstevel@tonic-gate } 6225*7c478bd9Sstevel@tonic-gate 6226*7c478bd9Sstevel@tonic-gate /* 6227*7c478bd9Sstevel@tonic-gate * Called when we are just released from direct PM. Bring ourself up 6228*7c478bd9Sstevel@tonic-gate * if our keeper is up since dependency is not honored while a kept 6229*7c478bd9Sstevel@tonic-gate * device is under direct PM. 6230*7c478bd9Sstevel@tonic-gate */ 6231*7c478bd9Sstevel@tonic-gate static void 6232*7c478bd9Sstevel@tonic-gate pm_bring_self_up(char *keptpath) 6233*7c478bd9Sstevel@tonic-gate { 6234*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "bring_self_up") 6235*7c478bd9Sstevel@tonic-gate dev_info_t *kept; 6236*7c478bd9Sstevel@tonic-gate dev_info_t *keeper; 6237*7c478bd9Sstevel@tonic-gate pm_pdr_t *dp; 6238*7c478bd9Sstevel@tonic-gate int i, j; 6239*7c478bd9Sstevel@tonic-gate int up = 0, circ; 6240*7c478bd9Sstevel@tonic-gate 6241*7c478bd9Sstevel@tonic-gate kept = pm_name_to_dip(keptpath, 1); 6242*7c478bd9Sstevel@tonic-gate if (kept == NULL) 6243*7c478bd9Sstevel@tonic-gate return; 6244*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: kept=%s@%s(%s#%d)\n", pmf, PM_DEVICE(kept))) 6245*7c478bd9Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 6246*7c478bd9Sstevel@tonic-gate if (dp->pdr_kept_count == 0) 6247*7c478bd9Sstevel@tonic-gate continue; 6248*7c478bd9Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 6249*7c478bd9Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], keptpath) != 0) 6250*7c478bd9Sstevel@tonic-gate continue; 6251*7c478bd9Sstevel@tonic-gate keeper = pm_name_to_dip(dp->pdr_keeper, 1); 6252*7c478bd9Sstevel@tonic-gate if (keeper) { 6253*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d)\n", 6254*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(keeper))) 6255*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(keeper, &circ); 6256*7c478bd9Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(keeper); 6257*7c478bd9Sstevel@tonic-gate j++) { 6258*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(keeper, j)) { 6259*7c478bd9Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: comp=" 6260*7c478bd9Sstevel@tonic-gate "%d is up\n", pmf, j)) 6261*7c478bd9Sstevel@tonic-gate up++; 6262*7c478bd9Sstevel@tonic-gate } 6263*7c478bd9Sstevel@tonic-gate } 6264*7c478bd9Sstevel@tonic-gate if (up) { 6265*7c478bd9Sstevel@tonic-gate if (PM_SKBU(kept)) 6266*7c478bd9Sstevel@tonic-gate DEVI(kept)->devi_pm_flags &= 6267*7c478bd9Sstevel@tonic-gate ~PMC_SKIP_BRINGUP; 6268*7c478bd9Sstevel@tonic-gate bring_pmdep_up(kept, 1); 6269*7c478bd9Sstevel@tonic-gate } 6270*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(keeper, circ); 6271*7c478bd9Sstevel@tonic-gate ddi_release_devi(keeper); 6272*7c478bd9Sstevel@tonic-gate } 6273*7c478bd9Sstevel@tonic-gate } 6274*7c478bd9Sstevel@tonic-gate } 6275*7c478bd9Sstevel@tonic-gate ddi_release_devi(kept); 6276*7c478bd9Sstevel@tonic-gate } 6277*7c478bd9Sstevel@tonic-gate 6278*7c478bd9Sstevel@tonic-gate static void 6279*7c478bd9Sstevel@tonic-gate pm_process_dep_request(pm_dep_wk_t *work) 6280*7c478bd9Sstevel@tonic-gate { 6281*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "dep_req") 6282*7c478bd9Sstevel@tonic-gate int ret; 6283*7c478bd9Sstevel@tonic-gate 6284*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: work=%s\n", pmf, 6285*7c478bd9Sstevel@tonic-gate pdw_type_decode(work->pdw_type))) 6286*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: keeper=%s, kept=%s\n", pmf, 6287*7c478bd9Sstevel@tonic-gate (work->pdw_keeper ? work->pdw_keeper : "NULL"), 6288*7c478bd9Sstevel@tonic-gate (work->pdw_kept ? work->pdw_kept : "NULL"))) 6289*7c478bd9Sstevel@tonic-gate 6290*7c478bd9Sstevel@tonic-gate switch (work->pdw_type) { 6291*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_POWER_ON: 6292*7c478bd9Sstevel@tonic-gate /* Bring up the kept devices and put a hold on them */ 6293*7c478bd9Sstevel@tonic-gate bring_wekeeps_up(work->pdw_keeper); 6294*7c478bd9Sstevel@tonic-gate break; 6295*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_POWER_OFF: 6296*7c478bd9Sstevel@tonic-gate /* Release the kept devices */ 6297*7c478bd9Sstevel@tonic-gate pm_rele_dep(work->pdw_keeper); 6298*7c478bd9Sstevel@tonic-gate break; 6299*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_DETACH: 6300*7c478bd9Sstevel@tonic-gate pm_free_keeps(work->pdw_keeper, work->pdw_pwr); 6301*7c478bd9Sstevel@tonic-gate break; 6302*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_REMOVE_DEP: 6303*7c478bd9Sstevel@tonic-gate pm_discard_dependencies(); 6304*7c478bd9Sstevel@tonic-gate break; 6305*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_BRINGUP_SELF: 6306*7c478bd9Sstevel@tonic-gate /* 6307*7c478bd9Sstevel@tonic-gate * We deferred satisfying our dependency till now, so satisfy 6308*7c478bd9Sstevel@tonic-gate * it again and bring ourselves up. 6309*7c478bd9Sstevel@tonic-gate */ 6310*7c478bd9Sstevel@tonic-gate pm_bring_self_up(work->pdw_kept); 6311*7c478bd9Sstevel@tonic-gate break; 6312*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER: 6313*7c478bd9Sstevel@tonic-gate (void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 0); 6314*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL); 6315*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL); 6316*7c478bd9Sstevel@tonic-gate break; 6317*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER_PROP: 6318*7c478bd9Sstevel@tonic-gate (void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 1); 6319*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL); 6320*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL); 6321*7c478bd9Sstevel@tonic-gate break; 6322*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_KEPT: 6323*7c478bd9Sstevel@tonic-gate ret = pm_kept(work->pdw_kept); 6324*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_KEPT: pm_kept returns %d\n", pmf, 6325*7c478bd9Sstevel@tonic-gate ret)) 6326*7c478bd9Sstevel@tonic-gate break; 6327*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_KEEPER: 6328*7c478bd9Sstevel@tonic-gate ret = pm_keeper(work->pdw_keeper); 6329*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_KEEPER: pm_keeper returns %d\n", 6330*7c478bd9Sstevel@tonic-gate pmf, ret)) 6331*7c478bd9Sstevel@tonic-gate break; 6332*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_ATTACH: 6333*7c478bd9Sstevel@tonic-gate ret = pm_keeper(work->pdw_keeper); 6334*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_keeper returns %d\n", 6335*7c478bd9Sstevel@tonic-gate pmf, ret)) 6336*7c478bd9Sstevel@tonic-gate ret = pm_kept(work->pdw_kept); 6337*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_kept returns %d\n", 6338*7c478bd9Sstevel@tonic-gate pmf, ret)) 6339*7c478bd9Sstevel@tonic-gate break; 6340*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_CHECK_KEPT: 6341*7c478bd9Sstevel@tonic-gate ret = pm_is_kept(work->pdw_kept); 6342*7c478bd9Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_CHECK_KEPT: kept=%s, ret=%d\n", 6343*7c478bd9Sstevel@tonic-gate pmf, work->pdw_kept, ret)) 6344*7c478bd9Sstevel@tonic-gate break; 6345*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_CPR_SUSPEND: 6346*7c478bd9Sstevel@tonic-gate pm_discard_dependencies(); 6347*7c478bd9Sstevel@tonic-gate break; 6348*7c478bd9Sstevel@tonic-gate case PM_DEP_WK_CPR_RESUME: 6349*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL); 6350*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL); 6351*7c478bd9Sstevel@tonic-gate break; 6352*7c478bd9Sstevel@tonic-gate default: 6353*7c478bd9Sstevel@tonic-gate ASSERT(0); 6354*7c478bd9Sstevel@tonic-gate break; 6355*7c478bd9Sstevel@tonic-gate } 6356*7c478bd9Sstevel@tonic-gate /* 6357*7c478bd9Sstevel@tonic-gate * Free the work structure if the requester is not waiting 6358*7c478bd9Sstevel@tonic-gate * Otherwise it is the requester's responsiblity to free it. 6359*7c478bd9Sstevel@tonic-gate */ 6360*7c478bd9Sstevel@tonic-gate if (!work->pdw_wait) { 6361*7c478bd9Sstevel@tonic-gate if (work->pdw_keeper) 6362*7c478bd9Sstevel@tonic-gate kmem_free(work->pdw_keeper, 6363*7c478bd9Sstevel@tonic-gate strlen(work->pdw_keeper) + 1); 6364*7c478bd9Sstevel@tonic-gate if (work->pdw_kept) 6365*7c478bd9Sstevel@tonic-gate kmem_free(work->pdw_kept, strlen(work->pdw_kept) + 1); 6366*7c478bd9Sstevel@tonic-gate kmem_free(work, sizeof (pm_dep_wk_t)); 6367*7c478bd9Sstevel@tonic-gate } else { 6368*7c478bd9Sstevel@tonic-gate /* 6369*7c478bd9Sstevel@tonic-gate * Notify requester if it is waiting for it. 6370*7c478bd9Sstevel@tonic-gate */ 6371*7c478bd9Sstevel@tonic-gate work->pdw_ret = ret; 6372*7c478bd9Sstevel@tonic-gate work->pdw_done = 1; 6373*7c478bd9Sstevel@tonic-gate cv_signal(&work->pdw_cv); 6374*7c478bd9Sstevel@tonic-gate } 6375*7c478bd9Sstevel@tonic-gate } 6376*7c478bd9Sstevel@tonic-gate 6377*7c478bd9Sstevel@tonic-gate /* 6378*7c478bd9Sstevel@tonic-gate * Process PM dependency requests. 6379*7c478bd9Sstevel@tonic-gate */ 6380*7c478bd9Sstevel@tonic-gate static void 6381*7c478bd9Sstevel@tonic-gate pm_dep_thread(void) 6382*7c478bd9Sstevel@tonic-gate { 6383*7c478bd9Sstevel@tonic-gate pm_dep_wk_t *work; 6384*7c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 6385*7c478bd9Sstevel@tonic-gate 6386*7c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &pm_dep_thread_lock, callb_generic_cpr, 6387*7c478bd9Sstevel@tonic-gate "pm_dep_thread"); 6388*7c478bd9Sstevel@tonic-gate for (;;) { 6389*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_dep_thread_lock); 6390*7c478bd9Sstevel@tonic-gate if (pm_dep_thread_workq == NULL) { 6391*7c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 6392*7c478bd9Sstevel@tonic-gate cv_wait(&pm_dep_thread_cv, &pm_dep_thread_lock); 6393*7c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, &pm_dep_thread_lock); 6394*7c478bd9Sstevel@tonic-gate } 6395*7c478bd9Sstevel@tonic-gate work = pm_dep_thread_workq; 6396*7c478bd9Sstevel@tonic-gate pm_dep_thread_workq = work->pdw_next; 6397*7c478bd9Sstevel@tonic-gate if (pm_dep_thread_tail == work) 6398*7c478bd9Sstevel@tonic-gate pm_dep_thread_tail = work->pdw_next; 6399*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_dep_thread_lock); 6400*7c478bd9Sstevel@tonic-gate pm_process_dep_request(work); 6401*7c478bd9Sstevel@tonic-gate 6402*7c478bd9Sstevel@tonic-gate } 6403*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 6404*7c478bd9Sstevel@tonic-gate } 6405*7c478bd9Sstevel@tonic-gate 6406*7c478bd9Sstevel@tonic-gate /* 6407*7c478bd9Sstevel@tonic-gate * Set the power level of the indicated device to unknown (if it is not a 6408*7c478bd9Sstevel@tonic-gate * backwards compatible device), as it has just been resumed, and it won't 6409*7c478bd9Sstevel@tonic-gate * know if the power was removed or not. Adjust parent's kidsupcnt if necessary. 6410*7c478bd9Sstevel@tonic-gate */ 6411*7c478bd9Sstevel@tonic-gate void 6412*7c478bd9Sstevel@tonic-gate pm_forget_power_level(dev_info_t *dip) 6413*7c478bd9Sstevel@tonic-gate { 6414*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 6415*7c478bd9Sstevel@tonic-gate int i, count = 0; 6416*7c478bd9Sstevel@tonic-gate 6417*7c478bd9Sstevel@tonic-gate if (!PM_ISBC(dip)) { 6418*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 6419*7c478bd9Sstevel@tonic-gate count += (PM_CURPOWER(dip, i) == 0); 6420*7c478bd9Sstevel@tonic-gate 6421*7c478bd9Sstevel@tonic-gate if (count && pdip && !PM_WANTS_NOTIFICATION(pdip)) 6422*7c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(pdip, count); 6423*7c478bd9Sstevel@tonic-gate 6424*7c478bd9Sstevel@tonic-gate /* 6425*7c478bd9Sstevel@tonic-gate * Count this as a power cycle if we care 6426*7c478bd9Sstevel@tonic-gate */ 6427*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd && 6428*7c478bd9Sstevel@tonic-gate PM_CP(dip, 0)->pmc_cur_pwr == 0) 6429*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0; 6430*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 6431*7c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN); 6432*7c478bd9Sstevel@tonic-gate } 6433*7c478bd9Sstevel@tonic-gate } 6434*7c478bd9Sstevel@tonic-gate 6435*7c478bd9Sstevel@tonic-gate /* 6436*7c478bd9Sstevel@tonic-gate * This function advises the caller whether it should make a power-off 6437*7c478bd9Sstevel@tonic-gate * transition at this time or not. If the transition is not advised 6438*7c478bd9Sstevel@tonic-gate * at this time, the time that the next power-off transition can 6439*7c478bd9Sstevel@tonic-gate * be made from now is returned through "intervalp" pointer. 6440*7c478bd9Sstevel@tonic-gate * This function returns: 6441*7c478bd9Sstevel@tonic-gate * 6442*7c478bd9Sstevel@tonic-gate * 1 power-off advised 6443*7c478bd9Sstevel@tonic-gate * 0 power-off not advised, intervalp will point to seconds from 6444*7c478bd9Sstevel@tonic-gate * now that a power-off is advised. If it is passed the number 6445*7c478bd9Sstevel@tonic-gate * of years that policy specifies the device should last, 6446*7c478bd9Sstevel@tonic-gate * a large number is returned as the time interval. 6447*7c478bd9Sstevel@tonic-gate * -1 error 6448*7c478bd9Sstevel@tonic-gate */ 6449*7c478bd9Sstevel@tonic-gate int 6450*7c478bd9Sstevel@tonic-gate pm_trans_check(struct pm_trans_data *datap, time_t *intervalp) 6451*7c478bd9Sstevel@tonic-gate { 6452*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_trans_check") 6453*7c478bd9Sstevel@tonic-gate char dbuf[DC_SCSI_MFR_LEN]; 6454*7c478bd9Sstevel@tonic-gate struct pm_scsi_cycles *scp; 6455*7c478bd9Sstevel@tonic-gate int service_years, service_weeks, full_years; 6456*7c478bd9Sstevel@tonic-gate time_t now, service_seconds, tdiff; 6457*7c478bd9Sstevel@tonic-gate time_t within_year, when_allowed; 6458*7c478bd9Sstevel@tonic-gate char *ptr; 6459*7c478bd9Sstevel@tonic-gate int lower_bound_cycles, upper_bound_cycles, cycles_allowed; 6460*7c478bd9Sstevel@tonic-gate int cycles_diff, cycles_over; 6461*7c478bd9Sstevel@tonic-gate 6462*7c478bd9Sstevel@tonic-gate if (datap == NULL) { 6463*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: NULL data pointer!\n", pmf)) 6464*7c478bd9Sstevel@tonic-gate return (-1); 6465*7c478bd9Sstevel@tonic-gate } 6466*7c478bd9Sstevel@tonic-gate 6467*7c478bd9Sstevel@tonic-gate if (datap->format == DC_SCSI_FORMAT) { 6468*7c478bd9Sstevel@tonic-gate /* 6469*7c478bd9Sstevel@tonic-gate * Power cycles of the scsi drives are distributed 6470*7c478bd9Sstevel@tonic-gate * over 5 years with the following percentage ratio: 6471*7c478bd9Sstevel@tonic-gate * 6472*7c478bd9Sstevel@tonic-gate * 30%, 25%, 20%, 15%, and 10% 6473*7c478bd9Sstevel@tonic-gate * 6474*7c478bd9Sstevel@tonic-gate * The power cycle quota for each year is distributed 6475*7c478bd9Sstevel@tonic-gate * linearly through out the year. The equation for 6476*7c478bd9Sstevel@tonic-gate * determining the expected cycles is: 6477*7c478bd9Sstevel@tonic-gate * 6478*7c478bd9Sstevel@tonic-gate * e = a * (n / y) 6479*7c478bd9Sstevel@tonic-gate * 6480*7c478bd9Sstevel@tonic-gate * e = expected cycles 6481*7c478bd9Sstevel@tonic-gate * a = allocated cycles for this year 6482*7c478bd9Sstevel@tonic-gate * n = number of seconds since beginning of this year 6483*7c478bd9Sstevel@tonic-gate * y = number of seconds in a year 6484*7c478bd9Sstevel@tonic-gate * 6485*7c478bd9Sstevel@tonic-gate * Note that beginning of the year starts the day that 6486*7c478bd9Sstevel@tonic-gate * the drive has been put on service. 6487*7c478bd9Sstevel@tonic-gate * 6488*7c478bd9Sstevel@tonic-gate * If the drive has passed its expected cycles, we 6489*7c478bd9Sstevel@tonic-gate * can determine when it can start to power cycle 6490*7c478bd9Sstevel@tonic-gate * again to keep it on track to meet the 5-year 6491*7c478bd9Sstevel@tonic-gate * life expectancy. The equation for determining 6492*7c478bd9Sstevel@tonic-gate * when to power cycle is: 6493*7c478bd9Sstevel@tonic-gate * 6494*7c478bd9Sstevel@tonic-gate * w = y * (c / a) 6495*7c478bd9Sstevel@tonic-gate * 6496*7c478bd9Sstevel@tonic-gate * w = when it can power cycle again 6497*7c478bd9Sstevel@tonic-gate * y = number of seconds in a year 6498*7c478bd9Sstevel@tonic-gate * c = current number of cycles 6499*7c478bd9Sstevel@tonic-gate * a = allocated cycles for the year 6500*7c478bd9Sstevel@tonic-gate * 6501*7c478bd9Sstevel@tonic-gate */ 6502*7c478bd9Sstevel@tonic-gate char pcnt[DC_SCSI_NPY] = { 30, 55, 75, 90, 100 }; 6503*7c478bd9Sstevel@tonic-gate 6504*7c478bd9Sstevel@tonic-gate scp = &datap->un.scsi_cycles; 6505*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: format=%d, lifemax=%d, ncycles=%d, " 6506*7c478bd9Sstevel@tonic-gate "svc_date=%s, svc_flag=%d\n", pmf, datap->format, 6507*7c478bd9Sstevel@tonic-gate scp->lifemax, scp->ncycles, scp->svc_date, scp->flag)) 6508*7c478bd9Sstevel@tonic-gate if (scp->ncycles < 0 || scp->flag != 0) { 6509*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: ncycles < 0 || flag != 0\n", pmf)) 6510*7c478bd9Sstevel@tonic-gate return (-1); 6511*7c478bd9Sstevel@tonic-gate } 6512*7c478bd9Sstevel@tonic-gate 6513*7c478bd9Sstevel@tonic-gate if (scp->ncycles > scp->lifemax) { 6514*7c478bd9Sstevel@tonic-gate *intervalp = (LONG_MAX / hz); 6515*7c478bd9Sstevel@tonic-gate return (0); 6516*7c478bd9Sstevel@tonic-gate } 6517*7c478bd9Sstevel@tonic-gate 6518*7c478bd9Sstevel@tonic-gate /* 6519*7c478bd9Sstevel@tonic-gate * convert service date to time_t 6520*7c478bd9Sstevel@tonic-gate */ 6521*7c478bd9Sstevel@tonic-gate bcopy(scp->svc_date, dbuf, DC_SCSI_YEAR_LEN); 6522*7c478bd9Sstevel@tonic-gate dbuf[DC_SCSI_YEAR_LEN] = '\0'; 6523*7c478bd9Sstevel@tonic-gate ptr = dbuf; 6524*7c478bd9Sstevel@tonic-gate service_years = stoi(&ptr) - EPOCH_YEAR; 6525*7c478bd9Sstevel@tonic-gate bcopy(&scp->svc_date[DC_SCSI_YEAR_LEN], dbuf, 6526*7c478bd9Sstevel@tonic-gate DC_SCSI_WEEK_LEN); 6527*7c478bd9Sstevel@tonic-gate dbuf[DC_SCSI_WEEK_LEN] = '\0'; 6528*7c478bd9Sstevel@tonic-gate 6529*7c478bd9Sstevel@tonic-gate /* 6530*7c478bd9Sstevel@tonic-gate * scsi standard does not specify WW data, 6531*7c478bd9Sstevel@tonic-gate * could be (00-51) or (01-52) 6532*7c478bd9Sstevel@tonic-gate */ 6533*7c478bd9Sstevel@tonic-gate ptr = dbuf; 6534*7c478bd9Sstevel@tonic-gate service_weeks = stoi(&ptr); 6535*7c478bd9Sstevel@tonic-gate if (service_years < 0 || 6536*7c478bd9Sstevel@tonic-gate service_weeks < 0 || service_weeks > 52) { 6537*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: service year %d and week %d\n", 6538*7c478bd9Sstevel@tonic-gate pmf, service_years, service_weeks)) 6539*7c478bd9Sstevel@tonic-gate return (-1); 6540*7c478bd9Sstevel@tonic-gate } 6541*7c478bd9Sstevel@tonic-gate 6542*7c478bd9Sstevel@tonic-gate /* 6543*7c478bd9Sstevel@tonic-gate * calculate service date in seconds-since-epoch, 6544*7c478bd9Sstevel@tonic-gate * adding one day for each leap-year. 6545*7c478bd9Sstevel@tonic-gate * 6546*7c478bd9Sstevel@tonic-gate * (years-since-epoch + 2) fixes integer truncation, 6547*7c478bd9Sstevel@tonic-gate * example: (8) leap-years during [1972, 2000] 6548*7c478bd9Sstevel@tonic-gate * (2000 - 1970) = 30; and (30 + 2) / 4 = 8; 6549*7c478bd9Sstevel@tonic-gate */ 6550*7c478bd9Sstevel@tonic-gate service_seconds = (service_years * DC_SPY) + 6551*7c478bd9Sstevel@tonic-gate (service_weeks * DC_SPW) + 6552*7c478bd9Sstevel@tonic-gate (((service_years + 2) / 4) * DC_SPD); 6553*7c478bd9Sstevel@tonic-gate 6554*7c478bd9Sstevel@tonic-gate now = gethrestime_sec(); 6555*7c478bd9Sstevel@tonic-gate /* 6556*7c478bd9Sstevel@tonic-gate * since the granularity of 'svc_date' is day not second, 6557*7c478bd9Sstevel@tonic-gate * 'now' should be rounded up to full day. 6558*7c478bd9Sstevel@tonic-gate */ 6559*7c478bd9Sstevel@tonic-gate now = ((now + DC_SPD -1) / DC_SPD) * DC_SPD; 6560*7c478bd9Sstevel@tonic-gate if (service_seconds > now) { 6561*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: service date (%ld) later " 6562*7c478bd9Sstevel@tonic-gate "than now (%ld)!\n", pmf, service_seconds, now)) 6563*7c478bd9Sstevel@tonic-gate return (-1); 6564*7c478bd9Sstevel@tonic-gate } 6565*7c478bd9Sstevel@tonic-gate 6566*7c478bd9Sstevel@tonic-gate tdiff = now - service_seconds; 6567*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: age is %ld sec\n", pmf, tdiff)) 6568*7c478bd9Sstevel@tonic-gate 6569*7c478bd9Sstevel@tonic-gate /* 6570*7c478bd9Sstevel@tonic-gate * NOTE - Leap years are not considered in the calculations 6571*7c478bd9Sstevel@tonic-gate * below. 6572*7c478bd9Sstevel@tonic-gate */ 6573*7c478bd9Sstevel@tonic-gate full_years = (tdiff / DC_SPY); 6574*7c478bd9Sstevel@tonic-gate if ((full_years >= DC_SCSI_NPY) && 6575*7c478bd9Sstevel@tonic-gate (scp->ncycles <= scp->lifemax)) 6576*7c478bd9Sstevel@tonic-gate return (1); 6577*7c478bd9Sstevel@tonic-gate 6578*7c478bd9Sstevel@tonic-gate /* 6579*7c478bd9Sstevel@tonic-gate * Determine what is the normal cycle usage for the 6580*7c478bd9Sstevel@tonic-gate * device at the beginning and the end of this year. 6581*7c478bd9Sstevel@tonic-gate */ 6582*7c478bd9Sstevel@tonic-gate lower_bound_cycles = (!full_years) ? 0 : 6583*7c478bd9Sstevel@tonic-gate ((scp->lifemax * pcnt[full_years - 1]) / 100); 6584*7c478bd9Sstevel@tonic-gate upper_bound_cycles = (scp->lifemax * pcnt[full_years]) / 100; 6585*7c478bd9Sstevel@tonic-gate 6586*7c478bd9Sstevel@tonic-gate if (scp->ncycles <= lower_bound_cycles) 6587*7c478bd9Sstevel@tonic-gate return (1); 6588*7c478bd9Sstevel@tonic-gate 6589*7c478bd9Sstevel@tonic-gate /* 6590*7c478bd9Sstevel@tonic-gate * The linear slope that determines how many cycles 6591*7c478bd9Sstevel@tonic-gate * are allowed this year is number of seconds 6592*7c478bd9Sstevel@tonic-gate * passed this year over total number of seconds in a year. 6593*7c478bd9Sstevel@tonic-gate */ 6594*7c478bd9Sstevel@tonic-gate cycles_diff = (upper_bound_cycles - lower_bound_cycles); 6595*7c478bd9Sstevel@tonic-gate within_year = (tdiff % DC_SPY); 6596*7c478bd9Sstevel@tonic-gate cycles_allowed = lower_bound_cycles + 6597*7c478bd9Sstevel@tonic-gate (((uint64_t)cycles_diff * (uint64_t)within_year) / DC_SPY); 6598*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: lived %d yrs and %ld secs\n", pmf, 6599*7c478bd9Sstevel@tonic-gate full_years, within_year)) 6600*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: # of cycles allowed %d\n", pmf, 6601*7c478bd9Sstevel@tonic-gate cycles_allowed)) 6602*7c478bd9Sstevel@tonic-gate 6603*7c478bd9Sstevel@tonic-gate if (scp->ncycles <= cycles_allowed) 6604*7c478bd9Sstevel@tonic-gate return (1); 6605*7c478bd9Sstevel@tonic-gate 6606*7c478bd9Sstevel@tonic-gate /* 6607*7c478bd9Sstevel@tonic-gate * The transition is not advised now but we can 6608*7c478bd9Sstevel@tonic-gate * determine when the next transition can be made. 6609*7c478bd9Sstevel@tonic-gate * 6610*7c478bd9Sstevel@tonic-gate * Depending on how many cycles the device has been 6611*7c478bd9Sstevel@tonic-gate * over-used, we may need to skip years with 6612*7c478bd9Sstevel@tonic-gate * different percentage quota in order to determine 6613*7c478bd9Sstevel@tonic-gate * when the next transition can be made. 6614*7c478bd9Sstevel@tonic-gate */ 6615*7c478bd9Sstevel@tonic-gate cycles_over = (scp->ncycles - lower_bound_cycles); 6616*7c478bd9Sstevel@tonic-gate while (cycles_over > cycles_diff) { 6617*7c478bd9Sstevel@tonic-gate full_years++; 6618*7c478bd9Sstevel@tonic-gate if (full_years >= DC_SCSI_NPY) { 6619*7c478bd9Sstevel@tonic-gate *intervalp = (LONG_MAX / hz); 6620*7c478bd9Sstevel@tonic-gate return (0); 6621*7c478bd9Sstevel@tonic-gate } 6622*7c478bd9Sstevel@tonic-gate cycles_over -= cycles_diff; 6623*7c478bd9Sstevel@tonic-gate lower_bound_cycles = upper_bound_cycles; 6624*7c478bd9Sstevel@tonic-gate upper_bound_cycles = 6625*7c478bd9Sstevel@tonic-gate (scp->lifemax * pcnt[full_years]) / 100; 6626*7c478bd9Sstevel@tonic-gate cycles_diff = (upper_bound_cycles - lower_bound_cycles); 6627*7c478bd9Sstevel@tonic-gate } 6628*7c478bd9Sstevel@tonic-gate 6629*7c478bd9Sstevel@tonic-gate /* 6630*7c478bd9Sstevel@tonic-gate * The linear slope that determines when the next transition 6631*7c478bd9Sstevel@tonic-gate * can be made is the relative position of used cycles within a 6632*7c478bd9Sstevel@tonic-gate * year over total number of cycles within that year. 6633*7c478bd9Sstevel@tonic-gate */ 6634*7c478bd9Sstevel@tonic-gate when_allowed = service_seconds + (full_years * DC_SPY) + 6635*7c478bd9Sstevel@tonic-gate (((uint64_t)DC_SPY * (uint64_t)cycles_over) / cycles_diff); 6636*7c478bd9Sstevel@tonic-gate *intervalp = (when_allowed - now); 6637*7c478bd9Sstevel@tonic-gate if (*intervalp > (LONG_MAX / hz)) 6638*7c478bd9Sstevel@tonic-gate *intervalp = (LONG_MAX / hz); 6639*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: no cycle is allowed in %ld secs\n", pmf, 6640*7c478bd9Sstevel@tonic-gate *intervalp)) 6641*7c478bd9Sstevel@tonic-gate return (0); 6642*7c478bd9Sstevel@tonic-gate } 6643*7c478bd9Sstevel@tonic-gate 6644*7c478bd9Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: unknown format!\n", pmf)) 6645*7c478bd9Sstevel@tonic-gate return (-1); 6646*7c478bd9Sstevel@tonic-gate } 6647*7c478bd9Sstevel@tonic-gate 6648*7c478bd9Sstevel@tonic-gate /* 6649*7c478bd9Sstevel@tonic-gate * Nexus drivers call into pm framework to indicate which child driver is about 6650*7c478bd9Sstevel@tonic-gate * to be installed. In some platforms, ppm may need to configure the hardware 6651*7c478bd9Sstevel@tonic-gate * for successful installation of a driver. 6652*7c478bd9Sstevel@tonic-gate */ 6653*7c478bd9Sstevel@tonic-gate int 6654*7c478bd9Sstevel@tonic-gate pm_init_child(dev_info_t *dip) 6655*7c478bd9Sstevel@tonic-gate { 6656*7c478bd9Sstevel@tonic-gate power_req_t power_req; 6657*7c478bd9Sstevel@tonic-gate 6658*7c478bd9Sstevel@tonic-gate ASSERT(ddi_binding_name(dip)); 6659*7c478bd9Sstevel@tonic-gate ASSERT(ddi_get_name_addr(dip)); 6660*7c478bd9Sstevel@tonic-gate pm_ppm_claim(dip); 6661*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */ 6662*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_INIT_CHILD; 6663*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6664*7c478bd9Sstevel@tonic-gate ASSERT(PPM(dip) != NULL); 6665*7c478bd9Sstevel@tonic-gate return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, 6666*7c478bd9Sstevel@tonic-gate NULL)); 6667*7c478bd9Sstevel@tonic-gate } else { 6668*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6669*7c478bd9Sstevel@tonic-gate /* pass it to the default handler so we can debug things */ 6670*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_INIT_CHILD; 6671*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6672*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 6673*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, NULL); 6674*7c478bd9Sstevel@tonic-gate #endif 6675*7c478bd9Sstevel@tonic-gate } 6676*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6677*7c478bd9Sstevel@tonic-gate } 6678*7c478bd9Sstevel@tonic-gate 6679*7c478bd9Sstevel@tonic-gate /* 6680*7c478bd9Sstevel@tonic-gate * Bring parent of a node that is about to be probed up to full power, and 6681*7c478bd9Sstevel@tonic-gate * arrange for it to stay up until pm_post_probe() or pm_post_attach() decide 6682*7c478bd9Sstevel@tonic-gate * it is time to let it go down again 6683*7c478bd9Sstevel@tonic-gate */ 6684*7c478bd9Sstevel@tonic-gate void 6685*7c478bd9Sstevel@tonic-gate pm_pre_probe(dev_info_t *dip, pm_ppm_cookie_t *cp) 6686*7c478bd9Sstevel@tonic-gate { 6687*7c478bd9Sstevel@tonic-gate int result; 6688*7c478bd9Sstevel@tonic-gate power_req_t power_req; 6689*7c478bd9Sstevel@tonic-gate 6690*7c478bd9Sstevel@tonic-gate bzero(cp, sizeof (*cp)); 6691*7c478bd9Sstevel@tonic-gate cp->ppc_dip = dip; 6692*7c478bd9Sstevel@tonic-gate 6693*7c478bd9Sstevel@tonic-gate pm_ppm_claim(dip); 6694*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */ 6695*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_PROBE; 6696*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6697*7c478bd9Sstevel@tonic-gate ASSERT(PPM(dip) != NULL); 6698*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, 6699*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6700*7c478bd9Sstevel@tonic-gate cp->ppc_ppm = PPM(dip); 6701*7c478bd9Sstevel@tonic-gate } else { 6702*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6703*7c478bd9Sstevel@tonic-gate /* pass it to the default handler so we can debug things */ 6704*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_PROBE; 6705*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6706*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 6707*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6708*7c478bd9Sstevel@tonic-gate #endif 6709*7c478bd9Sstevel@tonic-gate cp->ppc_ppm = NULL; 6710*7c478bd9Sstevel@tonic-gate } 6711*7c478bd9Sstevel@tonic-gate } 6712*7c478bd9Sstevel@tonic-gate 6713*7c478bd9Sstevel@tonic-gate int 6714*7c478bd9Sstevel@tonic-gate pm_pre_config(dev_info_t *dip, char *devnm) 6715*7c478bd9Sstevel@tonic-gate { 6716*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pre_config") 6717*7c478bd9Sstevel@tonic-gate int ret; 6718*7c478bd9Sstevel@tonic-gate 6719*7c478bd9Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6720*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6721*7c478bd9Sstevel@tonic-gate ret = mdi_power(dip, MDI_PM_PRE_CONFIG, NULL, devnm, 0); 6722*7c478bd9Sstevel@tonic-gate return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 6723*7c478bd9Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6724*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6725*7c478bd9Sstevel@tonic-gate 6726*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6727*7c478bd9Sstevel@tonic-gate pm_hold_power(dip); 6728*7c478bd9Sstevel@tonic-gate ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK); 6729*7c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) 6730*7c478bd9Sstevel@tonic-gate pm_rele_power(dip); 6731*7c478bd9Sstevel@tonic-gate return (ret); 6732*7c478bd9Sstevel@tonic-gate } 6733*7c478bd9Sstevel@tonic-gate 6734*7c478bd9Sstevel@tonic-gate /* 6735*7c478bd9Sstevel@tonic-gate * This routine is called by devfs during its walk to unconfigue a node. 6736*7c478bd9Sstevel@tonic-gate * If the call is due to auto mod_unloads and the dip is not at its 6737*7c478bd9Sstevel@tonic-gate * full power, we return DDI_FAILURE to terminate the walk, otherwise 6738*7c478bd9Sstevel@tonic-gate * return DDI_SUCCESS. 6739*7c478bd9Sstevel@tonic-gate */ 6740*7c478bd9Sstevel@tonic-gate int 6741*7c478bd9Sstevel@tonic-gate pm_pre_unconfig(dev_info_t *dip, int flags, int *held, char *devnm) 6742*7c478bd9Sstevel@tonic-gate { 6743*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pre_unconfig") 6744*7c478bd9Sstevel@tonic-gate int ret; 6745*7c478bd9Sstevel@tonic-gate 6746*7c478bd9Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6747*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, 6748*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), flags)) 6749*7c478bd9Sstevel@tonic-gate ret = mdi_power(dip, MDI_PM_PRE_UNCONFIG, held, devnm, flags); 6750*7c478bd9Sstevel@tonic-gate return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 6751*7c478bd9Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6752*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6753*7c478bd9Sstevel@tonic-gate 6754*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, PM_DEVICE(dip), 6755*7c478bd9Sstevel@tonic-gate flags)) 6756*7c478bd9Sstevel@tonic-gate *held = 0; 6757*7c478bd9Sstevel@tonic-gate 6758*7c478bd9Sstevel@tonic-gate /* 6759*7c478bd9Sstevel@tonic-gate * If the dip is a leaf node, don't power it up. 6760*7c478bd9Sstevel@tonic-gate */ 6761*7c478bd9Sstevel@tonic-gate if (!ddi_get_child(dip)) 6762*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6763*7c478bd9Sstevel@tonic-gate 6764*7c478bd9Sstevel@tonic-gate /* 6765*7c478bd9Sstevel@tonic-gate * Do not power up the node if it is called due to auto-modunload. 6766*7c478bd9Sstevel@tonic-gate */ 6767*7c478bd9Sstevel@tonic-gate if ((flags & NDI_AUTODETACH) && !pm_all_at_normal(dip)) 6768*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 6769*7c478bd9Sstevel@tonic-gate 6770*7c478bd9Sstevel@tonic-gate pm_hold_power(dip); 6771*7c478bd9Sstevel@tonic-gate *held = 1; 6772*7c478bd9Sstevel@tonic-gate ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK); 6773*7c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 6774*7c478bd9Sstevel@tonic-gate pm_rele_power(dip); 6775*7c478bd9Sstevel@tonic-gate *held = 0; 6776*7c478bd9Sstevel@tonic-gate } 6777*7c478bd9Sstevel@tonic-gate return (ret); 6778*7c478bd9Sstevel@tonic-gate } 6779*7c478bd9Sstevel@tonic-gate 6780*7c478bd9Sstevel@tonic-gate /* 6781*7c478bd9Sstevel@tonic-gate * Notify ppm of attach action. Parent is already held at full power by 6782*7c478bd9Sstevel@tonic-gate * probe action. 6783*7c478bd9Sstevel@tonic-gate */ 6784*7c478bd9Sstevel@tonic-gate void 6785*7c478bd9Sstevel@tonic-gate pm_pre_attach(dev_info_t *dip, pm_ppm_cookie_t *cp, ddi_attach_cmd_t cmd) 6786*7c478bd9Sstevel@tonic-gate { 6787*7c478bd9Sstevel@tonic-gate static char *me = "pm_pre_attach"; 6788*7c478bd9Sstevel@tonic-gate power_req_t power_req; 6789*7c478bd9Sstevel@tonic-gate int result; 6790*7c478bd9Sstevel@tonic-gate 6791*7c478bd9Sstevel@tonic-gate /* 6792*7c478bd9Sstevel@tonic-gate * Initialize and fill in the PPM cookie 6793*7c478bd9Sstevel@tonic-gate */ 6794*7c478bd9Sstevel@tonic-gate bzero(cp, sizeof (*cp)); 6795*7c478bd9Sstevel@tonic-gate cp->ppc_cmd = (int)cmd; 6796*7c478bd9Sstevel@tonic-gate cp->ppc_ppm = PPM(dip); 6797*7c478bd9Sstevel@tonic-gate cp->ppc_dip = dip; 6798*7c478bd9Sstevel@tonic-gate 6799*7c478bd9Sstevel@tonic-gate /* 6800*7c478bd9Sstevel@tonic-gate * DDI_ATTACH and DDI_RESUME cmds need to call platform specific 6801*7c478bd9Sstevel@tonic-gate * Power Management stuff. DDI_RESUME also has to purge it's 6802*7c478bd9Sstevel@tonic-gate * powerlevel information. 6803*7c478bd9Sstevel@tonic-gate */ 6804*7c478bd9Sstevel@tonic-gate switch (cmd) { 6805*7c478bd9Sstevel@tonic-gate case DDI_ATTACH: 6806*7c478bd9Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6807*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_ATTACH; 6808*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6809*7c478bd9Sstevel@tonic-gate ASSERT(PPM(dip)); 6810*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, dip, DDI_CTLOPS_POWER, 6811*7c478bd9Sstevel@tonic-gate &power_req, &result); 6812*7c478bd9Sstevel@tonic-gate } 6813*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6814*7c478bd9Sstevel@tonic-gate else { 6815*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_ATTACH; 6816*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6817*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 6818*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6819*7c478bd9Sstevel@tonic-gate } 6820*7c478bd9Sstevel@tonic-gate #endif 6821*7c478bd9Sstevel@tonic-gate break; 6822*7c478bd9Sstevel@tonic-gate case DDI_RESUME: 6823*7c478bd9Sstevel@tonic-gate pm_forget_power_level(dip); 6824*7c478bd9Sstevel@tonic-gate 6825*7c478bd9Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6826*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_RESUME; 6827*7c478bd9Sstevel@tonic-gate power_req.req.resume_req.who = cp->ppc_dip; 6828*7c478bd9Sstevel@tonic-gate power_req.req.resume_req.cmd = 6829*7c478bd9Sstevel@tonic-gate (ddi_attach_cmd_t)cp->ppc_cmd; 6830*7c478bd9Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 6831*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, 6832*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6833*7c478bd9Sstevel@tonic-gate } 6834*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6835*7c478bd9Sstevel@tonic-gate else { 6836*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_RESUME; 6837*7c478bd9Sstevel@tonic-gate power_req.req.resume_req.who = cp->ppc_dip; 6838*7c478bd9Sstevel@tonic-gate power_req.req.resume_req.cmd = 6839*7c478bd9Sstevel@tonic-gate (ddi_attach_cmd_t)cp->ppc_cmd; 6840*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, 6841*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6842*7c478bd9Sstevel@tonic-gate } 6843*7c478bd9Sstevel@tonic-gate #endif 6844*7c478bd9Sstevel@tonic-gate break; 6845*7c478bd9Sstevel@tonic-gate 6846*7c478bd9Sstevel@tonic-gate case DDI_PM_RESUME: 6847*7c478bd9Sstevel@tonic-gate break; 6848*7c478bd9Sstevel@tonic-gate 6849*7c478bd9Sstevel@tonic-gate default: 6850*7c478bd9Sstevel@tonic-gate panic(me); 6851*7c478bd9Sstevel@tonic-gate } 6852*7c478bd9Sstevel@tonic-gate } 6853*7c478bd9Sstevel@tonic-gate 6854*7c478bd9Sstevel@tonic-gate /* 6855*7c478bd9Sstevel@tonic-gate * Nexus drivers call into pm framework to indicate which child driver is 6856*7c478bd9Sstevel@tonic-gate * being uninstalled. In some platforms, ppm may need to reconfigure the 6857*7c478bd9Sstevel@tonic-gate * hardware since the device driver is no longer installed. 6858*7c478bd9Sstevel@tonic-gate */ 6859*7c478bd9Sstevel@tonic-gate int 6860*7c478bd9Sstevel@tonic-gate pm_uninit_child(dev_info_t *dip) 6861*7c478bd9Sstevel@tonic-gate { 6862*7c478bd9Sstevel@tonic-gate power_req_t power_req; 6863*7c478bd9Sstevel@tonic-gate 6864*7c478bd9Sstevel@tonic-gate ASSERT(ddi_binding_name(dip)); 6865*7c478bd9Sstevel@tonic-gate ASSERT(ddi_get_name_addr(dip)); 6866*7c478bd9Sstevel@tonic-gate pm_ppm_claim(dip); 6867*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */ 6868*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNINIT_CHILD; 6869*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6870*7c478bd9Sstevel@tonic-gate ASSERT(PPM(dip)); 6871*7c478bd9Sstevel@tonic-gate return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, 6872*7c478bd9Sstevel@tonic-gate NULL)); 6873*7c478bd9Sstevel@tonic-gate } else { 6874*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6875*7c478bd9Sstevel@tonic-gate /* pass it to the default handler so we can debug things */ 6876*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNINIT_CHILD; 6877*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6878*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, DDI_CTLOPS_POWER, &power_req, NULL); 6879*7c478bd9Sstevel@tonic-gate #endif 6880*7c478bd9Sstevel@tonic-gate } 6881*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6882*7c478bd9Sstevel@tonic-gate } 6883*7c478bd9Sstevel@tonic-gate /* 6884*7c478bd9Sstevel@tonic-gate * Decrement kidsupcnt so scan can turn the parent back off if it is idle 6885*7c478bd9Sstevel@tonic-gate * Also notify ppm of result of probe if there is a ppm that cares 6886*7c478bd9Sstevel@tonic-gate */ 6887*7c478bd9Sstevel@tonic-gate void 6888*7c478bd9Sstevel@tonic-gate pm_post_probe(pm_ppm_cookie_t *cp, int ret, int probe_failed) 6889*7c478bd9Sstevel@tonic-gate { 6890*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(probe_failed)) 6891*7c478bd9Sstevel@tonic-gate int result; 6892*7c478bd9Sstevel@tonic-gate power_req_t power_req; 6893*7c478bd9Sstevel@tonic-gate 6894*7c478bd9Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6895*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_PROBE; 6896*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6897*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6898*7c478bd9Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 6899*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, DDI_CTLOPS_POWER, 6900*7c478bd9Sstevel@tonic-gate &power_req, &result); 6901*7c478bd9Sstevel@tonic-gate } 6902*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6903*7c478bd9Sstevel@tonic-gate else { 6904*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_PROBE; 6905*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6906*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6907*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, DDI_CTLOPS_POWER, 6908*7c478bd9Sstevel@tonic-gate &power_req, &result); 6909*7c478bd9Sstevel@tonic-gate } 6910*7c478bd9Sstevel@tonic-gate #endif 6911*7c478bd9Sstevel@tonic-gate } 6912*7c478bd9Sstevel@tonic-gate 6913*7c478bd9Sstevel@tonic-gate void 6914*7c478bd9Sstevel@tonic-gate pm_post_config(dev_info_t *dip, char *devnm) 6915*7c478bd9Sstevel@tonic-gate { 6916*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "post_config") 6917*7c478bd9Sstevel@tonic-gate 6918*7c478bd9Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6919*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6920*7c478bd9Sstevel@tonic-gate (void) mdi_power(dip, MDI_PM_POST_CONFIG, NULL, devnm, 0); 6921*7c478bd9Sstevel@tonic-gate return; 6922*7c478bd9Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6923*7c478bd9Sstevel@tonic-gate return; 6924*7c478bd9Sstevel@tonic-gate 6925*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6926*7c478bd9Sstevel@tonic-gate pm_rele_power(dip); 6927*7c478bd9Sstevel@tonic-gate } 6928*7c478bd9Sstevel@tonic-gate 6929*7c478bd9Sstevel@tonic-gate void 6930*7c478bd9Sstevel@tonic-gate pm_post_unconfig(dev_info_t *dip, int held, char *devnm) 6931*7c478bd9Sstevel@tonic-gate { 6932*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "post_unconfig") 6933*7c478bd9Sstevel@tonic-gate 6934*7c478bd9Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6935*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, 6936*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), held)) 6937*7c478bd9Sstevel@tonic-gate (void) mdi_power(dip, MDI_PM_POST_UNCONFIG, &held, devnm, 0); 6938*7c478bd9Sstevel@tonic-gate return; 6939*7c478bd9Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6940*7c478bd9Sstevel@tonic-gate return; 6941*7c478bd9Sstevel@tonic-gate 6942*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, PM_DEVICE(dip), 6943*7c478bd9Sstevel@tonic-gate held)) 6944*7c478bd9Sstevel@tonic-gate if (!held) 6945*7c478bd9Sstevel@tonic-gate return; 6946*7c478bd9Sstevel@tonic-gate /* 6947*7c478bd9Sstevel@tonic-gate * We have held power in pre_unconfig, release it here. 6948*7c478bd9Sstevel@tonic-gate */ 6949*7c478bd9Sstevel@tonic-gate pm_rele_power(dip); 6950*7c478bd9Sstevel@tonic-gate } 6951*7c478bd9Sstevel@tonic-gate 6952*7c478bd9Sstevel@tonic-gate /* 6953*7c478bd9Sstevel@tonic-gate * Notify ppm of result of attach if there is a ppm that cares 6954*7c478bd9Sstevel@tonic-gate */ 6955*7c478bd9Sstevel@tonic-gate void 6956*7c478bd9Sstevel@tonic-gate pm_post_attach(pm_ppm_cookie_t *cp, int ret) 6957*7c478bd9Sstevel@tonic-gate { 6958*7c478bd9Sstevel@tonic-gate int result; 6959*7c478bd9Sstevel@tonic-gate power_req_t power_req; 6960*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 6961*7c478bd9Sstevel@tonic-gate 6962*7c478bd9Sstevel@tonic-gate if (cp->ppc_cmd != DDI_ATTACH) 6963*7c478bd9Sstevel@tonic-gate return; 6964*7c478bd9Sstevel@tonic-gate 6965*7c478bd9Sstevel@tonic-gate dip = cp->ppc_dip; 6966*7c478bd9Sstevel@tonic-gate 6967*7c478bd9Sstevel@tonic-gate if (ret == DDI_SUCCESS) { 6968*7c478bd9Sstevel@tonic-gate /* 6969*7c478bd9Sstevel@tonic-gate * Attach succeeded, so proceed to doing post-attach pm tasks 6970*7c478bd9Sstevel@tonic-gate */ 6971*7c478bd9Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) 6972*7c478bd9Sstevel@tonic-gate (void) pm_start(dip); 6973*7c478bd9Sstevel@tonic-gate } else { 6974*7c478bd9Sstevel@tonic-gate /* 6975*7c478bd9Sstevel@tonic-gate * Attach may have got pm started before failing 6976*7c478bd9Sstevel@tonic-gate */ 6977*7c478bd9Sstevel@tonic-gate pm_stop(dip); 6978*7c478bd9Sstevel@tonic-gate } 6979*7c478bd9Sstevel@tonic-gate 6980*7c478bd9Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6981*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_ATTACH; 6982*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6983*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6984*7c478bd9Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 6985*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, 6986*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6987*7c478bd9Sstevel@tonic-gate } 6988*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 6989*7c478bd9Sstevel@tonic-gate else { 6990*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_ATTACH; 6991*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6992*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6993*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, 6994*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6995*7c478bd9Sstevel@tonic-gate } 6996*7c478bd9Sstevel@tonic-gate #endif 6997*7c478bd9Sstevel@tonic-gate } 6998*7c478bd9Sstevel@tonic-gate 6999*7c478bd9Sstevel@tonic-gate /* 7000*7c478bd9Sstevel@tonic-gate * Notify ppm of attach action. Parent is already held at full power by 7001*7c478bd9Sstevel@tonic-gate * probe action. 7002*7c478bd9Sstevel@tonic-gate */ 7003*7c478bd9Sstevel@tonic-gate void 7004*7c478bd9Sstevel@tonic-gate pm_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, pm_ppm_cookie_t *cp) 7005*7c478bd9Sstevel@tonic-gate { 7006*7c478bd9Sstevel@tonic-gate int result; 7007*7c478bd9Sstevel@tonic-gate power_req_t power_req; 7008*7c478bd9Sstevel@tonic-gate 7009*7c478bd9Sstevel@tonic-gate bzero(cp, sizeof (*cp)); 7010*7c478bd9Sstevel@tonic-gate cp->ppc_dip = dip; 7011*7c478bd9Sstevel@tonic-gate cp->ppc_cmd = (int)cmd; 7012*7c478bd9Sstevel@tonic-gate 7013*7c478bd9Sstevel@tonic-gate switch (cmd) { 7014*7c478bd9Sstevel@tonic-gate case DDI_DETACH: 7015*7c478bd9Sstevel@tonic-gate pm_detaching(dip); /* suspend pm while detaching */ 7016*7c478bd9Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims node */ 7017*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_DETACH; 7018*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 7019*7c478bd9Sstevel@tonic-gate ASSERT(PPM(dip)); 7020*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 7021*7c478bd9Sstevel@tonic-gate &power_req, &result); 7022*7c478bd9Sstevel@tonic-gate cp->ppc_ppm = PPM(dip); 7023*7c478bd9Sstevel@tonic-gate } else { 7024*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7025*7c478bd9Sstevel@tonic-gate /* pass to the default handler so we can debug things */ 7026*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_DETACH; 7027*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 7028*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 7029*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 7030*7c478bd9Sstevel@tonic-gate #endif 7031*7c478bd9Sstevel@tonic-gate cp->ppc_ppm = NULL; 7032*7c478bd9Sstevel@tonic-gate } 7033*7c478bd9Sstevel@tonic-gate break; 7034*7c478bd9Sstevel@tonic-gate 7035*7c478bd9Sstevel@tonic-gate default: 7036*7c478bd9Sstevel@tonic-gate break; 7037*7c478bd9Sstevel@tonic-gate } 7038*7c478bd9Sstevel@tonic-gate } 7039*7c478bd9Sstevel@tonic-gate 7040*7c478bd9Sstevel@tonic-gate /* 7041*7c478bd9Sstevel@tonic-gate * Dip is either a leaf node that exported "no-involuntary-power-cycles" prop., 7042*7c478bd9Sstevel@tonic-gate * (if devi_pm_noinvol count is 0) or an ancestor of such a node. We need to 7043*7c478bd9Sstevel@tonic-gate * make an entry to record the details, which includes certain flag settings. 7044*7c478bd9Sstevel@tonic-gate */ 7045*7c478bd9Sstevel@tonic-gate static void 7046*7c478bd9Sstevel@tonic-gate pm_record_invol_path(char *path, int flags, int noinvolpm, int volpmd, 7047*7c478bd9Sstevel@tonic-gate int wasvolpmd, major_t major) 7048*7c478bd9Sstevel@tonic-gate { 7049*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "record_invol_path") 7050*7c478bd9Sstevel@tonic-gate major_t pm_path_to_major(char *); 7051*7c478bd9Sstevel@tonic-gate size_t plen; 7052*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip, *np, *pp; 7053*7c478bd9Sstevel@tonic-gate pp = NULL; 7054*7c478bd9Sstevel@tonic-gate 7055*7c478bd9Sstevel@tonic-gate plen = strlen(path) + 1; 7056*7c478bd9Sstevel@tonic-gate np = kmem_zalloc(sizeof (*np), KM_SLEEP); 7057*7c478bd9Sstevel@tonic-gate np->ni_size = plen; 7058*7c478bd9Sstevel@tonic-gate np->ni_path = kmem_alloc(plen, KM_SLEEP); 7059*7c478bd9Sstevel@tonic-gate np->ni_noinvolpm = noinvolpm; 7060*7c478bd9Sstevel@tonic-gate np->ni_volpmd = volpmd; 7061*7c478bd9Sstevel@tonic-gate np->ni_wasvolpmd = wasvolpmd; 7062*7c478bd9Sstevel@tonic-gate np->ni_flags = flags; 7063*7c478bd9Sstevel@tonic-gate (void) strcpy(np->ni_path, path); 7064*7c478bd9Sstevel@tonic-gate /* 7065*7c478bd9Sstevel@tonic-gate * If we haven't actually seen the node attached, it is hard to figure 7066*7c478bd9Sstevel@tonic-gate * out its major. If we could hold the node by path, we would be much 7067*7c478bd9Sstevel@tonic-gate * happier here. 7068*7c478bd9Sstevel@tonic-gate */ 7069*7c478bd9Sstevel@tonic-gate if (major == (major_t)-1) { 7070*7c478bd9Sstevel@tonic-gate np->ni_major = pm_path_to_major(path); 7071*7c478bd9Sstevel@tonic-gate } else { 7072*7c478bd9Sstevel@tonic-gate np->ni_major = major; 7073*7c478bd9Sstevel@tonic-gate } 7074*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 7075*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7076*7c478bd9Sstevel@tonic-gate int comp = strcmp(path, ip->ni_path); 7077*7c478bd9Sstevel@tonic-gate if (comp < 0) { 7078*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s insert before %s\n", 7079*7c478bd9Sstevel@tonic-gate pmf, path, ip->ni_path)) 7080*7c478bd9Sstevel@tonic-gate /* insert before current entry */ 7081*7c478bd9Sstevel@tonic-gate np->ni_next = ip; 7082*7c478bd9Sstevel@tonic-gate if (pp) { 7083*7c478bd9Sstevel@tonic-gate pp->ni_next = np; 7084*7c478bd9Sstevel@tonic-gate } else { 7085*7c478bd9Sstevel@tonic-gate pm_noinvol_head = np; 7086*7c478bd9Sstevel@tonic-gate } 7087*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7088*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7089*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL) 7090*7c478bd9Sstevel@tonic-gate pr_noinvol("record_invol_path exit0"); 7091*7c478bd9Sstevel@tonic-gate #endif 7092*7c478bd9Sstevel@tonic-gate return; 7093*7c478bd9Sstevel@tonic-gate } else if (comp == 0) { 7094*7c478bd9Sstevel@tonic-gate panic("%s already in pm_noinvol list", path); 7095*7c478bd9Sstevel@tonic-gate } 7096*7c478bd9Sstevel@tonic-gate } 7097*7c478bd9Sstevel@tonic-gate /* 7098*7c478bd9Sstevel@tonic-gate * If we did not find an entry in the list that this should go before, 7099*7c478bd9Sstevel@tonic-gate * then it must go at the end 7100*7c478bd9Sstevel@tonic-gate */ 7101*7c478bd9Sstevel@tonic-gate if (pp) { 7102*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s append after %s\n", pmf, path, 7103*7c478bd9Sstevel@tonic-gate pp->ni_path)) 7104*7c478bd9Sstevel@tonic-gate ASSERT(pp->ni_next == 0); 7105*7c478bd9Sstevel@tonic-gate pp->ni_next = np; 7106*7c478bd9Sstevel@tonic-gate } else { 7107*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s added to end-of-list\n", pmf, path)) 7108*7c478bd9Sstevel@tonic-gate ASSERT(!pm_noinvol_head); 7109*7c478bd9Sstevel@tonic-gate pm_noinvol_head = np; 7110*7c478bd9Sstevel@tonic-gate } 7111*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7112*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7113*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL) 7114*7c478bd9Sstevel@tonic-gate pr_noinvol("record_invol_path exit"); 7115*7c478bd9Sstevel@tonic-gate #endif 7116*7c478bd9Sstevel@tonic-gate } 7117*7c478bd9Sstevel@tonic-gate 7118*7c478bd9Sstevel@tonic-gate void 7119*7c478bd9Sstevel@tonic-gate pm_record_invol(dev_info_t *dip) 7120*7c478bd9Sstevel@tonic-gate { 7121*7c478bd9Sstevel@tonic-gate char *pathbuf; 7122*7c478bd9Sstevel@tonic-gate int pm_all_components_off(dev_info_t *); 7123*7c478bd9Sstevel@tonic-gate int volpmd = (PM_NUMCMPTS(dip) > 0) && pm_all_components_off(dip); 7124*7c478bd9Sstevel@tonic-gate 7125*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 7126*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 7127*7c478bd9Sstevel@tonic-gate 7128*7c478bd9Sstevel@tonic-gate pm_record_invol_path(pathbuf, (DEVI(dip)->devi_pm_flags & 7129*7c478bd9Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)), DEVI(dip)->devi_pm_noinvolpm, 7130*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, volpmd, PM_MAJOR(dip)); 7131*7c478bd9Sstevel@tonic-gate 7132*7c478bd9Sstevel@tonic-gate /* 7133*7c478bd9Sstevel@tonic-gate * If this child's detach will be holding up its ancestors, then we 7134*7c478bd9Sstevel@tonic-gate * allow for an exception to that if all children of this type have 7135*7c478bd9Sstevel@tonic-gate * gone down voluntarily. 7136*7c478bd9Sstevel@tonic-gate * Now walk down the tree incrementing devi_pm_noinvolpm 7137*7c478bd9Sstevel@tonic-gate */ 7138*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_DETACH, 0, volpmd, pathbuf, 7139*7c478bd9Sstevel@tonic-gate dip); 7140*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7141*7c478bd9Sstevel@tonic-gate } 7142*7c478bd9Sstevel@tonic-gate 7143*7c478bd9Sstevel@tonic-gate void 7144*7c478bd9Sstevel@tonic-gate pm_post_detach(pm_ppm_cookie_t *cp, int ret) 7145*7c478bd9Sstevel@tonic-gate { 7146*7c478bd9Sstevel@tonic-gate dev_info_t *dip = cp->ppc_dip; 7147*7c478bd9Sstevel@tonic-gate int result; 7148*7c478bd9Sstevel@tonic-gate power_req_t power_req; 7149*7c478bd9Sstevel@tonic-gate 7150*7c478bd9Sstevel@tonic-gate switch (cp->ppc_cmd) { 7151*7c478bd9Sstevel@tonic-gate case DDI_DETACH: 7152*7c478bd9Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 7153*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_DETACH; 7154*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 7155*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 7156*7c478bd9Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 7157*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, 7158*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 7159*7c478bd9Sstevel@tonic-gate } 7160*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7161*7c478bd9Sstevel@tonic-gate else { 7162*7c478bd9Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_DETACH; 7163*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 7164*7c478bd9Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 7165*7c478bd9Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, 7166*7c478bd9Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 7167*7c478bd9Sstevel@tonic-gate } 7168*7c478bd9Sstevel@tonic-gate #endif 7169*7c478bd9Sstevel@tonic-gate if (ret == DDI_SUCCESS) { 7170*7c478bd9Sstevel@tonic-gate /* 7171*7c478bd9Sstevel@tonic-gate * For hotplug detach we assume it is *really* gone 7172*7c478bd9Sstevel@tonic-gate */ 7173*7c478bd9Sstevel@tonic-gate if (cp->ppc_cmd == DDI_DETACH && 7174*7c478bd9Sstevel@tonic-gate ((DEVI(dip)->devi_pm_flags & 7175*7c478bd9Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)) || 7176*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm)) 7177*7c478bd9Sstevel@tonic-gate pm_record_invol(dip); 7178*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= 7179*7c478bd9Sstevel@tonic-gate ~(PMC_NO_INVOL | PMC_NOINVOL_DONE); 7180*7c478bd9Sstevel@tonic-gate 7181*7c478bd9Sstevel@tonic-gate /* 7182*7c478bd9Sstevel@tonic-gate * If console fb is detaching, then we don't need to 7183*7c478bd9Sstevel@tonic-gate * worry any more about it going off (pm_detaching has 7184*7c478bd9Sstevel@tonic-gate * brought up all components) 7185*7c478bd9Sstevel@tonic-gate */ 7186*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 7187*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7188*7c478bd9Sstevel@tonic-gate ASSERT(cfb_dip_detaching); 7189*7c478bd9Sstevel@tonic-gate ASSERT(cfb_dip == NULL); 7190*7c478bd9Sstevel@tonic-gate ASSERT(pm_cfb_comps_off == 0); 7191*7c478bd9Sstevel@tonic-gate cfb_dip_detaching = NULL; 7192*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7193*7c478bd9Sstevel@tonic-gate } 7194*7c478bd9Sstevel@tonic-gate pm_stop(dip); /* make it permanent */ 7195*7c478bd9Sstevel@tonic-gate } else { 7196*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 7197*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7198*7c478bd9Sstevel@tonic-gate ASSERT(cfb_dip_detaching); 7199*7c478bd9Sstevel@tonic-gate ASSERT(cfb_dip == NULL); 7200*7c478bd9Sstevel@tonic-gate ASSERT(pm_cfb_comps_off == 0); 7201*7c478bd9Sstevel@tonic-gate cfb_dip = cfb_dip_detaching; 7202*7c478bd9Sstevel@tonic-gate cfb_dip_detaching = NULL; 7203*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7204*7c478bd9Sstevel@tonic-gate } 7205*7c478bd9Sstevel@tonic-gate pm_detach_failed(dip); /* resume power management */ 7206*7c478bd9Sstevel@tonic-gate } 7207*7c478bd9Sstevel@tonic-gate break; 7208*7c478bd9Sstevel@tonic-gate case DDI_PM_SUSPEND: 7209*7c478bd9Sstevel@tonic-gate break; 7210*7c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 7211*7c478bd9Sstevel@tonic-gate break; /* legal, but nothing to do */ 7212*7c478bd9Sstevel@tonic-gate default: 7213*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7214*7c478bd9Sstevel@tonic-gate panic("pm_post_detach: unrecognized cmd %d for detach", 7215*7c478bd9Sstevel@tonic-gate cp->ppc_cmd); 7216*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 7217*7c478bd9Sstevel@tonic-gate #else 7218*7c478bd9Sstevel@tonic-gate break; 7219*7c478bd9Sstevel@tonic-gate #endif 7220*7c478bd9Sstevel@tonic-gate } 7221*7c478bd9Sstevel@tonic-gate } 7222*7c478bd9Sstevel@tonic-gate 7223*7c478bd9Sstevel@tonic-gate /* 7224*7c478bd9Sstevel@tonic-gate * Called after vfs_mountroot has got the clock started to fix up timestamps 7225*7c478bd9Sstevel@tonic-gate * that were set when root bush drivers attached. hresttime was 0 then, so the 7226*7c478bd9Sstevel@tonic-gate * devices look busy but have a 0 busycnt 7227*7c478bd9Sstevel@tonic-gate */ 7228*7c478bd9Sstevel@tonic-gate int 7229*7c478bd9Sstevel@tonic-gate pm_adjust_timestamps(dev_info_t *dip, void *arg) 7230*7c478bd9Sstevel@tonic-gate { 7231*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 7232*7c478bd9Sstevel@tonic-gate 7233*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 7234*7c478bd9Sstevel@tonic-gate struct pm_component *cp; 7235*7c478bd9Sstevel@tonic-gate int i; 7236*7c478bd9Sstevel@tonic-gate 7237*7c478bd9Sstevel@tonic-gate if (!info) 7238*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7239*7c478bd9Sstevel@tonic-gate PM_LOCK_BUSY(dip); 7240*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 7241*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 7242*7c478bd9Sstevel@tonic-gate if (cp->pmc_timestamp == 0 && cp->pmc_busycount == 0) 7243*7c478bd9Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 7244*7c478bd9Sstevel@tonic-gate } 7245*7c478bd9Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 7246*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7247*7c478bd9Sstevel@tonic-gate } 7248*7c478bd9Sstevel@tonic-gate 7249*7c478bd9Sstevel@tonic-gate /* 7250*7c478bd9Sstevel@tonic-gate * Called at attach time to see if the device being attached has a record in 7251*7c478bd9Sstevel@tonic-gate * the no involuntary power cycles list. If so, we do some bookkeeping on the 7252*7c478bd9Sstevel@tonic-gate * parents and set a flag in the dip 7253*7c478bd9Sstevel@tonic-gate */ 7254*7c478bd9Sstevel@tonic-gate void 7255*7c478bd9Sstevel@tonic-gate pm_noinvol_specd(dev_info_t *dip) 7256*7c478bd9Sstevel@tonic-gate { 7257*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_specd") 7258*7c478bd9Sstevel@tonic-gate char *pathbuf; 7259*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip, *pp = NULL; 7260*7c478bd9Sstevel@tonic-gate int wasvolpmd; 7261*7c478bd9Sstevel@tonic-gate int found = 0; 7262*7c478bd9Sstevel@tonic-gate 7263*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NOINVOL_DONE) 7264*7c478bd9Sstevel@tonic-gate return; 7265*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NOINVOL_DONE; 7266*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 7267*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 7268*7c478bd9Sstevel@tonic-gate 7269*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 7270*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0; 7271*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm = 0; 7272*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 7273*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7274*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n", 7275*7c478bd9Sstevel@tonic-gate pmf, pathbuf, ip->ni_path)) 7276*7c478bd9Sstevel@tonic-gate if (strcmp(pathbuf, ip->ni_path) == 0) { 7277*7c478bd9Sstevel@tonic-gate found++; 7278*7c478bd9Sstevel@tonic-gate break; 7279*7c478bd9Sstevel@tonic-gate } 7280*7c478bd9Sstevel@tonic-gate } 7281*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7282*7c478bd9Sstevel@tonic-gate if (!found) { 7283*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 7284*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7285*7c478bd9Sstevel@tonic-gate return; 7286*7c478bd9Sstevel@tonic-gate } 7287*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 7288*7c478bd9Sstevel@tonic-gate pp = NULL; 7289*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7290*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n", 7291*7c478bd9Sstevel@tonic-gate pmf, pathbuf, ip->ni_path)) 7292*7c478bd9Sstevel@tonic-gate if (strcmp(pathbuf, ip->ni_path) == 0) { 7293*7c478bd9Sstevel@tonic-gate ip->ni_flags &= ~PMC_DRIVER_REMOVED; 7294*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= ip->ni_flags; 7295*7c478bd9Sstevel@tonic-gate /* 7296*7c478bd9Sstevel@tonic-gate * Handle special case of console fb 7297*7c478bd9Sstevel@tonic-gate */ 7298*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 7299*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7300*7c478bd9Sstevel@tonic-gate cfb_dip = dip; 7301*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) setting " 7302*7c478bd9Sstevel@tonic-gate "cfb_dip\n", pmf, PM_DEVICE(dip))) 7303*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7304*7c478bd9Sstevel@tonic-gate } 7305*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm = ip->ni_noinvolpm; 7306*7c478bd9Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags & 7307*7c478bd9Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)) || 7308*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm); 7309*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = ip->ni_volpmd; 7310*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: noinvol=%d, volpmd=%d, " 7311*7c478bd9Sstevel@tonic-gate "wasvolpmd=%d, flags=%x, path=%s\n", pmf, 7312*7c478bd9Sstevel@tonic-gate ip->ni_noinvolpm, ip->ni_volpmd, 7313*7c478bd9Sstevel@tonic-gate ip->ni_wasvolpmd, ip->ni_flags, ip->ni_path)) 7314*7c478bd9Sstevel@tonic-gate /* 7315*7c478bd9Sstevel@tonic-gate * free the entry in hopes the list will now be empty 7316*7c478bd9Sstevel@tonic-gate * and we won't have to search it any more until the 7317*7c478bd9Sstevel@tonic-gate * device detaches 7318*7c478bd9Sstevel@tonic-gate */ 7319*7c478bd9Sstevel@tonic-gate if (pp) { 7320*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s, prev %s\n", 7321*7c478bd9Sstevel@tonic-gate pmf, ip->ni_path, pp->ni_path)) 7322*7c478bd9Sstevel@tonic-gate pp->ni_next = ip->ni_next; 7323*7c478bd9Sstevel@tonic-gate } else { 7324*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s head\n", 7325*7c478bd9Sstevel@tonic-gate pmf, ip->ni_path)) 7326*7c478bd9Sstevel@tonic-gate ASSERT(pm_noinvol_head == ip); 7327*7c478bd9Sstevel@tonic-gate pm_noinvol_head = ip->ni_next; 7328*7c478bd9Sstevel@tonic-gate } 7329*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 7330*7c478bd9Sstevel@tonic-gate wasvolpmd = ip->ni_wasvolpmd; 7331*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7332*7c478bd9Sstevel@tonic-gate kmem_free(ip->ni_path, ip->ni_size); 7333*7c478bd9Sstevel@tonic-gate kmem_free(ip, sizeof (*ip)); 7334*7c478bd9Sstevel@tonic-gate /* 7335*7c478bd9Sstevel@tonic-gate * Now walk up the tree decrementing devi_pm_noinvolpm 7336*7c478bd9Sstevel@tonic-gate * (and volpmd if appropriate) 7337*7c478bd9Sstevel@tonic-gate */ 7338*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_ATTACH, 0, 7339*7c478bd9Sstevel@tonic-gate wasvolpmd, pathbuf, dip); 7340*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7341*7c478bd9Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL) 7342*7c478bd9Sstevel@tonic-gate pr_noinvol("noinvol_specd exit"); 7343*7c478bd9Sstevel@tonic-gate #endif 7344*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7345*7c478bd9Sstevel@tonic-gate return; 7346*7c478bd9Sstevel@tonic-gate } 7347*7c478bd9Sstevel@tonic-gate } 7348*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7349*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7350*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 7351*7c478bd9Sstevel@tonic-gate } 7352*7c478bd9Sstevel@tonic-gate 7353*7c478bd9Sstevel@tonic-gate int 7354*7c478bd9Sstevel@tonic-gate pm_all_components_off(dev_info_t *dip) 7355*7c478bd9Sstevel@tonic-gate { 7356*7c478bd9Sstevel@tonic-gate int i; 7357*7c478bd9Sstevel@tonic-gate pm_component_t *cp; 7358*7c478bd9Sstevel@tonic-gate 7359*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 7360*7c478bd9Sstevel@tonic-gate cp = PM_CP(dip, i); 7361*7c478bd9Sstevel@tonic-gate if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN || 7362*7c478bd9Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]) 7363*7c478bd9Sstevel@tonic-gate return (0); 7364*7c478bd9Sstevel@tonic-gate } 7365*7c478bd9Sstevel@tonic-gate return (1); /* all off */ 7366*7c478bd9Sstevel@tonic-gate } 7367*7c478bd9Sstevel@tonic-gate 7368*7c478bd9Sstevel@tonic-gate /* 7369*7c478bd9Sstevel@tonic-gate * Make sure that all "no involuntary power cycles" devices are attached. 7370*7c478bd9Sstevel@tonic-gate * Called before doing a cpr suspend to make sure the driver has a say about 7371*7c478bd9Sstevel@tonic-gate * the power cycle 7372*7c478bd9Sstevel@tonic-gate */ 7373*7c478bd9Sstevel@tonic-gate int 7374*7c478bd9Sstevel@tonic-gate pm_reattach_noinvol(void) 7375*7c478bd9Sstevel@tonic-gate { 7376*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "reattach_noinvol") 7377*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip; 7378*7c478bd9Sstevel@tonic-gate char *path; 7379*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 7380*7c478bd9Sstevel@tonic-gate 7381*7c478bd9Sstevel@tonic-gate /* 7382*7c478bd9Sstevel@tonic-gate * Prevent the modunload thread from unloading any modules until we 7383*7c478bd9Sstevel@tonic-gate * have completely stopped all kernel threads. 7384*7c478bd9Sstevel@tonic-gate */ 7385*7c478bd9Sstevel@tonic-gate modunload_disable(); 7386*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 7387*7c478bd9Sstevel@tonic-gate /* 7388*7c478bd9Sstevel@tonic-gate * Forget we'v ever seen any entry 7389*7c478bd9Sstevel@tonic-gate */ 7390*7c478bd9Sstevel@tonic-gate ip->ni_persistent = 0; 7391*7c478bd9Sstevel@tonic-gate } 7392*7c478bd9Sstevel@tonic-gate restart: 7393*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 7394*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 7395*7c478bd9Sstevel@tonic-gate major_t maj; 7396*7c478bd9Sstevel@tonic-gate maj = ip->ni_major; 7397*7c478bd9Sstevel@tonic-gate path = ip->ni_path; 7398*7c478bd9Sstevel@tonic-gate if (path != NULL && !(ip->ni_flags & PMC_DRIVER_REMOVED)) { 7399*7c478bd9Sstevel@tonic-gate if (ip->ni_persistent) { 7400*7c478bd9Sstevel@tonic-gate /* 7401*7c478bd9Sstevel@tonic-gate * If we weren't able to make this entry 7402*7c478bd9Sstevel@tonic-gate * go away, then we give up, as 7403*7c478bd9Sstevel@tonic-gate * holding/attaching the driver ought to have 7404*7c478bd9Sstevel@tonic-gate * resulted in this entry being deleted 7405*7c478bd9Sstevel@tonic-gate */ 7406*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: can't reattach %s " 7407*7c478bd9Sstevel@tonic-gate "(%s|%d)\n", pmf, ip->ni_path, 7408*7c478bd9Sstevel@tonic-gate ddi_major_to_name(maj), (int)maj)) 7409*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable to reattach %s ", 7410*7c478bd9Sstevel@tonic-gate ip->ni_path); 7411*7c478bd9Sstevel@tonic-gate modunload_enable(); 7412*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7413*7c478bd9Sstevel@tonic-gate return (0); 7414*7c478bd9Sstevel@tonic-gate } 7415*7c478bd9Sstevel@tonic-gate ip->ni_persistent++; 7416*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7417*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: holding %s\n", pmf, path)) 7418*7c478bd9Sstevel@tonic-gate dip = e_ddi_hold_devi_by_path(path, 0); 7419*7c478bd9Sstevel@tonic-gate if (dip == NULL) { 7420*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: can't hold (%s|%d)\n", 7421*7c478bd9Sstevel@tonic-gate pmf, path, (int)maj)) 7422*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable to hold %s " 7423*7c478bd9Sstevel@tonic-gate "driver", path); 7424*7c478bd9Sstevel@tonic-gate modunload_enable(); 7425*7c478bd9Sstevel@tonic-gate return (0); 7426*7c478bd9Sstevel@tonic-gate } else { 7427*7c478bd9Sstevel@tonic-gate PMD(PMD_DHR, ("%s: release %s\n", pmf, path)) 7428*7c478bd9Sstevel@tonic-gate /* 7429*7c478bd9Sstevel@tonic-gate * Since the modunload thread is stopped, we 7430*7c478bd9Sstevel@tonic-gate * don't have to keep the driver held, which 7431*7c478bd9Sstevel@tonic-gate * saves a ton of bookkeeping 7432*7c478bd9Sstevel@tonic-gate */ 7433*7c478bd9Sstevel@tonic-gate ddi_release_devi(dip); 7434*7c478bd9Sstevel@tonic-gate goto restart; 7435*7c478bd9Sstevel@tonic-gate } 7436*7c478bd9Sstevel@tonic-gate } else { 7437*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: skip %s; unknown major\n", 7438*7c478bd9Sstevel@tonic-gate pmf, ip->ni_path)) 7439*7c478bd9Sstevel@tonic-gate continue; 7440*7c478bd9Sstevel@tonic-gate } 7441*7c478bd9Sstevel@tonic-gate } 7442*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7443*7c478bd9Sstevel@tonic-gate return (1); 7444*7c478bd9Sstevel@tonic-gate } 7445*7c478bd9Sstevel@tonic-gate 7446*7c478bd9Sstevel@tonic-gate void 7447*7c478bd9Sstevel@tonic-gate pm_reattach_noinvol_fini(void) 7448*7c478bd9Sstevel@tonic-gate { 7449*7c478bd9Sstevel@tonic-gate modunload_enable(); 7450*7c478bd9Sstevel@tonic-gate } 7451*7c478bd9Sstevel@tonic-gate 7452*7c478bd9Sstevel@tonic-gate /* 7453*7c478bd9Sstevel@tonic-gate * Display pm support code 7454*7c478bd9Sstevel@tonic-gate */ 7455*7c478bd9Sstevel@tonic-gate 7456*7c478bd9Sstevel@tonic-gate 7457*7c478bd9Sstevel@tonic-gate /* 7458*7c478bd9Sstevel@tonic-gate * console frame-buffer power-mgmt gets enabled when debugging 7459*7c478bd9Sstevel@tonic-gate * services are not present or console fbpm override is set 7460*7c478bd9Sstevel@tonic-gate */ 7461*7c478bd9Sstevel@tonic-gate void 7462*7c478bd9Sstevel@tonic-gate pm_cfb_setup(const char *stdout_path) 7463*7c478bd9Sstevel@tonic-gate { 7464*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "cfb_setup") 7465*7c478bd9Sstevel@tonic-gate extern int obpdebug; 7466*7c478bd9Sstevel@tonic-gate char *devname; 7467*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 7468*7c478bd9Sstevel@tonic-gate int devname_len; 7469*7c478bd9Sstevel@tonic-gate extern dev_info_t *fbdip; 7470*7c478bd9Sstevel@tonic-gate 7471*7c478bd9Sstevel@tonic-gate /* 7472*7c478bd9Sstevel@tonic-gate * By virtue of this function being called (from consconfig), 7473*7c478bd9Sstevel@tonic-gate * we know stdout is a framebuffer. 7474*7c478bd9Sstevel@tonic-gate */ 7475*7c478bd9Sstevel@tonic-gate stdout_is_framebuffer = 1; 7476*7c478bd9Sstevel@tonic-gate 7477*7c478bd9Sstevel@tonic-gate if (obpdebug || (boothowto & RB_DEBUG)) { 7478*7c478bd9Sstevel@tonic-gate if (pm_cfb_override == 0) { 7479*7c478bd9Sstevel@tonic-gate /* 7480*7c478bd9Sstevel@tonic-gate * Console is frame buffer, but we want to suppress 7481*7c478bd9Sstevel@tonic-gate * pm on it because of debugging setup 7482*7c478bd9Sstevel@tonic-gate */ 7483*7c478bd9Sstevel@tonic-gate pm_cfb_enabled = 0; 7484*7c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "Kernel debugger present: disabling " 7485*7c478bd9Sstevel@tonic-gate "console power management."); 7486*7c478bd9Sstevel@tonic-gate /* 7487*7c478bd9Sstevel@tonic-gate * however, we still need to know which is the console 7488*7c478bd9Sstevel@tonic-gate * fb in order to suppress pm on it 7489*7c478bd9Sstevel@tonic-gate */ 7490*7c478bd9Sstevel@tonic-gate } else { 7491*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Kernel debugger present: see " 7492*7c478bd9Sstevel@tonic-gate "kmdb(1M) for interaction with power management."); 7493*7c478bd9Sstevel@tonic-gate } 7494*7c478bd9Sstevel@tonic-gate } 7495*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7496*7c478bd9Sstevel@tonic-gate /* 7497*7c478bd9Sstevel@tonic-gate * IF console is fb and is power managed, don't do prom_printfs from 7498*7c478bd9Sstevel@tonic-gate * pm debug macro 7499*7c478bd9Sstevel@tonic-gate */ 7500*7c478bd9Sstevel@tonic-gate if (pm_cfb_enabled) { 7501*7c478bd9Sstevel@tonic-gate if (pm_debug) 7502*7c478bd9Sstevel@tonic-gate prom_printf("pm debug output will be to log only\n"); 7503*7c478bd9Sstevel@tonic-gate pm_divertdebug++; 7504*7c478bd9Sstevel@tonic-gate } 7505*7c478bd9Sstevel@tonic-gate #endif 7506*7c478bd9Sstevel@tonic-gate devname = i_ddi_strdup((char *)stdout_path, KM_SLEEP); 7507*7c478bd9Sstevel@tonic-gate devname_len = strlen(devname) + 1; 7508*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: stripped %s\n", pmf, devname)) 7509*7c478bd9Sstevel@tonic-gate /* if the driver is attached */ 7510*7c478bd9Sstevel@tonic-gate if ((dip = fbdip) != NULL) { 7511*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: attached: %s@%s(%s#%d)\n", pmf, 7512*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 7513*7c478bd9Sstevel@tonic-gate /* 7514*7c478bd9Sstevel@tonic-gate * We set up here as if the driver were power manageable in case 7515*7c478bd9Sstevel@tonic-gate * we get a later attach of a pm'able driver (which would result 7516*7c478bd9Sstevel@tonic-gate * in a panic later) 7517*7c478bd9Sstevel@tonic-gate */ 7518*7c478bd9Sstevel@tonic-gate cfb_dip = dip; 7519*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= (PMC_CONSOLE_FB | PMC_NO_INVOL); 7520*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: cfb_dip -> %s@%s(%s#%d)\n", pmf, 7521*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 7522*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7523*7c478bd9Sstevel@tonic-gate if (!(PM_GET_PM_INFO(dip) != NULL && PM_NUMCMPTS(dip))) { 7524*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) not power-managed\n", 7525*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 7526*7c478bd9Sstevel@tonic-gate } 7527*7c478bd9Sstevel@tonic-gate #endif 7528*7c478bd9Sstevel@tonic-gate } else { 7529*7c478bd9Sstevel@tonic-gate char *ep; 7530*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: pntd %s failed\n", pmf, devname)) 7531*7c478bd9Sstevel@tonic-gate pm_record_invol_path(devname, 7532*7c478bd9Sstevel@tonic-gate (PMC_CONSOLE_FB | PMC_NO_INVOL), 1, 0, 0, 7533*7c478bd9Sstevel@tonic-gate (major_t)-1); 7534*7c478bd9Sstevel@tonic-gate for (ep = strrchr(devname, '/'); ep != devname; 7535*7c478bd9Sstevel@tonic-gate ep = strrchr(devname, '/')) { 7536*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: devname %s\n", pmf, devname)) 7537*7c478bd9Sstevel@tonic-gate *ep = '\0'; 7538*7c478bd9Sstevel@tonic-gate dip = pm_name_to_dip(devname, 0); 7539*7c478bd9Sstevel@tonic-gate if (dip != NULL) { 7540*7c478bd9Sstevel@tonic-gate /* 7541*7c478bd9Sstevel@tonic-gate * Walk up the tree incrementing 7542*7c478bd9Sstevel@tonic-gate * devi_pm_noinvolpm 7543*7c478bd9Sstevel@tonic-gate */ 7544*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_CFB, 7545*7c478bd9Sstevel@tonic-gate 0, 0, devname, dip); 7546*7c478bd9Sstevel@tonic-gate break; 7547*7c478bd9Sstevel@tonic-gate } else { 7548*7c478bd9Sstevel@tonic-gate pm_record_invol_path(devname, 7549*7c478bd9Sstevel@tonic-gate PMC_NO_INVOL, 1, 0, 0, (major_t)-1); 7550*7c478bd9Sstevel@tonic-gate } 7551*7c478bd9Sstevel@tonic-gate } 7552*7c478bd9Sstevel@tonic-gate } 7553*7c478bd9Sstevel@tonic-gate kmem_free(devname, devname_len); 7554*7c478bd9Sstevel@tonic-gate } 7555*7c478bd9Sstevel@tonic-gate 7556*7c478bd9Sstevel@tonic-gate void 7557*7c478bd9Sstevel@tonic-gate pm_cfb_rele(void) 7558*7c478bd9Sstevel@tonic-gate { 7559*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7560*7c478bd9Sstevel@tonic-gate /* 7561*7c478bd9Sstevel@tonic-gate * this call isn't using the console any more, it is ok to take it 7562*7c478bd9Sstevel@tonic-gate * down if the count goes to 0 7563*7c478bd9Sstevel@tonic-gate */ 7564*7c478bd9Sstevel@tonic-gate cfb_inuse--; 7565*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7566*7c478bd9Sstevel@tonic-gate } 7567*7c478bd9Sstevel@tonic-gate 7568*7c478bd9Sstevel@tonic-gate /* 7569*7c478bd9Sstevel@tonic-gate * software interrupt handler for fbpm; this function exists because we can't 7570*7c478bd9Sstevel@tonic-gate * bring up the frame buffer power from above lock level. So if we need to, 7571*7c478bd9Sstevel@tonic-gate * we instead schedule a softint that runs this routine and takes us into 7572*7c478bd9Sstevel@tonic-gate * debug_enter (a bit delayed from the original request, but avoiding a panic). 7573*7c478bd9Sstevel@tonic-gate */ 7574*7c478bd9Sstevel@tonic-gate static uint_t 7575*7c478bd9Sstevel@tonic-gate pm_cfb_softint(caddr_t int_handler_arg) 7576*7c478bd9Sstevel@tonic-gate { 7577*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(int_handler_arg)) 7578*7c478bd9Sstevel@tonic-gate int rval = DDI_INTR_UNCLAIMED; 7579*7c478bd9Sstevel@tonic-gate 7580*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7581*7c478bd9Sstevel@tonic-gate if (pm_soft_pending) { 7582*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7583*7c478bd9Sstevel@tonic-gate debug_enter((char *)NULL); 7584*7c478bd9Sstevel@tonic-gate /* acquired in debug_enter before calling pm_cfb_trigger */ 7585*7c478bd9Sstevel@tonic-gate pm_cfb_rele(); 7586*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7587*7c478bd9Sstevel@tonic-gate pm_soft_pending = 0; 7588*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7589*7c478bd9Sstevel@tonic-gate rval = DDI_INTR_CLAIMED; 7590*7c478bd9Sstevel@tonic-gate } else 7591*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7592*7c478bd9Sstevel@tonic-gate 7593*7c478bd9Sstevel@tonic-gate return (rval); 7594*7c478bd9Sstevel@tonic-gate } 7595*7c478bd9Sstevel@tonic-gate 7596*7c478bd9Sstevel@tonic-gate void 7597*7c478bd9Sstevel@tonic-gate pm_cfb_setup_intr(void) 7598*7c478bd9Sstevel@tonic-gate { 7599*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "cfb_setup_intr") 7600*7c478bd9Sstevel@tonic-gate extern void prom_set_outfuncs(void (*)(void), void (*)(void)); 7601*7c478bd9Sstevel@tonic-gate void pm_cfb_check_and_powerup(void); 7602*7c478bd9Sstevel@tonic-gate 7603*7c478bd9Sstevel@tonic-gate if (!stdout_is_framebuffer) { 7604*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: console not fb\n", pmf)) 7605*7c478bd9Sstevel@tonic-gate return; 7606*7c478bd9Sstevel@tonic-gate } 7607*7c478bd9Sstevel@tonic-gate mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8)); 7608*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7609*7c478bd9Sstevel@tonic-gate mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8)); 7610*7c478bd9Sstevel@tonic-gate #endif 7611*7c478bd9Sstevel@tonic-gate /* 7612*7c478bd9Sstevel@tonic-gate * setup software interrupt handler 7613*7c478bd9Sstevel@tonic-gate */ 7614*7c478bd9Sstevel@tonic-gate if (ddi_add_softintr(ddi_root_node(), DDI_SOFTINT_HIGH, &pm_soft_id, 7615*7c478bd9Sstevel@tonic-gate NULL, NULL, pm_cfb_softint, NULL) != DDI_SUCCESS) 7616*7c478bd9Sstevel@tonic-gate panic("pm: unable to register soft intr."); 7617*7c478bd9Sstevel@tonic-gate 7618*7c478bd9Sstevel@tonic-gate prom_set_outfuncs(pm_cfb_check_and_powerup, pm_cfb_rele); 7619*7c478bd9Sstevel@tonic-gate } 7620*7c478bd9Sstevel@tonic-gate 7621*7c478bd9Sstevel@tonic-gate /* 7622*7c478bd9Sstevel@tonic-gate * Checks to see if it is safe to write to the console wrt power management 7623*7c478bd9Sstevel@tonic-gate * (i.e. if the console is a framebuffer, then it must be at full power) 7624*7c478bd9Sstevel@tonic-gate * returns 1 when power is off (power-up is needed) 7625*7c478bd9Sstevel@tonic-gate * returns 0 when power is on (power-up not needed) 7626*7c478bd9Sstevel@tonic-gate */ 7627*7c478bd9Sstevel@tonic-gate int 7628*7c478bd9Sstevel@tonic-gate pm_cfb_check_and_hold(void) 7629*7c478bd9Sstevel@tonic-gate { 7630*7c478bd9Sstevel@tonic-gate /* 7631*7c478bd9Sstevel@tonic-gate * cfb_dip is set iff console is a power manageable frame buffer 7632*7c478bd9Sstevel@tonic-gate * device 7633*7c478bd9Sstevel@tonic-gate */ 7634*7c478bd9Sstevel@tonic-gate extern int modrootloaded; 7635*7c478bd9Sstevel@tonic-gate 7636*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7637*7c478bd9Sstevel@tonic-gate cfb_inuse++; 7638*7c478bd9Sstevel@tonic-gate ASSERT(cfb_inuse); /* wrap? */ 7639*7c478bd9Sstevel@tonic-gate if (modrootloaded && cfb_dip) { 7640*7c478bd9Sstevel@tonic-gate /* 7641*7c478bd9Sstevel@tonic-gate * don't power down the frame buffer, the prom is using it 7642*7c478bd9Sstevel@tonic-gate */ 7643*7c478bd9Sstevel@tonic-gate if (pm_cfb_comps_off) { 7644*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7645*7c478bd9Sstevel@tonic-gate return (1); 7646*7c478bd9Sstevel@tonic-gate } 7647*7c478bd9Sstevel@tonic-gate } 7648*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7649*7c478bd9Sstevel@tonic-gate return (0); 7650*7c478bd9Sstevel@tonic-gate } 7651*7c478bd9Sstevel@tonic-gate 7652*7c478bd9Sstevel@tonic-gate /* 7653*7c478bd9Sstevel@tonic-gate * turn on cfb power (which is known to be off). 7654*7c478bd9Sstevel@tonic-gate * Must be called below lock level! 7655*7c478bd9Sstevel@tonic-gate */ 7656*7c478bd9Sstevel@tonic-gate void 7657*7c478bd9Sstevel@tonic-gate pm_cfb_powerup(void) 7658*7c478bd9Sstevel@tonic-gate { 7659*7c478bd9Sstevel@tonic-gate pm_info_t *info; 7660*7c478bd9Sstevel@tonic-gate int norm; 7661*7c478bd9Sstevel@tonic-gate int ccount, ci; 7662*7c478bd9Sstevel@tonic-gate int unused; 7663*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7664*7c478bd9Sstevel@tonic-gate /* 7665*7c478bd9Sstevel@tonic-gate * Can't reenter prom_prekern, so suppress pm debug messages 7666*7c478bd9Sstevel@tonic-gate * (still go to circular buffer). 7667*7c478bd9Sstevel@tonic-gate */ 7668*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 7669*7c478bd9Sstevel@tonic-gate pm_divertdebug++; 7670*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 7671*7c478bd9Sstevel@tonic-gate #endif 7672*7c478bd9Sstevel@tonic-gate info = PM_GET_PM_INFO(cfb_dip); 7673*7c478bd9Sstevel@tonic-gate ASSERT(info); 7674*7c478bd9Sstevel@tonic-gate 7675*7c478bd9Sstevel@tonic-gate ccount = PM_NUMCMPTS(cfb_dip); 7676*7c478bd9Sstevel@tonic-gate for (ci = 0; ci < ccount; ci++) { 7677*7c478bd9Sstevel@tonic-gate norm = pm_get_normal_power(cfb_dip, ci); 7678*7c478bd9Sstevel@tonic-gate (void) pm_set_power(cfb_dip, ci, norm, PM_LEVEL_UPONLY, 7679*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_BYPASS, 0, &unused); 7680*7c478bd9Sstevel@tonic-gate } 7681*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7682*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 7683*7c478bd9Sstevel@tonic-gate pm_divertdebug--; 7684*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 7685*7c478bd9Sstevel@tonic-gate #endif 7686*7c478bd9Sstevel@tonic-gate } 7687*7c478bd9Sstevel@tonic-gate 7688*7c478bd9Sstevel@tonic-gate /* 7689*7c478bd9Sstevel@tonic-gate * Check if the console framebuffer is powered up. If not power it up. 7690*7c478bd9Sstevel@tonic-gate * Note: Calling pm_cfb_check_and_hold has put a hold on the power state which 7691*7c478bd9Sstevel@tonic-gate * must be released by calling pm_cfb_rele when the console fb operation 7692*7c478bd9Sstevel@tonic-gate * is completed. 7693*7c478bd9Sstevel@tonic-gate */ 7694*7c478bd9Sstevel@tonic-gate void 7695*7c478bd9Sstevel@tonic-gate pm_cfb_check_and_powerup(void) 7696*7c478bd9Sstevel@tonic-gate { 7697*7c478bd9Sstevel@tonic-gate if (pm_cfb_check_and_hold()) 7698*7c478bd9Sstevel@tonic-gate pm_cfb_powerup(); 7699*7c478bd9Sstevel@tonic-gate } 7700*7c478bd9Sstevel@tonic-gate 7701*7c478bd9Sstevel@tonic-gate /* 7702*7c478bd9Sstevel@tonic-gate * Trigger a low level interrupt to power up console frame buffer. 7703*7c478bd9Sstevel@tonic-gate */ 7704*7c478bd9Sstevel@tonic-gate void 7705*7c478bd9Sstevel@tonic-gate pm_cfb_trigger(void) 7706*7c478bd9Sstevel@tonic-gate { 7707*7c478bd9Sstevel@tonic-gate if (cfb_dip == NULL) 7708*7c478bd9Sstevel@tonic-gate return; 7709*7c478bd9Sstevel@tonic-gate 7710*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7711*7c478bd9Sstevel@tonic-gate /* 7712*7c478bd9Sstevel@tonic-gate * If machine appears to be hung, pulling the keyboard connector of 7713*7c478bd9Sstevel@tonic-gate * the console will cause a high level interrupt and go to debug_enter. 7714*7c478bd9Sstevel@tonic-gate * But, if the fb is powered down, this routine will be called to bring 7715*7c478bd9Sstevel@tonic-gate * it up (by generating a softint to do the work). If soft interrupts 7716*7c478bd9Sstevel@tonic-gate * are not running, and the keyboard connector is pulled again, the 7717*7c478bd9Sstevel@tonic-gate * following code detects this condition and calls panic which allows 7718*7c478bd9Sstevel@tonic-gate * the fb to be brought up from high level. 7719*7c478bd9Sstevel@tonic-gate * 7720*7c478bd9Sstevel@tonic-gate * If two nearly simultaneous calls to debug_enter occur (both from 7721*7c478bd9Sstevel@tonic-gate * high level) the code described above will cause a panic. 7722*7c478bd9Sstevel@tonic-gate */ 7723*7c478bd9Sstevel@tonic-gate if (lbolt <= pm_soft_pending) { 7724*7c478bd9Sstevel@tonic-gate panicstr = "pm_cfb_trigger: lbolt not advancing"; 7725*7c478bd9Sstevel@tonic-gate panic(panicstr); /* does a power up at any intr level */ 7726*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 7727*7c478bd9Sstevel@tonic-gate } 7728*7c478bd9Sstevel@tonic-gate pm_soft_pending = lbolt; 7729*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7730*7c478bd9Sstevel@tonic-gate ddi_trigger_softintr(pm_soft_id); 7731*7c478bd9Sstevel@tonic-gate } 7732*7c478bd9Sstevel@tonic-gate 7733*7c478bd9Sstevel@tonic-gate major_t 7734*7c478bd9Sstevel@tonic-gate pm_path_to_major(char *path) 7735*7c478bd9Sstevel@tonic-gate { 7736*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "path_to_major") 7737*7c478bd9Sstevel@tonic-gate char *np, *ap, *bp; 7738*7c478bd9Sstevel@tonic-gate major_t ret; 7739*7c478bd9Sstevel@tonic-gate size_t len; 7740*7c478bd9Sstevel@tonic-gate static major_t i_path_to_major(char *, char *); 7741*7c478bd9Sstevel@tonic-gate 7742*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s\n", pmf, path)) 7743*7c478bd9Sstevel@tonic-gate 7744*7c478bd9Sstevel@tonic-gate np = strrchr(path, '/'); 7745*7c478bd9Sstevel@tonic-gate if (np != NULL) 7746*7c478bd9Sstevel@tonic-gate np++; 7747*7c478bd9Sstevel@tonic-gate else 7748*7c478bd9Sstevel@tonic-gate np = path; 7749*7c478bd9Sstevel@tonic-gate len = strlen(np) + 1; 7750*7c478bd9Sstevel@tonic-gate bp = kmem_alloc(len, KM_SLEEP); 7751*7c478bd9Sstevel@tonic-gate (void) strcpy(bp, np); 7752*7c478bd9Sstevel@tonic-gate if ((ap = strchr(bp, '@')) != NULL) { 7753*7c478bd9Sstevel@tonic-gate *ap = '\0'; 7754*7c478bd9Sstevel@tonic-gate } 7755*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %d\n", pmf, ddi_name_to_major(np))) 7756*7c478bd9Sstevel@tonic-gate ret = i_path_to_major(path, np); 7757*7c478bd9Sstevel@tonic-gate kmem_free(bp, len); 7758*7c478bd9Sstevel@tonic-gate return (ret); 7759*7c478bd9Sstevel@tonic-gate } 7760*7c478bd9Sstevel@tonic-gate 7761*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 7762*7c478bd9Sstevel@tonic-gate 7763*7c478bd9Sstevel@tonic-gate char *pm_msgp; 7764*7c478bd9Sstevel@tonic-gate char *pm_bufend; 7765*7c478bd9Sstevel@tonic-gate char *pm_msgbuf = NULL; 7766*7c478bd9Sstevel@tonic-gate int pm_logpages = 2; 7767*7c478bd9Sstevel@tonic-gate 7768*7c478bd9Sstevel@tonic-gate #define PMLOGPGS pm_logpages 7769*7c478bd9Sstevel@tonic-gate 7770*7c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/ 7771*7c478bd9Sstevel@tonic-gate void 7772*7c478bd9Sstevel@tonic-gate pm_log(const char *fmt, ...) 7773*7c478bd9Sstevel@tonic-gate { 7774*7c478bd9Sstevel@tonic-gate va_list adx; 7775*7c478bd9Sstevel@tonic-gate size_t size; 7776*7c478bd9Sstevel@tonic-gate 7777*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 7778*7c478bd9Sstevel@tonic-gate if (pm_msgbuf == NULL) { 7779*7c478bd9Sstevel@tonic-gate pm_msgbuf = kmem_zalloc(mmu_ptob(PMLOGPGS), KM_SLEEP); 7780*7c478bd9Sstevel@tonic-gate pm_bufend = pm_msgbuf + mmu_ptob(PMLOGPGS) - 1; 7781*7c478bd9Sstevel@tonic-gate pm_msgp = pm_msgbuf; 7782*7c478bd9Sstevel@tonic-gate } 7783*7c478bd9Sstevel@tonic-gate va_start(adx, fmt); 7784*7c478bd9Sstevel@tonic-gate size = vsnprintf(NULL, 0, fmt, adx) + 1; 7785*7c478bd9Sstevel@tonic-gate va_end(adx); 7786*7c478bd9Sstevel@tonic-gate va_start(adx, fmt); 7787*7c478bd9Sstevel@tonic-gate if (size > (pm_bufend - pm_msgp)) { /* wraps */ 7788*7c478bd9Sstevel@tonic-gate bzero(pm_msgp, pm_bufend - pm_msgp); 7789*7c478bd9Sstevel@tonic-gate (void) vsnprintf(pm_msgbuf, size, fmt, adx); 7790*7c478bd9Sstevel@tonic-gate if (!pm_divertdebug) 7791*7c478bd9Sstevel@tonic-gate prom_printf("%s", pm_msgp); 7792*7c478bd9Sstevel@tonic-gate pm_msgp = pm_msgbuf + size; 7793*7c478bd9Sstevel@tonic-gate } else { 7794*7c478bd9Sstevel@tonic-gate (void) vsnprintf(pm_msgp, size, fmt, adx); 7795*7c478bd9Sstevel@tonic-gate if (!pm_divertdebug) 7796*7c478bd9Sstevel@tonic-gate prom_printf("%s", pm_msgp); 7797*7c478bd9Sstevel@tonic-gate pm_msgp += size; 7798*7c478bd9Sstevel@tonic-gate } 7799*7c478bd9Sstevel@tonic-gate va_end(adx); 7800*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 7801*7c478bd9Sstevel@tonic-gate } 7802*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */ 7803*7c478bd9Sstevel@tonic-gate 7804*7c478bd9Sstevel@tonic-gate /* 7805*7c478bd9Sstevel@tonic-gate * We want to save the state of any directly pm'd devices over the suspend/ 7806*7c478bd9Sstevel@tonic-gate * resume process so that we can put them back the way the controlling 7807*7c478bd9Sstevel@tonic-gate * process left them. 7808*7c478bd9Sstevel@tonic-gate */ 7809*7c478bd9Sstevel@tonic-gate void 7810*7c478bd9Sstevel@tonic-gate pm_save_direct_levels(void) 7811*7c478bd9Sstevel@tonic-gate { 7812*7c478bd9Sstevel@tonic-gate pm_processes_stopped = 1; 7813*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_save_direct_lvl_walk, 0); 7814*7c478bd9Sstevel@tonic-gate } 7815*7c478bd9Sstevel@tonic-gate 7816*7c478bd9Sstevel@tonic-gate static int 7817*7c478bd9Sstevel@tonic-gate pm_save_direct_lvl_walk(dev_info_t *dip, void *arg) 7818*7c478bd9Sstevel@tonic-gate { 7819*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 7820*7c478bd9Sstevel@tonic-gate int i; 7821*7c478bd9Sstevel@tonic-gate int *ip; 7822*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 7823*7c478bd9Sstevel@tonic-gate 7824*7c478bd9Sstevel@tonic-gate if (!info) 7825*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7826*7c478bd9Sstevel@tonic-gate 7827*7c478bd9Sstevel@tonic-gate if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) { 7828*7c478bd9Sstevel@tonic-gate if (PM_NUMCMPTS(dip) > 2) { 7829*7c478bd9Sstevel@tonic-gate info->pmi_lp = kmem_alloc(PM_NUMCMPTS(dip) * 7830*7c478bd9Sstevel@tonic-gate sizeof (int), KM_SLEEP); 7831*7c478bd9Sstevel@tonic-gate ip = info->pmi_lp; 7832*7c478bd9Sstevel@tonic-gate } else { 7833*7c478bd9Sstevel@tonic-gate ip = info->pmi_levels; 7834*7c478bd9Sstevel@tonic-gate } 7835*7c478bd9Sstevel@tonic-gate /* autopm and processes are stopped, ok not to lock power */ 7836*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 7837*7c478bd9Sstevel@tonic-gate *ip++ = PM_CURPOWER(dip, i); 7838*7c478bd9Sstevel@tonic-gate /* 7839*7c478bd9Sstevel@tonic-gate * There is a small window between stopping the 7840*7c478bd9Sstevel@tonic-gate * processes and setting pm_processes_stopped where 7841*7c478bd9Sstevel@tonic-gate * a driver could get hung up in a pm_raise_power() 7842*7c478bd9Sstevel@tonic-gate * call. Free any such driver now. 7843*7c478bd9Sstevel@tonic-gate */ 7844*7c478bd9Sstevel@tonic-gate pm_proceed(dip, PMP_RELEASE, -1, -1); 7845*7c478bd9Sstevel@tonic-gate } 7846*7c478bd9Sstevel@tonic-gate 7847*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7848*7c478bd9Sstevel@tonic-gate } 7849*7c478bd9Sstevel@tonic-gate 7850*7c478bd9Sstevel@tonic-gate void 7851*7c478bd9Sstevel@tonic-gate pm_restore_direct_levels(void) 7852*7c478bd9Sstevel@tonic-gate { 7853*7c478bd9Sstevel@tonic-gate /* 7854*7c478bd9Sstevel@tonic-gate * If cpr didn't call pm_save_direct_levels, (because stopping user 7855*7c478bd9Sstevel@tonic-gate * threads failed) then we don't want to try to restore them 7856*7c478bd9Sstevel@tonic-gate */ 7857*7c478bd9Sstevel@tonic-gate if (!pm_processes_stopped) 7858*7c478bd9Sstevel@tonic-gate return; 7859*7c478bd9Sstevel@tonic-gate 7860*7c478bd9Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_restore_direct_lvl_walk, 0); 7861*7c478bd9Sstevel@tonic-gate pm_processes_stopped = 0; 7862*7c478bd9Sstevel@tonic-gate } 7863*7c478bd9Sstevel@tonic-gate 7864*7c478bd9Sstevel@tonic-gate static int 7865*7c478bd9Sstevel@tonic-gate pm_restore_direct_lvl_walk(dev_info_t *dip, void *arg) 7866*7c478bd9Sstevel@tonic-gate { 7867*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 7868*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "restore_direct_lvl_walk") 7869*7c478bd9Sstevel@tonic-gate int i, nc, result; 7870*7c478bd9Sstevel@tonic-gate int *ip; 7871*7c478bd9Sstevel@tonic-gate 7872*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 7873*7c478bd9Sstevel@tonic-gate if (!info) 7874*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7875*7c478bd9Sstevel@tonic-gate 7876*7c478bd9Sstevel@tonic-gate if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) { 7877*7c478bd9Sstevel@tonic-gate if ((nc = PM_NUMCMPTS(dip)) > 2) { 7878*7c478bd9Sstevel@tonic-gate ip = &info->pmi_lp[nc - 1]; 7879*7c478bd9Sstevel@tonic-gate } else { 7880*7c478bd9Sstevel@tonic-gate ip = &info->pmi_levels[nc - 1]; 7881*7c478bd9Sstevel@tonic-gate } 7882*7c478bd9Sstevel@tonic-gate /* 7883*7c478bd9Sstevel@tonic-gate * Because fb drivers fail attempts to turn off the 7884*7c478bd9Sstevel@tonic-gate * fb when the monitor is on, but treat a request to 7885*7c478bd9Sstevel@tonic-gate * turn on the monitor as a request to turn on the 7886*7c478bd9Sstevel@tonic-gate * fb too, we process components in descending order 7887*7c478bd9Sstevel@tonic-gate * Because autopm is disabled and processes aren't 7888*7c478bd9Sstevel@tonic-gate * running, it is ok to examine current power outside 7889*7c478bd9Sstevel@tonic-gate * of the power lock 7890*7c478bd9Sstevel@tonic-gate */ 7891*7c478bd9Sstevel@tonic-gate for (i = nc - 1; i >= 0; i--, ip--) { 7892*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == *ip) 7893*7c478bd9Sstevel@tonic-gate continue; 7894*7c478bd9Sstevel@tonic-gate if (pm_set_power(dip, i, *ip, PM_LEVEL_EXACT, 7895*7c478bd9Sstevel@tonic-gate PM_CANBLOCK_BYPASS, 0, &result) != 7896*7c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 7897*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable " 7898*7c478bd9Sstevel@tonic-gate "to restore power level of " 7899*7c478bd9Sstevel@tonic-gate "component %d of directly " 7900*7c478bd9Sstevel@tonic-gate "power manged device %s@%s" 7901*7c478bd9Sstevel@tonic-gate " to %d", 7902*7c478bd9Sstevel@tonic-gate i, PM_NAME(dip), 7903*7c478bd9Sstevel@tonic-gate PM_ADDR(dip), *ip); 7904*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to restore " 7905*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)[%d] exact(%d)->%d, " 7906*7c478bd9Sstevel@tonic-gate "errno %d\n", pmf, PM_DEVICE(dip), i, 7907*7c478bd9Sstevel@tonic-gate PM_CURPOWER(dip, i), *ip, result)) 7908*7c478bd9Sstevel@tonic-gate } 7909*7c478bd9Sstevel@tonic-gate } 7910*7c478bd9Sstevel@tonic-gate if (nc > 2) { 7911*7c478bd9Sstevel@tonic-gate kmem_free(info->pmi_lp, nc * sizeof (int)); 7912*7c478bd9Sstevel@tonic-gate info->pmi_lp = NULL; 7913*7c478bd9Sstevel@tonic-gate } 7914*7c478bd9Sstevel@tonic-gate } 7915*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7916*7c478bd9Sstevel@tonic-gate } 7917*7c478bd9Sstevel@tonic-gate 7918*7c478bd9Sstevel@tonic-gate /* 7919*7c478bd9Sstevel@tonic-gate * Stolen from the bootdev module 7920*7c478bd9Sstevel@tonic-gate * attempt to convert a path to a major number 7921*7c478bd9Sstevel@tonic-gate */ 7922*7c478bd9Sstevel@tonic-gate static major_t 7923*7c478bd9Sstevel@tonic-gate i_path_to_major(char *path, char *leaf_name) 7924*7c478bd9Sstevel@tonic-gate { 7925*7c478bd9Sstevel@tonic-gate extern major_t path_to_major(char *pathname); 7926*7c478bd9Sstevel@tonic-gate major_t maj; 7927*7c478bd9Sstevel@tonic-gate 7928*7c478bd9Sstevel@tonic-gate if ((maj = path_to_major(path)) == (major_t)-1) { 7929*7c478bd9Sstevel@tonic-gate maj = ddi_name_to_major(leaf_name); 7930*7c478bd9Sstevel@tonic-gate } 7931*7c478bd9Sstevel@tonic-gate 7932*7c478bd9Sstevel@tonic-gate return (maj); 7933*7c478bd9Sstevel@tonic-gate } 7934*7c478bd9Sstevel@tonic-gate 7935*7c478bd9Sstevel@tonic-gate /* 7936*7c478bd9Sstevel@tonic-gate * When user calls rem_drv, we need to forget no-involuntary-power-cycles state 7937*7c478bd9Sstevel@tonic-gate * An entry in the list means that the device is detached, so we need to 7938*7c478bd9Sstevel@tonic-gate * adjust its ancestors as if they had just seen this attach, and any detached 7939*7c478bd9Sstevel@tonic-gate * ancestors need to have their list entries adjusted. 7940*7c478bd9Sstevel@tonic-gate */ 7941*7c478bd9Sstevel@tonic-gate void 7942*7c478bd9Sstevel@tonic-gate pm_driver_removed(major_t major) 7943*7c478bd9Sstevel@tonic-gate { 7944*7c478bd9Sstevel@tonic-gate static void i_pm_driver_removed(major_t major); 7945*7c478bd9Sstevel@tonic-gate 7946*7c478bd9Sstevel@tonic-gate /* 7947*7c478bd9Sstevel@tonic-gate * Serialize removal of drivers. This is to keep ancestors of 7948*7c478bd9Sstevel@tonic-gate * a node that is being deleted from getting deleted and added back 7949*7c478bd9Sstevel@tonic-gate * with different counters. 7950*7c478bd9Sstevel@tonic-gate */ 7951*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_remdrv_lock); 7952*7c478bd9Sstevel@tonic-gate i_pm_driver_removed(major); 7953*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_remdrv_lock); 7954*7c478bd9Sstevel@tonic-gate } 7955*7c478bd9Sstevel@tonic-gate 7956*7c478bd9Sstevel@tonic-gate /* 7957*7c478bd9Sstevel@tonic-gate * This routine is called recursively by pm_noinvol_process_ancestors() 7958*7c478bd9Sstevel@tonic-gate */ 7959*7c478bd9Sstevel@tonic-gate static void 7960*7c478bd9Sstevel@tonic-gate i_pm_driver_removed(major_t major) 7961*7c478bd9Sstevel@tonic-gate { 7962*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "driver_removed") 7963*7c478bd9Sstevel@tonic-gate static void adjust_ancestors(char *, int); 7964*7c478bd9Sstevel@tonic-gate static int pm_is_noinvol_ancestor(pm_noinvol_t *); 7965*7c478bd9Sstevel@tonic-gate static void pm_noinvol_process_ancestors(char *); 7966*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip, *pp = NULL; 7967*7c478bd9Sstevel@tonic-gate int wasvolpmd; 7968*7c478bd9Sstevel@tonic-gate ASSERT(major != (major_t)-1); 7969*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s\n", pmf, ddi_major_to_name(major))) 7970*7c478bd9Sstevel@tonic-gate again: 7971*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 7972*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7973*7c478bd9Sstevel@tonic-gate if (major != ip->ni_major) 7974*7c478bd9Sstevel@tonic-gate continue; 7975*7c478bd9Sstevel@tonic-gate /* 7976*7c478bd9Sstevel@tonic-gate * If it is an ancestor of no-invol node, which is 7977*7c478bd9Sstevel@tonic-gate * not removed, skip it. This is to cover the case of 7978*7c478bd9Sstevel@tonic-gate * ancestor removed without removing its descendants. 7979*7c478bd9Sstevel@tonic-gate */ 7980*7c478bd9Sstevel@tonic-gate if (pm_is_noinvol_ancestor(ip)) { 7981*7c478bd9Sstevel@tonic-gate ip->ni_flags |= PMC_DRIVER_REMOVED; 7982*7c478bd9Sstevel@tonic-gate continue; 7983*7c478bd9Sstevel@tonic-gate } 7984*7c478bd9Sstevel@tonic-gate wasvolpmd = ip->ni_wasvolpmd; 7985*7c478bd9Sstevel@tonic-gate /* 7986*7c478bd9Sstevel@tonic-gate * remove the entry from the list 7987*7c478bd9Sstevel@tonic-gate */ 7988*7c478bd9Sstevel@tonic-gate if (pp) { 7989*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: freeing %s, prev is %s\n", 7990*7c478bd9Sstevel@tonic-gate pmf, ip->ni_path, pp->ni_path)) 7991*7c478bd9Sstevel@tonic-gate pp->ni_next = ip->ni_next; 7992*7c478bd9Sstevel@tonic-gate } else { 7993*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s head\n", pmf, 7994*7c478bd9Sstevel@tonic-gate ip->ni_path)) 7995*7c478bd9Sstevel@tonic-gate ASSERT(pm_noinvol_head == ip); 7996*7c478bd9Sstevel@tonic-gate pm_noinvol_head = ip->ni_next; 7997*7c478bd9Sstevel@tonic-gate } 7998*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7999*7c478bd9Sstevel@tonic-gate adjust_ancestors(ip->ni_path, wasvolpmd); 8000*7c478bd9Sstevel@tonic-gate /* 8001*7c478bd9Sstevel@tonic-gate * Had an ancestor been removed before this node, it would have 8002*7c478bd9Sstevel@tonic-gate * been skipped. Adjust the no-invol counters for such skipped 8003*7c478bd9Sstevel@tonic-gate * ancestors. 8004*7c478bd9Sstevel@tonic-gate */ 8005*7c478bd9Sstevel@tonic-gate pm_noinvol_process_ancestors(ip->ni_path); 8006*7c478bd9Sstevel@tonic-gate kmem_free(ip->ni_path, ip->ni_size); 8007*7c478bd9Sstevel@tonic-gate kmem_free(ip, sizeof (*ip)); 8008*7c478bd9Sstevel@tonic-gate goto again; 8009*7c478bd9Sstevel@tonic-gate } 8010*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8011*7c478bd9Sstevel@tonic-gate } 8012*7c478bd9Sstevel@tonic-gate 8013*7c478bd9Sstevel@tonic-gate /* 8014*7c478bd9Sstevel@tonic-gate * returns 1, if *aip is a ancestor of a no-invol node 8015*7c478bd9Sstevel@tonic-gate * 0, otherwise 8016*7c478bd9Sstevel@tonic-gate */ 8017*7c478bd9Sstevel@tonic-gate static int 8018*7c478bd9Sstevel@tonic-gate pm_is_noinvol_ancestor(pm_noinvol_t *aip) 8019*7c478bd9Sstevel@tonic-gate { 8020*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip; 8021*7c478bd9Sstevel@tonic-gate 8022*7c478bd9Sstevel@tonic-gate ASSERT(strlen(aip->ni_path) != 0); 8023*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 8024*7c478bd9Sstevel@tonic-gate if (ip == aip) 8025*7c478bd9Sstevel@tonic-gate continue; 8026*7c478bd9Sstevel@tonic-gate /* 8027*7c478bd9Sstevel@tonic-gate * To be an ancestor, the path must be an initial substring of 8028*7c478bd9Sstevel@tonic-gate * the descendent, and end just before a '/' in the 8029*7c478bd9Sstevel@tonic-gate * descendent's path. 8030*7c478bd9Sstevel@tonic-gate */ 8031*7c478bd9Sstevel@tonic-gate if ((strstr(ip->ni_path, aip->ni_path) == ip->ni_path) && 8032*7c478bd9Sstevel@tonic-gate (ip->ni_path[strlen(aip->ni_path)] == '/')) 8033*7c478bd9Sstevel@tonic-gate return (1); 8034*7c478bd9Sstevel@tonic-gate } 8035*7c478bd9Sstevel@tonic-gate return (0); 8036*7c478bd9Sstevel@tonic-gate } 8037*7c478bd9Sstevel@tonic-gate 8038*7c478bd9Sstevel@tonic-gate #define PM_MAJOR(dip) ddi_name_to_major(ddi_binding_name(dip)) 8039*7c478bd9Sstevel@tonic-gate /* 8040*7c478bd9Sstevel@tonic-gate * scan through the pm_noinvolpm list adjusting ancestors of the current 8041*7c478bd9Sstevel@tonic-gate * node; Modifies string *path. 8042*7c478bd9Sstevel@tonic-gate */ 8043*7c478bd9Sstevel@tonic-gate static void 8044*7c478bd9Sstevel@tonic-gate adjust_ancestors(char *path, int wasvolpmd) 8045*7c478bd9Sstevel@tonic-gate { 8046*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "adjust_ancestors") 8047*7c478bd9Sstevel@tonic-gate char *cp; 8048*7c478bd9Sstevel@tonic-gate pm_noinvol_t *lp; 8049*7c478bd9Sstevel@tonic-gate pm_noinvol_t *pp = NULL; 8050*7c478bd9Sstevel@tonic-gate major_t locked = (major_t)UINT_MAX; 8051*7c478bd9Sstevel@tonic-gate dev_info_t *dip; 8052*7c478bd9Sstevel@tonic-gate char *pathbuf; 8053*7c478bd9Sstevel@tonic-gate size_t pathbuflen = strlen(path) + 1; 8054*7c478bd9Sstevel@tonic-gate 8055*7c478bd9Sstevel@tonic-gate /* 8056*7c478bd9Sstevel@tonic-gate * First we look up the ancestor's dip. If we find it, then we 8057*7c478bd9Sstevel@tonic-gate * adjust counts up the tree 8058*7c478bd9Sstevel@tonic-gate */ 8059*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s wasvolpmd %d\n", pmf, path, wasvolpmd)) 8060*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(pathbuflen, KM_SLEEP); 8061*7c478bd9Sstevel@tonic-gate (void) strcpy(pathbuf, path); 8062*7c478bd9Sstevel@tonic-gate cp = strrchr(pathbuf, '/'); 8063*7c478bd9Sstevel@tonic-gate if (cp == NULL) { 8064*7c478bd9Sstevel@tonic-gate /* if no ancestors, then nothing to do */ 8065*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, pathbuflen); 8066*7c478bd9Sstevel@tonic-gate return; 8067*7c478bd9Sstevel@tonic-gate } 8068*7c478bd9Sstevel@tonic-gate *cp = '\0'; 8069*7c478bd9Sstevel@tonic-gate dip = pm_name_to_dip(pathbuf, 1); 8070*7c478bd9Sstevel@tonic-gate if (dip != NULL) { 8071*7c478bd9Sstevel@tonic-gate locked = PM_MAJOR(dip); 8072*7c478bd9Sstevel@tonic-gate 8073*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_REMDRV, 0, wasvolpmd, 8074*7c478bd9Sstevel@tonic-gate path, dip); 8075*7c478bd9Sstevel@tonic-gate 8076*7c478bd9Sstevel@tonic-gate if (locked != (major_t)UINT_MAX) 8077*7c478bd9Sstevel@tonic-gate ddi_release_devi(dip); 8078*7c478bd9Sstevel@tonic-gate } else { 8079*7c478bd9Sstevel@tonic-gate char *apath; 8080*7c478bd9Sstevel@tonic-gate size_t len = strlen(pathbuf) + 1; 8081*7c478bd9Sstevel@tonic-gate int lock_held = 1; 8082*7c478bd9Sstevel@tonic-gate 8083*7c478bd9Sstevel@tonic-gate /* 8084*7c478bd9Sstevel@tonic-gate * Now check for ancestors that exist only in the list 8085*7c478bd9Sstevel@tonic-gate */ 8086*7c478bd9Sstevel@tonic-gate apath = kmem_alloc(len, KM_SLEEP); 8087*7c478bd9Sstevel@tonic-gate (void) strcpy(apath, pathbuf); 8088*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 8089*7c478bd9Sstevel@tonic-gate for (lp = pm_noinvol_head; lp; pp = lp, lp = lp->ni_next) { 8090*7c478bd9Sstevel@tonic-gate /* 8091*7c478bd9Sstevel@tonic-gate * This can only happen once. Since we have to drop 8092*7c478bd9Sstevel@tonic-gate * the lock, we need to extract the relevant info. 8093*7c478bd9Sstevel@tonic-gate */ 8094*7c478bd9Sstevel@tonic-gate if (strcmp(pathbuf, lp->ni_path) == 0) { 8095*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s no %d -> %d\n", pmf, 8096*7c478bd9Sstevel@tonic-gate lp->ni_path, lp->ni_noinvolpm, 8097*7c478bd9Sstevel@tonic-gate lp->ni_noinvolpm - 1)) 8098*7c478bd9Sstevel@tonic-gate lp->ni_noinvolpm--; 8099*7c478bd9Sstevel@tonic-gate if (wasvolpmd && lp->ni_volpmd) { 8100*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s vol %d -> " 8101*7c478bd9Sstevel@tonic-gate "%d\n", pmf, lp->ni_path, 8102*7c478bd9Sstevel@tonic-gate lp->ni_volpmd, lp->ni_volpmd - 1)) 8103*7c478bd9Sstevel@tonic-gate lp->ni_volpmd--; 8104*7c478bd9Sstevel@tonic-gate } 8105*7c478bd9Sstevel@tonic-gate /* 8106*7c478bd9Sstevel@tonic-gate * remove the entry from the list, if there 8107*7c478bd9Sstevel@tonic-gate * are no more no-invol descendants and node 8108*7c478bd9Sstevel@tonic-gate * itself is not a no-invol node. 8109*7c478bd9Sstevel@tonic-gate */ 8110*7c478bd9Sstevel@tonic-gate if (!(lp->ni_noinvolpm || 8111*7c478bd9Sstevel@tonic-gate (lp->ni_flags & PMC_NO_INVOL))) { 8112*7c478bd9Sstevel@tonic-gate ASSERT(lp->ni_volpmd == 0); 8113*7c478bd9Sstevel@tonic-gate if (pp) { 8114*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: freeing " 8115*7c478bd9Sstevel@tonic-gate "%s, prev is %s\n", pmf, 8116*7c478bd9Sstevel@tonic-gate lp->ni_path, pp->ni_path)) 8117*7c478bd9Sstevel@tonic-gate pp->ni_next = lp->ni_next; 8118*7c478bd9Sstevel@tonic-gate } else { 8119*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s " 8120*7c478bd9Sstevel@tonic-gate "head\n", pmf, lp->ni_path)) 8121*7c478bd9Sstevel@tonic-gate ASSERT(pm_noinvol_head == lp); 8122*7c478bd9Sstevel@tonic-gate pm_noinvol_head = lp->ni_next; 8123*7c478bd9Sstevel@tonic-gate } 8124*7c478bd9Sstevel@tonic-gate lock_held = 0; 8125*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8126*7c478bd9Sstevel@tonic-gate adjust_ancestors(apath, wasvolpmd); 8127*7c478bd9Sstevel@tonic-gate /* restore apath */ 8128*7c478bd9Sstevel@tonic-gate (void) strcpy(apath, pathbuf); 8129*7c478bd9Sstevel@tonic-gate kmem_free(lp->ni_path, lp->ni_size); 8130*7c478bd9Sstevel@tonic-gate kmem_free(lp, sizeof (*lp)); 8131*7c478bd9Sstevel@tonic-gate } 8132*7c478bd9Sstevel@tonic-gate break; 8133*7c478bd9Sstevel@tonic-gate } 8134*7c478bd9Sstevel@tonic-gate } 8135*7c478bd9Sstevel@tonic-gate if (lock_held) 8136*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8137*7c478bd9Sstevel@tonic-gate adjust_ancestors(apath, wasvolpmd); 8138*7c478bd9Sstevel@tonic-gate kmem_free(apath, len); 8139*7c478bd9Sstevel@tonic-gate } 8140*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, pathbuflen); 8141*7c478bd9Sstevel@tonic-gate } 8142*7c478bd9Sstevel@tonic-gate 8143*7c478bd9Sstevel@tonic-gate /* 8144*7c478bd9Sstevel@tonic-gate * Do no-invol processing for any ancestors i.e. adjust counters of ancestors, 8145*7c478bd9Sstevel@tonic-gate * which were skipped even though their drivers were removed. 8146*7c478bd9Sstevel@tonic-gate */ 8147*7c478bd9Sstevel@tonic-gate static void 8148*7c478bd9Sstevel@tonic-gate pm_noinvol_process_ancestors(char *path) 8149*7c478bd9Sstevel@tonic-gate { 8150*7c478bd9Sstevel@tonic-gate pm_noinvol_t *lp; 8151*7c478bd9Sstevel@tonic-gate 8152*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 8153*7c478bd9Sstevel@tonic-gate for (lp = pm_noinvol_head; lp; lp = lp->ni_next) { 8154*7c478bd9Sstevel@tonic-gate if (strstr(path, lp->ni_path) && 8155*7c478bd9Sstevel@tonic-gate (lp->ni_flags & PMC_DRIVER_REMOVED)) { 8156*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8157*7c478bd9Sstevel@tonic-gate i_pm_driver_removed(lp->ni_major); 8158*7c478bd9Sstevel@tonic-gate return; 8159*7c478bd9Sstevel@tonic-gate } 8160*7c478bd9Sstevel@tonic-gate } 8161*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8162*7c478bd9Sstevel@tonic-gate } 8163*7c478bd9Sstevel@tonic-gate 8164*7c478bd9Sstevel@tonic-gate /* 8165*7c478bd9Sstevel@tonic-gate * Returns true if (detached) device needs to be kept up because it exported the 8166*7c478bd9Sstevel@tonic-gate * "no-involuntary-power-cycles" property or we're pretending it did (console 8167*7c478bd9Sstevel@tonic-gate * fb case) or it is an ancestor of such a device and has used up the "one 8168*7c478bd9Sstevel@tonic-gate * free cycle" allowed when all such leaf nodes have voluntarily powered down 8169*7c478bd9Sstevel@tonic-gate * upon detach. In any event, we need an exact hit on the path or we return 8170*7c478bd9Sstevel@tonic-gate * false. 8171*7c478bd9Sstevel@tonic-gate */ 8172*7c478bd9Sstevel@tonic-gate int 8173*7c478bd9Sstevel@tonic-gate pm_noinvol_detached(char *path) 8174*7c478bd9Sstevel@tonic-gate { 8175*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_detached") 8176*7c478bd9Sstevel@tonic-gate pm_noinvol_t *ip; 8177*7c478bd9Sstevel@tonic-gate int ret = 0; 8178*7c478bd9Sstevel@tonic-gate 8179*7c478bd9Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 8180*7c478bd9Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 8181*7c478bd9Sstevel@tonic-gate if (strcmp(path, ip->ni_path) == 0) { 8182*7c478bd9Sstevel@tonic-gate if (ip->ni_flags & PMC_CONSOLE_FB) { 8183*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB " 8184*7c478bd9Sstevel@tonic-gate "%s\n", pmf, path)) 8185*7c478bd9Sstevel@tonic-gate ret = 1; 8186*7c478bd9Sstevel@tonic-gate break; 8187*7c478bd9Sstevel@tonic-gate } 8188*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 8189*7c478bd9Sstevel@tonic-gate if (ip->ni_noinvolpm != ip->ni_volpmd) 8190*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s" 8191*7c478bd9Sstevel@tonic-gate "\n", pmf, ip->ni_noinvolpm, ip->ni_volpmd, 8192*7c478bd9Sstevel@tonic-gate path)) 8193*7c478bd9Sstevel@tonic-gate #endif 8194*7c478bd9Sstevel@tonic-gate ret = (ip->ni_noinvolpm != ip->ni_volpmd); 8195*7c478bd9Sstevel@tonic-gate break; 8196*7c478bd9Sstevel@tonic-gate } 8197*7c478bd9Sstevel@tonic-gate } 8198*7c478bd9Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8199*7c478bd9Sstevel@tonic-gate return (ret); 8200*7c478bd9Sstevel@tonic-gate } 8201*7c478bd9Sstevel@tonic-gate 8202*7c478bd9Sstevel@tonic-gate int 8203*7c478bd9Sstevel@tonic-gate pm_is_cfb(dev_info_t *dip) 8204*7c478bd9Sstevel@tonic-gate { 8205*7c478bd9Sstevel@tonic-gate return (dip == cfb_dip); 8206*7c478bd9Sstevel@tonic-gate } 8207*7c478bd9Sstevel@tonic-gate 8208*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 8209*7c478bd9Sstevel@tonic-gate /* 8210*7c478bd9Sstevel@tonic-gate * Return true if all components of the console frame buffer are at 8211*7c478bd9Sstevel@tonic-gate * "normal" power, i.e., fully on. For the case where the console is not 8212*7c478bd9Sstevel@tonic-gate * a framebuffer, we also return true 8213*7c478bd9Sstevel@tonic-gate */ 8214*7c478bd9Sstevel@tonic-gate int 8215*7c478bd9Sstevel@tonic-gate pm_cfb_is_up(void) 8216*7c478bd9Sstevel@tonic-gate { 8217*7c478bd9Sstevel@tonic-gate return (pm_cfb_comps_off == 0); 8218*7c478bd9Sstevel@tonic-gate } 8219*7c478bd9Sstevel@tonic-gate #endif 8220*7c478bd9Sstevel@tonic-gate 8221*7c478bd9Sstevel@tonic-gate /* 8222*7c478bd9Sstevel@tonic-gate * Preventing scan from powering down the node by incrementing the 8223*7c478bd9Sstevel@tonic-gate * kidsupcnt. 8224*7c478bd9Sstevel@tonic-gate */ 8225*7c478bd9Sstevel@tonic-gate void 8226*7c478bd9Sstevel@tonic-gate pm_hold_power(dev_info_t *dip) 8227*7c478bd9Sstevel@tonic-gate { 8228*7c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(dip, 1); 8229*7c478bd9Sstevel@tonic-gate } 8230*7c478bd9Sstevel@tonic-gate 8231*7c478bd9Sstevel@tonic-gate /* 8232*7c478bd9Sstevel@tonic-gate * Releasing the hold by decrementing the kidsupcnt allowing scan 8233*7c478bd9Sstevel@tonic-gate * to power down the node if all conditions are met. 8234*7c478bd9Sstevel@tonic-gate */ 8235*7c478bd9Sstevel@tonic-gate void 8236*7c478bd9Sstevel@tonic-gate pm_rele_power(dev_info_t *dip) 8237*7c478bd9Sstevel@tonic-gate { 8238*7c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(dip, -1); 8239*7c478bd9Sstevel@tonic-gate } 8240*7c478bd9Sstevel@tonic-gate 8241*7c478bd9Sstevel@tonic-gate /* 8242*7c478bd9Sstevel@tonic-gate * A wrapper of pm_all_to_normal() to power up a dip 8243*7c478bd9Sstevel@tonic-gate * to its normal level 8244*7c478bd9Sstevel@tonic-gate */ 8245*7c478bd9Sstevel@tonic-gate int 8246*7c478bd9Sstevel@tonic-gate pm_powerup(dev_info_t *dip) 8247*7c478bd9Sstevel@tonic-gate { 8248*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "pm_powerup") 8249*7c478bd9Sstevel@tonic-gate 8250*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 8251*7c478bd9Sstevel@tonic-gate ASSERT(!(servicing_interrupt())); 8252*7c478bd9Sstevel@tonic-gate 8253*7c478bd9Sstevel@tonic-gate /* 8254*7c478bd9Sstevel@tonic-gate * in case this node is not already participating pm 8255*7c478bd9Sstevel@tonic-gate */ 8256*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) { 8257*7c478bd9Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) 8258*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8259*7c478bd9Sstevel@tonic-gate if (pm_start(dip) != DDI_SUCCESS) 8260*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8261*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) 8262*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8263*7c478bd9Sstevel@tonic-gate } 8264*7c478bd9Sstevel@tonic-gate 8265*7c478bd9Sstevel@tonic-gate return (pm_all_to_normal(dip, PM_CANBLOCK_BLOCK)); 8266*7c478bd9Sstevel@tonic-gate } 8267*7c478bd9Sstevel@tonic-gate 8268*7c478bd9Sstevel@tonic-gate int 8269*7c478bd9Sstevel@tonic-gate pm_rescan_walk(dev_info_t *dip, void *arg) 8270*7c478bd9Sstevel@tonic-gate { 8271*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 8272*7c478bd9Sstevel@tonic-gate 8273*7c478bd9Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) 8274*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 8275*7c478bd9Sstevel@tonic-gate 8276*7c478bd9Sstevel@tonic-gate /* 8277*7c478bd9Sstevel@tonic-gate * Currently pm_cpr_callb/resume code is the only caller 8278*7c478bd9Sstevel@tonic-gate * and it needs to make sure that stopped scan get 8279*7c478bd9Sstevel@tonic-gate * reactivated. Otherwise, rescan walk needn't reactive 8280*7c478bd9Sstevel@tonic-gate * stopped scan. 8281*7c478bd9Sstevel@tonic-gate */ 8282*7c478bd9Sstevel@tonic-gate pm_scan_init(dip); 8283*7c478bd9Sstevel@tonic-gate 8284*7c478bd9Sstevel@tonic-gate (void) pm_rescan(dip); 8285*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 8286*7c478bd9Sstevel@tonic-gate } 8287*7c478bd9Sstevel@tonic-gate 8288*7c478bd9Sstevel@tonic-gate static dev_info_t * 8289*7c478bd9Sstevel@tonic-gate pm_get_next_descendent(dev_info_t *dip, dev_info_t *tdip) 8290*7c478bd9Sstevel@tonic-gate { 8291*7c478bd9Sstevel@tonic-gate dev_info_t *wdip, *pdip; 8292*7c478bd9Sstevel@tonic-gate 8293*7c478bd9Sstevel@tonic-gate for (wdip = tdip; wdip != dip; wdip = pdip) { 8294*7c478bd9Sstevel@tonic-gate pdip = ddi_get_parent(wdip); 8295*7c478bd9Sstevel@tonic-gate if (pdip == dip) 8296*7c478bd9Sstevel@tonic-gate return (wdip); 8297*7c478bd9Sstevel@tonic-gate } 8298*7c478bd9Sstevel@tonic-gate return (NULL); 8299*7c478bd9Sstevel@tonic-gate } 8300*7c478bd9Sstevel@tonic-gate 8301*7c478bd9Sstevel@tonic-gate int 8302*7c478bd9Sstevel@tonic-gate pm_busop_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 8303*7c478bd9Sstevel@tonic-gate void *arg, void *result) 8304*7c478bd9Sstevel@tonic-gate { 8305*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "bp_bus_power") 8306*7c478bd9Sstevel@tonic-gate dev_info_t *cdip; 8307*7c478bd9Sstevel@tonic-gate pm_info_t *cinfo; 8308*7c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc; 8309*7c478bd9Sstevel@tonic-gate pm_sp_misc_t *pspm; 8310*7c478bd9Sstevel@tonic-gate pm_bp_nexus_pwrup_t *bpn; 8311*7c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t new_bpc; 8312*7c478bd9Sstevel@tonic-gate pm_bp_noinvol_t *bpi; 8313*7c478bd9Sstevel@tonic-gate dev_info_t *tdip; 8314*7c478bd9Sstevel@tonic-gate char *pathbuf; 8315*7c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 8316*7c478bd9Sstevel@tonic-gate int errno = 0; 8317*7c478bd9Sstevel@tonic-gate pm_component_t *cp; 8318*7c478bd9Sstevel@tonic-gate 8319*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip), 8320*7c478bd9Sstevel@tonic-gate pm_decode_op(op))) 8321*7c478bd9Sstevel@tonic-gate switch (op) { 8322*7c478bd9Sstevel@tonic-gate case BUS_POWER_CHILD_PWRCHG: 8323*7c478bd9Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg; 8324*7c478bd9Sstevel@tonic-gate pspm = (pm_sp_misc_t *)bpc->bpc_private; 8325*7c478bd9Sstevel@tonic-gate tdip = bpc->bpc_dip; 8326*7c478bd9Sstevel@tonic-gate cdip = pm_get_next_descendent(dip, tdip); 8327*7c478bd9Sstevel@tonic-gate cinfo = PM_GET_PM_INFO(cdip); 8328*7c478bd9Sstevel@tonic-gate if (cdip != tdip) { 8329*7c478bd9Sstevel@tonic-gate /* 8330*7c478bd9Sstevel@tonic-gate * If the node is an involved parent, it needs to 8331*7c478bd9Sstevel@tonic-gate * power up the node as it is needed. There is nothing 8332*7c478bd9Sstevel@tonic-gate * else the framework can do here. 8333*7c478bd9Sstevel@tonic-gate */ 8334*7c478bd9Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(cdip)) { 8335*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: call bus_power for " 8336*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(cdip))) 8337*7c478bd9Sstevel@tonic-gate return ((*PM_BUS_POWER_FUNC(cdip))(cdip, 8338*7c478bd9Sstevel@tonic-gate impl_arg, op, arg, result)); 8339*7c478bd9Sstevel@tonic-gate } 8340*7c478bd9Sstevel@tonic-gate ASSERT(pspm->pspm_direction == PM_LEVEL_UPONLY || 8341*7c478bd9Sstevel@tonic-gate pspm->pspm_direction == PM_LEVEL_DOWNONLY || 8342*7c478bd9Sstevel@tonic-gate pspm->pspm_direction == PM_LEVEL_EXACT); 8343*7c478bd9Sstevel@tonic-gate /* 8344*7c478bd9Sstevel@tonic-gate * we presume that the parent needs to be up in 8345*7c478bd9Sstevel@tonic-gate * order for the child to change state (either 8346*7c478bd9Sstevel@tonic-gate * because it must already be on if the child is on 8347*7c478bd9Sstevel@tonic-gate * (and the pm_all_to_normal_nexus() will be a nop) 8348*7c478bd9Sstevel@tonic-gate * or because it will need to be on for the child 8349*7c478bd9Sstevel@tonic-gate * to come on; so we make the call regardless 8350*7c478bd9Sstevel@tonic-gate */ 8351*7c478bd9Sstevel@tonic-gate pm_hold_power(cdip); 8352*7c478bd9Sstevel@tonic-gate if (cinfo) { 8353*7c478bd9Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock; 8354*7c478bd9Sstevel@tonic-gate ret = pm_all_to_normal_nexus(cdip, canblock); 8355*7c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 8356*7c478bd9Sstevel@tonic-gate pm_rele_power(cdip); 8357*7c478bd9Sstevel@tonic-gate return (ret); 8358*7c478bd9Sstevel@tonic-gate } 8359*7c478bd9Sstevel@tonic-gate } 8360*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: walk down to %s@%s(%s#%d)\n", pmf, 8361*7c478bd9Sstevel@tonic-gate PM_DEVICE(cdip))) 8362*7c478bd9Sstevel@tonic-gate ret = pm_busop_bus_power(cdip, impl_arg, op, arg, 8363*7c478bd9Sstevel@tonic-gate result); 8364*7c478bd9Sstevel@tonic-gate pm_rele_power(cdip); 8365*7c478bd9Sstevel@tonic-gate } else { 8366*7c478bd9Sstevel@tonic-gate ret = pm_busop_set_power(cdip, impl_arg, op, arg, 8367*7c478bd9Sstevel@tonic-gate result); 8368*7c478bd9Sstevel@tonic-gate } 8369*7c478bd9Sstevel@tonic-gate return (ret); 8370*7c478bd9Sstevel@tonic-gate 8371*7c478bd9Sstevel@tonic-gate case BUS_POWER_NEXUS_PWRUP: 8372*7c478bd9Sstevel@tonic-gate bpn = (pm_bp_nexus_pwrup_t *)arg; 8373*7c478bd9Sstevel@tonic-gate pspm = (pm_sp_misc_t *)bpn->bpn_private; 8374*7c478bd9Sstevel@tonic-gate 8375*7c478bd9Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || 8376*7c478bd9Sstevel@tonic-gate !e_pm_valid_comp(dip, bpn->bpn_comp, &cp) || 8377*7c478bd9Sstevel@tonic-gate !e_pm_valid_power(dip, bpn->bpn_comp, bpn->bpn_level)) { 8378*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) has no pm info; EIO\n", 8379*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 8380*7c478bd9Sstevel@tonic-gate *pspm->pspm_errnop = EIO; 8381*7c478bd9Sstevel@tonic-gate *(int *)result = DDI_FAILURE; 8382*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8383*7c478bd9Sstevel@tonic-gate } 8384*7c478bd9Sstevel@tonic-gate 8385*7c478bd9Sstevel@tonic-gate ASSERT(bpn->bpn_dip == dip); 8386*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: nexus powerup for %s@%s(%s#%d)\n", pmf, 8387*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip))) 8388*7c478bd9Sstevel@tonic-gate new_bpc.bpc_dip = dip; 8389*7c478bd9Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 8390*7c478bd9Sstevel@tonic-gate new_bpc.bpc_path = ddi_pathname(dip, pathbuf); 8391*7c478bd9Sstevel@tonic-gate new_bpc.bpc_comp = bpn->bpn_comp; 8392*7c478bd9Sstevel@tonic-gate new_bpc.bpc_olevel = PM_CURPOWER(dip, bpn->bpn_comp); 8393*7c478bd9Sstevel@tonic-gate new_bpc.bpc_nlevel = bpn->bpn_level; 8394*7c478bd9Sstevel@tonic-gate new_bpc.bpc_private = bpn->bpn_private; 8395*7c478bd9Sstevel@tonic-gate ((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_direction = 8396*7c478bd9Sstevel@tonic-gate PM_LEVEL_UPONLY; 8397*7c478bd9Sstevel@tonic-gate ((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_errnop = 8398*7c478bd9Sstevel@tonic-gate &errno; 8399*7c478bd9Sstevel@tonic-gate ret = pm_busop_set_power(dip, impl_arg, BUS_POWER_CHILD_PWRCHG, 8400*7c478bd9Sstevel@tonic-gate (void *)&new_bpc, result); 8401*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 8402*7c478bd9Sstevel@tonic-gate return (ret); 8403*7c478bd9Sstevel@tonic-gate 8404*7c478bd9Sstevel@tonic-gate case BUS_POWER_NOINVOL: 8405*7c478bd9Sstevel@tonic-gate bpi = (pm_bp_noinvol_t *)arg; 8406*7c478bd9Sstevel@tonic-gate tdip = bpi->bpni_dip; 8407*7c478bd9Sstevel@tonic-gate cdip = pm_get_next_descendent(dip, tdip); 8408*7c478bd9Sstevel@tonic-gate 8409*7c478bd9Sstevel@tonic-gate /* In case of rem_drv, the leaf node has been removed */ 8410*7c478bd9Sstevel@tonic-gate if (cdip == NULL) 8411*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8412*7c478bd9Sstevel@tonic-gate 8413*7c478bd9Sstevel@tonic-gate cinfo = PM_GET_PM_INFO(cdip); 8414*7c478bd9Sstevel@tonic-gate if (cdip != tdip) { 8415*7c478bd9Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(cdip)) { 8416*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, 8417*7c478bd9Sstevel@tonic-gate ("%s: call bus_power for %s@%s(%s#%d)\n", 8418*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(cdip))) 8419*7c478bd9Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(cdip)) 8420*7c478bd9Sstevel@tonic-gate (cdip, NULL, op, arg, result); 8421*7c478bd9Sstevel@tonic-gate if ((cinfo) && (ret == DDI_SUCCESS)) 8422*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, 8423*7c478bd9Sstevel@tonic-gate bpi); 8424*7c478bd9Sstevel@tonic-gate return (ret); 8425*7c478bd9Sstevel@tonic-gate } else { 8426*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, 8427*7c478bd9Sstevel@tonic-gate ("%s: walk down to %s@%s(%s#%d)\n", pmf, 8428*7c478bd9Sstevel@tonic-gate PM_DEVICE(cdip))) 8429*7c478bd9Sstevel@tonic-gate ret = pm_busop_bus_power(cdip, NULL, op, 8430*7c478bd9Sstevel@tonic-gate arg, result); 8431*7c478bd9Sstevel@tonic-gate /* 8432*7c478bd9Sstevel@tonic-gate * Update the current node. 8433*7c478bd9Sstevel@tonic-gate */ 8434*7c478bd9Sstevel@tonic-gate if ((cinfo) && (ret == DDI_SUCCESS)) 8435*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, 8436*7c478bd9Sstevel@tonic-gate bpi); 8437*7c478bd9Sstevel@tonic-gate return (ret); 8438*7c478bd9Sstevel@tonic-gate } 8439*7c478bd9Sstevel@tonic-gate } else { 8440*7c478bd9Sstevel@tonic-gate /* 8441*7c478bd9Sstevel@tonic-gate * For attach, detach, power up: 8442*7c478bd9Sstevel@tonic-gate * Do nothing for leaf node since its 8443*7c478bd9Sstevel@tonic-gate * counts are already updated. 8444*7c478bd9Sstevel@tonic-gate * For CFB and driver removal, since the 8445*7c478bd9Sstevel@tonic-gate * path and the target dip passed in is up to and incl. 8446*7c478bd9Sstevel@tonic-gate * the immediate ancestor, need to do the update. 8447*7c478bd9Sstevel@tonic-gate */ 8448*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: target %s@%s(%s#%d) is " 8449*7c478bd9Sstevel@tonic-gate "reached\n", pmf, PM_DEVICE(cdip))) 8450*7c478bd9Sstevel@tonic-gate if (cinfo && ((bpi->bpni_cmd == PM_BP_NOINVOL_REMDRV) || 8451*7c478bd9Sstevel@tonic-gate (bpi->bpni_cmd == PM_BP_NOINVOL_CFB))) 8452*7c478bd9Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, bpi); 8453*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8454*7c478bd9Sstevel@tonic-gate } 8455*7c478bd9Sstevel@tonic-gate 8456*7c478bd9Sstevel@tonic-gate default: 8457*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: operation %d is not supported!\n", pmf, op)) 8458*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8459*7c478bd9Sstevel@tonic-gate } 8460*7c478bd9Sstevel@tonic-gate } 8461*7c478bd9Sstevel@tonic-gate 8462*7c478bd9Sstevel@tonic-gate static int 8463*7c478bd9Sstevel@tonic-gate pm_busop_set_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 8464*7c478bd9Sstevel@tonic-gate void *arg, void *resultp) 8465*7c478bd9Sstevel@tonic-gate { 8466*7c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(impl_arg)) 8467*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "bp_set_power") 8468*7c478bd9Sstevel@tonic-gate pm_ppm_devlist_t *devl; 8469*7c478bd9Sstevel@tonic-gate int clevel, circ; 8470*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 8471*7c478bd9Sstevel@tonic-gate int circ_db, ccirc_db; 8472*7c478bd9Sstevel@tonic-gate #endif 8473*7c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 8474*7c478bd9Sstevel@tonic-gate dev_info_t *cdip; 8475*7c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg; 8476*7c478bd9Sstevel@tonic-gate pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private; 8477*7c478bd9Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock; 8478*7c478bd9Sstevel@tonic-gate int scan = pspm->pspm_scan; 8479*7c478bd9Sstevel@tonic-gate int comp = bpc->bpc_comp; 8480*7c478bd9Sstevel@tonic-gate int olevel = bpc->bpc_olevel; 8481*7c478bd9Sstevel@tonic-gate int nlevel = bpc->bpc_nlevel; 8482*7c478bd9Sstevel@tonic-gate int comps_off_incr = 0; 8483*7c478bd9Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 8484*7c478bd9Sstevel@tonic-gate int dodeps; 8485*7c478bd9Sstevel@tonic-gate int direction = pspm->pspm_direction; 8486*7c478bd9Sstevel@tonic-gate int *errnop = pspm->pspm_errnop; 8487*7c478bd9Sstevel@tonic-gate char *dir = pm_decode_direction(direction); 8488*7c478bd9Sstevel@tonic-gate int *iresp = (int *)resultp; 8489*7c478bd9Sstevel@tonic-gate time_t idletime, thresh; 8490*7c478bd9Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, comp); 8491*7c478bd9Sstevel@tonic-gate int work_type; 8492*7c478bd9Sstevel@tonic-gate 8493*7c478bd9Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8494*7c478bd9Sstevel@tonic-gate *errnop = 0; 8495*7c478bd9Sstevel@tonic-gate ASSERT(op == BUS_POWER_CHILD_PWRCHG); 8496*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip), 8497*7c478bd9Sstevel@tonic-gate pm_decode_op(op))) 8498*7c478bd9Sstevel@tonic-gate 8499*7c478bd9Sstevel@tonic-gate /* 8500*7c478bd9Sstevel@tonic-gate * The following set of conditions indicate we are here to handle a 8501*7c478bd9Sstevel@tonic-gate * driver's pm_[raise|lower]_power request, but the device is being 8502*7c478bd9Sstevel@tonic-gate * power managed (PM_DIRECT_PM) by a user process. For that case 8503*7c478bd9Sstevel@tonic-gate * we want to pm_block and pass a status back to the caller based 8504*7c478bd9Sstevel@tonic-gate * on whether the controlling process's next activity on the device 8505*7c478bd9Sstevel@tonic-gate * matches the current request or not. This distinction tells 8506*7c478bd9Sstevel@tonic-gate * downstream functions to avoid calling into a driver or changing 8507*7c478bd9Sstevel@tonic-gate * the framework's power state. To actually block, we need: 8508*7c478bd9Sstevel@tonic-gate * 8509*7c478bd9Sstevel@tonic-gate * PM_ISDIRECT(dip) 8510*7c478bd9Sstevel@tonic-gate * no reason to block unless a process is directly controlling dev 8511*7c478bd9Sstevel@tonic-gate * direction != PM_LEVEL_EXACT 8512*7c478bd9Sstevel@tonic-gate * EXACT is used by controlling proc's PM_SET_CURRENT_POWER ioctl 8513*7c478bd9Sstevel@tonic-gate * !pm_processes_stopped 8514*7c478bd9Sstevel@tonic-gate * don't block if controlling proc already be stopped for cpr 8515*7c478bd9Sstevel@tonic-gate * canblock != PM_CANBLOCK_BYPASS 8516*7c478bd9Sstevel@tonic-gate * our caller must not have explicitly prevented blocking 8517*7c478bd9Sstevel@tonic-gate */ 8518*7c478bd9Sstevel@tonic-gate if (direction != PM_LEVEL_EXACT && canblock != PM_CANBLOCK_BYPASS) { 8519*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 8520*7c478bd9Sstevel@tonic-gate while (PM_ISDIRECT(dip) && !pm_processes_stopped) { 8521*7c478bd9Sstevel@tonic-gate /* releases dip lock */ 8522*7c478bd9Sstevel@tonic-gate ret = pm_busop_match_request(dip, bpc); 8523*7c478bd9Sstevel@tonic-gate if (ret == EAGAIN) { 8524*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 8525*7c478bd9Sstevel@tonic-gate continue; 8526*7c478bd9Sstevel@tonic-gate } 8527*7c478bd9Sstevel@tonic-gate return (*iresp = ret); 8528*7c478bd9Sstevel@tonic-gate } 8529*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8530*7c478bd9Sstevel@tonic-gate } 8531*7c478bd9Sstevel@tonic-gate /* BC device is never scanned, so power will stick until we are done */ 8532*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip) && comp != 0 && nlevel != 0 && 8533*7c478bd9Sstevel@tonic-gate direction != PM_LEVEL_DOWNONLY) { 8534*7c478bd9Sstevel@tonic-gate int nrmpwr0 = pm_get_normal_power(dip, 0); 8535*7c478bd9Sstevel@tonic-gate if (pm_set_power(dip, 0, nrmpwr0, direction, 8536*7c478bd9Sstevel@tonic-gate canblock, 0, resultp) != DDI_SUCCESS) { 8537*7c478bd9Sstevel@tonic-gate /* *resultp set by pm_set_power */ 8538*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8539*7c478bd9Sstevel@tonic-gate } 8540*7c478bd9Sstevel@tonic-gate } 8541*7c478bd9Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(pdip)) { 8542*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: pre_notify %s@%s(%s#%d) for child " 8543*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(pdip), PM_DEVICE(dip))) 8544*7c478bd9Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL, 8545*7c478bd9Sstevel@tonic-gate BUS_POWER_PRE_NOTIFICATION, bpc, resultp); 8546*7c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 8547*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: failed to pre_notify %s@%s(%s#%d)\n", 8548*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(pdip))) 8549*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8550*7c478bd9Sstevel@tonic-gate } 8551*7c478bd9Sstevel@tonic-gate } else { 8552*7c478bd9Sstevel@tonic-gate /* 8553*7c478bd9Sstevel@tonic-gate * Since we don't know what the actual power level is, 8554*7c478bd9Sstevel@tonic-gate * we place a power hold on the parent no matter what 8555*7c478bd9Sstevel@tonic-gate * component and level is changing. 8556*7c478bd9Sstevel@tonic-gate */ 8557*7c478bd9Sstevel@tonic-gate pm_hold_power(pdip); 8558*7c478bd9Sstevel@tonic-gate } 8559*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 8560*7c478bd9Sstevel@tonic-gate clevel = PM_CURPOWER(dip, comp); 8561*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, olvl=%d, nlvl=%d, clvl=%d, " 8562*7c478bd9Sstevel@tonic-gate "dir=%s\n", pmf, PM_DEVICE(dip), comp, bpc->bpc_olevel, nlevel, 8563*7c478bd9Sstevel@tonic-gate clevel, dir)) 8564*7c478bd9Sstevel@tonic-gate switch (direction) { 8565*7c478bd9Sstevel@tonic-gate case PM_LEVEL_UPONLY: 8566*7c478bd9Sstevel@tonic-gate /* Powering up */ 8567*7c478bd9Sstevel@tonic-gate if (clevel >= nlevel) { 8568*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already " 8569*7c478bd9Sstevel@tonic-gate "at or above the requested level.\n", pmf)) 8570*7c478bd9Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8571*7c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS; 8572*7c478bd9Sstevel@tonic-gate goto post_notify; 8573*7c478bd9Sstevel@tonic-gate } 8574*7c478bd9Sstevel@tonic-gate break; 8575*7c478bd9Sstevel@tonic-gate case PM_LEVEL_EXACT: 8576*7c478bd9Sstevel@tonic-gate /* specific level request */ 8577*7c478bd9Sstevel@tonic-gate if (clevel == nlevel && !PM_ISBC(dip)) { 8578*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already " 8579*7c478bd9Sstevel@tonic-gate "at the requested level.\n", pmf)) 8580*7c478bd9Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8581*7c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS; 8582*7c478bd9Sstevel@tonic-gate goto post_notify; 8583*7c478bd9Sstevel@tonic-gate } else if (PM_IS_CFB(dip) && (nlevel < clevel)) { 8584*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: powerdown of console\n", pmf)) 8585*7c478bd9Sstevel@tonic-gate if (!pm_cfb_enabled) { 8586*7c478bd9Sstevel@tonic-gate PMD(PMD_ERROR | PMD_CFB, 8587*7c478bd9Sstevel@tonic-gate ("%s: !pm_cfb_enabled, fails\n", pmf)) 8588*7c478bd9Sstevel@tonic-gate *errnop = EINVAL; 8589*7c478bd9Sstevel@tonic-gate *iresp = DDI_FAILURE; 8590*7c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 8591*7c478bd9Sstevel@tonic-gate goto post_notify; 8592*7c478bd9Sstevel@tonic-gate } 8593*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 8594*7c478bd9Sstevel@tonic-gate while (cfb_inuse) { 8595*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 8596*7c478bd9Sstevel@tonic-gate if (delay_sig(1) == EINTR) { 8597*7c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 8598*7c478bd9Sstevel@tonic-gate *iresp = DDI_FAILURE; 8599*7c478bd9Sstevel@tonic-gate *errnop = EINTR; 8600*7c478bd9Sstevel@tonic-gate goto post_notify; 8601*7c478bd9Sstevel@tonic-gate } 8602*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 8603*7c478bd9Sstevel@tonic-gate } 8604*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 8605*7c478bd9Sstevel@tonic-gate } 8606*7c478bd9Sstevel@tonic-gate break; 8607*7c478bd9Sstevel@tonic-gate case PM_LEVEL_DOWNONLY: 8608*7c478bd9Sstevel@tonic-gate /* Powering down */ 8609*7c478bd9Sstevel@tonic-gate thresh = cur_threshold(dip, comp); 8610*7c478bd9Sstevel@tonic-gate idletime = gethrestime_sec() - cp->pmc_timestamp; 8611*7c478bd9Sstevel@tonic-gate if (scan && ((PM_KUC(dip) != 0) || 8612*7c478bd9Sstevel@tonic-gate (cp->pmc_busycount > 0) || (idletime < thresh))) { 8613*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 8614*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_kidsupcnt != 0) 8615*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: " 8616*7c478bd9Sstevel@tonic-gate "kidsupcnt != 0\n", pmf)) 8617*7c478bd9Sstevel@tonic-gate if (cp->pmc_busycount > 0) 8618*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: " 8619*7c478bd9Sstevel@tonic-gate "device become busy\n", pmf)) 8620*7c478bd9Sstevel@tonic-gate if (idletime < thresh) 8621*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: device " 8622*7c478bd9Sstevel@tonic-gate "hasn't been idle long enough\n", pmf)) 8623*7c478bd9Sstevel@tonic-gate #endif 8624*7c478bd9Sstevel@tonic-gate *iresp = DDI_FAILURE; 8625*7c478bd9Sstevel@tonic-gate *errnop = EBUSY; 8626*7c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 8627*7c478bd9Sstevel@tonic-gate goto post_notify; 8628*7c478bd9Sstevel@tonic-gate } else if (clevel != PM_LEVEL_UNKNOWN && clevel <= nlevel) { 8629*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already at " 8630*7c478bd9Sstevel@tonic-gate "or below the requested level.\n", pmf)) 8631*7c478bd9Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8632*7c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS; 8633*7c478bd9Sstevel@tonic-gate goto post_notify; 8634*7c478bd9Sstevel@tonic-gate } 8635*7c478bd9Sstevel@tonic-gate break; 8636*7c478bd9Sstevel@tonic-gate } 8637*7c478bd9Sstevel@tonic-gate 8638*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip) && (comps_off_incr = 8639*7c478bd9Sstevel@tonic-gate calc_cfb_comps_incr(dip, comp, clevel, nlevel)) > 0) { 8640*7c478bd9Sstevel@tonic-gate /* 8641*7c478bd9Sstevel@tonic-gate * Pre-adjust pm_cfb_comps_off if lowering a console fb 8642*7c478bd9Sstevel@tonic-gate * component from full power. Remember that we tried to 8643*7c478bd9Sstevel@tonic-gate * lower power in case it fails and we need to back out 8644*7c478bd9Sstevel@tonic-gate * the adjustment. 8645*7c478bd9Sstevel@tonic-gate */ 8646*7c478bd9Sstevel@tonic-gate update_comps_off(comps_off_incr, dip); 8647*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d cfb_comps_off->%d\n", 8648*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, clevel, nlevel, 8649*7c478bd9Sstevel@tonic-gate pm_cfb_comps_off)) 8650*7c478bd9Sstevel@tonic-gate } 8651*7c478bd9Sstevel@tonic-gate 8652*7c478bd9Sstevel@tonic-gate if ((*iresp = power_dev(dip, 8653*7c478bd9Sstevel@tonic-gate comp, nlevel, clevel, canblock, &devl)) == DDI_SUCCESS) { 8654*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 8655*7c478bd9Sstevel@tonic-gate /* 8656*7c478bd9Sstevel@tonic-gate * All descendents of this node should already be powered off. 8657*7c478bd9Sstevel@tonic-gate */ 8658*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, comp) == 0) { 8659*7c478bd9Sstevel@tonic-gate pm_desc_pwrchk_t pdpchk; 8660*7c478bd9Sstevel@tonic-gate pdpchk.pdpc_dip = dip; 8661*7c478bd9Sstevel@tonic-gate pdpchk.pdpc_par_involved = PM_WANTS_NOTIFICATION(dip); 8662*7c478bd9Sstevel@tonic-gate ndi_devi_enter(dip, &circ_db); 8663*7c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip != NULL; 8664*7c478bd9Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 8665*7c478bd9Sstevel@tonic-gate ndi_devi_enter(cdip, &ccirc_db); 8666*7c478bd9Sstevel@tonic-gate ddi_walk_devs(cdip, pm_desc_pwrchk_walk, 8667*7c478bd9Sstevel@tonic-gate (void *)&pdpchk); 8668*7c478bd9Sstevel@tonic-gate ndi_devi_exit(cdip, ccirc_db); 8669*7c478bd9Sstevel@tonic-gate } 8670*7c478bd9Sstevel@tonic-gate ndi_devi_exit(dip, circ_db); 8671*7c478bd9Sstevel@tonic-gate } 8672*7c478bd9Sstevel@tonic-gate #endif 8673*7c478bd9Sstevel@tonic-gate /* 8674*7c478bd9Sstevel@tonic-gate * Post-adjust pm_cfb_comps_off if we brought an fb component 8675*7c478bd9Sstevel@tonic-gate * back up to full power. 8676*7c478bd9Sstevel@tonic-gate */ 8677*7c478bd9Sstevel@tonic-gate if (PM_IS_CFB(dip) && comps_off_incr < 0) { 8678*7c478bd9Sstevel@tonic-gate update_comps_off(comps_off_incr, dip); 8679*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d " 8680*7c478bd9Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip), 8681*7c478bd9Sstevel@tonic-gate comp, clevel, nlevel, pm_cfb_comps_off)) 8682*7c478bd9Sstevel@tonic-gate } 8683*7c478bd9Sstevel@tonic-gate dodeps = 0; 8684*7c478bd9Sstevel@tonic-gate if (POWERING_OFF(clevel, nlevel)) { 8685*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip)) { 8686*7c478bd9Sstevel@tonic-gate dodeps = (comp == 0); 8687*7c478bd9Sstevel@tonic-gate } else { 8688*7c478bd9Sstevel@tonic-gate int i; 8689*7c478bd9Sstevel@tonic-gate dodeps = 1; 8690*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 8691*7c478bd9Sstevel@tonic-gate /* if some component still on */ 8692*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, i)) { 8693*7c478bd9Sstevel@tonic-gate dodeps = 0; 8694*7c478bd9Sstevel@tonic-gate break; 8695*7c478bd9Sstevel@tonic-gate } 8696*7c478bd9Sstevel@tonic-gate } 8697*7c478bd9Sstevel@tonic-gate } 8698*7c478bd9Sstevel@tonic-gate if (dodeps) 8699*7c478bd9Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_OFF; 8700*7c478bd9Sstevel@tonic-gate } else if (POWERING_ON(clevel, nlevel)) { 8701*7c478bd9Sstevel@tonic-gate if (PM_ISBC(dip)) { 8702*7c478bd9Sstevel@tonic-gate dodeps = (comp == 0); 8703*7c478bd9Sstevel@tonic-gate } else { 8704*7c478bd9Sstevel@tonic-gate int i; 8705*7c478bd9Sstevel@tonic-gate dodeps = 1; 8706*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 8707*7c478bd9Sstevel@tonic-gate if (i == comp) 8708*7c478bd9Sstevel@tonic-gate continue; 8709*7c478bd9Sstevel@tonic-gate if (PM_CURPOWER(dip, i) > 0) { 8710*7c478bd9Sstevel@tonic-gate dodeps = 0; 8711*7c478bd9Sstevel@tonic-gate break; 8712*7c478bd9Sstevel@tonic-gate } 8713*7c478bd9Sstevel@tonic-gate } 8714*7c478bd9Sstevel@tonic-gate } 8715*7c478bd9Sstevel@tonic-gate if (dodeps) 8716*7c478bd9Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_ON; 8717*7c478bd9Sstevel@tonic-gate } 8718*7c478bd9Sstevel@tonic-gate 8719*7c478bd9Sstevel@tonic-gate if (dodeps) { 8720*7c478bd9Sstevel@tonic-gate char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 8721*7c478bd9Sstevel@tonic-gate 8722*7c478bd9Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 8723*7c478bd9Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, NULL, 8724*7c478bd9Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0); 8725*7c478bd9Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 8726*7c478bd9Sstevel@tonic-gate } 8727*7c478bd9Sstevel@tonic-gate if ((PM_CURPOWER(dip, comp) == nlevel) && pm_watchers()) { 8728*7c478bd9Sstevel@tonic-gate int old; 8729*7c478bd9Sstevel@tonic-gate 8730*7c478bd9Sstevel@tonic-gate /* If old power cached during deadlock, use it. */ 8731*7c478bd9Sstevel@tonic-gate old = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ? 8732*7c478bd9Sstevel@tonic-gate cp->pmc_phc_pwr : olevel); 8733*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 8734*7c478bd9Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, nlevel, 8735*7c478bd9Sstevel@tonic-gate old, canblock); 8736*7c478bd9Sstevel@tonic-gate pm_enqueue_notify_others(&devl, canblock); 8737*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 8738*7c478bd9Sstevel@tonic-gate } 8739*7c478bd9Sstevel@tonic-gate 8740*7c478bd9Sstevel@tonic-gate /* 8741*7c478bd9Sstevel@tonic-gate * If we are coming from a scan, don't do it again, 8742*7c478bd9Sstevel@tonic-gate * else we can have infinite loops. 8743*7c478bd9Sstevel@tonic-gate */ 8744*7c478bd9Sstevel@tonic-gate if (!scan) 8745*7c478bd9Sstevel@tonic-gate pm_rescan(dip); 8746*7c478bd9Sstevel@tonic-gate } else { 8747*7c478bd9Sstevel@tonic-gate /* if we incremented pm_comps_off_count, but failed */ 8748*7c478bd9Sstevel@tonic-gate if (comps_off_incr > 0) { 8749*7c478bd9Sstevel@tonic-gate update_comps_off(-comps_off_incr, dip); 8750*7c478bd9Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d " 8751*7c478bd9Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip), 8752*7c478bd9Sstevel@tonic-gate comp, clevel, nlevel, pm_cfb_comps_off)) 8753*7c478bd9Sstevel@tonic-gate } 8754*7c478bd9Sstevel@tonic-gate *errnop = EIO; 8755*7c478bd9Sstevel@tonic-gate } 8756*7c478bd9Sstevel@tonic-gate 8757*7c478bd9Sstevel@tonic-gate post_notify: 8758*7c478bd9Sstevel@tonic-gate /* 8759*7c478bd9Sstevel@tonic-gate * This thread may have been in deadlock with pm_power_has_changed. 8760*7c478bd9Sstevel@tonic-gate * Before releasing power lock, clear the flag which marks this 8761*7c478bd9Sstevel@tonic-gate * condition. 8762*7c478bd9Sstevel@tonic-gate */ 8763*7c478bd9Sstevel@tonic-gate cp->pmc_flags &= ~PM_PHC_WHILE_SET_POWER; 8764*7c478bd9Sstevel@tonic-gate 8765*7c478bd9Sstevel@tonic-gate /* 8766*7c478bd9Sstevel@tonic-gate * Update the old power level in the bus power structure with the 8767*7c478bd9Sstevel@tonic-gate * actual power level before the transition was made to the new level. 8768*7c478bd9Sstevel@tonic-gate * Some involved parents depend on this information to keep track of 8769*7c478bd9Sstevel@tonic-gate * their children's power transition. 8770*7c478bd9Sstevel@tonic-gate */ 8771*7c478bd9Sstevel@tonic-gate if (*iresp != DDI_FAILURE) 8772*7c478bd9Sstevel@tonic-gate bpc->bpc_olevel = clevel; 8773*7c478bd9Sstevel@tonic-gate 8774*7c478bd9Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(pdip)) { 8775*7c478bd9Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL, 8776*7c478bd9Sstevel@tonic-gate BUS_POWER_POST_NOTIFICATION, bpc, resultp); 8777*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8778*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: post_notify %s@%s(%s#%d) for " 8779*7c478bd9Sstevel@tonic-gate "child %s@%s(%s#%d), ret=%d\n", pmf, PM_DEVICE(pdip), 8780*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), ret)) 8781*7c478bd9Sstevel@tonic-gate } else { 8782*7c478bd9Sstevel@tonic-gate nlevel = cur_power(cp); /* in case phc deadlock updated pwr */ 8783*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8784*7c478bd9Sstevel@tonic-gate /* 8785*7c478bd9Sstevel@tonic-gate * Now that we know what power transition has occurred 8786*7c478bd9Sstevel@tonic-gate * (if any), release the power hold. Leave the hold 8787*7c478bd9Sstevel@tonic-gate * in effect in the case of OFF->ON transition. 8788*7c478bd9Sstevel@tonic-gate */ 8789*7c478bd9Sstevel@tonic-gate if (!(clevel == 0 && nlevel > 0 && 8790*7c478bd9Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0))) 8791*7c478bd9Sstevel@tonic-gate pm_rele_power(pdip); 8792*7c478bd9Sstevel@tonic-gate /* 8793*7c478bd9Sstevel@tonic-gate * If the power transition was an ON->OFF transition, 8794*7c478bd9Sstevel@tonic-gate * remove the power hold from the parent. 8795*7c478bd9Sstevel@tonic-gate */ 8796*7c478bd9Sstevel@tonic-gate if ((clevel > 0 || clevel == PM_LEVEL_UNKNOWN) && 8797*7c478bd9Sstevel@tonic-gate nlevel == 0 && (!PM_ISBC(dip) || comp == 0)) 8798*7c478bd9Sstevel@tonic-gate pm_rele_power(pdip); 8799*7c478bd9Sstevel@tonic-gate } 8800*7c478bd9Sstevel@tonic-gate if (*iresp != DDI_SUCCESS || ret != DDI_SUCCESS) 8801*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8802*7c478bd9Sstevel@tonic-gate else 8803*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8804*7c478bd9Sstevel@tonic-gate } 8805*7c478bd9Sstevel@tonic-gate 8806*7c478bd9Sstevel@tonic-gate /* 8807*7c478bd9Sstevel@tonic-gate * If an app (SunVTS or Xsun) has taken control, then block until it 8808*7c478bd9Sstevel@tonic-gate * gives it up or makes the requested power level change, unless 8809*7c478bd9Sstevel@tonic-gate * we have other instructions about blocking. Returns DDI_SUCCESS, 8810*7c478bd9Sstevel@tonic-gate * DDI_FAILURE or EAGAIN (owner released device from directpm). 8811*7c478bd9Sstevel@tonic-gate */ 8812*7c478bd9Sstevel@tonic-gate static int 8813*7c478bd9Sstevel@tonic-gate pm_busop_match_request(dev_info_t *dip, void *arg) 8814*7c478bd9Sstevel@tonic-gate { 8815*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "bp_match_request") 8816*7c478bd9Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg; 8817*7c478bd9Sstevel@tonic-gate pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private; 8818*7c478bd9Sstevel@tonic-gate int comp = bpc->bpc_comp; 8819*7c478bd9Sstevel@tonic-gate int nlevel = bpc->bpc_nlevel; 8820*7c478bd9Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock; 8821*7c478bd9Sstevel@tonic-gate int direction = pspm->pspm_direction; 8822*7c478bd9Sstevel@tonic-gate int clevel, circ; 8823*7c478bd9Sstevel@tonic-gate 8824*7c478bd9Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip)); 8825*7c478bd9Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 8826*7c478bd9Sstevel@tonic-gate clevel = PM_CURPOWER(dip, comp); 8827*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, nlvl=%d, clvl=%d\n", 8828*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, nlevel, clevel)) 8829*7c478bd9Sstevel@tonic-gate if (direction == PM_LEVEL_UPONLY) { 8830*7c478bd9Sstevel@tonic-gate if (clevel >= nlevel) { 8831*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8832*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8833*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8834*7c478bd9Sstevel@tonic-gate } 8835*7c478bd9Sstevel@tonic-gate } else if (clevel == nlevel) { 8836*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8837*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8838*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8839*7c478bd9Sstevel@tonic-gate } 8840*7c478bd9Sstevel@tonic-gate if (canblock == PM_CANBLOCK_FAIL) { 8841*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8842*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8843*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8844*7c478bd9Sstevel@tonic-gate } 8845*7c478bd9Sstevel@tonic-gate if (canblock == PM_CANBLOCK_BLOCK) { 8846*7c478bd9Sstevel@tonic-gate /* 8847*7c478bd9Sstevel@tonic-gate * To avoid a deadlock, we must not hold the 8848*7c478bd9Sstevel@tonic-gate * power lock when we pm_block. 8849*7c478bd9Sstevel@tonic-gate */ 8850*7c478bd9Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8851*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: blocking\n", pmf)) 8852*7c478bd9Sstevel@tonic-gate /* pm_block releases dip lock */ 8853*7c478bd9Sstevel@tonic-gate switch (pm_block(dip, comp, nlevel, clevel)) { 8854*7c478bd9Sstevel@tonic-gate case PMP_RELEASE: 8855*7c478bd9Sstevel@tonic-gate return (EAGAIN); 8856*7c478bd9Sstevel@tonic-gate case PMP_SUCCEED: 8857*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8858*7c478bd9Sstevel@tonic-gate case PMP_FAIL: 8859*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8860*7c478bd9Sstevel@tonic-gate } 8861*7c478bd9Sstevel@tonic-gate } else { 8862*7c478bd9Sstevel@tonic-gate ASSERT(0); 8863*7c478bd9Sstevel@tonic-gate } 8864*7c478bd9Sstevel@tonic-gate _NOTE(NOTREACHED); 8865*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); /* keep gcc happy */ 8866*7c478bd9Sstevel@tonic-gate } 8867*7c478bd9Sstevel@tonic-gate 8868*7c478bd9Sstevel@tonic-gate static int 8869*7c478bd9Sstevel@tonic-gate pm_all_to_normal_nexus(dev_info_t *dip, pm_canblock_t canblock) 8870*7c478bd9Sstevel@tonic-gate { 8871*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "all_to_normal_nexus") 8872*7c478bd9Sstevel@tonic-gate int *normal; 8873*7c478bd9Sstevel@tonic-gate int i, ncomps; 8874*7c478bd9Sstevel@tonic-gate size_t size; 8875*7c478bd9Sstevel@tonic-gate int changefailed = 0; 8876*7c478bd9Sstevel@tonic-gate int ret, result = DDI_SUCCESS; 8877*7c478bd9Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn; 8878*7c478bd9Sstevel@tonic-gate pm_sp_misc_t pspm; 8879*7c478bd9Sstevel@tonic-gate 8880*7c478bd9Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip)); 8881*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 8882*7c478bd9Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) { 8883*7c478bd9Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get norm pwrs\n", pmf)) 8884*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8885*7c478bd9Sstevel@tonic-gate } 8886*7c478bd9Sstevel@tonic-gate ncomps = PM_NUMCMPTS(dip); 8887*7c478bd9Sstevel@tonic-gate for (i = 0; i < ncomps; i++) { 8888*7c478bd9Sstevel@tonic-gate bpn.bpn_dip = dip; 8889*7c478bd9Sstevel@tonic-gate bpn.bpn_comp = i; 8890*7c478bd9Sstevel@tonic-gate bpn.bpn_level = normal[i]; 8891*7c478bd9Sstevel@tonic-gate pspm.pspm_canblock = canblock; 8892*7c478bd9Sstevel@tonic-gate pspm.pspm_scan = 0; 8893*7c478bd9Sstevel@tonic-gate bpn.bpn_private = &pspm; 8894*7c478bd9Sstevel@tonic-gate ret = pm_busop_bus_power(dip, NULL, BUS_POWER_NEXUS_PWRUP, 8895*7c478bd9Sstevel@tonic-gate (void *)&bpn, (void *)&result); 8896*7c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS || result != DDI_SUCCESS) { 8897*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL | PMD_ALLNORM, ("%s: %s@%s(%s#%d)[%d] " 8898*7c478bd9Sstevel@tonic-gate "->%d failure result %d\n", pmf, PM_DEVICE(dip), 8899*7c478bd9Sstevel@tonic-gate i, normal[i], result)) 8900*7c478bd9Sstevel@tonic-gate changefailed++; 8901*7c478bd9Sstevel@tonic-gate } 8902*7c478bd9Sstevel@tonic-gate } 8903*7c478bd9Sstevel@tonic-gate kmem_free(normal, size); 8904*7c478bd9Sstevel@tonic-gate if (changefailed) { 8905*7c478bd9Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) " 8906*7c478bd9Sstevel@tonic-gate "full power\n", pmf, changefailed, PM_DEVICE(dip))) 8907*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8908*7c478bd9Sstevel@tonic-gate } 8909*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8910*7c478bd9Sstevel@tonic-gate } 8911*7c478bd9Sstevel@tonic-gate 8912*7c478bd9Sstevel@tonic-gate int 8913*7c478bd9Sstevel@tonic-gate pm_noinvol_update(int subcmd, int volpmd, int wasvolpmd, char *path, 8914*7c478bd9Sstevel@tonic-gate dev_info_t *tdip) 8915*7c478bd9Sstevel@tonic-gate { 8916*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_update") 8917*7c478bd9Sstevel@tonic-gate pm_bp_noinvol_t args; 8918*7c478bd9Sstevel@tonic-gate int ret; 8919*7c478bd9Sstevel@tonic-gate int result = DDI_SUCCESS; 8920*7c478bd9Sstevel@tonic-gate 8921*7c478bd9Sstevel@tonic-gate args.bpni_path = path; 8922*7c478bd9Sstevel@tonic-gate args.bpni_dip = tdip; 8923*7c478bd9Sstevel@tonic-gate args.bpni_cmd = subcmd; 8924*7c478bd9Sstevel@tonic-gate args.bpni_wasvolpmd = wasvolpmd; 8925*7c478bd9Sstevel@tonic-gate args.bpni_volpmd = volpmd; 8926*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: update for path %s tdip %p subcmd %d " 8927*7c478bd9Sstevel@tonic-gate "volpmd %d wasvolpmd %d\n", pmf, 8928*7c478bd9Sstevel@tonic-gate path, (void *)tdip, subcmd, wasvolpmd, volpmd)) 8929*7c478bd9Sstevel@tonic-gate ret = pm_busop_bus_power(ddi_root_node(), NULL, BUS_POWER_NOINVOL, 8930*7c478bd9Sstevel@tonic-gate &args, &result); 8931*7c478bd9Sstevel@tonic-gate return (ret); 8932*7c478bd9Sstevel@tonic-gate } 8933*7c478bd9Sstevel@tonic-gate 8934*7c478bd9Sstevel@tonic-gate void 8935*7c478bd9Sstevel@tonic-gate pm_noinvol_update_node(dev_info_t *dip, pm_bp_noinvol_t *req) 8936*7c478bd9Sstevel@tonic-gate { 8937*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_update_node") 8938*7c478bd9Sstevel@tonic-gate 8939*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 8940*7c478bd9Sstevel@tonic-gate switch (req->bpni_cmd) { 8941*7c478bd9Sstevel@tonic-gate case PM_BP_NOINVOL_ATTACH: 8942*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_PB_NOINVOL_ATTACH %s@%s(%s#%d) " 8943*7c478bd9Sstevel@tonic-gate "noinvol %d->%d\n", pmf, PM_DEVICE(dip), 8944*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, 8945*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm - 1)) 8946*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_noinvolpm); 8947*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 8948*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm--; 8949*7c478bd9Sstevel@tonic-gate if (req->bpni_wasvolpmd) { 8950*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_ATTACH " 8951*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) volpmd %d->%d\n", pmf, 8952*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd, 8953*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd - 1)) 8954*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd) 8955*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd--; 8956*7c478bd9Sstevel@tonic-gate } 8957*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8958*7c478bd9Sstevel@tonic-gate break; 8959*7c478bd9Sstevel@tonic-gate 8960*7c478bd9Sstevel@tonic-gate case PM_BP_NOINVOL_DETACH: 8961*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH %s@%s(%s#%d) " 8962*7c478bd9Sstevel@tonic-gate "noinvolpm %d->%d\n", pmf, PM_DEVICE(dip), 8963*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, 8964*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm + 1)) 8965*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 8966*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm++; 8967*7c478bd9Sstevel@tonic-gate if (req->bpni_wasvolpmd) { 8968*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH " 8969*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d) volpmd %d->%d\n", pmf, 8970*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd, 8971*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd + 1)) 8972*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd++; 8973*7c478bd9Sstevel@tonic-gate } 8974*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8975*7c478bd9Sstevel@tonic-gate break; 8976*7c478bd9Sstevel@tonic-gate 8977*7c478bd9Sstevel@tonic-gate case PM_BP_NOINVOL_REMDRV: 8978*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) " 8979*7c478bd9Sstevel@tonic-gate "noinvol %d->%d\n", pmf, PM_DEVICE(dip), 8980*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, 8981*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm - 1)) 8982*7c478bd9Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_noinvolpm); 8983*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 8984*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm--; 8985*7c478bd9Sstevel@tonic-gate if (req->bpni_wasvolpmd) { 8986*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, 8987*7c478bd9Sstevel@tonic-gate ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) " 8988*7c478bd9Sstevel@tonic-gate "volpmd %d->%d\n", pmf, PM_DEVICE(dip), 8989*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, 8990*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd - 1)) 8991*7c478bd9Sstevel@tonic-gate /* 8992*7c478bd9Sstevel@tonic-gate * A power up could come in between and 8993*7c478bd9Sstevel@tonic-gate * clear the volpmd, if that's the case, 8994*7c478bd9Sstevel@tonic-gate * volpmd would be clear. 8995*7c478bd9Sstevel@tonic-gate */ 8996*7c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd) 8997*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd--; 8998*7c478bd9Sstevel@tonic-gate } 8999*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 9000*7c478bd9Sstevel@tonic-gate break; 9001*7c478bd9Sstevel@tonic-gate 9002*7c478bd9Sstevel@tonic-gate case PM_BP_NOINVOL_CFB: 9003*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, 9004*7c478bd9Sstevel@tonic-gate ("%s: PM_BP_NOIVOL_CFB %s@%s(%s#%d) noinvol %d->%d\n", 9005*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), DEVI(dip)->devi_pm_noinvolpm, 9006*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm + 1)) 9007*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 9008*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm++; 9009*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 9010*7c478bd9Sstevel@tonic-gate break; 9011*7c478bd9Sstevel@tonic-gate 9012*7c478bd9Sstevel@tonic-gate case PM_BP_NOINVOL_POWER: 9013*7c478bd9Sstevel@tonic-gate PMD(PMD_NOINVOL, 9014*7c478bd9Sstevel@tonic-gate ("%s: PM_BP_NOIVOL_PWR %s@%s(%s#%d) volpmd %d->%d\n", 9015*7c478bd9Sstevel@tonic-gate pmf, PM_DEVICE(dip), 9016*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, DEVI(dip)->devi_pm_volpmd - 9017*7c478bd9Sstevel@tonic-gate req->bpni_volpmd)) 9018*7c478bd9Sstevel@tonic-gate PM_LOCK_DIP(dip); 9019*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd -= req->bpni_volpmd; 9020*7c478bd9Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 9021*7c478bd9Sstevel@tonic-gate break; 9022*7c478bd9Sstevel@tonic-gate 9023*7c478bd9Sstevel@tonic-gate default: 9024*7c478bd9Sstevel@tonic-gate break; 9025*7c478bd9Sstevel@tonic-gate } 9026*7c478bd9Sstevel@tonic-gate 9027*7c478bd9Sstevel@tonic-gate } 9028*7c478bd9Sstevel@tonic-gate 9029*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 9030*7c478bd9Sstevel@tonic-gate static int 9031*7c478bd9Sstevel@tonic-gate pm_desc_pwrchk_walk(dev_info_t *dip, void *arg) 9032*7c478bd9Sstevel@tonic-gate { 9033*7c478bd9Sstevel@tonic-gate PMD_FUNC(pmf, "desc_pwrchk") 9034*7c478bd9Sstevel@tonic-gate pm_desc_pwrchk_t *pdpchk = (pm_desc_pwrchk_t *)arg; 9035*7c478bd9Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 9036*7c478bd9Sstevel@tonic-gate int i, curpwr, ce_level; 9037*7c478bd9Sstevel@tonic-gate 9038*7c478bd9Sstevel@tonic-gate if (!info) 9039*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 9040*7c478bd9Sstevel@tonic-gate 9041*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 9042*7c478bd9Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 9043*7c478bd9Sstevel@tonic-gate if ((curpwr = PM_CURPOWER(dip, i)) == 0) 9044*7c478bd9Sstevel@tonic-gate continue; 9045*7c478bd9Sstevel@tonic-gate ce_level = (pdpchk->pdpc_par_involved == 0) ? CE_PANIC : 9046*7c478bd9Sstevel@tonic-gate CE_WARN; 9047*7c478bd9Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) is powered off while desc " 9048*7c478bd9Sstevel@tonic-gate "%s@%s(%s#%d)[%d] is at %d\n", pmf, 9049*7c478bd9Sstevel@tonic-gate PM_DEVICE(pdpchk->pdpc_dip), PM_DEVICE(dip), i, curpwr)) 9050*7c478bd9Sstevel@tonic-gate cmn_err(ce_level, "!device %s@%s(%s#%d) is powered on, " 9051*7c478bd9Sstevel@tonic-gate "while its ancestor, %s@%s(%s#%d), is powering off!", 9052*7c478bd9Sstevel@tonic-gate PM_DEVICE(dip), PM_DEVICE(pdpchk->pdpc_dip)); 9053*7c478bd9Sstevel@tonic-gate } 9054*7c478bd9Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 9055*7c478bd9Sstevel@tonic-gate } 9056*7c478bd9Sstevel@tonic-gate #endif 9057*7c478bd9Sstevel@tonic-gate 9058*7c478bd9Sstevel@tonic-gate /* 9059*7c478bd9Sstevel@tonic-gate * Record the fact that one thread is borrowing the lock on a device node. 9060*7c478bd9Sstevel@tonic-gate * Use is restricted to the case where the lending thread will block until 9061*7c478bd9Sstevel@tonic-gate * the borrowing thread (always curthread) completes. 9062*7c478bd9Sstevel@tonic-gate */ 9063*7c478bd9Sstevel@tonic-gate void 9064*7c478bd9Sstevel@tonic-gate pm_borrow_lock(kthread_t *lender) 9065*7c478bd9Sstevel@tonic-gate { 9066*7c478bd9Sstevel@tonic-gate lock_loan_t *prev = &lock_loan_head; 9067*7c478bd9Sstevel@tonic-gate lock_loan_t *cur = (lock_loan_t *)kmem_zalloc(sizeof (*cur), KM_SLEEP); 9068*7c478bd9Sstevel@tonic-gate 9069*7c478bd9Sstevel@tonic-gate cur->pmlk_borrower = curthread; 9070*7c478bd9Sstevel@tonic-gate cur->pmlk_lender = lender; 9071*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 9072*7c478bd9Sstevel@tonic-gate cur->pmlk_next = prev->pmlk_next; 9073*7c478bd9Sstevel@tonic-gate prev->pmlk_next = cur; 9074*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 9075*7c478bd9Sstevel@tonic-gate } 9076*7c478bd9Sstevel@tonic-gate 9077*7c478bd9Sstevel@tonic-gate /* 9078*7c478bd9Sstevel@tonic-gate * Return the borrowed lock. A thread can borrow only one. 9079*7c478bd9Sstevel@tonic-gate */ 9080*7c478bd9Sstevel@tonic-gate void 9081*7c478bd9Sstevel@tonic-gate pm_return_lock(void) 9082*7c478bd9Sstevel@tonic-gate { 9083*7c478bd9Sstevel@tonic-gate lock_loan_t *cur; 9084*7c478bd9Sstevel@tonic-gate lock_loan_t *prev = &lock_loan_head; 9085*7c478bd9Sstevel@tonic-gate 9086*7c478bd9Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 9087*7c478bd9Sstevel@tonic-gate ASSERT(prev->pmlk_next != NULL); 9088*7c478bd9Sstevel@tonic-gate for (cur = prev->pmlk_next; cur; prev = cur, cur = cur->pmlk_next) 9089*7c478bd9Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 9090*7c478bd9Sstevel@tonic-gate break; 9091*7c478bd9Sstevel@tonic-gate 9092*7c478bd9Sstevel@tonic-gate ASSERT(cur != NULL); 9093*7c478bd9Sstevel@tonic-gate prev->pmlk_next = cur->pmlk_next; 9094*7c478bd9Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 9095*7c478bd9Sstevel@tonic-gate kmem_free(cur, sizeof (*cur)); 9096*7c478bd9Sstevel@tonic-gate } 9097