1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * sunpm.c builds sunpm.o "power management framework" 31*0Sstevel@tonic-gate * kernel-resident power management code. Implements power management 32*0Sstevel@tonic-gate * policy 33*0Sstevel@tonic-gate * Assumes: all backwards compat. device components wake up on & 34*0Sstevel@tonic-gate * the pm_info pointer in dev_info is initially NULL 35*0Sstevel@tonic-gate * 36*0Sstevel@tonic-gate * PM - (device) Power Management 37*0Sstevel@tonic-gate * 38*0Sstevel@tonic-gate * Each device may have 0 or more components. If a device has no components, 39*0Sstevel@tonic-gate * then it can't be power managed. Each component has 2 or more 40*0Sstevel@tonic-gate * power states. 41*0Sstevel@tonic-gate * 42*0Sstevel@tonic-gate * "Backwards Compatible" (bc) devices: 43*0Sstevel@tonic-gate * There are two different types of devices from the point of view of this 44*0Sstevel@tonic-gate * code. The original type, left over from the original PM implementation on 45*0Sstevel@tonic-gate * the voyager platform are known in this code as "backwards compatible" 46*0Sstevel@tonic-gate * devices (PM_ISBC(dip) returns true). 47*0Sstevel@tonic-gate * They are recognized by the pm code by the lack of a pm-components property 48*0Sstevel@tonic-gate * and a call made by the driver to pm_create_components(9F). 49*0Sstevel@tonic-gate * For these devices, component 0 is special, and represents the power state 50*0Sstevel@tonic-gate * of the device. If component 0 is to be set to power level 0 (off), then 51*0Sstevel@tonic-gate * the framework must first call into the driver's detach(9E) routine with 52*0Sstevel@tonic-gate * DDI_PM_SUSPEND, to get the driver to save the hardware state of the device. 53*0Sstevel@tonic-gate * After setting component 0 from 0 to a non-zero power level, a call must be 54*0Sstevel@tonic-gate * made into the driver's attach(9E) routine with DDI_PM_RESUME. 55*0Sstevel@tonic-gate * 56*0Sstevel@tonic-gate * Currently, the only way to get a bc device power managed is via a set of 57*0Sstevel@tonic-gate * ioctls (PM_DIRECT_PM, PM_SET_CURRENT_POWER) issued to /dev/pm. 58*0Sstevel@tonic-gate * 59*0Sstevel@tonic-gate * For non-bc devices, the driver describes the components by exporting a 60*0Sstevel@tonic-gate * pm-components(9P) property that tells how many components there are, 61*0Sstevel@tonic-gate * tells what each component's power state values are, and provides human 62*0Sstevel@tonic-gate * readable strings (currently unused) for each component name and power state. 63*0Sstevel@tonic-gate * Devices which export pm-components(9P) are automatically power managed 64*0Sstevel@tonic-gate * whenever autopm is enabled (via PM_START_PM ioctl issued by pmconfig(1M) 65*0Sstevel@tonic-gate * after parsing power.conf(4)). 66*0Sstevel@tonic-gate * For these devices, all components are considered independent of each other, 67*0Sstevel@tonic-gate * and it is up to the driver to decide when a transition requires saving or 68*0Sstevel@tonic-gate * restoring hardware state. 69*0Sstevel@tonic-gate * 70*0Sstevel@tonic-gate * Each device component also has a threshold time associated with each power 71*0Sstevel@tonic-gate * transition (see power.conf(4)), and a busy/idle state maintained by the 72*0Sstevel@tonic-gate * driver calling pm_idle_component(9F) and pm_busy_component(9F). 73*0Sstevel@tonic-gate * Components are created idle. 74*0Sstevel@tonic-gate * 75*0Sstevel@tonic-gate * The PM framework provides several functions: 76*0Sstevel@tonic-gate * -implement PM policy as described in power.conf(4) 77*0Sstevel@tonic-gate * Policy is set by pmconfig(1M) issuing pm ioctls based on power.conf(4). 78*0Sstevel@tonic-gate * Policies consist of: 79*0Sstevel@tonic-gate * -set threshold values (defaults if none provided by pmconfig) 80*0Sstevel@tonic-gate * -set dependencies among devices 81*0Sstevel@tonic-gate * -enable/disable autopm 82*0Sstevel@tonic-gate * -turn down idle components based on thresholds (if autopm is enabled) 83*0Sstevel@tonic-gate * (aka scanning) 84*0Sstevel@tonic-gate * -maintain power states based on dependencies among devices 85*0Sstevel@tonic-gate * -upon request, or when the frame buffer powers off, attempt to turn off 86*0Sstevel@tonic-gate * all components that are idle or become idle over the next (10 sec) 87*0Sstevel@tonic-gate * period in an attempt to get down to an EnergyStar compliant state 88*0Sstevel@tonic-gate * -prevent powering off of a device which exported the 89*0Sstevel@tonic-gate * pm-no-involuntary-power-cycles property without active involvement of 90*0Sstevel@tonic-gate * the device's driver (so no removing power when the device driver is 91*0Sstevel@tonic-gate * not attached) 92*0Sstevel@tonic-gate * -provide a mechanism for a device driver to request that a device's component 93*0Sstevel@tonic-gate * be brought back to the power level necessary for the use of the device 94*0Sstevel@tonic-gate * -allow a process to directly control the power levels of device components 95*0Sstevel@tonic-gate * (via ioctls issued to /dev/pm--see usr/src/uts/common/io/pm.c) 96*0Sstevel@tonic-gate * -ensure that the console frame buffer is powered up before being referenced 97*0Sstevel@tonic-gate * via prom_printf() or other prom calls that might generate console output 98*0Sstevel@tonic-gate * -maintain implicit dependencies (e.g. parent must be powered up if child is) 99*0Sstevel@tonic-gate * -provide "backwards compatible" behavior for devices without pm-components 100*0Sstevel@tonic-gate * property 101*0Sstevel@tonic-gate * 102*0Sstevel@tonic-gate * Scanning: 103*0Sstevel@tonic-gate * Whenever autopm is enabled, the framework attempts to bring each component 104*0Sstevel@tonic-gate * of each device to its lowest power based on the threshold of idleness 105*0Sstevel@tonic-gate * associated with each transition and the busy/idle state of the component. 106*0Sstevel@tonic-gate * 107*0Sstevel@tonic-gate * The actual work of this is done by pm_scan_dev(), which cycles through each 108*0Sstevel@tonic-gate * component of a device, checking its idleness against its current threshold, 109*0Sstevel@tonic-gate * and calling pm_set_power() as appropriate to change the power level. 110*0Sstevel@tonic-gate * This function also indicates when it would next be profitable to scan the 111*0Sstevel@tonic-gate * device again, and a new scan is scheduled after that time. 112*0Sstevel@tonic-gate * 113*0Sstevel@tonic-gate * Dependencies: 114*0Sstevel@tonic-gate * It is possible to establish a dependency between the power states of two 115*0Sstevel@tonic-gate * otherwise unrelated devices. This is currently done to ensure that the 116*0Sstevel@tonic-gate * cdrom is always up whenever the console framebuffer is up, so that the user 117*0Sstevel@tonic-gate * can insert a cdrom and see a popup as a result. 118*0Sstevel@tonic-gate * 119*0Sstevel@tonic-gate * The dependency terminology used in power.conf(4) is not easy to understand, 120*0Sstevel@tonic-gate * so we've adopted a different terminology in the implementation. We write 121*0Sstevel@tonic-gate * of a "keeps up" and a "kept up" device. A relationship can be established 122*0Sstevel@tonic-gate * where one device keeps up another. That means that if the keepsup device 123*0Sstevel@tonic-gate * has any component that is at a non-zero power level, all components of the 124*0Sstevel@tonic-gate * "kept up" device must be brought to full power. This relationship is 125*0Sstevel@tonic-gate * asynchronous. When the keeping device is powered up, a request is queued 126*0Sstevel@tonic-gate * to a worker thread to bring up the kept device. The caller does not wait. 127*0Sstevel@tonic-gate * Scan will not turn down a kept up device. 128*0Sstevel@tonic-gate * 129*0Sstevel@tonic-gate * Direct PM: 130*0Sstevel@tonic-gate * A device may be directly power managed by a process. If a device is 131*0Sstevel@tonic-gate * directly pm'd, then it will not be scanned, and dependencies will not be 132*0Sstevel@tonic-gate * enforced. * If a directly pm'd device's driver requests a power change (via 133*0Sstevel@tonic-gate * pm_raise_power(9F)), then the request is blocked and notification is sent 134*0Sstevel@tonic-gate * to the controlling process, which must issue the requested power change for 135*0Sstevel@tonic-gate * the driver to proceed. 136*0Sstevel@tonic-gate * 137*0Sstevel@tonic-gate */ 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate #include <sys/types.h> 140*0Sstevel@tonic-gate #include <sys/errno.h> 141*0Sstevel@tonic-gate #include <sys/callb.h> /* callback registration during CPR */ 142*0Sstevel@tonic-gate #include <sys/conf.h> /* driver flags and functions */ 143*0Sstevel@tonic-gate #include <sys/open.h> /* OTYP_CHR definition */ 144*0Sstevel@tonic-gate #include <sys/stat.h> /* S_IFCHR definition */ 145*0Sstevel@tonic-gate #include <sys/pathname.h> /* name -> dev_info xlation */ 146*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h> /* dev_info node fields */ 147*0Sstevel@tonic-gate #include <sys/kmem.h> /* memory alloc stuff */ 148*0Sstevel@tonic-gate #include <sys/debug.h> 149*0Sstevel@tonic-gate #include <sys/archsystm.h> 150*0Sstevel@tonic-gate #include <sys/pm.h> 151*0Sstevel@tonic-gate #include <sys/ddi.h> 152*0Sstevel@tonic-gate #include <sys/sunddi.h> 153*0Sstevel@tonic-gate #include <sys/sunndi.h> 154*0Sstevel@tonic-gate #include <sys/sunpm.h> 155*0Sstevel@tonic-gate #include <sys/epm.h> 156*0Sstevel@tonic-gate #include <sys/vfs.h> 157*0Sstevel@tonic-gate #include <sys/mode.h> 158*0Sstevel@tonic-gate #include <sys/mkdev.h> 159*0Sstevel@tonic-gate #include <sys/promif.h> 160*0Sstevel@tonic-gate #include <sys/consdev.h> 161*0Sstevel@tonic-gate #include <sys/esunddi.h> 162*0Sstevel@tonic-gate #include <sys/modctl.h> 163*0Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 164*0Sstevel@tonic-gate #include <sys/note.h> 165*0Sstevel@tonic-gate #include <sys/taskq.h> 166*0Sstevel@tonic-gate #include <sys/bootconf.h> 167*0Sstevel@tonic-gate #include <sys/reboot.h> 168*0Sstevel@tonic-gate #include <sys/spl.h> 169*0Sstevel@tonic-gate #include <sys/disp.h> 170*0Sstevel@tonic-gate #include <sys/sobject.h> 171*0Sstevel@tonic-gate #include <sys/sunmdi.h> 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate /* 175*0Sstevel@tonic-gate * PM LOCKING 176*0Sstevel@tonic-gate * The list of locks: 177*0Sstevel@tonic-gate * Global pm mutex locks. 178*0Sstevel@tonic-gate * 179*0Sstevel@tonic-gate * pm_scan_lock: 180*0Sstevel@tonic-gate * It protects the timeout id of the scan thread, and the value 181*0Sstevel@tonic-gate * of autopm_enabled. This lock is not held concurrently with 182*0Sstevel@tonic-gate * any other PM locks. 183*0Sstevel@tonic-gate * 184*0Sstevel@tonic-gate * pm_clone_lock: Protects the clone list and count of poll events 185*0Sstevel@tonic-gate * pending for the pm driver. 186*0Sstevel@tonic-gate * Lock ordering: 187*0Sstevel@tonic-gate * pm_clone_lock -> pm_pscc_interest_rwlock, 188*0Sstevel@tonic-gate * pm_clone_lock -> pm_pscc_direct_rwlock. 189*0Sstevel@tonic-gate * 190*0Sstevel@tonic-gate * pm_rsvp_lock: 191*0Sstevel@tonic-gate * Used to synchronize the data structures used for processes 192*0Sstevel@tonic-gate * to rendezvous with state change information when doing 193*0Sstevel@tonic-gate * direct PM. 194*0Sstevel@tonic-gate * Lock ordering: 195*0Sstevel@tonic-gate * pm_rsvp_lock -> pm_pscc_interest_rwlock, 196*0Sstevel@tonic-gate * pm_rsvp_lock -> pm_pscc_direct_rwlock, 197*0Sstevel@tonic-gate * pm_rsvp_lock -> pm_clone_lock. 198*0Sstevel@tonic-gate * 199*0Sstevel@tonic-gate * ppm_lock: protects the list of registered ppm drivers 200*0Sstevel@tonic-gate * Lock ordering: 201*0Sstevel@tonic-gate * ppm_lock -> ppm driver unit_lock 202*0Sstevel@tonic-gate * 203*0Sstevel@tonic-gate * pm_compcnt_lock: 204*0Sstevel@tonic-gate * Protects count of components that are not at their lowest 205*0Sstevel@tonic-gate * power level. 206*0Sstevel@tonic-gate * Lock ordering: 207*0Sstevel@tonic-gate * pm_compcnt_lock -> ppm_lock. 208*0Sstevel@tonic-gate * 209*0Sstevel@tonic-gate * pm_dep_thread_lock: 210*0Sstevel@tonic-gate * Protects work list for pm_dep_thread. Not taken concurrently 211*0Sstevel@tonic-gate * with any other pm lock. 212*0Sstevel@tonic-gate * 213*0Sstevel@tonic-gate * pm_remdrv_lock: 214*0Sstevel@tonic-gate * Serializes the operation of removing noinvol data structure 215*0Sstevel@tonic-gate * entries for a branch of the tree when a driver has been 216*0Sstevel@tonic-gate * removed from the system (modctl_rem_major). 217*0Sstevel@tonic-gate * Lock ordering: 218*0Sstevel@tonic-gate * pm_remdrv_lock -> pm_noinvol_rwlock. 219*0Sstevel@tonic-gate * 220*0Sstevel@tonic-gate * pm_cfb_lock: (High level spin lock) 221*0Sstevel@tonic-gate * Protects the count of how many components of the console 222*0Sstevel@tonic-gate * frame buffer are off (so we know if we have to bring up the 223*0Sstevel@tonic-gate * console as a result of a prom_printf, etc. 224*0Sstevel@tonic-gate * No other locks are taken while holding this lock. 225*0Sstevel@tonic-gate * 226*0Sstevel@tonic-gate * pm_loan_lock: 227*0Sstevel@tonic-gate * Protects the lock_loan list. List is used to record that one 228*0Sstevel@tonic-gate * thread has acquired a power lock but has launched another thread 229*0Sstevel@tonic-gate * to complete its processing. An entry in the list indicates that 230*0Sstevel@tonic-gate * the worker thread can borrow the lock held by the other thread, 231*0Sstevel@tonic-gate * which must block on the completion of the worker. Use is 232*0Sstevel@tonic-gate * specific to module loading. 233*0Sstevel@tonic-gate * No other locks are taken while holding this lock. 234*0Sstevel@tonic-gate * 235*0Sstevel@tonic-gate * Global PM rwlocks 236*0Sstevel@tonic-gate * 237*0Sstevel@tonic-gate * pm_thresh_rwlock: 238*0Sstevel@tonic-gate * Protects the list of thresholds recorded for future use (when 239*0Sstevel@tonic-gate * devices attach). 240*0Sstevel@tonic-gate * Lock ordering: 241*0Sstevel@tonic-gate * pm_thresh_rwlock -> devi_pm_lock 242*0Sstevel@tonic-gate * 243*0Sstevel@tonic-gate * pm_noinvol_rwlock: 244*0Sstevel@tonic-gate * Protects list of detached nodes that had noinvol registered. 245*0Sstevel@tonic-gate * No other PM locks are taken while holding pm_noinvol_rwlock. 246*0Sstevel@tonic-gate * 247*0Sstevel@tonic-gate * pm_pscc_direct_rwlock: 248*0Sstevel@tonic-gate * Protects the list that maps devices being directly power 249*0Sstevel@tonic-gate * managed to the processes that manage them. 250*0Sstevel@tonic-gate * Lock ordering: 251*0Sstevel@tonic-gate * pm_pscc_direct_rwlock -> psce_lock 252*0Sstevel@tonic-gate * 253*0Sstevel@tonic-gate * pm_pscc_interest_rwlock; 254*0Sstevel@tonic-gate * Protects the list that maps state change events to processes 255*0Sstevel@tonic-gate * that want to know about them. 256*0Sstevel@tonic-gate * Lock ordering: 257*0Sstevel@tonic-gate * pm_pscc_interest_rwlock -> psce_lock 258*0Sstevel@tonic-gate * 259*0Sstevel@tonic-gate * per-dip locks: 260*0Sstevel@tonic-gate * 261*0Sstevel@tonic-gate * Each node has these per-dip locks, which are only used if the device is 262*0Sstevel@tonic-gate * a candidate for power management (e.g. has pm components) 263*0Sstevel@tonic-gate * 264*0Sstevel@tonic-gate * devi_pm_lock: 265*0Sstevel@tonic-gate * Protects all power management state of the node except for 266*0Sstevel@tonic-gate * power level, which is protected by ndi_devi_enter(). 267*0Sstevel@tonic-gate * Encapsulated in macros PM_LOCK_DIP()/PM_UNLOCK_DIP(). 268*0Sstevel@tonic-gate * Lock ordering: 269*0Sstevel@tonic-gate * devi_pm_lock -> pm_rsvp_lock, 270*0Sstevel@tonic-gate * devi_pm_lock -> pm_dep_thread_lock, 271*0Sstevel@tonic-gate * devi_pm_lock -> pm_noinvol_rwlock, 272*0Sstevel@tonic-gate * devi_pm_lock -> power lock 273*0Sstevel@tonic-gate * 274*0Sstevel@tonic-gate * power lock (ndi_devi_enter()): 275*0Sstevel@tonic-gate * Since changing power level is possibly a slow operation (30 276*0Sstevel@tonic-gate * seconds to spin up a disk drive), this is locked separately. 277*0Sstevel@tonic-gate * Since a call into the driver to change the power level of one 278*0Sstevel@tonic-gate * component may result in a call back into the framework to change 279*0Sstevel@tonic-gate * the power level of another, this lock allows re-entrancy by 280*0Sstevel@tonic-gate * the same thread (ndi_devi_enter is used for this because 281*0Sstevel@tonic-gate * the USB framework uses ndi_devi_enter in its power entry point, 282*0Sstevel@tonic-gate * and use of any other lock would produce a deadlock. 283*0Sstevel@tonic-gate * 284*0Sstevel@tonic-gate * devi_pm_busy_lock: 285*0Sstevel@tonic-gate * This lock protects the integrity of the busy count. It is 286*0Sstevel@tonic-gate * only taken by pm_busy_component() and pm_idle_component and 287*0Sstevel@tonic-gate * some code that adjust the busy time after the timer gets set 288*0Sstevel@tonic-gate * up or after a CPR operation. It is per-dip to keep from 289*0Sstevel@tonic-gate * single-threading all the disk drivers on a system. 290*0Sstevel@tonic-gate * It could be per component instead, but most devices have 291*0Sstevel@tonic-gate * only one component. 292*0Sstevel@tonic-gate * No other PM locks are taken while holding this lock. 293*0Sstevel@tonic-gate * 294*0Sstevel@tonic-gate */ 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate static int stdout_is_framebuffer; 297*0Sstevel@tonic-gate static kmutex_t e_pm_power_lock; 298*0Sstevel@tonic-gate static kmutex_t pm_loan_lock; 299*0Sstevel@tonic-gate kmutex_t pm_scan_lock; 300*0Sstevel@tonic-gate callb_id_t pm_cpr_cb_id; 301*0Sstevel@tonic-gate callb_id_t pm_panic_cb_id; 302*0Sstevel@tonic-gate callb_id_t pm_halt_cb_id; 303*0Sstevel@tonic-gate int pm_comps_notlowest; /* no. of comps not at lowest power */ 304*0Sstevel@tonic-gate int pm_powering_down; /* cpr is source of DDI_SUSPEND calls */ 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate clock_t pm_min_scan = PM_MIN_SCAN; 307*0Sstevel@tonic-gate clock_t pm_id_ticks = 5; /* ticks to wait before scan during idle-down */ 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate static int pm_busop_set_power(dev_info_t *, 310*0Sstevel@tonic-gate void *, pm_bus_power_op_t, void *, void *); 311*0Sstevel@tonic-gate static int pm_busop_match_request(dev_info_t *, void *); 312*0Sstevel@tonic-gate static int pm_all_to_normal_nexus(dev_info_t *, pm_canblock_t); 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate /* 315*0Sstevel@tonic-gate * Dependency Processing is done thru a seperate thread. 316*0Sstevel@tonic-gate */ 317*0Sstevel@tonic-gate kmutex_t pm_dep_thread_lock; 318*0Sstevel@tonic-gate kcondvar_t pm_dep_thread_cv; 319*0Sstevel@tonic-gate pm_dep_wk_t *pm_dep_thread_workq = NULL; 320*0Sstevel@tonic-gate pm_dep_wk_t *pm_dep_thread_tail = NULL; 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate /* 323*0Sstevel@tonic-gate * Autopm must be turned on by a PM_START_PM ioctl, so we don't end up 324*0Sstevel@tonic-gate * power managing things in single user mode that have been suppressed via 325*0Sstevel@tonic-gate * power.conf entries. Protected by pm_scan_lock. 326*0Sstevel@tonic-gate */ 327*0Sstevel@tonic-gate int autopm_enabled; 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate /* 330*0Sstevel@tonic-gate * This flag is true while processes are stopped for a checkpoint/resume. 331*0Sstevel@tonic-gate * Controlling processes of direct pm'd devices are not available to 332*0Sstevel@tonic-gate * participate in power level changes, so we bypass them when this is set. 333*0Sstevel@tonic-gate */ 334*0Sstevel@tonic-gate static int pm_processes_stopped; 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate #ifdef DEBUG 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate /* 339*0Sstevel@tonic-gate * see common/sys/epm.h for PMD_* values 340*0Sstevel@tonic-gate */ 341*0Sstevel@tonic-gate uint_t pm_debug = 0; 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate /* 344*0Sstevel@tonic-gate * If pm_divertdebug is set, then no prom_printf calls will be made by 345*0Sstevel@tonic-gate * PMD(), which will prevent debug output from bringing up the console 346*0Sstevel@tonic-gate * frame buffer. Clearing this variable before setting pm_debug will result 347*0Sstevel@tonic-gate * in PMD output going to the console. 348*0Sstevel@tonic-gate * 349*0Sstevel@tonic-gate * pm_divertdebug is incremented in pm_set_power() if dip == cfb_dip to avoid 350*0Sstevel@tonic-gate * deadlocks and decremented at the end of pm_set_power() 351*0Sstevel@tonic-gate */ 352*0Sstevel@tonic-gate uint_t pm_divertdebug = 1; 353*0Sstevel@tonic-gate kmutex_t pm_debug_lock; /* protects pm_divertdebug */ 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate void prdeps(char *); 356*0Sstevel@tonic-gate #endif 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate /* Globals */ 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate /* 361*0Sstevel@tonic-gate * List of recorded thresholds and dependencies 362*0Sstevel@tonic-gate */ 363*0Sstevel@tonic-gate pm_thresh_rec_t *pm_thresh_head; 364*0Sstevel@tonic-gate krwlock_t pm_thresh_rwlock; 365*0Sstevel@tonic-gate 366*0Sstevel@tonic-gate pm_pdr_t *pm_dep_head; 367*0Sstevel@tonic-gate static int pm_unresolved_deps = 0; 368*0Sstevel@tonic-gate static int pm_prop_deps = 0; 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate /* 371*0Sstevel@tonic-gate * List of devices that exported no-involuntary-power-cycles property 372*0Sstevel@tonic-gate */ 373*0Sstevel@tonic-gate pm_noinvol_t *pm_noinvol_head; 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate /* 376*0Sstevel@tonic-gate * Locks used in noinvol processing 377*0Sstevel@tonic-gate */ 378*0Sstevel@tonic-gate krwlock_t pm_noinvol_rwlock; 379*0Sstevel@tonic-gate kmutex_t pm_remdrv_lock; 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate int pm_default_idle_threshold = PM_DEFAULT_SYS_IDLENESS; 382*0Sstevel@tonic-gate int pm_system_idle_threshold; 383*0Sstevel@tonic-gate /* 384*0Sstevel@tonic-gate * By default nexus has 0 threshold, and depends on its children to keep it up 385*0Sstevel@tonic-gate */ 386*0Sstevel@tonic-gate int pm_default_nexus_threshold = 0; 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate /* 389*0Sstevel@tonic-gate * Data structures shared with common/io/pm.c 390*0Sstevel@tonic-gate */ 391*0Sstevel@tonic-gate kmutex_t pm_clone_lock; 392*0Sstevel@tonic-gate kcondvar_t pm_clones_cv[PM_MAX_CLONE]; 393*0Sstevel@tonic-gate uint_t pm_poll_cnt[PM_MAX_CLONE]; /* count of events for poll */ 394*0Sstevel@tonic-gate unsigned char pm_interest[PM_MAX_CLONE]; 395*0Sstevel@tonic-gate struct pollhead pm_pollhead; 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate extern int hz; 398*0Sstevel@tonic-gate extern char *platform_module_list[]; 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate /* 401*0Sstevel@tonic-gate * Wrappers for use in ddi_walk_devs 402*0Sstevel@tonic-gate */ 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate static int pm_set_dev_thr_walk(dev_info_t *, void *); 405*0Sstevel@tonic-gate static int pm_restore_direct_lvl_walk(dev_info_t *, void *); 406*0Sstevel@tonic-gate static int pm_save_direct_lvl_walk(dev_info_t *, void *); 407*0Sstevel@tonic-gate static int pm_discard_dep_walk(dev_info_t *, void *); 408*0Sstevel@tonic-gate #ifdef DEBUG 409*0Sstevel@tonic-gate static int pm_desc_pwrchk_walk(dev_info_t *, void *); 410*0Sstevel@tonic-gate #endif 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate /* 413*0Sstevel@tonic-gate * Routines for managing noinvol devices 414*0Sstevel@tonic-gate */ 415*0Sstevel@tonic-gate int pm_noinvol_update(int, int, int, char *, dev_info_t *); 416*0Sstevel@tonic-gate void pm_noinvol_update_node(dev_info_t *, 417*0Sstevel@tonic-gate pm_bp_noinvol_t *req); 418*0Sstevel@tonic-gate 419*0Sstevel@tonic-gate kmutex_t pm_rsvp_lock; 420*0Sstevel@tonic-gate kmutex_t pm_compcnt_lock; 421*0Sstevel@tonic-gate krwlock_t pm_pscc_direct_rwlock; 422*0Sstevel@tonic-gate krwlock_t pm_pscc_interest_rwlock; 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate #define PSC_INTEREST 0 /* belongs to interest psc list */ 425*0Sstevel@tonic-gate #define PSC_DIRECT 1 /* belongs to direct psc list */ 426*0Sstevel@tonic-gate 427*0Sstevel@tonic-gate pscc_t *pm_pscc_interest; 428*0Sstevel@tonic-gate pscc_t *pm_pscc_direct; 429*0Sstevel@tonic-gate 430*0Sstevel@tonic-gate #define PM_MAJOR(dip) ddi_name_to_major(ddi_binding_name(dip)) 431*0Sstevel@tonic-gate #define PM_IS_NEXUS(dip) NEXUS_DRV(devopsp[PM_MAJOR(dip)]) 432*0Sstevel@tonic-gate #define POWERING_ON(old, new) ((old) == 0 && (new) != 0) 433*0Sstevel@tonic-gate #define POWERING_OFF(old, new) ((old) != 0 && (new) == 0) 434*0Sstevel@tonic-gate #define PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm) 435*0Sstevel@tonic-gate 436*0Sstevel@tonic-gate #define PM_INCR_NOTLOWEST(dip) { \ 437*0Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); \ 438*0Sstevel@tonic-gate if (!PM_IS_NEXUS(dip) || \ 439*0Sstevel@tonic-gate (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\ 440*0Sstevel@tonic-gate if (pm_comps_notlowest == 0) \ 441*0Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, PM_NOT_ALL_LOWEST);\ 442*0Sstevel@tonic-gate pm_comps_notlowest++; \ 443*0Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr notlowest->%d\n",\ 444*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_comps_notlowest)) \ 445*0Sstevel@tonic-gate } \ 446*0Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); \ 447*0Sstevel@tonic-gate } 448*0Sstevel@tonic-gate #define PM_DECR_NOTLOWEST(dip) { \ 449*0Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); \ 450*0Sstevel@tonic-gate if (!PM_IS_NEXUS(dip) || \ 451*0Sstevel@tonic-gate (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\ 452*0Sstevel@tonic-gate ASSERT(pm_comps_notlowest); \ 453*0Sstevel@tonic-gate pm_comps_notlowest--; \ 454*0Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr notlowest to " \ 455*0Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pm_comps_notlowest))\ 456*0Sstevel@tonic-gate if (pm_comps_notlowest == 0) \ 457*0Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, PM_ALL_LOWEST); \ 458*0Sstevel@tonic-gate } \ 459*0Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); \ 460*0Sstevel@tonic-gate } 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate /* 463*0Sstevel@tonic-gate * console frame-buffer power-management is not enabled when 464*0Sstevel@tonic-gate * debugging services are present. to override, set pm_cfb_override 465*0Sstevel@tonic-gate * to non-zero. 466*0Sstevel@tonic-gate */ 467*0Sstevel@tonic-gate uint_t pm_cfb_comps_off = 0; /* PM_LEVEL_UNKNOWN is considered on */ 468*0Sstevel@tonic-gate kmutex_t pm_cfb_lock; 469*0Sstevel@tonic-gate int pm_cfb_enabled = 1; /* non-zero allows pm of console frame buffer */ 470*0Sstevel@tonic-gate #ifdef DEBUG 471*0Sstevel@tonic-gate int pm_cfb_override = 1; /* non-zero allows pm of cfb with debuggers */ 472*0Sstevel@tonic-gate #else 473*0Sstevel@tonic-gate int pm_cfb_override = 0; /* non-zero allows pm of cfb with debuggers */ 474*0Sstevel@tonic-gate #endif 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate static dev_info_t *cfb_dip = 0; 477*0Sstevel@tonic-gate static dev_info_t *cfb_dip_detaching = 0; 478*0Sstevel@tonic-gate uint_t cfb_inuse = 0; 479*0Sstevel@tonic-gate static ddi_softintr_t pm_soft_id; 480*0Sstevel@tonic-gate static clock_t pm_soft_pending; 481*0Sstevel@tonic-gate int pm_scans_disabled = 0; 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate /* 484*0Sstevel@tonic-gate * A structure to record the fact that one thread has borrowed a lock held 485*0Sstevel@tonic-gate * by another thread. The context requires that the lender block on the 486*0Sstevel@tonic-gate * completion of the borrower. 487*0Sstevel@tonic-gate */ 488*0Sstevel@tonic-gate typedef struct lock_loan { 489*0Sstevel@tonic-gate struct lock_loan *pmlk_next; 490*0Sstevel@tonic-gate kthread_t *pmlk_borrower; 491*0Sstevel@tonic-gate kthread_t *pmlk_lender; 492*0Sstevel@tonic-gate dev_info_t *pmlk_dip; 493*0Sstevel@tonic-gate } lock_loan_t; 494*0Sstevel@tonic-gate static lock_loan_t lock_loan_head; /* list head is a dummy element */ 495*0Sstevel@tonic-gate 496*0Sstevel@tonic-gate #ifdef DEBUG 497*0Sstevel@tonic-gate #define PMD_FUNC(func, name) char *(func) = (name); 498*0Sstevel@tonic-gate #else 499*0Sstevel@tonic-gate #define PMD_FUNC(func, name) 500*0Sstevel@tonic-gate #endif 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate /* 504*0Sstevel@tonic-gate * Must be called before first device (including pseudo) attach 505*0Sstevel@tonic-gate */ 506*0Sstevel@tonic-gate void 507*0Sstevel@tonic-gate pm_init_locks(void) 508*0Sstevel@tonic-gate { 509*0Sstevel@tonic-gate mutex_init(&pm_scan_lock, NULL, MUTEX_DRIVER, NULL); 510*0Sstevel@tonic-gate mutex_init(&pm_rsvp_lock, NULL, MUTEX_DRIVER, NULL); 511*0Sstevel@tonic-gate mutex_init(&pm_compcnt_lock, NULL, MUTEX_DRIVER, NULL); 512*0Sstevel@tonic-gate mutex_init(&pm_dep_thread_lock, NULL, MUTEX_DRIVER, NULL); 513*0Sstevel@tonic-gate mutex_init(&pm_remdrv_lock, NULL, MUTEX_DRIVER, NULL); 514*0Sstevel@tonic-gate mutex_init(&pm_loan_lock, NULL, MUTEX_DRIVER, NULL); 515*0Sstevel@tonic-gate rw_init(&pm_thresh_rwlock, NULL, RW_DEFAULT, NULL); 516*0Sstevel@tonic-gate rw_init(&pm_noinvol_rwlock, NULL, RW_DEFAULT, NULL); 517*0Sstevel@tonic-gate cv_init(&pm_dep_thread_cv, NULL, CV_DEFAULT, NULL); 518*0Sstevel@tonic-gate } 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate static boolean_t 521*0Sstevel@tonic-gate pm_cpr_callb(void *arg, int code) 522*0Sstevel@tonic-gate { 523*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 524*0Sstevel@tonic-gate static int auto_save; 525*0Sstevel@tonic-gate static int pm_reset_timestamps(dev_info_t *, void *); 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate switch (code) { 528*0Sstevel@tonic-gate case CB_CODE_CPR_CHKPT: 529*0Sstevel@tonic-gate /* 530*0Sstevel@tonic-gate * Cancel scan or wait for scan in progress to finish 531*0Sstevel@tonic-gate * Other threads may be trying to restart the scan, so we 532*0Sstevel@tonic-gate * have to keep at it unil it sticks 533*0Sstevel@tonic-gate */ 534*0Sstevel@tonic-gate mutex_enter(&pm_scan_lock); 535*0Sstevel@tonic-gate ASSERT(!pm_scans_disabled); 536*0Sstevel@tonic-gate pm_scans_disabled = 1; 537*0Sstevel@tonic-gate auto_save = autopm_enabled; 538*0Sstevel@tonic-gate autopm_enabled = 0; 539*0Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 540*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_scan_stop_walk, NULL); 541*0Sstevel@tonic-gate break; 542*0Sstevel@tonic-gate 543*0Sstevel@tonic-gate case CB_CODE_CPR_RESUME: 544*0Sstevel@tonic-gate ASSERT(!autopm_enabled); 545*0Sstevel@tonic-gate ASSERT(pm_scans_disabled); 546*0Sstevel@tonic-gate pm_scans_disabled = 0; 547*0Sstevel@tonic-gate /* 548*0Sstevel@tonic-gate * Call pm_reset_timestamps to reset timestamps of each 549*0Sstevel@tonic-gate * device to the time when the system is resumed so that their 550*0Sstevel@tonic-gate * idleness can be re-calculated. That's to avoid devices from 551*0Sstevel@tonic-gate * being powered down right after resume if the system was in 552*0Sstevel@tonic-gate * suspended mode long enough. 553*0Sstevel@tonic-gate */ 554*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_reset_timestamps, NULL); 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate autopm_enabled = auto_save; 557*0Sstevel@tonic-gate /* 558*0Sstevel@tonic-gate * If there is any auto-pm device, get the scanning 559*0Sstevel@tonic-gate * going. Otherwise don't bother. 560*0Sstevel@tonic-gate */ 561*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_rescan_walk, NULL); 562*0Sstevel@tonic-gate break; 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate return (B_TRUE); 565*0Sstevel@tonic-gate } 566*0Sstevel@tonic-gate 567*0Sstevel@tonic-gate /* 568*0Sstevel@tonic-gate * This callback routine is called when there is a system panic. This function 569*0Sstevel@tonic-gate * exists for prototype matching. 570*0Sstevel@tonic-gate */ 571*0Sstevel@tonic-gate static boolean_t 572*0Sstevel@tonic-gate pm_panic_callb(void *arg, int code) 573*0Sstevel@tonic-gate { 574*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg, code)) 575*0Sstevel@tonic-gate void pm_cfb_check_and_powerup(void); 576*0Sstevel@tonic-gate PMD(PMD_CFB, ("pm_panic_callb\n")) 577*0Sstevel@tonic-gate pm_cfb_check_and_powerup(); 578*0Sstevel@tonic-gate return (B_TRUE); 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate 581*0Sstevel@tonic-gate static boolean_t 582*0Sstevel@tonic-gate pm_halt_callb(void *arg, int code) 583*0Sstevel@tonic-gate { 584*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg, code)) 585*0Sstevel@tonic-gate return (B_TRUE); /* XXX for now */ 586*0Sstevel@tonic-gate } 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate /* 589*0Sstevel@tonic-gate * This needs to be called after the root and platform drivers are loaded 590*0Sstevel@tonic-gate * and be single-threaded with respect to driver attach/detach 591*0Sstevel@tonic-gate */ 592*0Sstevel@tonic-gate void 593*0Sstevel@tonic-gate pm_init(void) 594*0Sstevel@tonic-gate { 595*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_init") 596*0Sstevel@tonic-gate char **mod; 597*0Sstevel@tonic-gate extern pri_t minclsyspri; 598*0Sstevel@tonic-gate static void pm_dep_thread(void); 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate pm_comps_notlowest = 0; 601*0Sstevel@tonic-gate pm_system_idle_threshold = pm_default_idle_threshold; 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gate pm_cpr_cb_id = callb_add(pm_cpr_callb, (void *)NULL, 604*0Sstevel@tonic-gate CB_CL_CPR_PM, "pm_cpr"); 605*0Sstevel@tonic-gate pm_panic_cb_id = callb_add(pm_panic_callb, (void *)NULL, 606*0Sstevel@tonic-gate CB_CL_PANIC, "pm_panic"); 607*0Sstevel@tonic-gate pm_halt_cb_id = callb_add(pm_halt_callb, (void *)NULL, 608*0Sstevel@tonic-gate CB_CL_HALT, "pm_halt"); 609*0Sstevel@tonic-gate 610*0Sstevel@tonic-gate /* 611*0Sstevel@tonic-gate * Create a thread to do dependency processing. 612*0Sstevel@tonic-gate */ 613*0Sstevel@tonic-gate (void) thread_create(NULL, 0, (void (*)())pm_dep_thread, NULL, 0, &p0, 614*0Sstevel@tonic-gate TS_RUN, minclsyspri); 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate /* 617*0Sstevel@tonic-gate * loadrootmodules already loaded these ppm drivers, now get them 618*0Sstevel@tonic-gate * attached so they can claim the root drivers as they attach 619*0Sstevel@tonic-gate */ 620*0Sstevel@tonic-gate for (mod = platform_module_list; *mod; mod++) { 621*0Sstevel@tonic-gate if (i_ddi_attach_hw_nodes(*mod) != DDI_SUCCESS) { 622*0Sstevel@tonic-gate cmn_err(CE_WARN, "!cannot load platform pm driver %s\n", 623*0Sstevel@tonic-gate *mod); 624*0Sstevel@tonic-gate } else { 625*0Sstevel@tonic-gate PMD(PMD_DHR, ("%s: %s (%s)\n", pmf, *mod, 626*0Sstevel@tonic-gate ddi_major_to_name(ddi_name_to_major(*mod)))) 627*0Sstevel@tonic-gate } 628*0Sstevel@tonic-gate } 629*0Sstevel@tonic-gate } 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate /* 632*0Sstevel@tonic-gate * pm_scan_init - create pm scan data structure. Called (if autopm enabled) 633*0Sstevel@tonic-gate * when device becomes power managed or after a failed detach and when autopm 634*0Sstevel@tonic-gate * is started via PM_START_PM ioctl, and after a CPR resume to get all the 635*0Sstevel@tonic-gate * devices scanning again. 636*0Sstevel@tonic-gate */ 637*0Sstevel@tonic-gate void 638*0Sstevel@tonic-gate pm_scan_init(dev_info_t *dip) 639*0Sstevel@tonic-gate { 640*0Sstevel@tonic-gate PMD_FUNC(pmf, "scan_init") 641*0Sstevel@tonic-gate pm_scan_t *scanp; 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate ASSERT(!PM_ISBC(dip)); 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 646*0Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 647*0Sstevel@tonic-gate if (!scanp) { 648*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): create scan data\n", 649*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 650*0Sstevel@tonic-gate scanp = kmem_zalloc(sizeof (pm_scan_t), KM_SLEEP); 651*0Sstevel@tonic-gate DEVI(dip)->devi_pm_scan = scanp; 652*0Sstevel@tonic-gate } else if (scanp->ps_scan_flags & PM_SCAN_STOP) { 653*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): " 654*0Sstevel@tonic-gate "clear PM_SCAN_STOP flag\n", pmf, PM_DEVICE(dip))) 655*0Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_STOP; 656*0Sstevel@tonic-gate } 657*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 658*0Sstevel@tonic-gate } 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate /* 661*0Sstevel@tonic-gate * pm_scan_fini - remove pm scan data structure when stopping pm on the device 662*0Sstevel@tonic-gate */ 663*0Sstevel@tonic-gate void 664*0Sstevel@tonic-gate pm_scan_fini(dev_info_t *dip) 665*0Sstevel@tonic-gate { 666*0Sstevel@tonic-gate PMD_FUNC(pmf, "scan_fini") 667*0Sstevel@tonic-gate pm_scan_t *scanp; 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 670*0Sstevel@tonic-gate ASSERT(!PM_ISBC(dip)); 671*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 672*0Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 673*0Sstevel@tonic-gate if (!scanp) { 674*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 675*0Sstevel@tonic-gate return; 676*0Sstevel@tonic-gate } 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate ASSERT(!scanp->ps_scan_id && !(scanp->ps_scan_flags & 679*0Sstevel@tonic-gate (PM_SCANNING | PM_SCAN_DISPATCHED | PM_SCAN_AGAIN))); 680*0Sstevel@tonic-gate 681*0Sstevel@tonic-gate kmem_free(scanp, sizeof (pm_scan_t)); 682*0Sstevel@tonic-gate DEVI(dip)->devi_pm_scan = NULL; 683*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 684*0Sstevel@tonic-gate } 685*0Sstevel@tonic-gate 686*0Sstevel@tonic-gate /* 687*0Sstevel@tonic-gate * Given a pointer to a component struct, return the current power level 688*0Sstevel@tonic-gate * (struct contains index unless it is a continuous level). 689*0Sstevel@tonic-gate * Located here in hopes of getting both this and dev_is_needed into the 690*0Sstevel@tonic-gate * cache together 691*0Sstevel@tonic-gate */ 692*0Sstevel@tonic-gate static int 693*0Sstevel@tonic-gate cur_power(pm_component_t *cp) 694*0Sstevel@tonic-gate { 695*0Sstevel@tonic-gate if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN) 696*0Sstevel@tonic-gate return (cp->pmc_cur_pwr); 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate return (cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]); 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate static char * 702*0Sstevel@tonic-gate pm_decode_direction(int direction) 703*0Sstevel@tonic-gate { 704*0Sstevel@tonic-gate switch (direction) { 705*0Sstevel@tonic-gate case PM_LEVEL_UPONLY: 706*0Sstevel@tonic-gate return ("up"); 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate case PM_LEVEL_EXACT: 709*0Sstevel@tonic-gate return ("exact"); 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate case PM_LEVEL_DOWNONLY: 712*0Sstevel@tonic-gate return ("down"); 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate default: 715*0Sstevel@tonic-gate return ("INVALID DIRECTION"); 716*0Sstevel@tonic-gate } 717*0Sstevel@tonic-gate _NOTE(NOTREACHED); 718*0Sstevel@tonic-gate ASSERT(0); 719*0Sstevel@tonic-gate } 720*0Sstevel@tonic-gate 721*0Sstevel@tonic-gate char * 722*0Sstevel@tonic-gate pm_decode_op(pm_bus_power_op_t op) 723*0Sstevel@tonic-gate { 724*0Sstevel@tonic-gate switch (op) { 725*0Sstevel@tonic-gate case BUS_POWER_CHILD_PWRCHG: 726*0Sstevel@tonic-gate return ("CHILD_PWRCHG"); 727*0Sstevel@tonic-gate case BUS_POWER_NEXUS_PWRUP: 728*0Sstevel@tonic-gate return ("NEXUS_PWRUP"); 729*0Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION: 730*0Sstevel@tonic-gate return ("PRE_NOTIFICATION"); 731*0Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION: 732*0Sstevel@tonic-gate return ("POST_NOTIFICATION"); 733*0Sstevel@tonic-gate case BUS_POWER_HAS_CHANGED: 734*0Sstevel@tonic-gate return ("HAS_CHANGED"); 735*0Sstevel@tonic-gate case BUS_POWER_NOINVOL: 736*0Sstevel@tonic-gate return ("NOINVOL"); 737*0Sstevel@tonic-gate default: 738*0Sstevel@tonic-gate return ("UNKNOWN OP"); 739*0Sstevel@tonic-gate } 740*0Sstevel@tonic-gate _NOTE(NOTREACHED); 741*0Sstevel@tonic-gate ASSERT(0); 742*0Sstevel@tonic-gate } 743*0Sstevel@tonic-gate 744*0Sstevel@tonic-gate /* 745*0Sstevel@tonic-gate * Returns true if level is a possible (valid) power level for component 746*0Sstevel@tonic-gate */ 747*0Sstevel@tonic-gate int 748*0Sstevel@tonic-gate e_pm_valid_power(dev_info_t *dip, int cmpt, int level) 749*0Sstevel@tonic-gate { 750*0Sstevel@tonic-gate PMD_FUNC(pmf, "e_pm_valid_power") 751*0Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, cmpt); 752*0Sstevel@tonic-gate int i; 753*0Sstevel@tonic-gate int *ip = cp->pmc_comp.pmc_lvals; 754*0Sstevel@tonic-gate int limit = cp->pmc_comp.pmc_numlevels; 755*0Sstevel@tonic-gate 756*0Sstevel@tonic-gate if (level < 0) 757*0Sstevel@tonic-gate return (0); 758*0Sstevel@tonic-gate for (i = 0; i < limit; i++) { 759*0Sstevel@tonic-gate if (level == *ip++) 760*0Sstevel@tonic-gate return (1); 761*0Sstevel@tonic-gate } 762*0Sstevel@tonic-gate #ifdef DEBUG 763*0Sstevel@tonic-gate if (pm_debug & PMD_FAIL) { 764*0Sstevel@tonic-gate ip = cp->pmc_comp.pmc_lvals; 765*0Sstevel@tonic-gate 766*0Sstevel@tonic-gate for (i = 0; i < limit; i++) 767*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: index=%d, level=%d\n", 768*0Sstevel@tonic-gate pmf, i, *ip++)) 769*0Sstevel@tonic-gate } 770*0Sstevel@tonic-gate #endif 771*0Sstevel@tonic-gate return (0); 772*0Sstevel@tonic-gate } 773*0Sstevel@tonic-gate 774*0Sstevel@tonic-gate /* 775*0Sstevel@tonic-gate * Returns true if device is pm'd (after calling pm_start if need be) 776*0Sstevel@tonic-gate */ 777*0Sstevel@tonic-gate int 778*0Sstevel@tonic-gate e_pm_valid_info(dev_info_t *dip, pm_info_t **infop) 779*0Sstevel@tonic-gate { 780*0Sstevel@tonic-gate pm_info_t *info; 781*0Sstevel@tonic-gate static int pm_start(dev_info_t *dip); 782*0Sstevel@tonic-gate 783*0Sstevel@tonic-gate /* 784*0Sstevel@tonic-gate * Check if the device is power managed if not. 785*0Sstevel@tonic-gate * To make the common case (device is power managed already) 786*0Sstevel@tonic-gate * fast, we check without the lock. If device is not already 787*0Sstevel@tonic-gate * power managed, then we take the lock and the long route through 788*0Sstevel@tonic-gate * go get it managed. Devices never go unmanaged until they 789*0Sstevel@tonic-gate * detach. 790*0Sstevel@tonic-gate */ 791*0Sstevel@tonic-gate info = PM_GET_PM_INFO(dip); 792*0Sstevel@tonic-gate if (!info) { 793*0Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) { 794*0Sstevel@tonic-gate return (0); 795*0Sstevel@tonic-gate } 796*0Sstevel@tonic-gate if (pm_start(dip) != DDI_SUCCESS) { 797*0Sstevel@tonic-gate return (0); 798*0Sstevel@tonic-gate } 799*0Sstevel@tonic-gate info = PM_GET_PM_INFO(dip); 800*0Sstevel@tonic-gate } 801*0Sstevel@tonic-gate ASSERT(info); 802*0Sstevel@tonic-gate if (infop != NULL) 803*0Sstevel@tonic-gate *infop = info; 804*0Sstevel@tonic-gate return (1); 805*0Sstevel@tonic-gate } 806*0Sstevel@tonic-gate 807*0Sstevel@tonic-gate int 808*0Sstevel@tonic-gate e_pm_valid_comp(dev_info_t *dip, int cmpt, pm_component_t **cpp) 809*0Sstevel@tonic-gate { 810*0Sstevel@tonic-gate if (cmpt >= 0 && cmpt < PM_NUMCMPTS(dip)) { 811*0Sstevel@tonic-gate if (cpp != NULL) 812*0Sstevel@tonic-gate *cpp = PM_CP(dip, cmpt); 813*0Sstevel@tonic-gate return (1); 814*0Sstevel@tonic-gate } else { 815*0Sstevel@tonic-gate return (0); 816*0Sstevel@tonic-gate } 817*0Sstevel@tonic-gate } 818*0Sstevel@tonic-gate 819*0Sstevel@tonic-gate /* 820*0Sstevel@tonic-gate * Internal guts of ddi_dev_is_needed and pm_raise/lower_power 821*0Sstevel@tonic-gate */ 822*0Sstevel@tonic-gate static int 823*0Sstevel@tonic-gate dev_is_needed(dev_info_t *dip, int cmpt, int level, int direction) 824*0Sstevel@tonic-gate { 825*0Sstevel@tonic-gate PMD_FUNC(pmf, "din") 826*0Sstevel@tonic-gate pm_component_t *cp; 827*0Sstevel@tonic-gate char *pathbuf; 828*0Sstevel@tonic-gate int result; 829*0Sstevel@tonic-gate 830*0Sstevel@tonic-gate ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY); 831*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp) || 832*0Sstevel@tonic-gate !e_pm_valid_power(dip, cmpt, level)) 833*0Sstevel@tonic-gate return (DDI_FAILURE); 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate PMD(PMD_DIN, ("%s: %s@%s(%s#%d) cmpt=%d, dir=%s, new=%d, cur=%d\n", 836*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), cmpt, pm_decode_direction(direction), 837*0Sstevel@tonic-gate level, cur_power(cp))) 838*0Sstevel@tonic-gate 839*0Sstevel@tonic-gate if (pm_set_power(dip, cmpt, level, direction, 840*0Sstevel@tonic-gate PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) { 841*0Sstevel@tonic-gate if (direction == PM_LEVEL_UPONLY) { 842*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 843*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 844*0Sstevel@tonic-gate cmn_err(CE_WARN, "Device %s failed to power up.", 845*0Sstevel@tonic-gate pathbuf); 846*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 847*0Sstevel@tonic-gate } 848*0Sstevel@tonic-gate PMD(PMD_DIN | PMD_FAIL, ("%s: %s@%s(%s#%d) [%d] %s->%d failed, " 849*0Sstevel@tonic-gate "errno %d\n", pmf, PM_DEVICE(dip), cmpt, 850*0Sstevel@tonic-gate pm_decode_direction(direction), level, result)) 851*0Sstevel@tonic-gate return (DDI_FAILURE); 852*0Sstevel@tonic-gate } 853*0Sstevel@tonic-gate 854*0Sstevel@tonic-gate PMD(PMD_RESCAN | PMD_DIN, ("%s: pm_rescan %s@%s(%s#%d)\n", pmf, 855*0Sstevel@tonic-gate PM_DEVICE(dip))) 856*0Sstevel@tonic-gate pm_rescan(dip); 857*0Sstevel@tonic-gate return (DDI_SUCCESS); 858*0Sstevel@tonic-gate } 859*0Sstevel@tonic-gate 860*0Sstevel@tonic-gate /* 861*0Sstevel@tonic-gate * We can get multiple pm_rescan() threads, if one of them discovers 862*0Sstevel@tonic-gate * that no scan is running at the moment, it kicks it into action. 863*0Sstevel@tonic-gate * Otherwise, it tells the current scanning thread to scan again when 864*0Sstevel@tonic-gate * it is done by asserting the PM_SCAN_AGAIN flag. The PM_SCANNING and 865*0Sstevel@tonic-gate * PM_SCAN_AGAIN flags are used to regulate scan, to make sure only one 866*0Sstevel@tonic-gate * thread at a time runs the pm_scan_dev() code. 867*0Sstevel@tonic-gate */ 868*0Sstevel@tonic-gate void 869*0Sstevel@tonic-gate pm_rescan(void *arg) 870*0Sstevel@tonic-gate { 871*0Sstevel@tonic-gate PMD_FUNC(pmf, "rescan") 872*0Sstevel@tonic-gate dev_info_t *dip = (dev_info_t *)arg; 873*0Sstevel@tonic-gate pm_info_t *info; 874*0Sstevel@tonic-gate pm_scan_t *scanp; 875*0Sstevel@tonic-gate timeout_id_t scanid; 876*0Sstevel@tonic-gate 877*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 878*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 879*0Sstevel@tonic-gate info = PM_GET_PM_INFO(dip); 880*0Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 881*0Sstevel@tonic-gate if (pm_scans_disabled || !autopm_enabled || !info || !scanp || 882*0Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP)) { 883*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 884*0Sstevel@tonic-gate return; 885*0Sstevel@tonic-gate } 886*0Sstevel@tonic-gate if (scanp->ps_scan_flags & PM_SCANNING) { 887*0Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_AGAIN; 888*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 889*0Sstevel@tonic-gate return; 890*0Sstevel@tonic-gate } else if (scanp->ps_scan_id) { 891*0Sstevel@tonic-gate scanid = scanp->ps_scan_id; 892*0Sstevel@tonic-gate scanp->ps_scan_id = 0; 893*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): cancel timeout scanid %lx\n", 894*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), (ulong_t)scanid)) 895*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 896*0Sstevel@tonic-gate (void) untimeout(scanid); 897*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 898*0Sstevel@tonic-gate } 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate /* 901*0Sstevel@tonic-gate * Dispatching pm_scan during attach time is risky due to the fact that 902*0Sstevel@tonic-gate * attach might soon fail and dip dissolved, and panic may happen while 903*0Sstevel@tonic-gate * attempting to stop scan. So schedule a pm_rescan instead. 904*0Sstevel@tonic-gate * (Note that if either of the first two terms are true, taskq_dispatch 905*0Sstevel@tonic-gate * will not be invoked). 906*0Sstevel@tonic-gate * 907*0Sstevel@tonic-gate * Multiple pm_scan dispatching is unecessary and costly to keep track 908*0Sstevel@tonic-gate * of. The PM_SCAN_DISPATCHED flag is used between pm_rescan and pm_scan 909*0Sstevel@tonic-gate * to regulate the dispatching. 910*0Sstevel@tonic-gate * 911*0Sstevel@tonic-gate * Scan is stopped before the device is detached (in pm_detaching()) 912*0Sstevel@tonic-gate * but it may get re-started during the post_detach processing if the 913*0Sstevel@tonic-gate * driver fails to detach. 914*0Sstevel@tonic-gate */ 915*0Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip) || 916*0Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_DISPATCHED) || 917*0Sstevel@tonic-gate !taskq_dispatch(system_taskq, pm_scan, (void *)dip, TQ_NOSLEEP)) { 918*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): attaching, pm_scan already " 919*0Sstevel@tonic-gate "dispatched or dispatching failed\n", pmf, PM_DEVICE(dip))) 920*0Sstevel@tonic-gate if (scanp->ps_scan_id) { 921*0Sstevel@tonic-gate scanid = scanp->ps_scan_id; 922*0Sstevel@tonic-gate scanp->ps_scan_id = 0; 923*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 924*0Sstevel@tonic-gate (void) untimeout(scanid); 925*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 926*0Sstevel@tonic-gate if (scanp->ps_scan_id) { 927*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): a competing " 928*0Sstevel@tonic-gate "thread scheduled pm_rescan, scanid %lx\n", 929*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), 930*0Sstevel@tonic-gate (ulong_t)scanp->ps_scan_id)) 931*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 932*0Sstevel@tonic-gate return; 933*0Sstevel@tonic-gate } 934*0Sstevel@tonic-gate } 935*0Sstevel@tonic-gate scanp->ps_scan_id = timeout(pm_rescan, (void *)dip, 936*0Sstevel@tonic-gate (scanp->ps_idle_down ? pm_id_ticks : 937*0Sstevel@tonic-gate (pm_min_scan * hz))); 938*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): scheduled next pm_rescan, " 939*0Sstevel@tonic-gate "scanid %lx\n", pmf, PM_DEVICE(dip), 940*0Sstevel@tonic-gate (ulong_t)scanp->ps_scan_id)) 941*0Sstevel@tonic-gate } else { 942*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: dispatched pm_scan for %s@%s(%s#%d)\n", 943*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 944*0Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_DISPATCHED; 945*0Sstevel@tonic-gate } 946*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 947*0Sstevel@tonic-gate } 948*0Sstevel@tonic-gate 949*0Sstevel@tonic-gate void 950*0Sstevel@tonic-gate pm_scan(void *arg) 951*0Sstevel@tonic-gate { 952*0Sstevel@tonic-gate PMD_FUNC(pmf, "scan") 953*0Sstevel@tonic-gate dev_info_t *dip = (dev_info_t *)arg; 954*0Sstevel@tonic-gate pm_scan_t *scanp; 955*0Sstevel@tonic-gate time_t nextscan; 956*0Sstevel@tonic-gate 957*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 958*0Sstevel@tonic-gate 959*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 960*0Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 961*0Sstevel@tonic-gate ASSERT(scanp && PM_GET_PM_INFO(dip)); 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate if (pm_scans_disabled || !autopm_enabled || 964*0Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP)) { 965*0Sstevel@tonic-gate scanp->ps_scan_flags &= ~(PM_SCAN_AGAIN | PM_SCAN_DISPATCHED); 966*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 967*0Sstevel@tonic-gate return; 968*0Sstevel@tonic-gate } 969*0Sstevel@tonic-gate 970*0Sstevel@tonic-gate if (scanp->ps_idle_down) { 971*0Sstevel@tonic-gate /* 972*0Sstevel@tonic-gate * make sure we remember idledown was in affect until 973*0Sstevel@tonic-gate * we've completed the scan 974*0Sstevel@tonic-gate */ 975*0Sstevel@tonic-gate PMID_SET_SCANS(scanp->ps_idle_down) 976*0Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown starts " 977*0Sstevel@tonic-gate "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down)) 978*0Sstevel@tonic-gate } 979*0Sstevel@tonic-gate 980*0Sstevel@tonic-gate /* possible having two threads running pm_scan() */ 981*0Sstevel@tonic-gate if (scanp->ps_scan_flags & PM_SCANNING) { 982*0Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_AGAIN; 983*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: scanning, will scan %s@%s(%s#%d) again\n", 984*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 985*0Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED; 986*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 987*0Sstevel@tonic-gate return; 988*0Sstevel@tonic-gate } 989*0Sstevel@tonic-gate 990*0Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCANNING; 991*0Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED; 992*0Sstevel@tonic-gate do { 993*0Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_AGAIN; 994*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 995*0Sstevel@tonic-gate nextscan = pm_scan_dev(dip); 996*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 997*0Sstevel@tonic-gate } while (scanp->ps_scan_flags & PM_SCAN_AGAIN); 998*0Sstevel@tonic-gate 999*0Sstevel@tonic-gate ASSERT(scanp->ps_scan_flags & PM_SCANNING); 1000*0Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCANNING; 1001*0Sstevel@tonic-gate 1002*0Sstevel@tonic-gate if (scanp->ps_idle_down) { 1003*0Sstevel@tonic-gate scanp->ps_idle_down &= ~PMID_SCANS; 1004*0Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown ends " 1005*0Sstevel@tonic-gate "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down)) 1006*0Sstevel@tonic-gate } 1007*0Sstevel@tonic-gate 1008*0Sstevel@tonic-gate /* schedule for next idle check */ 1009*0Sstevel@tonic-gate if (nextscan != LONG_MAX) { 1010*0Sstevel@tonic-gate if (nextscan > (LONG_MAX / hz)) 1011*0Sstevel@tonic-gate nextscan = (LONG_MAX - 1) / hz; 1012*0Sstevel@tonic-gate if (scanp->ps_scan_id) { 1013*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): while scanning " 1014*0Sstevel@tonic-gate "another rescan scheduled scanid(%lx)\n", pmf, 1015*0Sstevel@tonic-gate PM_DEVICE(dip), (ulong_t)scanp->ps_scan_id)) 1016*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1017*0Sstevel@tonic-gate return; 1018*0Sstevel@tonic-gate } else if (!(scanp->ps_scan_flags & PM_SCAN_STOP)) { 1019*0Sstevel@tonic-gate scanp->ps_scan_id = timeout(pm_rescan, (void *)dip, 1020*0Sstevel@tonic-gate (clock_t)(nextscan * hz)); 1021*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: nextscan for %s@%s(%s#%d) in " 1022*0Sstevel@tonic-gate "%lx sec, scanid(%lx) \n", pmf, PM_DEVICE(dip), 1023*0Sstevel@tonic-gate (ulong_t)nextscan, (ulong_t)scanp->ps_scan_id)) 1024*0Sstevel@tonic-gate } 1025*0Sstevel@tonic-gate } 1026*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1027*0Sstevel@tonic-gate } 1028*0Sstevel@tonic-gate 1029*0Sstevel@tonic-gate void 1030*0Sstevel@tonic-gate pm_get_timestamps(dev_info_t *dip, time_t *valuep) 1031*0Sstevel@tonic-gate { 1032*0Sstevel@tonic-gate int components = PM_NUMCMPTS(dip); 1033*0Sstevel@tonic-gate int i; 1034*0Sstevel@tonic-gate 1035*0Sstevel@tonic-gate ASSERT(components > 0); 1036*0Sstevel@tonic-gate PM_LOCK_BUSY(dip); /* so we get a consistent view */ 1037*0Sstevel@tonic-gate for (i = 0; i < components; i++) { 1038*0Sstevel@tonic-gate valuep[i] = PM_CP(dip, i)->pmc_timestamp; 1039*0Sstevel@tonic-gate } 1040*0Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 1041*0Sstevel@tonic-gate } 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate /* 1044*0Sstevel@tonic-gate * Returns true if device needs to be kept up because it exported the 1045*0Sstevel@tonic-gate * "no-involuntary-power-cycles" property or we're pretending it did (console 1046*0Sstevel@tonic-gate * fb case) or it is an ancestor of such a device and has used up the "one 1047*0Sstevel@tonic-gate * free cycle" allowed when all such leaf nodes have voluntarily powered down 1048*0Sstevel@tonic-gate * upon detach 1049*0Sstevel@tonic-gate */ 1050*0Sstevel@tonic-gate int 1051*0Sstevel@tonic-gate pm_noinvol(dev_info_t *dip) 1052*0Sstevel@tonic-gate { 1053*0Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol") 1054*0Sstevel@tonic-gate 1055*0Sstevel@tonic-gate /* 1056*0Sstevel@tonic-gate * This doesn't change over the life of a driver, so no locking needed 1057*0Sstevel@tonic-gate */ 1058*0Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 1059*0Sstevel@tonic-gate PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB %s@%s(%s#%d)\n", 1060*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 1061*0Sstevel@tonic-gate return (1); 1062*0Sstevel@tonic-gate } 1063*0Sstevel@tonic-gate /* 1064*0Sstevel@tonic-gate * Not an issue if no such kids 1065*0Sstevel@tonic-gate */ 1066*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_noinvolpm == 0) { 1067*0Sstevel@tonic-gate #ifdef DEBUG 1068*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd != 0) { 1069*0Sstevel@tonic-gate dev_info_t *pdip = dip; 1070*0Sstevel@tonic-gate do { 1071*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d) noinvol %d " 1072*0Sstevel@tonic-gate "volpmd %d\n", pmf, PM_DEVICE(pdip), 1073*0Sstevel@tonic-gate DEVI(pdip)->devi_pm_noinvolpm, 1074*0Sstevel@tonic-gate DEVI(pdip)->devi_pm_volpmd)) 1075*0Sstevel@tonic-gate pdip = ddi_get_parent(pdip); 1076*0Sstevel@tonic-gate } while (pdip); 1077*0Sstevel@tonic-gate } 1078*0Sstevel@tonic-gate #endif 1079*0Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_volpmd == 0); 1080*0Sstevel@tonic-gate return (0); 1081*0Sstevel@tonic-gate } 1082*0Sstevel@tonic-gate 1083*0Sstevel@tonic-gate /* 1084*0Sstevel@tonic-gate * Since we now maintain the counts correct at every node, we no longer 1085*0Sstevel@tonic-gate * need to look up the tree. An ancestor cannot use up the free cycle 1086*0Sstevel@tonic-gate * without the children getting their counts adjusted. 1087*0Sstevel@tonic-gate */ 1088*0Sstevel@tonic-gate 1089*0Sstevel@tonic-gate #ifdef DEBUG 1090*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd) 1091*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s@%s(%s#%d)\n", pmf, 1092*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, DEVI(dip)->devi_pm_volpmd, 1093*0Sstevel@tonic-gate PM_DEVICE(dip))) 1094*0Sstevel@tonic-gate #endif 1095*0Sstevel@tonic-gate return (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd); 1096*0Sstevel@tonic-gate } 1097*0Sstevel@tonic-gate 1098*0Sstevel@tonic-gate /* 1099*0Sstevel@tonic-gate * This function performs the actual scanning of the device. 1100*0Sstevel@tonic-gate * It attempts to power off the indicated device's components if they have 1101*0Sstevel@tonic-gate * been idle and other restrictions are met. 1102*0Sstevel@tonic-gate * pm_scan_dev calculates and returns when the next scan should happen for 1103*0Sstevel@tonic-gate * this device. 1104*0Sstevel@tonic-gate */ 1105*0Sstevel@tonic-gate time_t 1106*0Sstevel@tonic-gate pm_scan_dev(dev_info_t *dip) 1107*0Sstevel@tonic-gate { 1108*0Sstevel@tonic-gate PMD_FUNC(pmf, "scan_dev") 1109*0Sstevel@tonic-gate pm_scan_t *scanp; 1110*0Sstevel@tonic-gate time_t *timestamp, idletime, now, thresh; 1111*0Sstevel@tonic-gate time_t timeleft = 0; 1112*0Sstevel@tonic-gate int i, nxtpwr, curpwr, pwrndx, unused; 1113*0Sstevel@tonic-gate size_t size; 1114*0Sstevel@tonic-gate pm_component_t *cp; 1115*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 1116*0Sstevel@tonic-gate int circ; 1117*0Sstevel@tonic-gate static int cur_threshold(dev_info_t *, int); 1118*0Sstevel@tonic-gate static int pm_next_lower_power(pm_component_t *, int); 1119*0Sstevel@tonic-gate 1120*0Sstevel@tonic-gate /* 1121*0Sstevel@tonic-gate * skip attaching device 1122*0Sstevel@tonic-gate */ 1123*0Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) { 1124*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) is attaching, timeleft(%lx)\n", 1125*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_min_scan)) 1126*0Sstevel@tonic-gate return (pm_min_scan); 1127*0Sstevel@tonic-gate } 1128*0Sstevel@tonic-gate 1129*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 1130*0Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 1131*0Sstevel@tonic-gate ASSERT(scanp && PM_GET_PM_INFO(dip)); 1132*0Sstevel@tonic-gate 1133*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip))) 1134*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): kuc is %d\n", pmf, PM_DEVICE(dip), 1135*0Sstevel@tonic-gate PM_KUC(dip))) 1136*0Sstevel@tonic-gate 1137*0Sstevel@tonic-gate /* no scan under the following conditions */ 1138*0Sstevel@tonic-gate if (pm_scans_disabled || !autopm_enabled || 1139*0Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP) || 1140*0Sstevel@tonic-gate (PM_KUC(dip) != 0) || 1141*0Sstevel@tonic-gate PM_ISDIRECT(dip) || pm_noinvol(dip)) { 1142*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1143*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END, %s@%s(%s#%d)] no scan, " 1144*0Sstevel@tonic-gate "scan_disabled(%d), apm_enabled(%d), kuc(%d), " 1145*0Sstevel@tonic-gate "%s directpm, %s pm_noinvol\n", pmf, PM_DEVICE(dip), 1146*0Sstevel@tonic-gate pm_scans_disabled, autopm_enabled, PM_KUC(dip), 1147*0Sstevel@tonic-gate PM_ISDIRECT(dip) ? "is" : "is not", 1148*0Sstevel@tonic-gate pm_noinvol(dip) ? "is" : "is not")) 1149*0Sstevel@tonic-gate return (LONG_MAX); 1150*0Sstevel@tonic-gate } 1151*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1152*0Sstevel@tonic-gate 1153*0Sstevel@tonic-gate if (!ndi_devi_tryenter(pdip, &circ)) { 1154*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) can't hold pdip", 1155*0Sstevel@tonic-gate pmf, PM_DEVICE(pdip))) 1156*0Sstevel@tonic-gate return ((time_t)1); 1157*0Sstevel@tonic-gate } 1158*0Sstevel@tonic-gate now = gethrestime_sec(); 1159*0Sstevel@tonic-gate size = PM_NUMCMPTS(dip) * sizeof (time_t); 1160*0Sstevel@tonic-gate timestamp = kmem_alloc(size, KM_SLEEP); 1161*0Sstevel@tonic-gate pm_get_timestamps(dip, timestamp); 1162*0Sstevel@tonic-gate 1163*0Sstevel@tonic-gate /* 1164*0Sstevel@tonic-gate * Since we removed support for backwards compatible devices, 1165*0Sstevel@tonic-gate * (see big comment at top of file) 1166*0Sstevel@tonic-gate * it is no longer required to deal with component 0 last. 1167*0Sstevel@tonic-gate */ 1168*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 1169*0Sstevel@tonic-gate /* 1170*0Sstevel@tonic-gate * If already off (an optimization, perhaps) 1171*0Sstevel@tonic-gate */ 1172*0Sstevel@tonic-gate cp = PM_CP(dip, i); 1173*0Sstevel@tonic-gate pwrndx = cp->pmc_cur_pwr; 1174*0Sstevel@tonic-gate curpwr = (pwrndx == PM_LEVEL_UNKNOWN) ? 1175*0Sstevel@tonic-gate PM_LEVEL_UNKNOWN : 1176*0Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[pwrndx]; 1177*0Sstevel@tonic-gate 1178*0Sstevel@tonic-gate if (pwrndx == 0) { 1179*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d off or " 1180*0Sstevel@tonic-gate "lowest\n", pmf, PM_DEVICE(dip), i)) 1181*0Sstevel@tonic-gate /* skip device if off or at its lowest */ 1182*0Sstevel@tonic-gate continue; 1183*0Sstevel@tonic-gate } 1184*0Sstevel@tonic-gate 1185*0Sstevel@tonic-gate thresh = cur_threshold(dip, i); /* comp i threshold */ 1186*0Sstevel@tonic-gate if ((timestamp[i] == 0) || (cp->pmc_busycount > 0)) { 1187*0Sstevel@tonic-gate /* were busy or newly became busy by another thread */ 1188*0Sstevel@tonic-gate if (timeleft == 0) 1189*0Sstevel@tonic-gate timeleft = max(thresh, pm_min_scan); 1190*0Sstevel@tonic-gate else 1191*0Sstevel@tonic-gate timeleft = min( 1192*0Sstevel@tonic-gate timeleft, max(thresh, pm_min_scan)); 1193*0Sstevel@tonic-gate continue; 1194*0Sstevel@tonic-gate } 1195*0Sstevel@tonic-gate 1196*0Sstevel@tonic-gate idletime = now - timestamp[i]; /* idle time */ 1197*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d idle time %lx\n", 1198*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), i, idletime)) 1199*0Sstevel@tonic-gate if (idletime >= thresh || PM_IS_PID(dip)) { 1200*0Sstevel@tonic-gate nxtpwr = pm_next_lower_power(cp, pwrndx); 1201*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, %d->%d\n", 1202*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), i, curpwr, nxtpwr)) 1203*0Sstevel@tonic-gate if (pm_set_power(dip, i, nxtpwr, PM_LEVEL_DOWNONLY, 1204*0Sstevel@tonic-gate PM_CANBLOCK_FAIL, 1, &unused) != DDI_SUCCESS && 1205*0Sstevel@tonic-gate PM_CURPOWER(dip, i) != nxtpwr) { 1206*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, " 1207*0Sstevel@tonic-gate "%d->%d Failed\n", pmf, PM_DEVICE(dip), 1208*0Sstevel@tonic-gate i, curpwr, nxtpwr)) 1209*0Sstevel@tonic-gate timeleft = pm_min_scan; 1210*0Sstevel@tonic-gate continue; 1211*0Sstevel@tonic-gate } else { 1212*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, " 1213*0Sstevel@tonic-gate "%d->%d, GOOD curpwr %d\n", pmf, 1214*0Sstevel@tonic-gate PM_DEVICE(dip), i, curpwr, nxtpwr, 1215*0Sstevel@tonic-gate cur_power(cp))) 1216*0Sstevel@tonic-gate 1217*0Sstevel@tonic-gate if (nxtpwr == 0) /* component went off */ 1218*0Sstevel@tonic-gate continue; 1219*0Sstevel@tonic-gate 1220*0Sstevel@tonic-gate /* 1221*0Sstevel@tonic-gate * scan to next lower level 1222*0Sstevel@tonic-gate */ 1223*0Sstevel@tonic-gate if (timeleft == 0) 1224*0Sstevel@tonic-gate timeleft = max( 1225*0Sstevel@tonic-gate 1, cur_threshold(dip, i)); 1226*0Sstevel@tonic-gate else 1227*0Sstevel@tonic-gate timeleft = min(timeleft, 1228*0Sstevel@tonic-gate max(1, cur_threshold(dip, i))); 1229*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, " 1230*0Sstevel@tonic-gate "timeleft(%lx)\n", pmf, PM_DEVICE(dip), 1231*0Sstevel@tonic-gate i, timeleft)) 1232*0Sstevel@tonic-gate } 1233*0Sstevel@tonic-gate } else { /* comp not idle long enough */ 1234*0Sstevel@tonic-gate if (timeleft == 0) 1235*0Sstevel@tonic-gate timeleft = thresh - idletime; 1236*0Sstevel@tonic-gate else 1237*0Sstevel@tonic-gate timeleft = min(timeleft, (thresh - idletime)); 1238*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, timeleft=" 1239*0Sstevel@tonic-gate "%lx\n", pmf, PM_DEVICE(dip), i, timeleft)) 1240*0Sstevel@tonic-gate } 1241*0Sstevel@tonic-gate } 1242*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 1243*0Sstevel@tonic-gate kmem_free(timestamp, size); 1244*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] timeleft(%lx)\n", pmf, 1245*0Sstevel@tonic-gate PM_DEVICE(dip), timeleft)) 1246*0Sstevel@tonic-gate 1247*0Sstevel@tonic-gate /* 1248*0Sstevel@tonic-gate * if components are already at lowest level, timeleft is left 0 1249*0Sstevel@tonic-gate */ 1250*0Sstevel@tonic-gate return ((timeleft == 0) ? LONG_MAX : timeleft); 1251*0Sstevel@tonic-gate } 1252*0Sstevel@tonic-gate 1253*0Sstevel@tonic-gate /* 1254*0Sstevel@tonic-gate * pm_scan_stop - cancel scheduled pm_rescan, 1255*0Sstevel@tonic-gate * wait for termination of dispatched pm_scan thread 1256*0Sstevel@tonic-gate * and active pm_scan_dev thread. 1257*0Sstevel@tonic-gate */ 1258*0Sstevel@tonic-gate void 1259*0Sstevel@tonic-gate pm_scan_stop(dev_info_t *dip) 1260*0Sstevel@tonic-gate { 1261*0Sstevel@tonic-gate PMD_FUNC(pmf, "scan_stop") 1262*0Sstevel@tonic-gate pm_scan_t *scanp; 1263*0Sstevel@tonic-gate timeout_id_t scanid; 1264*0Sstevel@tonic-gate 1265*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip))) 1266*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 1267*0Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip); 1268*0Sstevel@tonic-gate if (!scanp) { 1269*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] scan not initialized\n", 1270*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 1271*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1272*0Sstevel@tonic-gate return; 1273*0Sstevel@tonic-gate } 1274*0Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_STOP; 1275*0Sstevel@tonic-gate 1276*0Sstevel@tonic-gate /* cancel scheduled scan taskq */ 1277*0Sstevel@tonic-gate while (scanp->ps_scan_id) { 1278*0Sstevel@tonic-gate scanid = scanp->ps_scan_id; 1279*0Sstevel@tonic-gate scanp->ps_scan_id = 0; 1280*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1281*0Sstevel@tonic-gate (void) untimeout(scanid); 1282*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 1283*0Sstevel@tonic-gate } 1284*0Sstevel@tonic-gate 1285*0Sstevel@tonic-gate while (scanp->ps_scan_flags & (PM_SCANNING | PM_SCAN_DISPATCHED)) { 1286*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1287*0Sstevel@tonic-gate delay(1); 1288*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 1289*0Sstevel@tonic-gate } 1290*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 1291*0Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip))) 1292*0Sstevel@tonic-gate } 1293*0Sstevel@tonic-gate 1294*0Sstevel@tonic-gate int 1295*0Sstevel@tonic-gate pm_scan_stop_walk(dev_info_t *dip, void *arg) 1296*0Sstevel@tonic-gate { 1297*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 1298*0Sstevel@tonic-gate 1299*0Sstevel@tonic-gate if (!PM_GET_PM_SCAN(dip)) 1300*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 1301*0Sstevel@tonic-gate ASSERT(!PM_ISBC(dip)); 1302*0Sstevel@tonic-gate pm_scan_stop(dip); 1303*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 1304*0Sstevel@tonic-gate } 1305*0Sstevel@tonic-gate 1306*0Sstevel@tonic-gate /* 1307*0Sstevel@tonic-gate * Converts a power level value to its index 1308*0Sstevel@tonic-gate */ 1309*0Sstevel@tonic-gate static int 1310*0Sstevel@tonic-gate power_val_to_index(pm_component_t *cp, int val) 1311*0Sstevel@tonic-gate { 1312*0Sstevel@tonic-gate int limit, i, *ip; 1313*0Sstevel@tonic-gate 1314*0Sstevel@tonic-gate ASSERT(val != PM_LEVEL_UPONLY && val != PM_LEVEL_DOWNONLY && 1315*0Sstevel@tonic-gate val != PM_LEVEL_EXACT); 1316*0Sstevel@tonic-gate /* convert power value into index (i) */ 1317*0Sstevel@tonic-gate limit = cp->pmc_comp.pmc_numlevels; 1318*0Sstevel@tonic-gate ip = cp->pmc_comp.pmc_lvals; 1319*0Sstevel@tonic-gate for (i = 0; i < limit; i++) 1320*0Sstevel@tonic-gate if (val == *ip++) 1321*0Sstevel@tonic-gate return (i); 1322*0Sstevel@tonic-gate return (-1); 1323*0Sstevel@tonic-gate } 1324*0Sstevel@tonic-gate 1325*0Sstevel@tonic-gate /* 1326*0Sstevel@tonic-gate * Converts a numeric power level to a printable string 1327*0Sstevel@tonic-gate */ 1328*0Sstevel@tonic-gate static char * 1329*0Sstevel@tonic-gate power_val_to_string(pm_component_t *cp, int val) 1330*0Sstevel@tonic-gate { 1331*0Sstevel@tonic-gate int index; 1332*0Sstevel@tonic-gate 1333*0Sstevel@tonic-gate if (val == PM_LEVEL_UPONLY) 1334*0Sstevel@tonic-gate return ("<UPONLY>"); 1335*0Sstevel@tonic-gate 1336*0Sstevel@tonic-gate if (val == PM_LEVEL_UNKNOWN || 1337*0Sstevel@tonic-gate (index = power_val_to_index(cp, val)) == -1) 1338*0Sstevel@tonic-gate return ("<LEVEL_UNKNOWN>"); 1339*0Sstevel@tonic-gate 1340*0Sstevel@tonic-gate return (cp->pmc_comp.pmc_lnames[index]); 1341*0Sstevel@tonic-gate } 1342*0Sstevel@tonic-gate 1343*0Sstevel@tonic-gate /* 1344*0Sstevel@tonic-gate * Return true if this node has been claimed by a ppm. 1345*0Sstevel@tonic-gate */ 1346*0Sstevel@tonic-gate static int 1347*0Sstevel@tonic-gate pm_ppm_claimed(dev_info_t *dip) 1348*0Sstevel@tonic-gate { 1349*0Sstevel@tonic-gate return (PPM(dip) != NULL); 1350*0Sstevel@tonic-gate } 1351*0Sstevel@tonic-gate 1352*0Sstevel@tonic-gate /* 1353*0Sstevel@tonic-gate * A node which was voluntarily power managed has just used up its "free cycle" 1354*0Sstevel@tonic-gate * and need is volpmd field cleared, and the same done to all its descendents 1355*0Sstevel@tonic-gate */ 1356*0Sstevel@tonic-gate static void 1357*0Sstevel@tonic-gate pm_clear_volpm_dip(dev_info_t *dip) 1358*0Sstevel@tonic-gate { 1359*0Sstevel@tonic-gate PMD_FUNC(pmf, "clear_volpm_dip") 1360*0Sstevel@tonic-gate 1361*0Sstevel@tonic-gate if (dip == NULL) 1362*0Sstevel@tonic-gate return; 1363*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm from %s@%s(%s#%d)\n", pmf, 1364*0Sstevel@tonic-gate PM_DEVICE(dip))) 1365*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0; 1366*0Sstevel@tonic-gate for (dip = ddi_get_child(dip); dip; dip = ddi_get_next_sibling(dip)) { 1367*0Sstevel@tonic-gate pm_clear_volpm_dip(dip); 1368*0Sstevel@tonic-gate } 1369*0Sstevel@tonic-gate } 1370*0Sstevel@tonic-gate 1371*0Sstevel@tonic-gate /* 1372*0Sstevel@tonic-gate * A node which was voluntarily power managed has used up the "free cycles" 1373*0Sstevel@tonic-gate * for the subtree that it is the root of. Scan through the list of detached 1374*0Sstevel@tonic-gate * nodes and adjust the counts of any that are descendents of the node. 1375*0Sstevel@tonic-gate */ 1376*0Sstevel@tonic-gate static void 1377*0Sstevel@tonic-gate pm_clear_volpm_list(dev_info_t *dip) 1378*0Sstevel@tonic-gate { 1379*0Sstevel@tonic-gate PMD_FUNC(pmf, "clear_volpm_list") 1380*0Sstevel@tonic-gate char *pathbuf; 1381*0Sstevel@tonic-gate size_t len; 1382*0Sstevel@tonic-gate pm_noinvol_t *ip; 1383*0Sstevel@tonic-gate 1384*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1385*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 1386*0Sstevel@tonic-gate len = strlen(pathbuf); 1387*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm list %s\n", pmf, pathbuf)) 1388*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 1389*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 1390*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm: ni_path %s\n", pmf, 1391*0Sstevel@tonic-gate ip->ni_path)) 1392*0Sstevel@tonic-gate if (strncmp(pathbuf, ip->ni_path, len) == 0 && 1393*0Sstevel@tonic-gate ip->ni_path[len] == '/') { 1394*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm: %s\n", pmf, 1395*0Sstevel@tonic-gate ip->ni_path)) 1396*0Sstevel@tonic-gate ip->ni_volpmd = 0; 1397*0Sstevel@tonic-gate ip->ni_wasvolpmd = 0; 1398*0Sstevel@tonic-gate } 1399*0Sstevel@tonic-gate } 1400*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 1401*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 1402*0Sstevel@tonic-gate } 1403*0Sstevel@tonic-gate 1404*0Sstevel@tonic-gate /* 1405*0Sstevel@tonic-gate * Powers a device, suspending or resuming the driver if it is a backward 1406*0Sstevel@tonic-gate * compatible device, calling into ppm to change power level. 1407*0Sstevel@tonic-gate * Called with the component's power lock held. 1408*0Sstevel@tonic-gate */ 1409*0Sstevel@tonic-gate static int 1410*0Sstevel@tonic-gate power_dev(dev_info_t *dip, int comp, int level, int old_level, 1411*0Sstevel@tonic-gate pm_canblock_t canblock, pm_ppm_devlist_t **devlist) 1412*0Sstevel@tonic-gate { 1413*0Sstevel@tonic-gate PMD_FUNC(pmf, "power_dev") 1414*0Sstevel@tonic-gate power_req_t power_req; 1415*0Sstevel@tonic-gate int power_op_ret; /* DDI_SUCCESS or DDI_FAILURE */ 1416*0Sstevel@tonic-gate int resume_needed = 0; 1417*0Sstevel@tonic-gate int suspended = 0; 1418*0Sstevel@tonic-gate int result; 1419*0Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, comp); 1420*0Sstevel@tonic-gate int bc = PM_ISBC(dip); 1421*0Sstevel@tonic-gate int pm_all_components_off(dev_info_t *); 1422*0Sstevel@tonic-gate int clearvolpmd = 0; 1423*0Sstevel@tonic-gate char pathbuf[MAXNAMELEN]; 1424*0Sstevel@tonic-gate #ifdef DEBUG 1425*0Sstevel@tonic-gate char *ppmname, *ppmaddr; 1426*0Sstevel@tonic-gate #endif 1427*0Sstevel@tonic-gate /* 1428*0Sstevel@tonic-gate * If this is comp 0 of a backwards compat device and we are 1429*0Sstevel@tonic-gate * going to take the power away, we need to detach it with 1430*0Sstevel@tonic-gate * DDI_PM_SUSPEND command. 1431*0Sstevel@tonic-gate */ 1432*0Sstevel@tonic-gate if (bc && comp == 0 && POWERING_OFF(old_level, level)) { 1433*0Sstevel@tonic-gate if (devi_detach(dip, DDI_PM_SUSPEND) != DDI_SUCCESS) { 1434*0Sstevel@tonic-gate /* We could not suspend before turning cmpt zero off */ 1435*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: could not suspend %s@%s(%s#%d)\n", 1436*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 1437*0Sstevel@tonic-gate return (DDI_FAILURE); 1438*0Sstevel@tonic-gate } else { 1439*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_SUSPENDED; 1440*0Sstevel@tonic-gate suspended++; 1441*0Sstevel@tonic-gate } 1442*0Sstevel@tonic-gate } 1443*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_SET_POWER; 1444*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.who = dip; 1445*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.cmpt = comp; 1446*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.old_level = old_level; 1447*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.new_level = level; 1448*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.canblock = canblock; 1449*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.cookie = NULL; 1450*0Sstevel@tonic-gate #ifdef DEBUG 1451*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { 1452*0Sstevel@tonic-gate ppmname = PM_NAME(PPM(dip)); 1453*0Sstevel@tonic-gate ppmaddr = PM_ADDR(PPM(dip)); 1454*0Sstevel@tonic-gate 1455*0Sstevel@tonic-gate } else { 1456*0Sstevel@tonic-gate ppmname = "noppm"; 1457*0Sstevel@tonic-gate ppmaddr = "0"; 1458*0Sstevel@tonic-gate } 1459*0Sstevel@tonic-gate PMD(PMD_PPM, ("%s: %s@%s(%s#%d):%s[%d] %s (%d) -> %s (%d) via %s@%s\n", 1460*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), cp->pmc_comp.pmc_name, comp, 1461*0Sstevel@tonic-gate power_val_to_string(cp, old_level), old_level, 1462*0Sstevel@tonic-gate power_val_to_string(cp, level), level, ppmname, ppmaddr)) 1463*0Sstevel@tonic-gate #endif 1464*0Sstevel@tonic-gate /* 1465*0Sstevel@tonic-gate * If non-bc noinvolpm device is turning first comp on, or noinvolpm 1466*0Sstevel@tonic-gate * bc device comp 0 is powering on, then we count it as a power cycle 1467*0Sstevel@tonic-gate * against its voluntary count. 1468*0Sstevel@tonic-gate */ 1469*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd && 1470*0Sstevel@tonic-gate (!bc && pm_all_components_off(dip) && level != 0) || 1471*0Sstevel@tonic-gate (bc && comp == 0 && POWERING_ON(old_level, level))) 1472*0Sstevel@tonic-gate clearvolpmd = 1; 1473*0Sstevel@tonic-gate if ((power_op_ret = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 1474*0Sstevel@tonic-gate &power_req, &result)) == DDI_SUCCESS) { 1475*0Sstevel@tonic-gate /* 1476*0Sstevel@tonic-gate * Now do involuntary pm accounting; If we've just cycled power 1477*0Sstevel@tonic-gate * on a voluntarily pm'd node, and by inference on its entire 1478*0Sstevel@tonic-gate * subtree, we need to set the subtree (including those nodes 1479*0Sstevel@tonic-gate * already detached) volpmd counts to 0, and subtract out the 1480*0Sstevel@tonic-gate * value of the current node's volpmd count from the ancestors 1481*0Sstevel@tonic-gate */ 1482*0Sstevel@tonic-gate if (clearvolpmd) { 1483*0Sstevel@tonic-gate int volpmd = DEVI(dip)->devi_pm_volpmd; 1484*0Sstevel@tonic-gate pm_clear_volpm_dip(dip); 1485*0Sstevel@tonic-gate pm_clear_volpm_list(dip); 1486*0Sstevel@tonic-gate if (volpmd) { 1487*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 1488*0Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_POWER, 1489*0Sstevel@tonic-gate volpmd, 0, pathbuf, dip); 1490*0Sstevel@tonic-gate } 1491*0Sstevel@tonic-gate } 1492*0Sstevel@tonic-gate } else { 1493*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't set comp %d (%s) of %s@%s(%s#%d) " 1494*0Sstevel@tonic-gate "to level %d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, 1495*0Sstevel@tonic-gate PM_DEVICE(dip), level, power_val_to_string(cp, level))) 1496*0Sstevel@tonic-gate } 1497*0Sstevel@tonic-gate /* 1498*0Sstevel@tonic-gate * If some other devices were also powered up (e.g. other cpus in 1499*0Sstevel@tonic-gate * the same domain) return a pointer to that list 1500*0Sstevel@tonic-gate */ 1501*0Sstevel@tonic-gate if (devlist) { 1502*0Sstevel@tonic-gate *devlist = (pm_ppm_devlist_t *) 1503*0Sstevel@tonic-gate power_req.req.ppm_set_power_req.cookie; 1504*0Sstevel@tonic-gate } 1505*0Sstevel@tonic-gate /* 1506*0Sstevel@tonic-gate * We will have to resume the device if the device is backwards compat 1507*0Sstevel@tonic-gate * device and either of the following is true: 1508*0Sstevel@tonic-gate * -This is comp 0 and we have successfully powered it up 1509*0Sstevel@tonic-gate * -This is comp 0 and we have failed to power it down. Resume is 1510*0Sstevel@tonic-gate * needed because we have suspended it above 1511*0Sstevel@tonic-gate */ 1512*0Sstevel@tonic-gate 1513*0Sstevel@tonic-gate if (bc && comp == 0) { 1514*0Sstevel@tonic-gate ASSERT(PM_ISDIRECT(dip) || DEVI_IS_DETACHING(dip)); 1515*0Sstevel@tonic-gate if (power_op_ret == DDI_SUCCESS) { 1516*0Sstevel@tonic-gate if (POWERING_ON(old_level, level)) { 1517*0Sstevel@tonic-gate /* 1518*0Sstevel@tonic-gate * It must be either suspended or resumed 1519*0Sstevel@tonic-gate * via pm_power_has_changed path 1520*0Sstevel@tonic-gate */ 1521*0Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags & 1522*0Sstevel@tonic-gate PMC_SUSPENDED) || 1523*0Sstevel@tonic-gate (PM_CP(dip, comp)->pmc_flags & 1524*0Sstevel@tonic-gate PM_PHC_WHILE_SET_POWER)); 1525*0Sstevel@tonic-gate 1526*0Sstevel@tonic-gate resume_needed = suspended; 1527*0Sstevel@tonic-gate } 1528*0Sstevel@tonic-gate } else { 1529*0Sstevel@tonic-gate if (POWERING_OFF(old_level, level)) { 1530*0Sstevel@tonic-gate /* 1531*0Sstevel@tonic-gate * It must be either suspended or resumed 1532*0Sstevel@tonic-gate * via pm_power_has_changed path 1533*0Sstevel@tonic-gate */ 1534*0Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags & 1535*0Sstevel@tonic-gate PMC_SUSPENDED) || 1536*0Sstevel@tonic-gate (PM_CP(dip, comp)->pmc_flags & 1537*0Sstevel@tonic-gate PM_PHC_WHILE_SET_POWER)); 1538*0Sstevel@tonic-gate 1539*0Sstevel@tonic-gate resume_needed = suspended; 1540*0Sstevel@tonic-gate } 1541*0Sstevel@tonic-gate } 1542*0Sstevel@tonic-gate } 1543*0Sstevel@tonic-gate if (resume_needed) { 1544*0Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED); 1545*0Sstevel@tonic-gate /* ppm is not interested in DDI_PM_RESUME */ 1546*0Sstevel@tonic-gate if ((power_op_ret = devi_attach(dip, DDI_PM_RESUME)) == 1547*0Sstevel@tonic-gate DDI_SUCCESS) { 1548*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED; 1549*0Sstevel@tonic-gate } else 1550*0Sstevel@tonic-gate cmn_err(CE_WARN, "!pm: Can't resume %s@%s(%s#%d)", 1551*0Sstevel@tonic-gate PM_DEVICE(dip)); 1552*0Sstevel@tonic-gate } 1553*0Sstevel@tonic-gate return (power_op_ret); 1554*0Sstevel@tonic-gate } 1555*0Sstevel@tonic-gate 1556*0Sstevel@tonic-gate /* 1557*0Sstevel@tonic-gate * Return true if we are the owner or a borrower of the devi lock. See 1558*0Sstevel@tonic-gate * pm_lock_power_single() about borrowing the lock. 1559*0Sstevel@tonic-gate */ 1560*0Sstevel@tonic-gate static int 1561*0Sstevel@tonic-gate pm_devi_lock_held(dev_info_t *dip) 1562*0Sstevel@tonic-gate { 1563*0Sstevel@tonic-gate lock_loan_t *cur; 1564*0Sstevel@tonic-gate 1565*0Sstevel@tonic-gate if (DEVI_BUSY_OWNED(dip)) 1566*0Sstevel@tonic-gate return (1); 1567*0Sstevel@tonic-gate 1568*0Sstevel@tonic-gate /* return false if no locks borrowed */ 1569*0Sstevel@tonic-gate if (lock_loan_head.pmlk_next == NULL) 1570*0Sstevel@tonic-gate return (0); 1571*0Sstevel@tonic-gate 1572*0Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 1573*0Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */ 1574*0Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next) 1575*0Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 1576*0Sstevel@tonic-gate break; 1577*0Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 1578*0Sstevel@tonic-gate 1579*0Sstevel@tonic-gate return (cur != NULL && cur->pmlk_lender == DEVI(dip)->devi_busy_thread); 1580*0Sstevel@tonic-gate } 1581*0Sstevel@tonic-gate 1582*0Sstevel@tonic-gate /* 1583*0Sstevel@tonic-gate * pm_set_power: adjusts power level of device. Assumes device is power 1584*0Sstevel@tonic-gate * manageable & component exists. 1585*0Sstevel@tonic-gate * 1586*0Sstevel@tonic-gate * Cases which require us to bring up devices we keep up ("wekeepups") for 1587*0Sstevel@tonic-gate * backwards compatible devices: 1588*0Sstevel@tonic-gate * component 0 is off and we're bringing it up from 0 1589*0Sstevel@tonic-gate * bring up wekeepup first 1590*0Sstevel@tonic-gate * and recursively when component 0 is off and we bring some other 1591*0Sstevel@tonic-gate * component up from 0 1592*0Sstevel@tonic-gate * For devices which are not backward compatible, our dependency notion is much 1593*0Sstevel@tonic-gate * simpler. Unless all components are off, then wekeeps must be on. 1594*0Sstevel@tonic-gate * We don't treat component 0 differently. 1595*0Sstevel@tonic-gate * Canblock tells how to deal with a direct pm'd device. 1596*0Sstevel@tonic-gate * Scan arg tells us if we were called from scan, in which case we don't need 1597*0Sstevel@tonic-gate * to go back to the root node and walk down to change power. 1598*0Sstevel@tonic-gate */ 1599*0Sstevel@tonic-gate int 1600*0Sstevel@tonic-gate pm_set_power(dev_info_t *dip, int comp, int level, int direction, 1601*0Sstevel@tonic-gate pm_canblock_t canblock, int scan, int *retp) 1602*0Sstevel@tonic-gate { 1603*0Sstevel@tonic-gate PMD_FUNC(pmf, "set_power") 1604*0Sstevel@tonic-gate char *pathbuf; 1605*0Sstevel@tonic-gate pm_bp_child_pwrchg_t bpc; 1606*0Sstevel@tonic-gate pm_sp_misc_t pspm; 1607*0Sstevel@tonic-gate int ret = DDI_SUCCESS; 1608*0Sstevel@tonic-gate int unused = DDI_SUCCESS; 1609*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 1610*0Sstevel@tonic-gate 1611*0Sstevel@tonic-gate #ifdef DEBUG 1612*0Sstevel@tonic-gate int diverted = 0; 1613*0Sstevel@tonic-gate 1614*0Sstevel@tonic-gate /* 1615*0Sstevel@tonic-gate * This prevents operations on the console from calling prom_printf and 1616*0Sstevel@tonic-gate * either deadlocking or bringing up the console because of debug 1617*0Sstevel@tonic-gate * output 1618*0Sstevel@tonic-gate */ 1619*0Sstevel@tonic-gate if (dip == cfb_dip) { 1620*0Sstevel@tonic-gate diverted++; 1621*0Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 1622*0Sstevel@tonic-gate pm_divertdebug++; 1623*0Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 1624*0Sstevel@tonic-gate } 1625*0Sstevel@tonic-gate #endif 1626*0Sstevel@tonic-gate ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY || 1627*0Sstevel@tonic-gate direction == PM_LEVEL_EXACT); 1628*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d, dir=%s, new=%d\n", 1629*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, pm_decode_direction(direction), level)) 1630*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1631*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 1632*0Sstevel@tonic-gate bpc.bpc_dip = dip; 1633*0Sstevel@tonic-gate bpc.bpc_path = pathbuf; 1634*0Sstevel@tonic-gate bpc.bpc_comp = comp; 1635*0Sstevel@tonic-gate bpc.bpc_olevel = PM_CURPOWER(dip, comp); 1636*0Sstevel@tonic-gate bpc.bpc_nlevel = level; 1637*0Sstevel@tonic-gate pspm.pspm_direction = direction; 1638*0Sstevel@tonic-gate pspm.pspm_errnop = retp; 1639*0Sstevel@tonic-gate pspm.pspm_canblock = canblock; 1640*0Sstevel@tonic-gate pspm.pspm_scan = scan; 1641*0Sstevel@tonic-gate bpc.bpc_private = &pspm; 1642*0Sstevel@tonic-gate 1643*0Sstevel@tonic-gate /* 1644*0Sstevel@tonic-gate * If a config operation is being done (we've locked the parent) or 1645*0Sstevel@tonic-gate * we already hold the power lock (we've locked the node) 1646*0Sstevel@tonic-gate * then we can operate directly on the node because we have already 1647*0Sstevel@tonic-gate * brought up all the ancestors, otherwise, we have to go back to the 1648*0Sstevel@tonic-gate * top of the tree. 1649*0Sstevel@tonic-gate */ 1650*0Sstevel@tonic-gate if (pm_devi_lock_held(pdip) || pm_devi_lock_held(dip)) 1651*0Sstevel@tonic-gate ret = pm_busop_set_power(dip, NULL, BUS_POWER_CHILD_PWRCHG, 1652*0Sstevel@tonic-gate (void *)&bpc, (void *)&unused); 1653*0Sstevel@tonic-gate else 1654*0Sstevel@tonic-gate ret = pm_busop_bus_power(ddi_root_node(), NULL, 1655*0Sstevel@tonic-gate BUS_POWER_CHILD_PWRCHG, (void *)&bpc, (void *)&unused); 1656*0Sstevel@tonic-gate #ifdef DEBUG 1657*0Sstevel@tonic-gate if (ret != DDI_SUCCESS || *retp != DDI_SUCCESS) { 1658*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) can't change power, ret=%d, " 1659*0Sstevel@tonic-gate "errno=%d\n", pmf, PM_DEVICE(dip), ret, *retp)) 1660*0Sstevel@tonic-gate } 1661*0Sstevel@tonic-gate if (diverted) { 1662*0Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 1663*0Sstevel@tonic-gate pm_divertdebug--; 1664*0Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 1665*0Sstevel@tonic-gate } 1666*0Sstevel@tonic-gate #endif 1667*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 1668*0Sstevel@tonic-gate return (ret); 1669*0Sstevel@tonic-gate } 1670*0Sstevel@tonic-gate 1671*0Sstevel@tonic-gate 1672*0Sstevel@tonic-gate static dev_info_t * 1673*0Sstevel@tonic-gate find_dip(dev_info_t *dip, char *dev_name, int holddip) 1674*0Sstevel@tonic-gate { 1675*0Sstevel@tonic-gate PMD_FUNC(pmf, "find_dip") 1676*0Sstevel@tonic-gate dev_info_t *cdip; 1677*0Sstevel@tonic-gate char *child_dev, *addr; 1678*0Sstevel@tonic-gate char *device; /* writeable copy of path */ 1679*0Sstevel@tonic-gate int dev_len = strlen(dev_name)+1; 1680*0Sstevel@tonic-gate int circ; 1681*0Sstevel@tonic-gate 1682*0Sstevel@tonic-gate device = kmem_zalloc(dev_len, KM_SLEEP); 1683*0Sstevel@tonic-gate (void) strcpy(device, dev_name); 1684*0Sstevel@tonic-gate addr = strchr(device, '@'); 1685*0Sstevel@tonic-gate child_dev = strchr(device, '/'); 1686*0Sstevel@tonic-gate if ((addr != NULL) && (child_dev == NULL || addr < child_dev)) { 1687*0Sstevel@tonic-gate /* 1688*0Sstevel@tonic-gate * We have device = "name@addr..." form 1689*0Sstevel@tonic-gate */ 1690*0Sstevel@tonic-gate *addr++ = '\0'; /* for strcmp (and skip '@') */ 1691*0Sstevel@tonic-gate if (child_dev != NULL) 1692*0Sstevel@tonic-gate *child_dev++ = '\0'; /* for strcmp (and skip '/') */ 1693*0Sstevel@tonic-gate } else { 1694*0Sstevel@tonic-gate /* 1695*0Sstevel@tonic-gate * We have device = "name/..." or "name" 1696*0Sstevel@tonic-gate */ 1697*0Sstevel@tonic-gate addr = ""; 1698*0Sstevel@tonic-gate if (child_dev != NULL) 1699*0Sstevel@tonic-gate *child_dev++ = '\0'; /* for strcmp (and skip '/') */ 1700*0Sstevel@tonic-gate } 1701*0Sstevel@tonic-gate for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 1702*0Sstevel@tonic-gate if (strcmp(ddi_node_name(dip), device) == 0) { 1703*0Sstevel@tonic-gate /* If the driver isn't loaded, we prune the search */ 1704*0Sstevel@tonic-gate if (i_ddi_node_state(dip) < DS_READY) { 1705*0Sstevel@tonic-gate continue; 1706*0Sstevel@tonic-gate } 1707*0Sstevel@tonic-gate if (strcmp(ddi_get_name_addr(dip), addr) == 0) { 1708*0Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: matched %s@%s" 1709*0Sstevel@tonic-gate "(%s#%d)\n", pmf, PM_DEVICE(dip))) 1710*0Sstevel@tonic-gate if (child_dev != NULL) { 1711*0Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: %s@%s(%s#%d): " 1712*0Sstevel@tonic-gate "held, call find_dip %s\n", pmf, 1713*0Sstevel@tonic-gate PM_DEVICE(dip), child_dev)) 1714*0Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 1715*0Sstevel@tonic-gate cdip = dip; 1716*0Sstevel@tonic-gate dip = find_dip(ddi_get_child(dip), 1717*0Sstevel@tonic-gate child_dev, holddip); 1718*0Sstevel@tonic-gate ndi_devi_exit(cdip, circ); 1719*0Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: %s@%s(%s#%d): " 1720*0Sstevel@tonic-gate "release, find_dip rets %s\n", pmf, 1721*0Sstevel@tonic-gate PM_DEVICE(cdip), child_dev)) 1722*0Sstevel@tonic-gate } else { 1723*0Sstevel@tonic-gate if (holddip) { 1724*0Sstevel@tonic-gate e_ddi_hold_devi(dip); 1725*0Sstevel@tonic-gate PMD(PMD_DHR | PMD_NAMETODIP, 1726*0Sstevel@tonic-gate ("%s: held %s@%s(%s#%d), " 1727*0Sstevel@tonic-gate "refcnt=%d\n", pmf, 1728*0Sstevel@tonic-gate PM_DEVICE(dip), 1729*0Sstevel@tonic-gate e_ddi_devi_holdcnt(dip))) 1730*0Sstevel@tonic-gate } 1731*0Sstevel@tonic-gate } 1732*0Sstevel@tonic-gate kmem_free(device, dev_len); 1733*0Sstevel@tonic-gate return (dip); 1734*0Sstevel@tonic-gate } 1735*0Sstevel@tonic-gate } 1736*0Sstevel@tonic-gate } 1737*0Sstevel@tonic-gate kmem_free(device, dev_len); 1738*0Sstevel@tonic-gate return (dip); 1739*0Sstevel@tonic-gate } 1740*0Sstevel@tonic-gate 1741*0Sstevel@tonic-gate /* 1742*0Sstevel@tonic-gate * If holddip is set, then if a dip is found we return with the node held 1743*0Sstevel@tonic-gate */ 1744*0Sstevel@tonic-gate dev_info_t * 1745*0Sstevel@tonic-gate pm_name_to_dip(char *pathname, int holddip) 1746*0Sstevel@tonic-gate { 1747*0Sstevel@tonic-gate PMD_FUNC(pmf, "name_to_dip") 1748*0Sstevel@tonic-gate dev_info_t *dip = NULL; 1749*0Sstevel@tonic-gate char dev_name[MAXNAMELEN]; 1750*0Sstevel@tonic-gate dev_info_t *first_child; 1751*0Sstevel@tonic-gate int circular; 1752*0Sstevel@tonic-gate 1753*0Sstevel@tonic-gate if (!pathname) 1754*0Sstevel@tonic-gate return (NULL); 1755*0Sstevel@tonic-gate 1756*0Sstevel@tonic-gate (void) strncpy(dev_name, pathname, MAXNAMELEN); 1757*0Sstevel@tonic-gate 1758*0Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: devname: %s\n", pmf, dev_name)) 1759*0Sstevel@tonic-gate 1760*0Sstevel@tonic-gate /* 1761*0Sstevel@tonic-gate * First we attempt to match the node in the tree. If we succeed 1762*0Sstevel@tonic-gate * we hold the driver and look up the dip again. 1763*0Sstevel@tonic-gate * No need to hold the root as that node is always held. 1764*0Sstevel@tonic-gate */ 1765*0Sstevel@tonic-gate if (dev_name[0] == '/') { 1766*0Sstevel@tonic-gate ndi_devi_enter(ddi_root_node(), &circular); 1767*0Sstevel@tonic-gate first_child = ddi_get_child(ddi_root_node()); 1768*0Sstevel@tonic-gate dip = find_dip(first_child, dev_name + 1, holddip); 1769*0Sstevel@tonic-gate ndi_devi_exit(ddi_root_node(), circular); 1770*0Sstevel@tonic-gate 1771*0Sstevel@tonic-gate } else { 1772*0Sstevel@tonic-gate PMD(PMD_NAMETODIP, ("%s: physpath with unrooted " 1773*0Sstevel@tonic-gate "search\n", pmf)) 1774*0Sstevel@tonic-gate return (NULL); 1775*0Sstevel@tonic-gate } 1776*0Sstevel@tonic-gate 1777*0Sstevel@tonic-gate ASSERT(!dip || 1778*0Sstevel@tonic-gate (ddi_name_to_major(ddi_binding_name(dip)) != (major_t)-1)); 1779*0Sstevel@tonic-gate 1780*0Sstevel@tonic-gate return (dip); 1781*0Sstevel@tonic-gate } 1782*0Sstevel@tonic-gate 1783*0Sstevel@tonic-gate /* 1784*0Sstevel@tonic-gate * Search for a dependency and mark it unsatisfied 1785*0Sstevel@tonic-gate */ 1786*0Sstevel@tonic-gate static void 1787*0Sstevel@tonic-gate pm_unsatisfy(char *keeper, char *kept) 1788*0Sstevel@tonic-gate { 1789*0Sstevel@tonic-gate PMD_FUNC(pmf, "unsatisfy") 1790*0Sstevel@tonic-gate pm_pdr_t *dp; 1791*0Sstevel@tonic-gate 1792*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, keeper, kept)) 1793*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 1794*0Sstevel@tonic-gate if (!dp->pdr_isprop) { 1795*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) == 0 && 1796*0Sstevel@tonic-gate (dp->pdr_kept_count > 0) && 1797*0Sstevel@tonic-gate strcmp(dp->pdr_kept_paths[0], kept) == 0) { 1798*0Sstevel@tonic-gate if (dp->pdr_satisfied) { 1799*0Sstevel@tonic-gate dp->pdr_satisfied = 0; 1800*0Sstevel@tonic-gate pm_unresolved_deps++; 1801*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: clear satisfied, " 1802*0Sstevel@tonic-gate "pm_unresolved_deps now %d\n", pmf, 1803*0Sstevel@tonic-gate pm_unresolved_deps)) 1804*0Sstevel@tonic-gate } 1805*0Sstevel@tonic-gate } 1806*0Sstevel@tonic-gate } 1807*0Sstevel@tonic-gate } 1808*0Sstevel@tonic-gate } 1809*0Sstevel@tonic-gate 1810*0Sstevel@tonic-gate /* 1811*0Sstevel@tonic-gate * Device dip is being un power managed, it keeps up count other devices. 1812*0Sstevel@tonic-gate * We need to release any hold we have on the kept devices, and also 1813*0Sstevel@tonic-gate * mark the dependency no longer satisfied. 1814*0Sstevel@tonic-gate */ 1815*0Sstevel@tonic-gate static void 1816*0Sstevel@tonic-gate pm_unkeeps(int count, char *keeper, char **keptpaths, int pwr) 1817*0Sstevel@tonic-gate { 1818*0Sstevel@tonic-gate PMD_FUNC(pmf, "unkeeps") 1819*0Sstevel@tonic-gate int i, j; 1820*0Sstevel@tonic-gate dev_info_t *kept; 1821*0Sstevel@tonic-gate dev_info_t *dip; 1822*0Sstevel@tonic-gate struct pm_component *cp; 1823*0Sstevel@tonic-gate int keeper_on = 0, circ; 1824*0Sstevel@tonic-gate 1825*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: count=%d, keeper=%s, keptpaths=%p\n", pmf, count, 1826*0Sstevel@tonic-gate keeper, (void *)keptpaths)) 1827*0Sstevel@tonic-gate /* 1828*0Sstevel@tonic-gate * Try to grab keeper. Keeper may have gone away by now, 1829*0Sstevel@tonic-gate * in this case, used the passed in value pwr 1830*0Sstevel@tonic-gate */ 1831*0Sstevel@tonic-gate dip = pm_name_to_dip(keeper, 1); 1832*0Sstevel@tonic-gate for (i = 0; i < count; i++) { 1833*0Sstevel@tonic-gate /* Release power hold */ 1834*0Sstevel@tonic-gate kept = pm_name_to_dip(keptpaths[i], 1); 1835*0Sstevel@tonic-gate if (kept) { 1836*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)[%d]\n", pmf, 1837*0Sstevel@tonic-gate PM_DEVICE(kept), i)) 1838*0Sstevel@tonic-gate /* 1839*0Sstevel@tonic-gate * We need to check if we skipped a bringup here 1840*0Sstevel@tonic-gate * because we could have failed the bringup 1841*0Sstevel@tonic-gate * (ie DIRECT PM device) and have 1842*0Sstevel@tonic-gate * not increment the count. 1843*0Sstevel@tonic-gate */ 1844*0Sstevel@tonic-gate if ((dip != NULL) && (PM_GET_PM_INFO(dip) != NULL)) { 1845*0Sstevel@tonic-gate keeper_on = 0; 1846*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 1847*0Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(dip); j++) { 1848*0Sstevel@tonic-gate cp = &DEVI(dip)->devi_pm_components[j]; 1849*0Sstevel@tonic-gate if (cur_power(cp)) { 1850*0Sstevel@tonic-gate keeper_on++; 1851*0Sstevel@tonic-gate break; 1852*0Sstevel@tonic-gate } 1853*0Sstevel@tonic-gate } 1854*0Sstevel@tonic-gate if (keeper_on && (PM_SKBU(kept) == 0)) { 1855*0Sstevel@tonic-gate pm_rele_power(kept); 1856*0Sstevel@tonic-gate DEVI(kept)->devi_pm_flags 1857*0Sstevel@tonic-gate &= ~PMC_SKIP_BRINGUP; 1858*0Sstevel@tonic-gate } 1859*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 1860*0Sstevel@tonic-gate } else if (pwr) { 1861*0Sstevel@tonic-gate if (PM_SKBU(kept) == 0) { 1862*0Sstevel@tonic-gate pm_rele_power(kept); 1863*0Sstevel@tonic-gate DEVI(kept)->devi_pm_flags 1864*0Sstevel@tonic-gate &= ~PMC_SKIP_BRINGUP; 1865*0Sstevel@tonic-gate } 1866*0Sstevel@tonic-gate } 1867*0Sstevel@tonic-gate ddi_release_devi(kept); 1868*0Sstevel@tonic-gate } 1869*0Sstevel@tonic-gate /* 1870*0Sstevel@tonic-gate * mark this dependency not satisfied 1871*0Sstevel@tonic-gate */ 1872*0Sstevel@tonic-gate pm_unsatisfy(keeper, keptpaths[i]); 1873*0Sstevel@tonic-gate } 1874*0Sstevel@tonic-gate if (dip) 1875*0Sstevel@tonic-gate ddi_release_devi(dip); 1876*0Sstevel@tonic-gate } 1877*0Sstevel@tonic-gate 1878*0Sstevel@tonic-gate /* 1879*0Sstevel@tonic-gate * Device kept is being un power managed, it is kept up by keeper. 1880*0Sstevel@tonic-gate * We need to mark the dependency no longer satisfied. 1881*0Sstevel@tonic-gate */ 1882*0Sstevel@tonic-gate static void 1883*0Sstevel@tonic-gate pm_unkepts(char *kept, char *keeper) 1884*0Sstevel@tonic-gate { 1885*0Sstevel@tonic-gate PMD_FUNC(pmf, "unkepts") 1886*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: kept=%s, keeper=%s\n", pmf, kept, keeper)) 1887*0Sstevel@tonic-gate ASSERT(keeper != NULL); 1888*0Sstevel@tonic-gate /* 1889*0Sstevel@tonic-gate * mark this dependency not satisfied 1890*0Sstevel@tonic-gate */ 1891*0Sstevel@tonic-gate pm_unsatisfy(keeper, kept); 1892*0Sstevel@tonic-gate } 1893*0Sstevel@tonic-gate 1894*0Sstevel@tonic-gate /* 1895*0Sstevel@tonic-gate * Removes dependency information and hold on the kepts, if the path is a 1896*0Sstevel@tonic-gate * path of a keeper. 1897*0Sstevel@tonic-gate */ 1898*0Sstevel@tonic-gate static void 1899*0Sstevel@tonic-gate pm_free_keeper(char *path, int pwr) 1900*0Sstevel@tonic-gate { 1901*0Sstevel@tonic-gate pm_pdr_t *dp; 1902*0Sstevel@tonic-gate int i; 1903*0Sstevel@tonic-gate size_t length; 1904*0Sstevel@tonic-gate 1905*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 1906*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, path) != 0) 1907*0Sstevel@tonic-gate continue; 1908*0Sstevel@tonic-gate /* 1909*0Sstevel@tonic-gate * Remove all our kept holds and the dependency records, 1910*0Sstevel@tonic-gate * then free up the kept lists. 1911*0Sstevel@tonic-gate */ 1912*0Sstevel@tonic-gate pm_unkeeps(dp->pdr_kept_count, path, dp->pdr_kept_paths, pwr); 1913*0Sstevel@tonic-gate if (dp->pdr_kept_count) { 1914*0Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 1915*0Sstevel@tonic-gate length = strlen(dp->pdr_kept_paths[i]); 1916*0Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths[i], length + 1); 1917*0Sstevel@tonic-gate } 1918*0Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, 1919*0Sstevel@tonic-gate dp->pdr_kept_count * sizeof (char **)); 1920*0Sstevel@tonic-gate dp->pdr_kept_paths = NULL; 1921*0Sstevel@tonic-gate dp->pdr_kept_count = 0; 1922*0Sstevel@tonic-gate } 1923*0Sstevel@tonic-gate } 1924*0Sstevel@tonic-gate } 1925*0Sstevel@tonic-gate 1926*0Sstevel@tonic-gate /* 1927*0Sstevel@tonic-gate * Removes the device represented by path from the list of kepts, if the 1928*0Sstevel@tonic-gate * path is a path of a kept 1929*0Sstevel@tonic-gate */ 1930*0Sstevel@tonic-gate static void 1931*0Sstevel@tonic-gate pm_free_kept(char *path) 1932*0Sstevel@tonic-gate { 1933*0Sstevel@tonic-gate pm_pdr_t *dp; 1934*0Sstevel@tonic-gate int i; 1935*0Sstevel@tonic-gate int j, count; 1936*0Sstevel@tonic-gate size_t length; 1937*0Sstevel@tonic-gate char **paths; 1938*0Sstevel@tonic-gate 1939*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 1940*0Sstevel@tonic-gate if (dp->pdr_kept_count == 0) 1941*0Sstevel@tonic-gate continue; 1942*0Sstevel@tonic-gate count = dp->pdr_kept_count; 1943*0Sstevel@tonic-gate /* Remove this device from the kept path lists */ 1944*0Sstevel@tonic-gate for (i = 0; i < count; i++) { 1945*0Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], path) == 0) { 1946*0Sstevel@tonic-gate pm_unkepts(path, dp->pdr_keeper); 1947*0Sstevel@tonic-gate length = strlen(dp->pdr_kept_paths[i]) + 1; 1948*0Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths[i], length); 1949*0Sstevel@tonic-gate dp->pdr_kept_paths[i] = NULL; 1950*0Sstevel@tonic-gate dp->pdr_kept_count--; 1951*0Sstevel@tonic-gate } 1952*0Sstevel@tonic-gate } 1953*0Sstevel@tonic-gate /* Compact the kept paths array */ 1954*0Sstevel@tonic-gate if (dp->pdr_kept_count) { 1955*0Sstevel@tonic-gate length = dp->pdr_kept_count * sizeof (char **); 1956*0Sstevel@tonic-gate paths = kmem_zalloc(length, KM_SLEEP); 1957*0Sstevel@tonic-gate j = 0; 1958*0Sstevel@tonic-gate for (i = 0; i < count; i++) { 1959*0Sstevel@tonic-gate if (dp->pdr_kept_paths[i] != NULL) { 1960*0Sstevel@tonic-gate paths[j] = dp->pdr_kept_paths[i]; 1961*0Sstevel@tonic-gate j++; 1962*0Sstevel@tonic-gate } 1963*0Sstevel@tonic-gate } 1964*0Sstevel@tonic-gate ASSERT(j == dp->pdr_kept_count); 1965*0Sstevel@tonic-gate } 1966*0Sstevel@tonic-gate /* Now free the old array and point to the new one */ 1967*0Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, count * sizeof (char **)); 1968*0Sstevel@tonic-gate if (dp->pdr_kept_count) 1969*0Sstevel@tonic-gate dp->pdr_kept_paths = paths; 1970*0Sstevel@tonic-gate else 1971*0Sstevel@tonic-gate dp->pdr_kept_paths = NULL; 1972*0Sstevel@tonic-gate } 1973*0Sstevel@tonic-gate } 1974*0Sstevel@tonic-gate 1975*0Sstevel@tonic-gate /* 1976*0Sstevel@tonic-gate * Free the dependency information for a device. 1977*0Sstevel@tonic-gate */ 1978*0Sstevel@tonic-gate void 1979*0Sstevel@tonic-gate pm_free_keeps(char *path, int pwr) 1980*0Sstevel@tonic-gate { 1981*0Sstevel@tonic-gate PMD_FUNC(pmf, "free_keeps") 1982*0Sstevel@tonic-gate 1983*0Sstevel@tonic-gate #ifdef DEBUG 1984*0Sstevel@tonic-gate int doprdeps = 0; 1985*0Sstevel@tonic-gate void prdeps(char *); 1986*0Sstevel@tonic-gate 1987*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s\n", pmf, path)) 1988*0Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) { 1989*0Sstevel@tonic-gate doprdeps = 1; 1990*0Sstevel@tonic-gate prdeps("pm_free_keeps before"); 1991*0Sstevel@tonic-gate } 1992*0Sstevel@tonic-gate #endif 1993*0Sstevel@tonic-gate /* 1994*0Sstevel@tonic-gate * First assume we are a keeper and remove all our kepts. 1995*0Sstevel@tonic-gate */ 1996*0Sstevel@tonic-gate pm_free_keeper(path, pwr); 1997*0Sstevel@tonic-gate /* 1998*0Sstevel@tonic-gate * Now assume we a kept device, and remove all our records. 1999*0Sstevel@tonic-gate */ 2000*0Sstevel@tonic-gate pm_free_kept(path); 2001*0Sstevel@tonic-gate #ifdef DEBUG 2002*0Sstevel@tonic-gate if (doprdeps) { 2003*0Sstevel@tonic-gate prdeps("pm_free_keeps after"); 2004*0Sstevel@tonic-gate } 2005*0Sstevel@tonic-gate #endif 2006*0Sstevel@tonic-gate } 2007*0Sstevel@tonic-gate 2008*0Sstevel@tonic-gate static int 2009*0Sstevel@tonic-gate pm_is_kept(char *path) 2010*0Sstevel@tonic-gate { 2011*0Sstevel@tonic-gate pm_pdr_t *dp; 2012*0Sstevel@tonic-gate int i; 2013*0Sstevel@tonic-gate 2014*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 2015*0Sstevel@tonic-gate if (dp->pdr_kept_count == 0) 2016*0Sstevel@tonic-gate continue; 2017*0Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 2018*0Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], path) == 0) 2019*0Sstevel@tonic-gate return (1); 2020*0Sstevel@tonic-gate } 2021*0Sstevel@tonic-gate } 2022*0Sstevel@tonic-gate return (0); 2023*0Sstevel@tonic-gate } 2024*0Sstevel@tonic-gate 2025*0Sstevel@tonic-gate static void 2026*0Sstevel@tonic-gate e_pm_hold_rele_power(dev_info_t *dip, int cnt) 2027*0Sstevel@tonic-gate { 2028*0Sstevel@tonic-gate PMD_FUNC(pmf, "hold_rele_power") 2029*0Sstevel@tonic-gate int circ; 2030*0Sstevel@tonic-gate 2031*0Sstevel@tonic-gate if ((dip == NULL) || 2032*0Sstevel@tonic-gate (PM_GET_PM_INFO(dip) == NULL) || PM_ISBC(dip)) 2033*0Sstevel@tonic-gate return; 2034*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 2035*0Sstevel@tonic-gate ASSERT(cnt >= 0 && PM_KUC(dip) >= 0 || cnt < 0 && PM_KUC(dip) > 0); 2036*0Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: kidsupcnt for %s@%s(%s#%d) %d->%d\n", pmf, 2037*0Sstevel@tonic-gate PM_DEVICE(dip), PM_KUC(dip), (PM_KUC(dip) + cnt))) 2038*0Sstevel@tonic-gate PM_KUC(dip) += cnt; 2039*0Sstevel@tonic-gate ASSERT(PM_KUC(dip) >= 0); 2040*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 2041*0Sstevel@tonic-gate if (cnt < 0 && PM_KUC(dip) == 0) 2042*0Sstevel@tonic-gate pm_rescan(dip); 2043*0Sstevel@tonic-gate } 2044*0Sstevel@tonic-gate 2045*0Sstevel@tonic-gate #define MAX_PPM_HANDLERS 4 2046*0Sstevel@tonic-gate 2047*0Sstevel@tonic-gate kmutex_t ppm_lock; /* in case we ever do multi-threaded startup */ 2048*0Sstevel@tonic-gate 2049*0Sstevel@tonic-gate struct ppm_callbacks { 2050*0Sstevel@tonic-gate int (*ppmc_func)(dev_info_t *); 2051*0Sstevel@tonic-gate dev_info_t *ppmc_dip; 2052*0Sstevel@tonic-gate } ppm_callbacks[MAX_PPM_HANDLERS + 1]; 2053*0Sstevel@tonic-gate 2054*0Sstevel@tonic-gate 2055*0Sstevel@tonic-gate /* 2056*0Sstevel@tonic-gate * This routine calls into all the registered ppms to notify them 2057*0Sstevel@tonic-gate * that either all components of power-managed devices are at their 2058*0Sstevel@tonic-gate * lowest levels or no longer all are at their lowest levels. 2059*0Sstevel@tonic-gate */ 2060*0Sstevel@tonic-gate static void 2061*0Sstevel@tonic-gate pm_ppm_notify_all_lowest(dev_info_t *dip, int mode) 2062*0Sstevel@tonic-gate { 2063*0Sstevel@tonic-gate struct ppm_callbacks *ppmcp; 2064*0Sstevel@tonic-gate power_req_t power_req; 2065*0Sstevel@tonic-gate int result = 0; 2066*0Sstevel@tonic-gate 2067*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_ALL_LOWEST; 2068*0Sstevel@tonic-gate power_req.req.ppm_all_lowest_req.mode = mode; 2069*0Sstevel@tonic-gate mutex_enter(&ppm_lock); 2070*0Sstevel@tonic-gate for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) 2071*0Sstevel@tonic-gate (void) pm_ctlops((dev_info_t *)ppmcp->ppmc_dip, dip, 2072*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 2073*0Sstevel@tonic-gate mutex_exit(&ppm_lock); 2074*0Sstevel@tonic-gate } 2075*0Sstevel@tonic-gate 2076*0Sstevel@tonic-gate static void 2077*0Sstevel@tonic-gate pm_set_pm_info(dev_info_t *dip, void *value) 2078*0Sstevel@tonic-gate { 2079*0Sstevel@tonic-gate DEVI(dip)->devi_pm_info = value; 2080*0Sstevel@tonic-gate } 2081*0Sstevel@tonic-gate 2082*0Sstevel@tonic-gate pm_rsvp_t *pm_blocked_list; 2083*0Sstevel@tonic-gate 2084*0Sstevel@tonic-gate /* 2085*0Sstevel@tonic-gate * Look up an entry in the blocked list by dip and component 2086*0Sstevel@tonic-gate */ 2087*0Sstevel@tonic-gate static pm_rsvp_t * 2088*0Sstevel@tonic-gate pm_rsvp_lookup(dev_info_t *dip, int comp) 2089*0Sstevel@tonic-gate { 2090*0Sstevel@tonic-gate pm_rsvp_t *p; 2091*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 2092*0Sstevel@tonic-gate for (p = pm_blocked_list; p; p = p->pr_next) 2093*0Sstevel@tonic-gate if (p->pr_dip == dip && p->pr_comp == comp) { 2094*0Sstevel@tonic-gate return (p); 2095*0Sstevel@tonic-gate } 2096*0Sstevel@tonic-gate return (NULL); 2097*0Sstevel@tonic-gate } 2098*0Sstevel@tonic-gate 2099*0Sstevel@tonic-gate /* 2100*0Sstevel@tonic-gate * Called when a device which is direct power managed (or the parent or 2101*0Sstevel@tonic-gate * dependent of such a device) changes power, or when a pm clone is closed 2102*0Sstevel@tonic-gate * that was direct power managing a device. This call results in pm_blocked() 2103*0Sstevel@tonic-gate * (below) returning. 2104*0Sstevel@tonic-gate */ 2105*0Sstevel@tonic-gate void 2106*0Sstevel@tonic-gate pm_proceed(dev_info_t *dip, int cmd, int comp, int newlevel) 2107*0Sstevel@tonic-gate { 2108*0Sstevel@tonic-gate PMD_FUNC(pmf, "proceed") 2109*0Sstevel@tonic-gate pm_rsvp_t *found = NULL; 2110*0Sstevel@tonic-gate pm_rsvp_t *p; 2111*0Sstevel@tonic-gate 2112*0Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 2113*0Sstevel@tonic-gate switch (cmd) { 2114*0Sstevel@tonic-gate /* 2115*0Sstevel@tonic-gate * we're giving up control, let any pending op continue 2116*0Sstevel@tonic-gate */ 2117*0Sstevel@tonic-gate case PMP_RELEASE: 2118*0Sstevel@tonic-gate for (p = pm_blocked_list; p; p = p->pr_next) { 2119*0Sstevel@tonic-gate if (dip == p->pr_dip) { 2120*0Sstevel@tonic-gate p->pr_retval = PMP_RELEASE; 2121*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: RELEASE %s@%s(%s#%d)\n", 2122*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 2123*0Sstevel@tonic-gate cv_signal(&p->pr_cv); 2124*0Sstevel@tonic-gate } 2125*0Sstevel@tonic-gate } 2126*0Sstevel@tonic-gate break; 2127*0Sstevel@tonic-gate 2128*0Sstevel@tonic-gate /* 2129*0Sstevel@tonic-gate * process has done PM_SET_CURRENT_POWER; let a matching request 2130*0Sstevel@tonic-gate * succeed and a non-matching request for the same device fail 2131*0Sstevel@tonic-gate */ 2132*0Sstevel@tonic-gate case PMP_SETPOWER: 2133*0Sstevel@tonic-gate found = pm_rsvp_lookup(dip, comp); 2134*0Sstevel@tonic-gate if (!found) /* if driver not waiting */ 2135*0Sstevel@tonic-gate break; 2136*0Sstevel@tonic-gate /* 2137*0Sstevel@tonic-gate * This cannot be pm_lower_power, since that can only happen 2138*0Sstevel@tonic-gate * during detach or probe 2139*0Sstevel@tonic-gate */ 2140*0Sstevel@tonic-gate if (found->pr_newlevel <= newlevel) { 2141*0Sstevel@tonic-gate found->pr_retval = PMP_SUCCEED; 2142*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: SUCCEED %s@%s(%s#%d)\n", pmf, 2143*0Sstevel@tonic-gate PM_DEVICE(dip))) 2144*0Sstevel@tonic-gate } else { 2145*0Sstevel@tonic-gate found->pr_retval = PMP_FAIL; 2146*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: FAIL %s@%s(%s#%d)\n", pmf, 2147*0Sstevel@tonic-gate PM_DEVICE(dip))) 2148*0Sstevel@tonic-gate } 2149*0Sstevel@tonic-gate cv_signal(&found->pr_cv); 2150*0Sstevel@tonic-gate break; 2151*0Sstevel@tonic-gate 2152*0Sstevel@tonic-gate default: 2153*0Sstevel@tonic-gate panic("pm_proceed unknown cmd %d", cmd); 2154*0Sstevel@tonic-gate } 2155*0Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 2156*0Sstevel@tonic-gate } 2157*0Sstevel@tonic-gate 2158*0Sstevel@tonic-gate /* 2159*0Sstevel@tonic-gate * This routine dispatches new work to the dependency thread. Caller must 2160*0Sstevel@tonic-gate * be prepared to block for memory if necessary. 2161*0Sstevel@tonic-gate */ 2162*0Sstevel@tonic-gate void 2163*0Sstevel@tonic-gate pm_dispatch_to_dep_thread(int cmd, char *keeper, char *kept, int wait, 2164*0Sstevel@tonic-gate int *res, int cached_pwr) 2165*0Sstevel@tonic-gate { 2166*0Sstevel@tonic-gate pm_dep_wk_t *new_work; 2167*0Sstevel@tonic-gate 2168*0Sstevel@tonic-gate new_work = kmem_zalloc(sizeof (pm_dep_wk_t), KM_SLEEP); 2169*0Sstevel@tonic-gate new_work->pdw_type = cmd; 2170*0Sstevel@tonic-gate new_work->pdw_wait = wait; 2171*0Sstevel@tonic-gate new_work->pdw_done = 0; 2172*0Sstevel@tonic-gate new_work->pdw_ret = 0; 2173*0Sstevel@tonic-gate new_work->pdw_pwr = cached_pwr; 2174*0Sstevel@tonic-gate cv_init(&new_work->pdw_cv, NULL, CV_DEFAULT, NULL); 2175*0Sstevel@tonic-gate if (keeper != NULL) { 2176*0Sstevel@tonic-gate new_work->pdw_keeper = kmem_zalloc(strlen(keeper) + 1, 2177*0Sstevel@tonic-gate KM_SLEEP); 2178*0Sstevel@tonic-gate (void) strcpy(new_work->pdw_keeper, keeper); 2179*0Sstevel@tonic-gate } 2180*0Sstevel@tonic-gate if (kept != NULL) { 2181*0Sstevel@tonic-gate new_work->pdw_kept = kmem_zalloc(strlen(kept) + 1, KM_SLEEP); 2182*0Sstevel@tonic-gate (void) strcpy(new_work->pdw_kept, kept); 2183*0Sstevel@tonic-gate } 2184*0Sstevel@tonic-gate mutex_enter(&pm_dep_thread_lock); 2185*0Sstevel@tonic-gate if (pm_dep_thread_workq == NULL) { 2186*0Sstevel@tonic-gate pm_dep_thread_workq = new_work; 2187*0Sstevel@tonic-gate pm_dep_thread_tail = new_work; 2188*0Sstevel@tonic-gate new_work->pdw_next = NULL; 2189*0Sstevel@tonic-gate } else { 2190*0Sstevel@tonic-gate pm_dep_thread_tail->pdw_next = new_work; 2191*0Sstevel@tonic-gate pm_dep_thread_tail = new_work; 2192*0Sstevel@tonic-gate new_work->pdw_next = NULL; 2193*0Sstevel@tonic-gate } 2194*0Sstevel@tonic-gate cv_signal(&pm_dep_thread_cv); 2195*0Sstevel@tonic-gate /* If caller asked for it, wait till it is done. */ 2196*0Sstevel@tonic-gate if (wait) { 2197*0Sstevel@tonic-gate while (!new_work->pdw_done) 2198*0Sstevel@tonic-gate cv_wait(&new_work->pdw_cv, &pm_dep_thread_lock); 2199*0Sstevel@tonic-gate /* 2200*0Sstevel@tonic-gate * Pass return status, if any, back. 2201*0Sstevel@tonic-gate */ 2202*0Sstevel@tonic-gate if (res != NULL) 2203*0Sstevel@tonic-gate *res = new_work->pdw_ret; 2204*0Sstevel@tonic-gate /* 2205*0Sstevel@tonic-gate * If we asked to wait, it is our job to free the request 2206*0Sstevel@tonic-gate * structure. 2207*0Sstevel@tonic-gate */ 2208*0Sstevel@tonic-gate if (new_work->pdw_keeper) 2209*0Sstevel@tonic-gate kmem_free(new_work->pdw_keeper, 2210*0Sstevel@tonic-gate strlen(new_work->pdw_keeper) + 1); 2211*0Sstevel@tonic-gate if (new_work->pdw_kept) 2212*0Sstevel@tonic-gate kmem_free(new_work->pdw_kept, 2213*0Sstevel@tonic-gate strlen(new_work->pdw_kept) + 1); 2214*0Sstevel@tonic-gate kmem_free(new_work, sizeof (pm_dep_wk_t)); 2215*0Sstevel@tonic-gate } 2216*0Sstevel@tonic-gate mutex_exit(&pm_dep_thread_lock); 2217*0Sstevel@tonic-gate } 2218*0Sstevel@tonic-gate 2219*0Sstevel@tonic-gate /* 2220*0Sstevel@tonic-gate * Release the pm resource for this device. 2221*0Sstevel@tonic-gate */ 2222*0Sstevel@tonic-gate void 2223*0Sstevel@tonic-gate pm_rem_info(dev_info_t *dip) 2224*0Sstevel@tonic-gate { 2225*0Sstevel@tonic-gate PMD_FUNC(pmf, "rem_info") 2226*0Sstevel@tonic-gate int i, count = 0; 2227*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 2228*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 2229*0Sstevel@tonic-gate char *pathbuf; 2230*0Sstevel@tonic-gate int work_type = PM_DEP_WK_DETACH; 2231*0Sstevel@tonic-gate 2232*0Sstevel@tonic-gate ASSERT(info); 2233*0Sstevel@tonic-gate 2234*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 2235*0Sstevel@tonic-gate if (PM_ISDIRECT(dip)) { 2236*0Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_DIRECT; 2237*0Sstevel@tonic-gate ASSERT(info->pmi_clone); 2238*0Sstevel@tonic-gate info->pmi_clone = 0; 2239*0Sstevel@tonic-gate pm_proceed(dip, PMP_RELEASE, -1, -1); 2240*0Sstevel@tonic-gate } 2241*0Sstevel@tonic-gate ASSERT(!PM_GET_PM_SCAN(dip)); 2242*0Sstevel@tonic-gate 2243*0Sstevel@tonic-gate /* 2244*0Sstevel@tonic-gate * Now adjust parent's kidsupcnt. BC nodes we check only comp 0, 2245*0Sstevel@tonic-gate * Others we check all components. BC node that has already 2246*0Sstevel@tonic-gate * called pm_destroy_components() has zero component count. 2247*0Sstevel@tonic-gate * Parents that get notification are not adjusted because their 2248*0Sstevel@tonic-gate * kidsupcnt is always 0 (or 1 during configuration). 2249*0Sstevel@tonic-gate */ 2250*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d) has %d components\n", pmf, 2251*0Sstevel@tonic-gate PM_DEVICE(dip), PM_NUMCMPTS(dip))) 2252*0Sstevel@tonic-gate 2253*0Sstevel@tonic-gate /* node is detached, so we can examine power without locking */ 2254*0Sstevel@tonic-gate if (PM_ISBC(dip)) { 2255*0Sstevel@tonic-gate count = (PM_CURPOWER(dip, 0) != 0); 2256*0Sstevel@tonic-gate } else { 2257*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 2258*0Sstevel@tonic-gate count += (PM_CURPOWER(dip, i) != 0); 2259*0Sstevel@tonic-gate } 2260*0Sstevel@tonic-gate 2261*0Sstevel@tonic-gate if (PM_NUMCMPTS(dip) && pdip && !PM_WANTS_NOTIFICATION(pdip)) 2262*0Sstevel@tonic-gate e_pm_hold_rele_power(pdip, -count); 2263*0Sstevel@tonic-gate 2264*0Sstevel@tonic-gate /* Schedule a request to clean up dependency records */ 2265*0Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 2266*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 2267*0Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, pathbuf, 2268*0Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, (count > 0)); 2269*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 2270*0Sstevel@tonic-gate 2271*0Sstevel@tonic-gate /* 2272*0Sstevel@tonic-gate * Adjust the pm_comps_notlowest count since this device is 2273*0Sstevel@tonic-gate * not being power-managed anymore. 2274*0Sstevel@tonic-gate */ 2275*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 2276*0Sstevel@tonic-gate if (PM_CURPOWER(dip, i) != 0) 2277*0Sstevel@tonic-gate PM_DECR_NOTLOWEST(dip); 2278*0Sstevel@tonic-gate } 2279*0Sstevel@tonic-gate /* 2280*0Sstevel@tonic-gate * Once we clear the info pointer, it looks like it is not power 2281*0Sstevel@tonic-gate * managed to everybody else. 2282*0Sstevel@tonic-gate */ 2283*0Sstevel@tonic-gate pm_set_pm_info(dip, NULL); 2284*0Sstevel@tonic-gate kmem_free(info, sizeof (pm_info_t)); 2285*0Sstevel@tonic-gate } 2286*0Sstevel@tonic-gate 2287*0Sstevel@tonic-gate int 2288*0Sstevel@tonic-gate pm_get_norm_pwrs(dev_info_t *dip, int **valuep, size_t *length) 2289*0Sstevel@tonic-gate { 2290*0Sstevel@tonic-gate int components = PM_NUMCMPTS(dip); 2291*0Sstevel@tonic-gate int *bufp; 2292*0Sstevel@tonic-gate size_t size; 2293*0Sstevel@tonic-gate int i; 2294*0Sstevel@tonic-gate 2295*0Sstevel@tonic-gate if (components <= 0) { 2296*0Sstevel@tonic-gate cmn_err(CE_NOTE, "!pm: %s@%s(%s#%d) has no components, " 2297*0Sstevel@tonic-gate "can't get normal power values\n", PM_DEVICE(dip)); 2298*0Sstevel@tonic-gate return (DDI_FAILURE); 2299*0Sstevel@tonic-gate } else { 2300*0Sstevel@tonic-gate size = components * sizeof (int); 2301*0Sstevel@tonic-gate bufp = kmem_alloc(size, KM_SLEEP); 2302*0Sstevel@tonic-gate for (i = 0; i < components; i++) { 2303*0Sstevel@tonic-gate bufp[i] = pm_get_normal_power(dip, i); 2304*0Sstevel@tonic-gate } 2305*0Sstevel@tonic-gate } 2306*0Sstevel@tonic-gate *length = size; 2307*0Sstevel@tonic-gate *valuep = bufp; 2308*0Sstevel@tonic-gate return (DDI_SUCCESS); 2309*0Sstevel@tonic-gate } 2310*0Sstevel@tonic-gate 2311*0Sstevel@tonic-gate static int 2312*0Sstevel@tonic-gate pm_reset_timestamps(dev_info_t *dip, void *arg) 2313*0Sstevel@tonic-gate { 2314*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 2315*0Sstevel@tonic-gate 2316*0Sstevel@tonic-gate int components; 2317*0Sstevel@tonic-gate int i; 2318*0Sstevel@tonic-gate 2319*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) 2320*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 2321*0Sstevel@tonic-gate components = PM_NUMCMPTS(dip); 2322*0Sstevel@tonic-gate ASSERT(components > 0); 2323*0Sstevel@tonic-gate PM_LOCK_BUSY(dip); 2324*0Sstevel@tonic-gate for (i = 0; i < components; i++) { 2325*0Sstevel@tonic-gate struct pm_component *cp; 2326*0Sstevel@tonic-gate /* 2327*0Sstevel@tonic-gate * If the component was not marked as busy, 2328*0Sstevel@tonic-gate * reset its timestamp to now. 2329*0Sstevel@tonic-gate */ 2330*0Sstevel@tonic-gate cp = PM_CP(dip, i); 2331*0Sstevel@tonic-gate if (cp->pmc_timestamp) 2332*0Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 2333*0Sstevel@tonic-gate } 2334*0Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 2335*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 2336*0Sstevel@tonic-gate } 2337*0Sstevel@tonic-gate 2338*0Sstevel@tonic-gate /* 2339*0Sstevel@tonic-gate * Convert a power level to an index into the levels array (or 2340*0Sstevel@tonic-gate * just PM_LEVEL_UNKNOWN in that special case). 2341*0Sstevel@tonic-gate */ 2342*0Sstevel@tonic-gate static int 2343*0Sstevel@tonic-gate pm_level_to_index(dev_info_t *dip, pm_component_t *cp, int level) 2344*0Sstevel@tonic-gate { 2345*0Sstevel@tonic-gate PMD_FUNC(pmf, "level_to_index") 2346*0Sstevel@tonic-gate int i; 2347*0Sstevel@tonic-gate int limit = cp->pmc_comp.pmc_numlevels; 2348*0Sstevel@tonic-gate int *ip = cp->pmc_comp.pmc_lvals; 2349*0Sstevel@tonic-gate 2350*0Sstevel@tonic-gate if (level == PM_LEVEL_UNKNOWN) 2351*0Sstevel@tonic-gate return (level); 2352*0Sstevel@tonic-gate 2353*0Sstevel@tonic-gate for (i = 0; i < limit; i++) { 2354*0Sstevel@tonic-gate if (level == *ip++) { 2355*0Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d)[%d] to %x\n", 2356*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), 2357*0Sstevel@tonic-gate (int)(cp - DEVI(dip)->devi_pm_components), level)) 2358*0Sstevel@tonic-gate return (i); 2359*0Sstevel@tonic-gate } 2360*0Sstevel@tonic-gate } 2361*0Sstevel@tonic-gate panic("pm_level_to_index: level %d not found for device " 2362*0Sstevel@tonic-gate "%s@%s(%s#%d)", level, PM_DEVICE(dip)); 2363*0Sstevel@tonic-gate /*NOTREACHED*/ 2364*0Sstevel@tonic-gate } 2365*0Sstevel@tonic-gate 2366*0Sstevel@tonic-gate /* 2367*0Sstevel@tonic-gate * Internal function to set current power level 2368*0Sstevel@tonic-gate */ 2369*0Sstevel@tonic-gate static void 2370*0Sstevel@tonic-gate e_pm_set_cur_pwr(dev_info_t *dip, pm_component_t *cp, int level) 2371*0Sstevel@tonic-gate { 2372*0Sstevel@tonic-gate PMD_FUNC(pmf, "set_cur_pwr") 2373*0Sstevel@tonic-gate int curpwr = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ? 2374*0Sstevel@tonic-gate cp->pmc_phc_pwr : cp->pmc_cur_pwr); 2375*0Sstevel@tonic-gate 2376*0Sstevel@tonic-gate /* 2377*0Sstevel@tonic-gate * Nothing to adjust if current & new levels are the same. 2378*0Sstevel@tonic-gate */ 2379*0Sstevel@tonic-gate if (curpwr != PM_LEVEL_UNKNOWN && 2380*0Sstevel@tonic-gate level == cp->pmc_comp.pmc_lvals[curpwr]) 2381*0Sstevel@tonic-gate return; 2382*0Sstevel@tonic-gate 2383*0Sstevel@tonic-gate /* 2384*0Sstevel@tonic-gate * Keep the count for comps doing transition to/from lowest 2385*0Sstevel@tonic-gate * level. 2386*0Sstevel@tonic-gate */ 2387*0Sstevel@tonic-gate if (curpwr == 0) { 2388*0Sstevel@tonic-gate PM_INCR_NOTLOWEST(dip); 2389*0Sstevel@tonic-gate } else if (level == cp->pmc_comp.pmc_lvals[0]) { 2390*0Sstevel@tonic-gate PM_DECR_NOTLOWEST(dip); 2391*0Sstevel@tonic-gate } 2392*0Sstevel@tonic-gate cp->pmc_phc_pwr = PM_LEVEL_UNKNOWN; 2393*0Sstevel@tonic-gate cp->pmc_cur_pwr = pm_level_to_index(dip, cp, level); 2394*0Sstevel@tonic-gate } 2395*0Sstevel@tonic-gate 2396*0Sstevel@tonic-gate /* 2397*0Sstevel@tonic-gate * This is the default method of setting the power of a device if no ppm 2398*0Sstevel@tonic-gate * driver has claimed it. 2399*0Sstevel@tonic-gate */ 2400*0Sstevel@tonic-gate int 2401*0Sstevel@tonic-gate pm_power(dev_info_t *dip, int comp, int level) 2402*0Sstevel@tonic-gate { 2403*0Sstevel@tonic-gate PMD_FUNC(pmf, "power") 2404*0Sstevel@tonic-gate struct dev_ops *ops; 2405*0Sstevel@tonic-gate int (*fn)(dev_info_t *, int, int); 2406*0Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, comp); 2407*0Sstevel@tonic-gate int retval; 2408*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 2409*0Sstevel@tonic-gate static int pm_phc_impl(dev_info_t *, int, int, int); 2410*0Sstevel@tonic-gate 2411*0Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf, 2412*0Sstevel@tonic-gate PM_DEVICE(dip), comp, level)) 2413*0Sstevel@tonic-gate if (!(ops = ddi_get_driver(dip))) { 2414*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) has no ops\n", pmf, 2415*0Sstevel@tonic-gate PM_DEVICE(dip))) 2416*0Sstevel@tonic-gate return (DDI_FAILURE); 2417*0Sstevel@tonic-gate } 2418*0Sstevel@tonic-gate if ((ops->devo_rev < 2) || !(fn = ops->devo_power)) { 2419*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s%s\n", pmf, 2420*0Sstevel@tonic-gate (ops->devo_rev < 2 ? " wrong devo_rev" : ""), 2421*0Sstevel@tonic-gate (!fn ? " devo_power NULL" : ""))) 2422*0Sstevel@tonic-gate return (DDI_FAILURE); 2423*0Sstevel@tonic-gate } 2424*0Sstevel@tonic-gate cp->pmc_flags |= PM_POWER_OP; 2425*0Sstevel@tonic-gate retval = (*fn)(dip, comp, level); 2426*0Sstevel@tonic-gate cp->pmc_flags &= ~PM_POWER_OP; 2427*0Sstevel@tonic-gate if (retval == DDI_SUCCESS) { 2428*0Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level); 2429*0Sstevel@tonic-gate return (DDI_SUCCESS); 2430*0Sstevel@tonic-gate } 2431*0Sstevel@tonic-gate 2432*0Sstevel@tonic-gate /* 2433*0Sstevel@tonic-gate * If pm_power_has_changed() detected a deadlock with pm_power() it 2434*0Sstevel@tonic-gate * updated only the power level of the component. If our attempt to 2435*0Sstevel@tonic-gate * set the device new to a power level above has failed we sync the 2436*0Sstevel@tonic-gate * total power state via phc code now. 2437*0Sstevel@tonic-gate */ 2438*0Sstevel@tonic-gate if (cp->pmc_flags & PM_PHC_WHILE_SET_POWER) { 2439*0Sstevel@tonic-gate int phc_lvl = 2440*0Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]; 2441*0Sstevel@tonic-gate 2442*0Sstevel@tonic-gate ASSERT(info); 2443*0Sstevel@tonic-gate (void) pm_phc_impl(dip, comp, phc_lvl, 0); 2444*0Sstevel@tonic-gate PMD(PMD_PHC, ("%s: phc %s@%s(%s#%d) comp=%d level=%d\n", 2445*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, phc_lvl)) 2446*0Sstevel@tonic-gate } 2447*0Sstevel@tonic-gate 2448*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't set comp=%d (%s) of %s@%s(%s#%d) to " 2449*0Sstevel@tonic-gate "level=%d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, PM_DEVICE(dip), 2450*0Sstevel@tonic-gate level, power_val_to_string(cp, level))); 2451*0Sstevel@tonic-gate return (DDI_FAILURE); 2452*0Sstevel@tonic-gate } 2453*0Sstevel@tonic-gate 2454*0Sstevel@tonic-gate int 2455*0Sstevel@tonic-gate pm_unmanage(dev_info_t *dip) 2456*0Sstevel@tonic-gate { 2457*0Sstevel@tonic-gate PMD_FUNC(pmf, "unmanage") 2458*0Sstevel@tonic-gate power_req_t power_req; 2459*0Sstevel@tonic-gate int result, retval = 0; 2460*0Sstevel@tonic-gate 2461*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 2462*0Sstevel@tonic-gate PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, 2463*0Sstevel@tonic-gate PM_DEVICE(dip))) 2464*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNMANAGE; 2465*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 2466*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) 2467*0Sstevel@tonic-gate retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 2468*0Sstevel@tonic-gate &power_req, &result); 2469*0Sstevel@tonic-gate #ifdef DEBUG 2470*0Sstevel@tonic-gate else 2471*0Sstevel@tonic-gate retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 2472*0Sstevel@tonic-gate &power_req, &result); 2473*0Sstevel@tonic-gate #endif 2474*0Sstevel@tonic-gate ASSERT(retval == DDI_SUCCESS); 2475*0Sstevel@tonic-gate pm_rem_info(dip); 2476*0Sstevel@tonic-gate return (retval); 2477*0Sstevel@tonic-gate } 2478*0Sstevel@tonic-gate 2479*0Sstevel@tonic-gate int 2480*0Sstevel@tonic-gate pm_raise_power(dev_info_t *dip, int comp, int level) 2481*0Sstevel@tonic-gate { 2482*0Sstevel@tonic-gate if (level < 0) 2483*0Sstevel@tonic-gate return (DDI_FAILURE); 2484*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) || 2485*0Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) 2486*0Sstevel@tonic-gate return (DDI_FAILURE); 2487*0Sstevel@tonic-gate 2488*0Sstevel@tonic-gate return (dev_is_needed(dip, comp, level, PM_LEVEL_UPONLY)); 2489*0Sstevel@tonic-gate } 2490*0Sstevel@tonic-gate 2491*0Sstevel@tonic-gate int 2492*0Sstevel@tonic-gate pm_lower_power(dev_info_t *dip, int comp, int level) 2493*0Sstevel@tonic-gate { 2494*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_lower_power") 2495*0Sstevel@tonic-gate 2496*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) || 2497*0Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) { 2498*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) " 2499*0Sstevel@tonic-gate "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 2500*0Sstevel@tonic-gate return (DDI_FAILURE); 2501*0Sstevel@tonic-gate } 2502*0Sstevel@tonic-gate 2503*0Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) { 2504*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) not detaching\n", 2505*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 2506*0Sstevel@tonic-gate return (DDI_FAILURE); 2507*0Sstevel@tonic-gate } 2508*0Sstevel@tonic-gate 2509*0Sstevel@tonic-gate /* 2510*0Sstevel@tonic-gate * If we don't care about saving power, or we're treating this node 2511*0Sstevel@tonic-gate * specially, then this is a no-op 2512*0Sstevel@tonic-gate */ 2513*0Sstevel@tonic-gate if (!autopm_enabled || pm_noinvol(dip)) { 2514*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) %s%s\n", pmf, PM_DEVICE(dip), 2515*0Sstevel@tonic-gate !autopm_enabled ? "!autopm_enabled " : "", 2516*0Sstevel@tonic-gate pm_noinvol(dip) ? "pm_noinvol()" : "")) 2517*0Sstevel@tonic-gate return (DDI_SUCCESS); 2518*0Sstevel@tonic-gate } 2519*0Sstevel@tonic-gate 2520*0Sstevel@tonic-gate if (dev_is_needed(dip, comp, level, PM_LEVEL_DOWNONLY) != DDI_SUCCESS) { 2521*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) dev_is_needed failed\n", pmf, 2522*0Sstevel@tonic-gate PM_DEVICE(dip))) 2523*0Sstevel@tonic-gate return (DDI_FAILURE); 2524*0Sstevel@tonic-gate } 2525*0Sstevel@tonic-gate return (DDI_SUCCESS); 2526*0Sstevel@tonic-gate } 2527*0Sstevel@tonic-gate 2528*0Sstevel@tonic-gate /* 2529*0Sstevel@tonic-gate * Find the entries struct for a given dip in the blocked list, return it locked 2530*0Sstevel@tonic-gate */ 2531*0Sstevel@tonic-gate static psce_t * 2532*0Sstevel@tonic-gate pm_psc_dip_to_direct(dev_info_t *dip, pscc_t **psccp) 2533*0Sstevel@tonic-gate { 2534*0Sstevel@tonic-gate pscc_t *p; 2535*0Sstevel@tonic-gate psce_t *psce; 2536*0Sstevel@tonic-gate 2537*0Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_READER); 2538*0Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = p->pscc_next) { 2539*0Sstevel@tonic-gate if (p->pscc_dip == dip) { 2540*0Sstevel@tonic-gate *psccp = p; 2541*0Sstevel@tonic-gate psce = p->pscc_entries; 2542*0Sstevel@tonic-gate mutex_enter(&psce->psce_lock); 2543*0Sstevel@tonic-gate ASSERT(psce); 2544*0Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 2545*0Sstevel@tonic-gate return (psce); 2546*0Sstevel@tonic-gate } 2547*0Sstevel@tonic-gate } 2548*0Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 2549*0Sstevel@tonic-gate panic("sunpm: no entry for dip %p in direct list", (void *)dip); 2550*0Sstevel@tonic-gate /*NOTREACHED*/ 2551*0Sstevel@tonic-gate } 2552*0Sstevel@tonic-gate 2553*0Sstevel@tonic-gate /* 2554*0Sstevel@tonic-gate * Write an entry indicating a power level change (to be passed to a process 2555*0Sstevel@tonic-gate * later) in the given psce. 2556*0Sstevel@tonic-gate * If we were called in the path that brings up the console fb in the 2557*0Sstevel@tonic-gate * case of entering the prom, we don't want to sleep. If the alloc fails, then 2558*0Sstevel@tonic-gate * we create a record that has a size of -1, a physaddr of NULL, and that 2559*0Sstevel@tonic-gate * has the overflow flag set. 2560*0Sstevel@tonic-gate */ 2561*0Sstevel@tonic-gate static int 2562*0Sstevel@tonic-gate psc_entry(ushort_t event, psce_t *psce, dev_info_t *dip, int comp, int new, 2563*0Sstevel@tonic-gate int old, int which, pm_canblock_t canblock) 2564*0Sstevel@tonic-gate { 2565*0Sstevel@tonic-gate char buf[MAXNAMELEN]; 2566*0Sstevel@tonic-gate pm_state_change_t *p; 2567*0Sstevel@tonic-gate size_t size; 2568*0Sstevel@tonic-gate caddr_t physpath = NULL; 2569*0Sstevel@tonic-gate int overrun = 0; 2570*0Sstevel@tonic-gate 2571*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&psce->psce_lock)); 2572*0Sstevel@tonic-gate (void) ddi_pathname(dip, buf); 2573*0Sstevel@tonic-gate size = strlen(buf) + 1; 2574*0Sstevel@tonic-gate p = psce->psce_in; 2575*0Sstevel@tonic-gate if (canblock == PM_CANBLOCK_BYPASS) { 2576*0Sstevel@tonic-gate physpath = kmem_alloc(size, KM_NOSLEEP); 2577*0Sstevel@tonic-gate if (physpath == NULL) { 2578*0Sstevel@tonic-gate /* 2579*0Sstevel@tonic-gate * mark current entry as overrun 2580*0Sstevel@tonic-gate */ 2581*0Sstevel@tonic-gate p->flags |= PSC_EVENT_LOST; 2582*0Sstevel@tonic-gate size = (size_t)-1; 2583*0Sstevel@tonic-gate } 2584*0Sstevel@tonic-gate } else 2585*0Sstevel@tonic-gate physpath = kmem_alloc(size, KM_SLEEP); 2586*0Sstevel@tonic-gate if (p->size) { /* overflow; mark the next entry */ 2587*0Sstevel@tonic-gate if (p->size != (size_t)-1) 2588*0Sstevel@tonic-gate kmem_free(p->physpath, p->size); 2589*0Sstevel@tonic-gate ASSERT(psce->psce_out == p); 2590*0Sstevel@tonic-gate if (p == psce->psce_last) { 2591*0Sstevel@tonic-gate psce->psce_first->flags |= PSC_EVENT_LOST; 2592*0Sstevel@tonic-gate psce->psce_out = psce->psce_first; 2593*0Sstevel@tonic-gate } else { 2594*0Sstevel@tonic-gate (p + 1)->flags |= PSC_EVENT_LOST; 2595*0Sstevel@tonic-gate psce->psce_out = (p + 1); 2596*0Sstevel@tonic-gate } 2597*0Sstevel@tonic-gate overrun++; 2598*0Sstevel@tonic-gate } else if (physpath == NULL) { /* alloc failed, mark this entry */ 2599*0Sstevel@tonic-gate p->flags |= PSC_EVENT_LOST; 2600*0Sstevel@tonic-gate p->size = 0; 2601*0Sstevel@tonic-gate p->physpath = NULL; 2602*0Sstevel@tonic-gate } 2603*0Sstevel@tonic-gate if (which == PSC_INTEREST) { 2604*0Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); 2605*0Sstevel@tonic-gate if (pm_comps_notlowest == 0) 2606*0Sstevel@tonic-gate p->flags |= PSC_ALL_LOWEST; 2607*0Sstevel@tonic-gate else 2608*0Sstevel@tonic-gate p->flags &= ~PSC_ALL_LOWEST; 2609*0Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); 2610*0Sstevel@tonic-gate } 2611*0Sstevel@tonic-gate p->event = event; 2612*0Sstevel@tonic-gate p->timestamp = gethrestime_sec(); 2613*0Sstevel@tonic-gate p->component = comp; 2614*0Sstevel@tonic-gate p->old_level = old; 2615*0Sstevel@tonic-gate p->new_level = new; 2616*0Sstevel@tonic-gate p->physpath = physpath; 2617*0Sstevel@tonic-gate p->size = size; 2618*0Sstevel@tonic-gate if (physpath != NULL) 2619*0Sstevel@tonic-gate (void) strcpy(p->physpath, buf); 2620*0Sstevel@tonic-gate if (p == psce->psce_last) 2621*0Sstevel@tonic-gate psce->psce_in = psce->psce_first; 2622*0Sstevel@tonic-gate else 2623*0Sstevel@tonic-gate psce->psce_in = ++p; 2624*0Sstevel@tonic-gate mutex_exit(&psce->psce_lock); 2625*0Sstevel@tonic-gate return (overrun); 2626*0Sstevel@tonic-gate } 2627*0Sstevel@tonic-gate 2628*0Sstevel@tonic-gate /* 2629*0Sstevel@tonic-gate * Find the next entry on the interest list. We keep a pointer to the item we 2630*0Sstevel@tonic-gate * last returned in the user's cooke. Returns a locked entries struct. 2631*0Sstevel@tonic-gate */ 2632*0Sstevel@tonic-gate static psce_t * 2633*0Sstevel@tonic-gate psc_interest(void **cookie, pscc_t **psccp) 2634*0Sstevel@tonic-gate { 2635*0Sstevel@tonic-gate pscc_t *pscc; 2636*0Sstevel@tonic-gate pscc_t **cookiep = (pscc_t **)cookie; 2637*0Sstevel@tonic-gate 2638*0Sstevel@tonic-gate if (*cookiep == NULL) 2639*0Sstevel@tonic-gate pscc = pm_pscc_interest; 2640*0Sstevel@tonic-gate else 2641*0Sstevel@tonic-gate pscc = (*cookiep)->pscc_next; 2642*0Sstevel@tonic-gate if (pscc) { 2643*0Sstevel@tonic-gate *cookiep = pscc; 2644*0Sstevel@tonic-gate *psccp = pscc; 2645*0Sstevel@tonic-gate mutex_enter(&pscc->pscc_entries->psce_lock); 2646*0Sstevel@tonic-gate return (pscc->pscc_entries); 2647*0Sstevel@tonic-gate } else { 2648*0Sstevel@tonic-gate return (NULL); 2649*0Sstevel@tonic-gate } 2650*0Sstevel@tonic-gate } 2651*0Sstevel@tonic-gate 2652*0Sstevel@tonic-gate /* 2653*0Sstevel@tonic-gate * Create an entry for a process to pick up indicating a power level change. 2654*0Sstevel@tonic-gate */ 2655*0Sstevel@tonic-gate static void 2656*0Sstevel@tonic-gate pm_enqueue_notify(ushort_t cmd, dev_info_t *dip, int comp, 2657*0Sstevel@tonic-gate int newlevel, int oldlevel, pm_canblock_t canblock) 2658*0Sstevel@tonic-gate { 2659*0Sstevel@tonic-gate PMD_FUNC(pmf, "enqueue_notify") 2660*0Sstevel@tonic-gate pscc_t *pscc; 2661*0Sstevel@tonic-gate psce_t *psce; 2662*0Sstevel@tonic-gate void *cookie = NULL; 2663*0Sstevel@tonic-gate int overrun; 2664*0Sstevel@tonic-gate 2665*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 2666*0Sstevel@tonic-gate switch (cmd) { 2667*0Sstevel@tonic-gate case PSC_PENDING_CHANGE: /* only for controlling process */ 2668*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: PENDING %s@%s(%s#%d), comp %d, %d -> %d\n", 2669*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, oldlevel, newlevel)) 2670*0Sstevel@tonic-gate psce = pm_psc_dip_to_direct(dip, &pscc); 2671*0Sstevel@tonic-gate ASSERT(psce); 2672*0Sstevel@tonic-gate PMD(PMD_IOCTL, ("%s: PENDING: %s@%s(%s#%d) pm_poll_cnt[%d] " 2673*0Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone, 2674*0Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone])) 2675*0Sstevel@tonic-gate overrun = psc_entry(cmd, psce, dip, comp, newlevel, oldlevel, 2676*0Sstevel@tonic-gate PSC_DIRECT, canblock); 2677*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone)) 2678*0Sstevel@tonic-gate mutex_enter(&pm_clone_lock); 2679*0Sstevel@tonic-gate if (!overrun) 2680*0Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]++; 2681*0Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]); 2682*0Sstevel@tonic-gate pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN)); 2683*0Sstevel@tonic-gate mutex_exit(&pm_clone_lock); 2684*0Sstevel@tonic-gate break; 2685*0Sstevel@tonic-gate case PSC_HAS_CHANGED: 2686*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: HAS %s@%s(%s#%d), comp %d, %d -> %d\n", 2687*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, oldlevel, newlevel)) 2688*0Sstevel@tonic-gate if (PM_ISDIRECT(dip) && canblock != PM_CANBLOCK_BYPASS) { 2689*0Sstevel@tonic-gate psce = pm_psc_dip_to_direct(dip, &pscc); 2690*0Sstevel@tonic-gate PMD(PMD_IOCTL, ("%s: HAS: %s@%s(%s#%d) pm_poll_cnt[%d] " 2691*0Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone, 2692*0Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone])) 2693*0Sstevel@tonic-gate overrun = psc_entry(cmd, psce, dip, comp, newlevel, 2694*0Sstevel@tonic-gate oldlevel, PSC_DIRECT, canblock); 2695*0Sstevel@tonic-gate PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone)) 2696*0Sstevel@tonic-gate mutex_enter(&pm_clone_lock); 2697*0Sstevel@tonic-gate if (!overrun) 2698*0Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]++; 2699*0Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]); 2700*0Sstevel@tonic-gate pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN)); 2701*0Sstevel@tonic-gate mutex_exit(&pm_clone_lock); 2702*0Sstevel@tonic-gate } 2703*0Sstevel@tonic-gate mutex_enter(&pm_clone_lock); 2704*0Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_READER); 2705*0Sstevel@tonic-gate while ((psce = psc_interest(&cookie, &pscc)) != NULL) { 2706*0Sstevel@tonic-gate (void) psc_entry(cmd, psce, dip, comp, newlevel, 2707*0Sstevel@tonic-gate oldlevel, PSC_INTEREST, canblock); 2708*0Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]); 2709*0Sstevel@tonic-gate } 2710*0Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock); 2711*0Sstevel@tonic-gate mutex_exit(&pm_clone_lock); 2712*0Sstevel@tonic-gate break; 2713*0Sstevel@tonic-gate #ifdef DEBUG 2714*0Sstevel@tonic-gate default: 2715*0Sstevel@tonic-gate ASSERT(0); 2716*0Sstevel@tonic-gate #endif 2717*0Sstevel@tonic-gate } 2718*0Sstevel@tonic-gate } 2719*0Sstevel@tonic-gate 2720*0Sstevel@tonic-gate static void 2721*0Sstevel@tonic-gate pm_enqueue_notify_others(pm_ppm_devlist_t **listp, pm_canblock_t canblock) 2722*0Sstevel@tonic-gate { 2723*0Sstevel@tonic-gate if (listp) { 2724*0Sstevel@tonic-gate pm_ppm_devlist_t *p, *next = NULL; 2725*0Sstevel@tonic-gate 2726*0Sstevel@tonic-gate for (p = *listp; p; p = next) { 2727*0Sstevel@tonic-gate next = p->ppd_next; 2728*0Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, p->ppd_who, 2729*0Sstevel@tonic-gate p->ppd_cmpt, p->ppd_new_level, p->ppd_old_level, 2730*0Sstevel@tonic-gate canblock); 2731*0Sstevel@tonic-gate kmem_free(p, sizeof (pm_ppm_devlist_t)); 2732*0Sstevel@tonic-gate } 2733*0Sstevel@tonic-gate *listp = NULL; 2734*0Sstevel@tonic-gate } 2735*0Sstevel@tonic-gate } 2736*0Sstevel@tonic-gate 2737*0Sstevel@tonic-gate /* 2738*0Sstevel@tonic-gate * Try to get the power locks of the parent node and target (child) 2739*0Sstevel@tonic-gate * node. Return true if successful (with both locks held) or false 2740*0Sstevel@tonic-gate * (with no locks held). 2741*0Sstevel@tonic-gate */ 2742*0Sstevel@tonic-gate static int 2743*0Sstevel@tonic-gate pm_try_parent_child_locks(dev_info_t *pdip, 2744*0Sstevel@tonic-gate dev_info_t *dip, int *pcircp, int *circp) 2745*0Sstevel@tonic-gate { 2746*0Sstevel@tonic-gate if (ndi_devi_tryenter(pdip, pcircp)) 2747*0Sstevel@tonic-gate if (PM_TRY_LOCK_POWER(dip, circp)) { 2748*0Sstevel@tonic-gate return (1); 2749*0Sstevel@tonic-gate } else { 2750*0Sstevel@tonic-gate ndi_devi_exit(pdip, *pcircp); 2751*0Sstevel@tonic-gate } 2752*0Sstevel@tonic-gate return (0); 2753*0Sstevel@tonic-gate } 2754*0Sstevel@tonic-gate 2755*0Sstevel@tonic-gate /* 2756*0Sstevel@tonic-gate * Determine if the power lock owner is blocked by current thread. 2757*0Sstevel@tonic-gate * returns : 2758*0Sstevel@tonic-gate * 1 - If the thread owning the effective power lock (the first lock on 2759*0Sstevel@tonic-gate * which a thread blocks when it does PM_LOCK_POWER) is blocked by 2760*0Sstevel@tonic-gate * a mutex held by the current thread. 2761*0Sstevel@tonic-gate * 2762*0Sstevel@tonic-gate * 0 - otherwise 2763*0Sstevel@tonic-gate * 2764*0Sstevel@tonic-gate * Note : This function is called by pm_power_has_changed to determine whether 2765*0Sstevel@tonic-gate * it is executing in parallel with pm_set_power. 2766*0Sstevel@tonic-gate */ 2767*0Sstevel@tonic-gate static int 2768*0Sstevel@tonic-gate pm_blocked_by_us(dev_info_t *dip) 2769*0Sstevel@tonic-gate { 2770*0Sstevel@tonic-gate power_req_t power_req; 2771*0Sstevel@tonic-gate kthread_t *owner; 2772*0Sstevel@tonic-gate int result; 2773*0Sstevel@tonic-gate kmutex_t *mp; 2774*0Sstevel@tonic-gate dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm; 2775*0Sstevel@tonic-gate 2776*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_LOCK_OWNER; 2777*0Sstevel@tonic-gate power_req.req.ppm_power_lock_owner_req.who = dip; 2778*0Sstevel@tonic-gate if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, &result) != 2779*0Sstevel@tonic-gate DDI_SUCCESS) { 2780*0Sstevel@tonic-gate /* 2781*0Sstevel@tonic-gate * It is assumed that if the device is claimed by ppm, ppm 2782*0Sstevel@tonic-gate * will always implement this request type and it'll always 2783*0Sstevel@tonic-gate * return success. We panic here, if it fails. 2784*0Sstevel@tonic-gate */ 2785*0Sstevel@tonic-gate panic("pm: Can't determine power lock owner of %s@%s(%s#%d)\n", 2786*0Sstevel@tonic-gate PM_DEVICE(dip)); 2787*0Sstevel@tonic-gate /*NOTREACHED*/ 2788*0Sstevel@tonic-gate } 2789*0Sstevel@tonic-gate 2790*0Sstevel@tonic-gate if ((owner = power_req.req.ppm_power_lock_owner_req.owner) != NULL && 2791*0Sstevel@tonic-gate owner->t_state == TS_SLEEP && 2792*0Sstevel@tonic-gate owner->t_sobj_ops && 2793*0Sstevel@tonic-gate SOBJ_TYPE(owner->t_sobj_ops) == SOBJ_MUTEX && 2794*0Sstevel@tonic-gate (mp = (kmutex_t *)owner->t_wchan) && 2795*0Sstevel@tonic-gate mutex_owner(mp) == curthread) 2796*0Sstevel@tonic-gate return (1); 2797*0Sstevel@tonic-gate 2798*0Sstevel@tonic-gate return (0); 2799*0Sstevel@tonic-gate } 2800*0Sstevel@tonic-gate 2801*0Sstevel@tonic-gate /* 2802*0Sstevel@tonic-gate * Notify parent which wants to hear about a child's power changes. 2803*0Sstevel@tonic-gate */ 2804*0Sstevel@tonic-gate static void 2805*0Sstevel@tonic-gate pm_notify_parent(dev_info_t *dip, 2806*0Sstevel@tonic-gate dev_info_t *pdip, int comp, int old_level, int level) 2807*0Sstevel@tonic-gate { 2808*0Sstevel@tonic-gate pm_bp_has_changed_t bphc; 2809*0Sstevel@tonic-gate pm_sp_misc_t pspm; 2810*0Sstevel@tonic-gate char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2811*0Sstevel@tonic-gate int result = DDI_SUCCESS; 2812*0Sstevel@tonic-gate 2813*0Sstevel@tonic-gate bphc.bphc_dip = dip; 2814*0Sstevel@tonic-gate bphc.bphc_path = ddi_pathname(dip, pathbuf); 2815*0Sstevel@tonic-gate bphc.bphc_comp = comp; 2816*0Sstevel@tonic-gate bphc.bphc_olevel = old_level; 2817*0Sstevel@tonic-gate bphc.bphc_nlevel = level; 2818*0Sstevel@tonic-gate pspm.pspm_canblock = PM_CANBLOCK_BLOCK; 2819*0Sstevel@tonic-gate pspm.pspm_scan = 0; 2820*0Sstevel@tonic-gate bphc.bphc_private = &pspm; 2821*0Sstevel@tonic-gate (void) (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL, 2822*0Sstevel@tonic-gate BUS_POWER_HAS_CHANGED, (void *)&bphc, (void *)&result); 2823*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 2824*0Sstevel@tonic-gate } 2825*0Sstevel@tonic-gate 2826*0Sstevel@tonic-gate /* 2827*0Sstevel@tonic-gate * Check if we need to resume a BC device, and make the attach call as required. 2828*0Sstevel@tonic-gate */ 2829*0Sstevel@tonic-gate static int 2830*0Sstevel@tonic-gate pm_check_and_resume(dev_info_t *dip, int comp, int old_level, int level) 2831*0Sstevel@tonic-gate { 2832*0Sstevel@tonic-gate int ret = DDI_SUCCESS; 2833*0Sstevel@tonic-gate 2834*0Sstevel@tonic-gate if (PM_ISBC(dip) && comp == 0 && old_level == 0 && level != 0) { 2835*0Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED); 2836*0Sstevel@tonic-gate /* ppm is not interested in DDI_PM_RESUME */ 2837*0Sstevel@tonic-gate if ((ret = devi_attach(dip, DDI_PM_RESUME)) != DDI_SUCCESS) 2838*0Sstevel@tonic-gate /* XXX Should we mark it resumed, */ 2839*0Sstevel@tonic-gate /* even though it failed? */ 2840*0Sstevel@tonic-gate cmn_err(CE_WARN, "!pm: Can't resume %s@%s", 2841*0Sstevel@tonic-gate PM_NAME(dip), PM_ADDR(dip)); 2842*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED; 2843*0Sstevel@tonic-gate } 2844*0Sstevel@tonic-gate 2845*0Sstevel@tonic-gate return (ret); 2846*0Sstevel@tonic-gate } 2847*0Sstevel@tonic-gate 2848*0Sstevel@tonic-gate /* 2849*0Sstevel@tonic-gate * Tests outside the lock to see if we should bother to enqueue an entry 2850*0Sstevel@tonic-gate * for any watching process. If yes, then caller will take the lock and 2851*0Sstevel@tonic-gate * do the full protocol 2852*0Sstevel@tonic-gate */ 2853*0Sstevel@tonic-gate static int 2854*0Sstevel@tonic-gate pm_watchers() 2855*0Sstevel@tonic-gate { 2856*0Sstevel@tonic-gate if (pm_processes_stopped) 2857*0Sstevel@tonic-gate return (0); 2858*0Sstevel@tonic-gate return (pm_pscc_direct || pm_pscc_interest); 2859*0Sstevel@tonic-gate } 2860*0Sstevel@tonic-gate 2861*0Sstevel@tonic-gate /* 2862*0Sstevel@tonic-gate * A driver is reporting that the power of one of its device's components 2863*0Sstevel@tonic-gate * has changed. Update the power state accordingly. 2864*0Sstevel@tonic-gate */ 2865*0Sstevel@tonic-gate int 2866*0Sstevel@tonic-gate pm_power_has_changed(dev_info_t *dip, int comp, int level) 2867*0Sstevel@tonic-gate { 2868*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_power_has_changed") 2869*0Sstevel@tonic-gate int ret; 2870*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 2871*0Sstevel@tonic-gate struct pm_component *cp; 2872*0Sstevel@tonic-gate int blocked, circ, pcirc, old_level; 2873*0Sstevel@tonic-gate static int pm_phc_impl(dev_info_t *, int, int, int); 2874*0Sstevel@tonic-gate 2875*0Sstevel@tonic-gate if (level < 0) { 2876*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d): bad level=%d\n", pmf, 2877*0Sstevel@tonic-gate PM_DEVICE(dip), level)) 2878*0Sstevel@tonic-gate return (DDI_FAILURE); 2879*0Sstevel@tonic-gate } 2880*0Sstevel@tonic-gate 2881*0Sstevel@tonic-gate PMD(PMD_KIDSUP | PMD_DEP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf, 2882*0Sstevel@tonic-gate PM_DEVICE(dip), comp, level)) 2883*0Sstevel@tonic-gate 2884*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, &cp) || 2885*0Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) 2886*0Sstevel@tonic-gate return (DDI_FAILURE); 2887*0Sstevel@tonic-gate 2888*0Sstevel@tonic-gate /* 2889*0Sstevel@tonic-gate * A driver thread calling pm_power_has_changed and another thread 2890*0Sstevel@tonic-gate * calling pm_set_power can deadlock. The problem is not resolvable 2891*0Sstevel@tonic-gate * by changing lock order, so we use pm_blocked_by_us() to detect 2892*0Sstevel@tonic-gate * this specific deadlock. If we can't get the lock immediately 2893*0Sstevel@tonic-gate * and we are deadlocked, just update the component's level, do 2894*0Sstevel@tonic-gate * notifications, and return. We intend to update the total power 2895*0Sstevel@tonic-gate * state later (if the other thread fails to set power to the 2896*0Sstevel@tonic-gate * desired level). If we were called because of a power change on a 2897*0Sstevel@tonic-gate * component that isn't involved in a set_power op, update all state 2898*0Sstevel@tonic-gate * immediately. 2899*0Sstevel@tonic-gate */ 2900*0Sstevel@tonic-gate cp = PM_CP(dip, comp); 2901*0Sstevel@tonic-gate while (!pm_try_parent_child_locks(pdip, dip, &pcirc, &circ)) { 2902*0Sstevel@tonic-gate if (((blocked = pm_blocked_by_us(dip)) != 0) && 2903*0Sstevel@tonic-gate (cp->pmc_flags & PM_POWER_OP)) { 2904*0Sstevel@tonic-gate if (pm_watchers()) { 2905*0Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 2906*0Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, 2907*0Sstevel@tonic-gate level, cur_power(cp), PM_CANBLOCK_BLOCK); 2908*0Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 2909*0Sstevel@tonic-gate } 2910*0Sstevel@tonic-gate if (pdip && PM_WANTS_NOTIFICATION(pdip)) 2911*0Sstevel@tonic-gate pm_notify_parent(dip, 2912*0Sstevel@tonic-gate pdip, comp, cur_power(cp), level); 2913*0Sstevel@tonic-gate (void) pm_check_and_resume(dip, 2914*0Sstevel@tonic-gate comp, cur_power(cp), level); 2915*0Sstevel@tonic-gate 2916*0Sstevel@tonic-gate /* 2917*0Sstevel@tonic-gate * Stash the old power index, update curpwr, and flag 2918*0Sstevel@tonic-gate * that the total power state needs to be synched. 2919*0Sstevel@tonic-gate */ 2920*0Sstevel@tonic-gate cp->pmc_flags |= PM_PHC_WHILE_SET_POWER; 2921*0Sstevel@tonic-gate /* 2922*0Sstevel@tonic-gate * Several pm_power_has_changed calls could arrive 2923*0Sstevel@tonic-gate * while the set power path remains blocked. Keep the 2924*0Sstevel@tonic-gate * oldest old power and the newest new power of any 2925*0Sstevel@tonic-gate * sequence of phc calls which arrive during deadlock. 2926*0Sstevel@tonic-gate */ 2927*0Sstevel@tonic-gate if (cp->pmc_phc_pwr == PM_LEVEL_UNKNOWN) 2928*0Sstevel@tonic-gate cp->pmc_phc_pwr = cp->pmc_cur_pwr; 2929*0Sstevel@tonic-gate cp->pmc_cur_pwr = 2930*0Sstevel@tonic-gate pm_level_to_index(dip, cp, level); 2931*0Sstevel@tonic-gate PMD(PMD_PHC, ("%s: deadlock for %s@%s(%s#%d), comp=%d, " 2932*0Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 2933*0Sstevel@tonic-gate return (DDI_SUCCESS); 2934*0Sstevel@tonic-gate } else 2935*0Sstevel@tonic-gate if (blocked) { /* blocked, but different cmpt? */ 2936*0Sstevel@tonic-gate if (!ndi_devi_tryenter(pdip, &pcirc)) { 2937*0Sstevel@tonic-gate cmn_err(CE_NOTE, 2938*0Sstevel@tonic-gate "!pm: parent kuc not updated due " 2939*0Sstevel@tonic-gate "to possible deadlock.\n"); 2940*0Sstevel@tonic-gate return (pm_phc_impl(dip, 2941*0Sstevel@tonic-gate comp, level, 1)); 2942*0Sstevel@tonic-gate } 2943*0Sstevel@tonic-gate old_level = cur_power(cp); 2944*0Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip) && 2945*0Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0) && 2946*0Sstevel@tonic-gate POWERING_ON(old_level, level)) 2947*0Sstevel@tonic-gate pm_hold_power(pdip); 2948*0Sstevel@tonic-gate ret = pm_phc_impl(dip, comp, level, 1); 2949*0Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 2950*0Sstevel@tonic-gate if ((!PM_ISBC(dip) || 2951*0Sstevel@tonic-gate comp == 0) && level == 0 && 2952*0Sstevel@tonic-gate old_level != PM_LEVEL_UNKNOWN) 2953*0Sstevel@tonic-gate pm_rele_power(pdip); 2954*0Sstevel@tonic-gate } 2955*0Sstevel@tonic-gate ndi_devi_exit(pdip, pcirc); 2956*0Sstevel@tonic-gate /* child lock not held: deadlock */ 2957*0Sstevel@tonic-gate return (ret); 2958*0Sstevel@tonic-gate } 2959*0Sstevel@tonic-gate delay(1); 2960*0Sstevel@tonic-gate PMD(PMD_PHC, ("%s: try lock again\n", pmf)) 2961*0Sstevel@tonic-gate } 2962*0Sstevel@tonic-gate 2963*0Sstevel@tonic-gate /* non-deadlock case */ 2964*0Sstevel@tonic-gate old_level = cur_power(cp); 2965*0Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip) && 2966*0Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0) && POWERING_ON(old_level, level)) 2967*0Sstevel@tonic-gate pm_hold_power(pdip); 2968*0Sstevel@tonic-gate ret = pm_phc_impl(dip, comp, level, 1); 2969*0Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 2970*0Sstevel@tonic-gate if ((!PM_ISBC(dip) || comp == 0) && level == 0 && 2971*0Sstevel@tonic-gate old_level != PM_LEVEL_UNKNOWN) 2972*0Sstevel@tonic-gate pm_rele_power(pdip); 2973*0Sstevel@tonic-gate } 2974*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 2975*0Sstevel@tonic-gate ndi_devi_exit(pdip, pcirc); 2976*0Sstevel@tonic-gate return (ret); 2977*0Sstevel@tonic-gate } 2978*0Sstevel@tonic-gate 2979*0Sstevel@tonic-gate /* 2980*0Sstevel@tonic-gate * Account for power changes to a component of the the console frame buffer. 2981*0Sstevel@tonic-gate * If lowering power from full (or "unkown", which is treatd as full) 2982*0Sstevel@tonic-gate * we will increment the "components off" count of the fb device. 2983*0Sstevel@tonic-gate * Subsequent lowering of the same component doesn't affect the count. If 2984*0Sstevel@tonic-gate * raising a component back to full power, we will decrement the count. 2985*0Sstevel@tonic-gate * 2986*0Sstevel@tonic-gate * Return: the increment value for pm_cfb_comps_off (-1, 0, or 1) 2987*0Sstevel@tonic-gate */ 2988*0Sstevel@tonic-gate static int 2989*0Sstevel@tonic-gate calc_cfb_comps_incr(dev_info_t *dip, int cmpt, int old, int new) 2990*0Sstevel@tonic-gate { 2991*0Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, cmpt); 2992*0Sstevel@tonic-gate int on = (old == PM_LEVEL_UNKNOWN || old == cp->pmc_norm_pwr); 2993*0Sstevel@tonic-gate int want_normal = (new == cp->pmc_norm_pwr); 2994*0Sstevel@tonic-gate int incr = 0; 2995*0Sstevel@tonic-gate 2996*0Sstevel@tonic-gate if (on && !want_normal) 2997*0Sstevel@tonic-gate incr = 1; 2998*0Sstevel@tonic-gate else if (!on && want_normal) 2999*0Sstevel@tonic-gate incr = -1; 3000*0Sstevel@tonic-gate return (incr); 3001*0Sstevel@tonic-gate } 3002*0Sstevel@tonic-gate 3003*0Sstevel@tonic-gate /* 3004*0Sstevel@tonic-gate * Adjust the count of console frame buffer components < full power. 3005*0Sstevel@tonic-gate */ 3006*0Sstevel@tonic-gate static void 3007*0Sstevel@tonic-gate update_comps_off(int incr, dev_info_t *dip) 3008*0Sstevel@tonic-gate { 3009*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 3010*0Sstevel@tonic-gate pm_cfb_comps_off += incr; 3011*0Sstevel@tonic-gate ASSERT(pm_cfb_comps_off <= PM_NUMCMPTS(dip)); 3012*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 3013*0Sstevel@tonic-gate } 3014*0Sstevel@tonic-gate 3015*0Sstevel@tonic-gate /* 3016*0Sstevel@tonic-gate * Update the power state in the framework (via the ppm). The 'notify' 3017*0Sstevel@tonic-gate * argument tells whether to notify watchers. Power lock is already held. 3018*0Sstevel@tonic-gate */ 3019*0Sstevel@tonic-gate static int 3020*0Sstevel@tonic-gate pm_phc_impl(dev_info_t *dip, int comp, int level, int notify) 3021*0Sstevel@tonic-gate { 3022*0Sstevel@tonic-gate PMD_FUNC(pmf, "phc_impl") 3023*0Sstevel@tonic-gate power_req_t power_req; 3024*0Sstevel@tonic-gate int i, dodeps = 0; 3025*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 3026*0Sstevel@tonic-gate int result; 3027*0Sstevel@tonic-gate int old_level; 3028*0Sstevel@tonic-gate struct pm_component *cp; 3029*0Sstevel@tonic-gate int incr = 0; 3030*0Sstevel@tonic-gate dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm; 3031*0Sstevel@tonic-gate int work_type = 0; 3032*0Sstevel@tonic-gate char *pathbuf; 3033*0Sstevel@tonic-gate 3034*0Sstevel@tonic-gate /* Must use "official" power level for this test. */ 3035*0Sstevel@tonic-gate cp = PM_CP(dip, comp); 3036*0Sstevel@tonic-gate old_level = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ? 3037*0Sstevel@tonic-gate cp->pmc_phc_pwr : cp->pmc_cur_pwr); 3038*0Sstevel@tonic-gate if (old_level != PM_LEVEL_UNKNOWN) 3039*0Sstevel@tonic-gate old_level = cp->pmc_comp.pmc_lvals[old_level]; 3040*0Sstevel@tonic-gate 3041*0Sstevel@tonic-gate if (level == old_level) { 3042*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d is already at " 3043*0Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 3044*0Sstevel@tonic-gate return (DDI_SUCCESS); 3045*0Sstevel@tonic-gate } 3046*0Sstevel@tonic-gate 3047*0Sstevel@tonic-gate /* 3048*0Sstevel@tonic-gate * Tell ppm about this. 3049*0Sstevel@tonic-gate */ 3050*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY; 3051*0Sstevel@tonic-gate power_req.req.ppm_notify_level_req.who = dip; 3052*0Sstevel@tonic-gate power_req.req.ppm_notify_level_req.cmpt = comp; 3053*0Sstevel@tonic-gate power_req.req.ppm_notify_level_req.new_level = level; 3054*0Sstevel@tonic-gate power_req.req.ppm_notify_level_req.old_level = old_level; 3055*0Sstevel@tonic-gate if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, 3056*0Sstevel@tonic-gate &result) == DDI_FAILURE) { 3057*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: pm_ctlops %s@%s(%s#%d) to %d failed\n", 3058*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), level)) 3059*0Sstevel@tonic-gate return (DDI_FAILURE); 3060*0Sstevel@tonic-gate } 3061*0Sstevel@tonic-gate 3062*0Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 3063*0Sstevel@tonic-gate incr = calc_cfb_comps_incr(dip, comp, old_level, level); 3064*0Sstevel@tonic-gate 3065*0Sstevel@tonic-gate if (incr) { 3066*0Sstevel@tonic-gate update_comps_off(incr, dip); 3067*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) comp=%d %d->%d " 3068*0Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip), 3069*0Sstevel@tonic-gate comp, old_level, level, pm_cfb_comps_off)) 3070*0Sstevel@tonic-gate } 3071*0Sstevel@tonic-gate } 3072*0Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level); 3073*0Sstevel@tonic-gate result = DDI_SUCCESS; 3074*0Sstevel@tonic-gate 3075*0Sstevel@tonic-gate if (notify) { 3076*0Sstevel@tonic-gate if (pdip && PM_WANTS_NOTIFICATION(pdip)) 3077*0Sstevel@tonic-gate pm_notify_parent(dip, pdip, comp, old_level, level); 3078*0Sstevel@tonic-gate (void) pm_check_and_resume(dip, comp, old_level, level); 3079*0Sstevel@tonic-gate } 3080*0Sstevel@tonic-gate 3081*0Sstevel@tonic-gate /* 3082*0Sstevel@tonic-gate * Decrement the dependency kidsup count if we turn a device 3083*0Sstevel@tonic-gate * off. 3084*0Sstevel@tonic-gate */ 3085*0Sstevel@tonic-gate if (POWERING_OFF(old_level, level)) { 3086*0Sstevel@tonic-gate dodeps = 1; 3087*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3088*0Sstevel@tonic-gate cp = PM_CP(dip, i); 3089*0Sstevel@tonic-gate if (cur_power(cp)) { 3090*0Sstevel@tonic-gate dodeps = 0; 3091*0Sstevel@tonic-gate break; 3092*0Sstevel@tonic-gate } 3093*0Sstevel@tonic-gate } 3094*0Sstevel@tonic-gate if (dodeps) 3095*0Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_OFF; 3096*0Sstevel@tonic-gate } 3097*0Sstevel@tonic-gate 3098*0Sstevel@tonic-gate /* 3099*0Sstevel@tonic-gate * Increment if we turn it on. Check to see 3100*0Sstevel@tonic-gate * if other comps are already on, if so, 3101*0Sstevel@tonic-gate * dont increment. 3102*0Sstevel@tonic-gate */ 3103*0Sstevel@tonic-gate if (POWERING_ON(old_level, level)) { 3104*0Sstevel@tonic-gate dodeps = 1; 3105*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3106*0Sstevel@tonic-gate cp = PM_CP(dip, i); 3107*0Sstevel@tonic-gate if (comp == i) 3108*0Sstevel@tonic-gate continue; 3109*0Sstevel@tonic-gate /* -1 also treated as 0 in this case */ 3110*0Sstevel@tonic-gate if (cur_power(cp) > 0) { 3111*0Sstevel@tonic-gate dodeps = 0; 3112*0Sstevel@tonic-gate break; 3113*0Sstevel@tonic-gate } 3114*0Sstevel@tonic-gate } 3115*0Sstevel@tonic-gate if (dodeps) 3116*0Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_ON; 3117*0Sstevel@tonic-gate } 3118*0Sstevel@tonic-gate 3119*0Sstevel@tonic-gate if (dodeps) { 3120*0Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 3121*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 3122*0Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, NULL, 3123*0Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0); 3124*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 3125*0Sstevel@tonic-gate } 3126*0Sstevel@tonic-gate 3127*0Sstevel@tonic-gate if (notify && (level != old_level) && pm_watchers()) { 3128*0Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 3129*0Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, level, old_level, 3130*0Sstevel@tonic-gate PM_CANBLOCK_BLOCK); 3131*0Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 3132*0Sstevel@tonic-gate } 3133*0Sstevel@tonic-gate 3134*0Sstevel@tonic-gate PMD(PMD_RESCAN, ("%s: %s@%s(%s#%d): pm_rescan\n", pmf, PM_DEVICE(dip))) 3135*0Sstevel@tonic-gate pm_rescan(dip); 3136*0Sstevel@tonic-gate return (DDI_SUCCESS); 3137*0Sstevel@tonic-gate } 3138*0Sstevel@tonic-gate 3139*0Sstevel@tonic-gate /* 3140*0Sstevel@tonic-gate * This function is called at startup time to notify pm of the existence 3141*0Sstevel@tonic-gate * of any platform power managers for this platform. As a result of 3142*0Sstevel@tonic-gate * this registration, each function provided will be called each time 3143*0Sstevel@tonic-gate * a device node is attached, until one returns true, and it must claim the 3144*0Sstevel@tonic-gate * device node (by returning non-zero) if it wants to be involved in the 3145*0Sstevel@tonic-gate * node's power management. If it does claim the node, then it will 3146*0Sstevel@tonic-gate * subsequently be notified of attach and detach events. 3147*0Sstevel@tonic-gate * 3148*0Sstevel@tonic-gate */ 3149*0Sstevel@tonic-gate 3150*0Sstevel@tonic-gate int 3151*0Sstevel@tonic-gate pm_register_ppm(int (*func)(dev_info_t *), dev_info_t *dip) 3152*0Sstevel@tonic-gate { 3153*0Sstevel@tonic-gate PMD_FUNC(pmf, "register_ppm") 3154*0Sstevel@tonic-gate struct ppm_callbacks *ppmcp; 3155*0Sstevel@tonic-gate pm_component_t *cp; 3156*0Sstevel@tonic-gate int i, pwr, result, circ; 3157*0Sstevel@tonic-gate power_req_t power_req; 3158*0Sstevel@tonic-gate struct ppm_notify_level_req *p = &power_req.req.ppm_notify_level_req; 3159*0Sstevel@tonic-gate void pm_ppm_claim(dev_info_t *); 3160*0Sstevel@tonic-gate 3161*0Sstevel@tonic-gate mutex_enter(&ppm_lock); 3162*0Sstevel@tonic-gate ppmcp = ppm_callbacks; 3163*0Sstevel@tonic-gate for (i = 0; i < MAX_PPM_HANDLERS; i++, ppmcp++) { 3164*0Sstevel@tonic-gate if (ppmcp->ppmc_func == NULL) { 3165*0Sstevel@tonic-gate ppmcp->ppmc_func = func; 3166*0Sstevel@tonic-gate ppmcp->ppmc_dip = dip; 3167*0Sstevel@tonic-gate break; 3168*0Sstevel@tonic-gate } 3169*0Sstevel@tonic-gate } 3170*0Sstevel@tonic-gate mutex_exit(&ppm_lock); 3171*0Sstevel@tonic-gate 3172*0Sstevel@tonic-gate if (i >= MAX_PPM_HANDLERS) 3173*0Sstevel@tonic-gate return (DDI_FAILURE); 3174*0Sstevel@tonic-gate while ((dip = ddi_get_parent(dip)) != NULL) { 3175*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) 3176*0Sstevel@tonic-gate continue; 3177*0Sstevel@tonic-gate pm_ppm_claim(dip); 3178*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { 3179*0Sstevel@tonic-gate /* 3180*0Sstevel@tonic-gate * Tell ppm about this. 3181*0Sstevel@tonic-gate */ 3182*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY; 3183*0Sstevel@tonic-gate p->old_level = PM_LEVEL_UNKNOWN; 3184*0Sstevel@tonic-gate p->who = dip; 3185*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 3186*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3187*0Sstevel@tonic-gate cp = PM_CP(dip, i); 3188*0Sstevel@tonic-gate pwr = cp->pmc_cur_pwr; 3189*0Sstevel@tonic-gate if (pwr != PM_LEVEL_UNKNOWN) { 3190*0Sstevel@tonic-gate p->cmpt = i; 3191*0Sstevel@tonic-gate p->new_level = cur_power(cp); 3192*0Sstevel@tonic-gate p->old_level = PM_LEVEL_UNKNOWN; 3193*0Sstevel@tonic-gate if (pm_ctlops(PPM(dip), dip, 3194*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, 3195*0Sstevel@tonic-gate &result) == DDI_FAILURE) { 3196*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: pc " 3197*0Sstevel@tonic-gate "%s@%s(%s#%d) to %d " 3198*0Sstevel@tonic-gate "fails\n", pmf, 3199*0Sstevel@tonic-gate PM_DEVICE(dip), pwr)) 3200*0Sstevel@tonic-gate } 3201*0Sstevel@tonic-gate } 3202*0Sstevel@tonic-gate } 3203*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 3204*0Sstevel@tonic-gate } 3205*0Sstevel@tonic-gate } 3206*0Sstevel@tonic-gate return (DDI_SUCCESS); 3207*0Sstevel@tonic-gate } 3208*0Sstevel@tonic-gate 3209*0Sstevel@tonic-gate /* 3210*0Sstevel@tonic-gate * Call the ppm's that have registered and adjust the devinfo struct as 3211*0Sstevel@tonic-gate * appropriate. First one to claim it gets it. The sets of devices claimed 3212*0Sstevel@tonic-gate * by each ppm are assumed to be disjoint. 3213*0Sstevel@tonic-gate */ 3214*0Sstevel@tonic-gate void 3215*0Sstevel@tonic-gate pm_ppm_claim(dev_info_t *dip) 3216*0Sstevel@tonic-gate { 3217*0Sstevel@tonic-gate struct ppm_callbacks *ppmcp; 3218*0Sstevel@tonic-gate 3219*0Sstevel@tonic-gate if (PPM(dip)) { 3220*0Sstevel@tonic-gate return; 3221*0Sstevel@tonic-gate } 3222*0Sstevel@tonic-gate mutex_enter(&ppm_lock); 3223*0Sstevel@tonic-gate for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) { 3224*0Sstevel@tonic-gate if ((*ppmcp->ppmc_func)(dip)) { 3225*0Sstevel@tonic-gate DEVI(dip)->devi_pm_ppm = 3226*0Sstevel@tonic-gate (struct dev_info *)ppmcp->ppmc_dip; 3227*0Sstevel@tonic-gate mutex_exit(&ppm_lock); 3228*0Sstevel@tonic-gate return; 3229*0Sstevel@tonic-gate } 3230*0Sstevel@tonic-gate } 3231*0Sstevel@tonic-gate mutex_exit(&ppm_lock); 3232*0Sstevel@tonic-gate } 3233*0Sstevel@tonic-gate 3234*0Sstevel@tonic-gate /* 3235*0Sstevel@tonic-gate * Node is being detached so stop autopm until we see if it succeeds, in which 3236*0Sstevel@tonic-gate * case pm_stop will be called. For backwards compatible devices we bring the 3237*0Sstevel@tonic-gate * device up to full power on the assumption the detach will succeed. 3238*0Sstevel@tonic-gate */ 3239*0Sstevel@tonic-gate void 3240*0Sstevel@tonic-gate pm_detaching(dev_info_t *dip) 3241*0Sstevel@tonic-gate { 3242*0Sstevel@tonic-gate PMD_FUNC(pmf, "detaching") 3243*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 3244*0Sstevel@tonic-gate int iscons; 3245*0Sstevel@tonic-gate 3246*0Sstevel@tonic-gate PMD(PMD_REMDEV, ("%s: %s@%s(%s#%d), %d comps\n", pmf, PM_DEVICE(dip), 3247*0Sstevel@tonic-gate PM_NUMCMPTS(dip))) 3248*0Sstevel@tonic-gate if (info == NULL) 3249*0Sstevel@tonic-gate return; 3250*0Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip)); 3251*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 3252*0Sstevel@tonic-gate info->pmi_dev_pm_state |= PM_DETACHING; 3253*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 3254*0Sstevel@tonic-gate if (!PM_ISBC(dip)) 3255*0Sstevel@tonic-gate pm_scan_stop(dip); 3256*0Sstevel@tonic-gate 3257*0Sstevel@tonic-gate /* 3258*0Sstevel@tonic-gate * console and old-style devices get brought up when detaching. 3259*0Sstevel@tonic-gate */ 3260*0Sstevel@tonic-gate iscons = PM_IS_CFB(dip); 3261*0Sstevel@tonic-gate if (iscons || PM_ISBC(dip)) { 3262*0Sstevel@tonic-gate (void) pm_all_to_normal(dip, PM_CANBLOCK_BYPASS); 3263*0Sstevel@tonic-gate if (iscons) { 3264*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 3265*0Sstevel@tonic-gate while (cfb_inuse) { 3266*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 3267*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: delay; cfb_inuse\n", pmf)) 3268*0Sstevel@tonic-gate delay(1); 3269*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 3270*0Sstevel@tonic-gate } 3271*0Sstevel@tonic-gate ASSERT(cfb_dip_detaching == NULL); 3272*0Sstevel@tonic-gate ASSERT(cfb_dip); 3273*0Sstevel@tonic-gate cfb_dip_detaching = cfb_dip; /* case detach fails */ 3274*0Sstevel@tonic-gate cfb_dip = NULL; 3275*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 3276*0Sstevel@tonic-gate } 3277*0Sstevel@tonic-gate } 3278*0Sstevel@tonic-gate } 3279*0Sstevel@tonic-gate 3280*0Sstevel@tonic-gate /* 3281*0Sstevel@tonic-gate * Node failed to detach. If it used to be autopm'd, make it so again. 3282*0Sstevel@tonic-gate */ 3283*0Sstevel@tonic-gate void 3284*0Sstevel@tonic-gate pm_detach_failed(dev_info_t *dip) 3285*0Sstevel@tonic-gate { 3286*0Sstevel@tonic-gate PMD_FUNC(pmf, "detach_failed") 3287*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 3288*0Sstevel@tonic-gate int pm_all_at_normal(dev_info_t *); 3289*0Sstevel@tonic-gate 3290*0Sstevel@tonic-gate if (info == NULL) 3291*0Sstevel@tonic-gate return; 3292*0Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip)); 3293*0Sstevel@tonic-gate if (info->pmi_dev_pm_state & PM_DETACHING) { 3294*0Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_DETACHING; 3295*0Sstevel@tonic-gate if (info->pmi_dev_pm_state & PM_ALLNORM_DEFERRED) { 3296*0Sstevel@tonic-gate /* Make sure the operation is still needed */ 3297*0Sstevel@tonic-gate if (!pm_all_at_normal(dip)) { 3298*0Sstevel@tonic-gate if (pm_all_to_normal(dip, 3299*0Sstevel@tonic-gate PM_CANBLOCK_FAIL) != DDI_SUCCESS) { 3300*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: could not bring " 3301*0Sstevel@tonic-gate "%s@%s(%s#%d) to normal\n", pmf, 3302*0Sstevel@tonic-gate PM_DEVICE(dip))) 3303*0Sstevel@tonic-gate } 3304*0Sstevel@tonic-gate } 3305*0Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_ALLNORM_DEFERRED; 3306*0Sstevel@tonic-gate } 3307*0Sstevel@tonic-gate } 3308*0Sstevel@tonic-gate if (!PM_ISBC(dip)) { 3309*0Sstevel@tonic-gate mutex_enter(&pm_scan_lock); 3310*0Sstevel@tonic-gate if (autopm_enabled) 3311*0Sstevel@tonic-gate pm_scan_init(dip); 3312*0Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 3313*0Sstevel@tonic-gate pm_rescan(dip); 3314*0Sstevel@tonic-gate } 3315*0Sstevel@tonic-gate } 3316*0Sstevel@tonic-gate 3317*0Sstevel@tonic-gate /* generic Backwards Compatible component */ 3318*0Sstevel@tonic-gate static char *bc_names[] = {"off", "on"}; 3319*0Sstevel@tonic-gate 3320*0Sstevel@tonic-gate static pm_comp_t bc_comp = {"unknown", 2, NULL, NULL, &bc_names[0]}; 3321*0Sstevel@tonic-gate 3322*0Sstevel@tonic-gate static void 3323*0Sstevel@tonic-gate e_pm_default_levels(dev_info_t *dip, pm_component_t *cp, int norm) 3324*0Sstevel@tonic-gate { 3325*0Sstevel@tonic-gate pm_comp_t *pmc; 3326*0Sstevel@tonic-gate pmc = &cp->pmc_comp; 3327*0Sstevel@tonic-gate pmc->pmc_numlevels = 2; 3328*0Sstevel@tonic-gate pmc->pmc_lvals[0] = 0; 3329*0Sstevel@tonic-gate pmc->pmc_lvals[1] = norm; 3330*0Sstevel@tonic-gate e_pm_set_cur_pwr(dip, cp, norm); 3331*0Sstevel@tonic-gate } 3332*0Sstevel@tonic-gate 3333*0Sstevel@tonic-gate static void 3334*0Sstevel@tonic-gate e_pm_default_components(dev_info_t *dip, int cmpts) 3335*0Sstevel@tonic-gate { 3336*0Sstevel@tonic-gate int i; 3337*0Sstevel@tonic-gate pm_component_t *p = DEVI(dip)->devi_pm_components; 3338*0Sstevel@tonic-gate 3339*0Sstevel@tonic-gate p = DEVI(dip)->devi_pm_components; 3340*0Sstevel@tonic-gate for (i = 0; i < cmpts; i++, p++) { 3341*0Sstevel@tonic-gate p->pmc_comp = bc_comp; /* struct assignment */ 3342*0Sstevel@tonic-gate p->pmc_comp.pmc_lvals = kmem_zalloc(2 * sizeof (int), 3343*0Sstevel@tonic-gate KM_SLEEP); 3344*0Sstevel@tonic-gate p->pmc_comp.pmc_thresh = kmem_alloc(2 * sizeof (int), 3345*0Sstevel@tonic-gate KM_SLEEP); 3346*0Sstevel@tonic-gate p->pmc_comp.pmc_numlevels = 2; 3347*0Sstevel@tonic-gate p->pmc_comp.pmc_thresh[0] = INT_MAX; 3348*0Sstevel@tonic-gate p->pmc_comp.pmc_thresh[1] = INT_MAX; 3349*0Sstevel@tonic-gate } 3350*0Sstevel@tonic-gate } 3351*0Sstevel@tonic-gate 3352*0Sstevel@tonic-gate /* 3353*0Sstevel@tonic-gate * Called from functions that require components to exist already to allow 3354*0Sstevel@tonic-gate * for their creation by parsing the pm-components property. 3355*0Sstevel@tonic-gate * Device will not be power managed as a result of this call 3356*0Sstevel@tonic-gate * No locking needed because we're single threaded by the ndi_devi_enter 3357*0Sstevel@tonic-gate * done while attaching, and the device isn't visible until after it has 3358*0Sstevel@tonic-gate * attached 3359*0Sstevel@tonic-gate */ 3360*0Sstevel@tonic-gate int 3361*0Sstevel@tonic-gate pm_premanage(dev_info_t *dip, int style) 3362*0Sstevel@tonic-gate { 3363*0Sstevel@tonic-gate PMD_FUNC(pmf, "premanage") 3364*0Sstevel@tonic-gate pm_comp_t *pcp, *compp; 3365*0Sstevel@tonic-gate int cmpts, i, norm, error; 3366*0Sstevel@tonic-gate pm_component_t *p = DEVI(dip)->devi_pm_components; 3367*0Sstevel@tonic-gate pm_comp_t *pm_autoconfig(dev_info_t *, int *); 3368*0Sstevel@tonic-gate 3369*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3370*0Sstevel@tonic-gate /* 3371*0Sstevel@tonic-gate * If this dip has already been processed, don't mess with it 3372*0Sstevel@tonic-gate */ 3373*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE) 3374*0Sstevel@tonic-gate return (DDI_SUCCESS); 3375*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_FAILED) { 3376*0Sstevel@tonic-gate return (DDI_FAILURE); 3377*0Sstevel@tonic-gate } 3378*0Sstevel@tonic-gate /* 3379*0Sstevel@tonic-gate * Look up pm-components property and create components accordingly 3380*0Sstevel@tonic-gate * If that fails, fall back to backwards compatibility 3381*0Sstevel@tonic-gate */ 3382*0Sstevel@tonic-gate if ((compp = pm_autoconfig(dip, &error)) == NULL) { 3383*0Sstevel@tonic-gate /* 3384*0Sstevel@tonic-gate * If error is set, the property existed but was not well formed 3385*0Sstevel@tonic-gate */ 3386*0Sstevel@tonic-gate if (error || (style == PM_STYLE_NEW)) { 3387*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_FAILED; 3388*0Sstevel@tonic-gate return (DDI_FAILURE); 3389*0Sstevel@tonic-gate } 3390*0Sstevel@tonic-gate /* 3391*0Sstevel@tonic-gate * If they don't have the pm-components property, then we 3392*0Sstevel@tonic-gate * want the old "no pm until PM_SET_DEVICE_THRESHOLDS ioctl" 3393*0Sstevel@tonic-gate * behavior driver must have called pm_create_components, and 3394*0Sstevel@tonic-gate * we need to flesh out dummy components 3395*0Sstevel@tonic-gate */ 3396*0Sstevel@tonic-gate if ((cmpts = PM_NUMCMPTS(dip)) == 0) { 3397*0Sstevel@tonic-gate /* 3398*0Sstevel@tonic-gate * Not really failure, but we don't want the 3399*0Sstevel@tonic-gate * caller to treat it as success 3400*0Sstevel@tonic-gate */ 3401*0Sstevel@tonic-gate return (DDI_FAILURE); 3402*0Sstevel@tonic-gate } 3403*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_BC; 3404*0Sstevel@tonic-gate e_pm_default_components(dip, cmpts); 3405*0Sstevel@tonic-gate for (i = 0; i < cmpts; i++) { 3406*0Sstevel@tonic-gate /* 3407*0Sstevel@tonic-gate * if normal power not set yet, we don't really know 3408*0Sstevel@tonic-gate * what *ANY* of the power values are. If normal 3409*0Sstevel@tonic-gate * power is set, then we assume for this backwards 3410*0Sstevel@tonic-gate * compatible case that the values are 0, normal power. 3411*0Sstevel@tonic-gate */ 3412*0Sstevel@tonic-gate norm = pm_get_normal_power(dip, i); 3413*0Sstevel@tonic-gate if (norm == (uint_t)-1) { 3414*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d)[%d]\n", pmf, 3415*0Sstevel@tonic-gate PM_DEVICE(dip), i)) 3416*0Sstevel@tonic-gate return (DDI_FAILURE); 3417*0Sstevel@tonic-gate } 3418*0Sstevel@tonic-gate /* 3419*0Sstevel@tonic-gate * Components of BC devices start at their normal power, 3420*0Sstevel@tonic-gate * so count them to be not at their lowest power. 3421*0Sstevel@tonic-gate */ 3422*0Sstevel@tonic-gate PM_INCR_NOTLOWEST(dip); 3423*0Sstevel@tonic-gate e_pm_default_levels(dip, PM_CP(dip, i), norm); 3424*0Sstevel@tonic-gate } 3425*0Sstevel@tonic-gate } else { 3426*0Sstevel@tonic-gate /* 3427*0Sstevel@tonic-gate * e_pm_create_components was called from pm_autoconfig(), it 3428*0Sstevel@tonic-gate * creates components with no descriptions (or known levels) 3429*0Sstevel@tonic-gate */ 3430*0Sstevel@tonic-gate cmpts = PM_NUMCMPTS(dip); 3431*0Sstevel@tonic-gate ASSERT(cmpts != 0); 3432*0Sstevel@tonic-gate pcp = compp; 3433*0Sstevel@tonic-gate p = DEVI(dip)->devi_pm_components; 3434*0Sstevel@tonic-gate for (i = 0; i < cmpts; i++, p++) { 3435*0Sstevel@tonic-gate p->pmc_comp = *pcp++; /* struct assignment */ 3436*0Sstevel@tonic-gate ASSERT(PM_CP(dip, i)->pmc_cur_pwr == 0); 3437*0Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN); 3438*0Sstevel@tonic-gate } 3439*0Sstevel@tonic-gate pm_set_device_threshold(dip, pm_system_idle_threshold, 3440*0Sstevel@tonic-gate PMC_DEF_THRESH); 3441*0Sstevel@tonic-gate kmem_free(compp, cmpts * sizeof (pm_comp_t)); 3442*0Sstevel@tonic-gate } 3443*0Sstevel@tonic-gate return (DDI_SUCCESS); 3444*0Sstevel@tonic-gate } 3445*0Sstevel@tonic-gate 3446*0Sstevel@tonic-gate /* 3447*0Sstevel@tonic-gate * Called from during or after the device's attach to let us know it is ready 3448*0Sstevel@tonic-gate * to play autopm. Look up the pm model and manage the device accordingly. 3449*0Sstevel@tonic-gate * Returns system call errno value. 3450*0Sstevel@tonic-gate * If DDI_ATTACH and DDI_DETACH were in same namespace, this would be 3451*0Sstevel@tonic-gate * a little cleaner 3452*0Sstevel@tonic-gate * 3453*0Sstevel@tonic-gate * Called with dip lock held, return with dip lock unheld. 3454*0Sstevel@tonic-gate */ 3455*0Sstevel@tonic-gate 3456*0Sstevel@tonic-gate int 3457*0Sstevel@tonic-gate e_pm_manage(dev_info_t *dip, int style) 3458*0Sstevel@tonic-gate { 3459*0Sstevel@tonic-gate PMD_FUNC(pmf, "e_manage") 3460*0Sstevel@tonic-gate pm_info_t *info; 3461*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 3462*0Sstevel@tonic-gate int pm_thresh_specd(dev_info_t *); 3463*0Sstevel@tonic-gate int count; 3464*0Sstevel@tonic-gate char *pathbuf; 3465*0Sstevel@tonic-gate 3466*0Sstevel@tonic-gate if (pm_premanage(dip, style) != DDI_SUCCESS) { 3467*0Sstevel@tonic-gate return (DDI_FAILURE); 3468*0Sstevel@tonic-gate } 3469*0Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3470*0Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip) == NULL); 3471*0Sstevel@tonic-gate info = kmem_zalloc(sizeof (pm_info_t), KM_SLEEP); 3472*0Sstevel@tonic-gate 3473*0Sstevel@tonic-gate /* 3474*0Sstevel@tonic-gate * Now set up parent's kidsupcnt. BC nodes are assumed to start 3475*0Sstevel@tonic-gate * out at their normal power, so they are "up", others start out 3476*0Sstevel@tonic-gate * unknown, which is effectively "up". Parent which want notification 3477*0Sstevel@tonic-gate * get kidsupcnt of 0 always. 3478*0Sstevel@tonic-gate */ 3479*0Sstevel@tonic-gate count = (PM_ISBC(dip)) ? 1 : PM_NUMCMPTS(dip); 3480*0Sstevel@tonic-gate if (count && pdip && !PM_WANTS_NOTIFICATION(pdip)) 3481*0Sstevel@tonic-gate e_pm_hold_rele_power(pdip, count); 3482*0Sstevel@tonic-gate 3483*0Sstevel@tonic-gate pm_set_pm_info(dip, info); 3484*0Sstevel@tonic-gate /* 3485*0Sstevel@tonic-gate * Apply any recorded thresholds 3486*0Sstevel@tonic-gate */ 3487*0Sstevel@tonic-gate (void) pm_thresh_specd(dip); 3488*0Sstevel@tonic-gate 3489*0Sstevel@tonic-gate /* 3490*0Sstevel@tonic-gate * Do dependency processing. 3491*0Sstevel@tonic-gate */ 3492*0Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 3493*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 3494*0Sstevel@tonic-gate pm_dispatch_to_dep_thread(PM_DEP_WK_ATTACH, pathbuf, pathbuf, 3495*0Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0); 3496*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 3497*0Sstevel@tonic-gate 3498*0Sstevel@tonic-gate if (!PM_ISBC(dip)) { 3499*0Sstevel@tonic-gate mutex_enter(&pm_scan_lock); 3500*0Sstevel@tonic-gate if (autopm_enabled) { 3501*0Sstevel@tonic-gate pm_scan_init(dip); 3502*0Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 3503*0Sstevel@tonic-gate pm_rescan(dip); 3504*0Sstevel@tonic-gate } else { 3505*0Sstevel@tonic-gate mutex_exit(&pm_scan_lock); 3506*0Sstevel@tonic-gate } 3507*0Sstevel@tonic-gate } 3508*0Sstevel@tonic-gate return (0); 3509*0Sstevel@tonic-gate } 3510*0Sstevel@tonic-gate 3511*0Sstevel@tonic-gate /* 3512*0Sstevel@tonic-gate * This is the obsolete exported interface for a driver to find out its 3513*0Sstevel@tonic-gate * "normal" (max) power. 3514*0Sstevel@tonic-gate * We only get components destroyed while no power management is 3515*0Sstevel@tonic-gate * going on (and the device is detached), so we don't need a mutex here 3516*0Sstevel@tonic-gate */ 3517*0Sstevel@tonic-gate int 3518*0Sstevel@tonic-gate pm_get_normal_power(dev_info_t *dip, int comp) 3519*0Sstevel@tonic-gate { 3520*0Sstevel@tonic-gate 3521*0Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip)) { 3522*0Sstevel@tonic-gate return (PM_CP(dip, comp)->pmc_norm_pwr); 3523*0Sstevel@tonic-gate } 3524*0Sstevel@tonic-gate return (DDI_FAILURE); 3525*0Sstevel@tonic-gate } 3526*0Sstevel@tonic-gate 3527*0Sstevel@tonic-gate /* 3528*0Sstevel@tonic-gate * Fetches the current power level. Return DDI_SUCCESS or DDI_FAILURE. 3529*0Sstevel@tonic-gate */ 3530*0Sstevel@tonic-gate int 3531*0Sstevel@tonic-gate pm_get_current_power(dev_info_t *dip, int comp, int *levelp) 3532*0Sstevel@tonic-gate { 3533*0Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip)) { 3534*0Sstevel@tonic-gate *levelp = PM_CURPOWER(dip, comp); 3535*0Sstevel@tonic-gate return (DDI_SUCCESS); 3536*0Sstevel@tonic-gate } 3537*0Sstevel@tonic-gate return (DDI_FAILURE); 3538*0Sstevel@tonic-gate } 3539*0Sstevel@tonic-gate 3540*0Sstevel@tonic-gate /* 3541*0Sstevel@tonic-gate * Returns current threshold of indicated component 3542*0Sstevel@tonic-gate */ 3543*0Sstevel@tonic-gate static int 3544*0Sstevel@tonic-gate cur_threshold(dev_info_t *dip, int comp) 3545*0Sstevel@tonic-gate { 3546*0Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, comp); 3547*0Sstevel@tonic-gate int pwr; 3548*0Sstevel@tonic-gate 3549*0Sstevel@tonic-gate if (PM_ISBC(dip)) { 3550*0Sstevel@tonic-gate /* 3551*0Sstevel@tonic-gate * backwards compatible nodes only have one threshold 3552*0Sstevel@tonic-gate */ 3553*0Sstevel@tonic-gate return (cp->pmc_comp.pmc_thresh[1]); 3554*0Sstevel@tonic-gate } 3555*0Sstevel@tonic-gate pwr = cp->pmc_cur_pwr; 3556*0Sstevel@tonic-gate if (pwr == PM_LEVEL_UNKNOWN) { 3557*0Sstevel@tonic-gate int thresh; 3558*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) 3559*0Sstevel@tonic-gate thresh = pm_default_nexus_threshold; 3560*0Sstevel@tonic-gate else 3561*0Sstevel@tonic-gate thresh = pm_system_idle_threshold; 3562*0Sstevel@tonic-gate return (thresh); 3563*0Sstevel@tonic-gate } 3564*0Sstevel@tonic-gate ASSERT(cp->pmc_comp.pmc_thresh); 3565*0Sstevel@tonic-gate return (cp->pmc_comp.pmc_thresh[pwr]); 3566*0Sstevel@tonic-gate } 3567*0Sstevel@tonic-gate 3568*0Sstevel@tonic-gate /* 3569*0Sstevel@tonic-gate * Compute next lower component power level given power index. 3570*0Sstevel@tonic-gate */ 3571*0Sstevel@tonic-gate static int 3572*0Sstevel@tonic-gate pm_next_lower_power(pm_component_t *cp, int pwrndx) 3573*0Sstevel@tonic-gate { 3574*0Sstevel@tonic-gate int nxt_pwr; 3575*0Sstevel@tonic-gate 3576*0Sstevel@tonic-gate if (pwrndx == PM_LEVEL_UNKNOWN) { 3577*0Sstevel@tonic-gate nxt_pwr = cp->pmc_comp.pmc_lvals[0]; 3578*0Sstevel@tonic-gate } else { 3579*0Sstevel@tonic-gate pwrndx--; 3580*0Sstevel@tonic-gate ASSERT(pwrndx >= 0); 3581*0Sstevel@tonic-gate nxt_pwr = cp->pmc_comp.pmc_lvals[pwrndx]; 3582*0Sstevel@tonic-gate } 3583*0Sstevel@tonic-gate return (nxt_pwr); 3584*0Sstevel@tonic-gate } 3585*0Sstevel@tonic-gate 3586*0Sstevel@tonic-gate /* 3587*0Sstevel@tonic-gate * Bring all components of device to normal power 3588*0Sstevel@tonic-gate */ 3589*0Sstevel@tonic-gate int 3590*0Sstevel@tonic-gate pm_all_to_normal(dev_info_t *dip, pm_canblock_t canblock) 3591*0Sstevel@tonic-gate { 3592*0Sstevel@tonic-gate PMD_FUNC(pmf, "all_to_normal") 3593*0Sstevel@tonic-gate int *normal; 3594*0Sstevel@tonic-gate int i, ncomps, result; 3595*0Sstevel@tonic-gate size_t size; 3596*0Sstevel@tonic-gate int changefailed = 0; 3597*0Sstevel@tonic-gate 3598*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3599*0Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip)); 3600*0Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) { 3601*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get norm pwrs for " 3602*0Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3603*0Sstevel@tonic-gate return (DDI_FAILURE); 3604*0Sstevel@tonic-gate } 3605*0Sstevel@tonic-gate ncomps = PM_NUMCMPTS(dip); 3606*0Sstevel@tonic-gate for (i = 0; i < ncomps; i++) { 3607*0Sstevel@tonic-gate if (pm_set_power(dip, i, normal[i], 3608*0Sstevel@tonic-gate PM_LEVEL_UPONLY, canblock, 0, &result) != DDI_SUCCESS) { 3609*0Sstevel@tonic-gate changefailed++; 3610*0Sstevel@tonic-gate PMD(PMD_ALLNORM | PMD_FAIL, ("%s: failed to set " 3611*0Sstevel@tonic-gate "%s@%s(%s#%d)[%d] to %d, errno %d\n", pmf, 3612*0Sstevel@tonic-gate PM_DEVICE(dip), i, normal[i], result)) 3613*0Sstevel@tonic-gate } 3614*0Sstevel@tonic-gate } 3615*0Sstevel@tonic-gate kmem_free(normal, size); 3616*0Sstevel@tonic-gate if (changefailed) { 3617*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) " 3618*0Sstevel@tonic-gate "to full power\n", pmf, changefailed, PM_DEVICE(dip))) 3619*0Sstevel@tonic-gate return (DDI_FAILURE); 3620*0Sstevel@tonic-gate } 3621*0Sstevel@tonic-gate return (DDI_SUCCESS); 3622*0Sstevel@tonic-gate } 3623*0Sstevel@tonic-gate 3624*0Sstevel@tonic-gate /* 3625*0Sstevel@tonic-gate * Returns true if all components of device are at normal power 3626*0Sstevel@tonic-gate */ 3627*0Sstevel@tonic-gate int 3628*0Sstevel@tonic-gate pm_all_at_normal(dev_info_t *dip) 3629*0Sstevel@tonic-gate { 3630*0Sstevel@tonic-gate PMD_FUNC(pmf, "all_at_normal") 3631*0Sstevel@tonic-gate int *normal; 3632*0Sstevel@tonic-gate int i; 3633*0Sstevel@tonic-gate size_t size; 3634*0Sstevel@tonic-gate 3635*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 3636*0Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) { 3637*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get normal power\n", pmf)) 3638*0Sstevel@tonic-gate return (DDI_FAILURE); 3639*0Sstevel@tonic-gate } 3640*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 3641*0Sstevel@tonic-gate int current = PM_CURPOWER(dip, i); 3642*0Sstevel@tonic-gate if (normal[i] > current) { 3643*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d) comp=%d, " 3644*0Sstevel@tonic-gate "norm=%d, cur=%d\n", pmf, PM_DEVICE(dip), i, 3645*0Sstevel@tonic-gate normal[i], current)) 3646*0Sstevel@tonic-gate break; 3647*0Sstevel@tonic-gate } 3648*0Sstevel@tonic-gate } 3649*0Sstevel@tonic-gate kmem_free(normal, size); 3650*0Sstevel@tonic-gate if (i != PM_NUMCMPTS(dip)) { 3651*0Sstevel@tonic-gate return (0); 3652*0Sstevel@tonic-gate } 3653*0Sstevel@tonic-gate return (1); 3654*0Sstevel@tonic-gate } 3655*0Sstevel@tonic-gate 3656*0Sstevel@tonic-gate static void 3657*0Sstevel@tonic-gate bring_wekeeps_up(char *keeper) 3658*0Sstevel@tonic-gate { 3659*0Sstevel@tonic-gate PMD_FUNC(pmf, "bring_wekeeps_up") 3660*0Sstevel@tonic-gate int i; 3661*0Sstevel@tonic-gate pm_pdr_t *dp; 3662*0Sstevel@tonic-gate pm_info_t *wku_info; 3663*0Sstevel@tonic-gate char *kept_path; 3664*0Sstevel@tonic-gate dev_info_t *kept; 3665*0Sstevel@tonic-gate static void bring_pmdep_up(dev_info_t *, int); 3666*0Sstevel@tonic-gate 3667*0Sstevel@tonic-gate if (panicstr) { 3668*0Sstevel@tonic-gate return; 3669*0Sstevel@tonic-gate } 3670*0Sstevel@tonic-gate /* 3671*0Sstevel@tonic-gate * We process the request even if the keeper detaches because 3672*0Sstevel@tonic-gate * detach processing expects this to increment kidsupcnt of kept. 3673*0Sstevel@tonic-gate */ 3674*0Sstevel@tonic-gate PMD(PMD_BRING, ("%s: keeper= %s\n", pmf, keeper)) 3675*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 3676*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0) 3677*0Sstevel@tonic-gate continue; 3678*0Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 3679*0Sstevel@tonic-gate kept_path = dp->pdr_kept_paths[i]; 3680*0Sstevel@tonic-gate if (kept_path == NULL) 3681*0Sstevel@tonic-gate continue; 3682*0Sstevel@tonic-gate ASSERT(kept_path[0] != '\0'); 3683*0Sstevel@tonic-gate if ((kept = pm_name_to_dip(kept_path, 1)) == NULL) 3684*0Sstevel@tonic-gate continue; 3685*0Sstevel@tonic-gate wku_info = PM_GET_PM_INFO(kept); 3686*0Sstevel@tonic-gate if (wku_info == NULL) { 3687*0Sstevel@tonic-gate if (kept) 3688*0Sstevel@tonic-gate ddi_release_devi(kept); 3689*0Sstevel@tonic-gate continue; 3690*0Sstevel@tonic-gate } 3691*0Sstevel@tonic-gate /* 3692*0Sstevel@tonic-gate * Don't mess with it if it is being detached, it isn't 3693*0Sstevel@tonic-gate * safe to call its power entry point 3694*0Sstevel@tonic-gate */ 3695*0Sstevel@tonic-gate if (wku_info->pmi_dev_pm_state & PM_DETACHING) { 3696*0Sstevel@tonic-gate if (kept) 3697*0Sstevel@tonic-gate ddi_release_devi(kept); 3698*0Sstevel@tonic-gate continue; 3699*0Sstevel@tonic-gate } 3700*0Sstevel@tonic-gate bring_pmdep_up(kept, 1); 3701*0Sstevel@tonic-gate ddi_release_devi(kept); 3702*0Sstevel@tonic-gate } 3703*0Sstevel@tonic-gate } 3704*0Sstevel@tonic-gate } 3705*0Sstevel@tonic-gate 3706*0Sstevel@tonic-gate /* 3707*0Sstevel@tonic-gate * Bring up the 'kept' device passed as argument 3708*0Sstevel@tonic-gate */ 3709*0Sstevel@tonic-gate static void 3710*0Sstevel@tonic-gate bring_pmdep_up(dev_info_t *kept_dip, int hold) 3711*0Sstevel@tonic-gate { 3712*0Sstevel@tonic-gate PMD_FUNC(pmf, "bring_pmdep_up") 3713*0Sstevel@tonic-gate int is_all_at_normal = 0; 3714*0Sstevel@tonic-gate 3715*0Sstevel@tonic-gate /* 3716*0Sstevel@tonic-gate * If the kept device has been unmanaged, do nothing. 3717*0Sstevel@tonic-gate */ 3718*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(kept_dip)) 3719*0Sstevel@tonic-gate return; 3720*0Sstevel@tonic-gate 3721*0Sstevel@tonic-gate /* Just ignore DIRECT PM device till they are released. */ 3722*0Sstevel@tonic-gate if (!pm_processes_stopped && PM_ISDIRECT(kept_dip) && 3723*0Sstevel@tonic-gate !(is_all_at_normal = pm_all_at_normal(kept_dip))) { 3724*0Sstevel@tonic-gate PMD(PMD_BRING, ("%s: can't bring up PM_DIRECT %s@%s(%s#%d) " 3725*0Sstevel@tonic-gate "controlling process did something else\n", pmf, 3726*0Sstevel@tonic-gate PM_DEVICE(kept_dip))) 3727*0Sstevel@tonic-gate DEVI(kept_dip)->devi_pm_flags |= PMC_SKIP_BRINGUP; 3728*0Sstevel@tonic-gate return; 3729*0Sstevel@tonic-gate } 3730*0Sstevel@tonic-gate /* if we got here the keeper had a transition from OFF->ON */ 3731*0Sstevel@tonic-gate if (hold) 3732*0Sstevel@tonic-gate pm_hold_power(kept_dip); 3733*0Sstevel@tonic-gate 3734*0Sstevel@tonic-gate if (!is_all_at_normal) 3735*0Sstevel@tonic-gate (void) pm_all_to_normal(kept_dip, PM_CANBLOCK_FAIL); 3736*0Sstevel@tonic-gate } 3737*0Sstevel@tonic-gate 3738*0Sstevel@tonic-gate /* 3739*0Sstevel@tonic-gate * A bunch of stuff that belongs only to the next routine (or two) 3740*0Sstevel@tonic-gate */ 3741*0Sstevel@tonic-gate 3742*0Sstevel@tonic-gate static const char namestr[] = "NAME="; 3743*0Sstevel@tonic-gate static const int nameln = sizeof (namestr) - 1; 3744*0Sstevel@tonic-gate static const char pmcompstr[] = "pm-components"; 3745*0Sstevel@tonic-gate 3746*0Sstevel@tonic-gate struct pm_comp_pkg { 3747*0Sstevel@tonic-gate pm_comp_t *comp; 3748*0Sstevel@tonic-gate struct pm_comp_pkg *next; 3749*0Sstevel@tonic-gate }; 3750*0Sstevel@tonic-gate 3751*0Sstevel@tonic-gate #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 3752*0Sstevel@tonic-gate 3753*0Sstevel@tonic-gate #define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \ 3754*0Sstevel@tonic-gate ((ch) >= 'A' && (ch) <= 'F')) 3755*0Sstevel@tonic-gate 3756*0Sstevel@tonic-gate /* 3757*0Sstevel@tonic-gate * Rather than duplicate this code ... 3758*0Sstevel@tonic-gate * (this code excerpted from the function that follows it) 3759*0Sstevel@tonic-gate */ 3760*0Sstevel@tonic-gate #define FINISH_COMP { \ 3761*0Sstevel@tonic-gate ASSERT(compp); \ 3762*0Sstevel@tonic-gate compp->pmc_lnames_sz = size; \ 3763*0Sstevel@tonic-gate tp = compp->pmc_lname_buf = kmem_alloc(size, KM_SLEEP); \ 3764*0Sstevel@tonic-gate compp->pmc_numlevels = level; \ 3765*0Sstevel@tonic-gate compp->pmc_lnames = kmem_alloc(level * sizeof (char *), KM_SLEEP); \ 3766*0Sstevel@tonic-gate compp->pmc_lvals = kmem_alloc(level * sizeof (int), KM_SLEEP); \ 3767*0Sstevel@tonic-gate compp->pmc_thresh = kmem_alloc(level * sizeof (int), KM_SLEEP); \ 3768*0Sstevel@tonic-gate /* copy string out of prop array into buffer */ \ 3769*0Sstevel@tonic-gate for (j = 0; j < level; j++) { \ 3770*0Sstevel@tonic-gate compp->pmc_thresh[j] = INT_MAX; /* only [0] sticks */ \ 3771*0Sstevel@tonic-gate compp->pmc_lvals[j] = lvals[j]; \ 3772*0Sstevel@tonic-gate (void) strcpy(tp, lnames[j]); \ 3773*0Sstevel@tonic-gate compp->pmc_lnames[j] = tp; \ 3774*0Sstevel@tonic-gate tp += lszs[j]; \ 3775*0Sstevel@tonic-gate } \ 3776*0Sstevel@tonic-gate ASSERT(tp > compp->pmc_lname_buf && tp <= \ 3777*0Sstevel@tonic-gate compp->pmc_lname_buf + compp->pmc_lnames_sz); \ 3778*0Sstevel@tonic-gate } 3779*0Sstevel@tonic-gate 3780*0Sstevel@tonic-gate /* 3781*0Sstevel@tonic-gate * Create (empty) component data structures. 3782*0Sstevel@tonic-gate */ 3783*0Sstevel@tonic-gate static void 3784*0Sstevel@tonic-gate e_pm_create_components(dev_info_t *dip, int num_components) 3785*0Sstevel@tonic-gate { 3786*0Sstevel@tonic-gate struct pm_component *compp, *ocompp; 3787*0Sstevel@tonic-gate int i, size = 0; 3788*0Sstevel@tonic-gate 3789*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3790*0Sstevel@tonic-gate ASSERT(!DEVI(dip)->devi_pm_components); 3791*0Sstevel@tonic-gate ASSERT(!(DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE)); 3792*0Sstevel@tonic-gate size = sizeof (struct pm_component) * num_components; 3793*0Sstevel@tonic-gate 3794*0Sstevel@tonic-gate compp = kmem_zalloc(size, KM_SLEEP); 3795*0Sstevel@tonic-gate ocompp = compp; 3796*0Sstevel@tonic-gate DEVI(dip)->devi_pm_comp_size = size; 3797*0Sstevel@tonic-gate DEVI(dip)->devi_pm_num_components = num_components; 3798*0Sstevel@tonic-gate PM_LOCK_BUSY(dip); 3799*0Sstevel@tonic-gate for (i = 0; i < num_components; i++) { 3800*0Sstevel@tonic-gate compp->pmc_timestamp = gethrestime_sec(); 3801*0Sstevel@tonic-gate compp->pmc_norm_pwr = (uint_t)-1; 3802*0Sstevel@tonic-gate compp++; 3803*0Sstevel@tonic-gate } 3804*0Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 3805*0Sstevel@tonic-gate DEVI(dip)->devi_pm_components = ocompp; 3806*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_DONE; 3807*0Sstevel@tonic-gate } 3808*0Sstevel@tonic-gate 3809*0Sstevel@tonic-gate /* 3810*0Sstevel@tonic-gate * Parse hex or decimal value from char string 3811*0Sstevel@tonic-gate */ 3812*0Sstevel@tonic-gate static char * 3813*0Sstevel@tonic-gate pm_parsenum(char *cp, int *valp) 3814*0Sstevel@tonic-gate { 3815*0Sstevel@tonic-gate int ch, offset; 3816*0Sstevel@tonic-gate char numbuf[256]; 3817*0Sstevel@tonic-gate char *np = numbuf; 3818*0Sstevel@tonic-gate int value = 0; 3819*0Sstevel@tonic-gate 3820*0Sstevel@tonic-gate ch = *cp++; 3821*0Sstevel@tonic-gate if (isdigit(ch)) { 3822*0Sstevel@tonic-gate if (ch == '0') { 3823*0Sstevel@tonic-gate if ((ch = *cp++) == 'x' || ch == 'X') { 3824*0Sstevel@tonic-gate ch = *cp++; 3825*0Sstevel@tonic-gate while (isxdigit(ch)) { 3826*0Sstevel@tonic-gate *np++ = (char)ch; 3827*0Sstevel@tonic-gate ch = *cp++; 3828*0Sstevel@tonic-gate } 3829*0Sstevel@tonic-gate *np = 0; 3830*0Sstevel@tonic-gate cp--; 3831*0Sstevel@tonic-gate goto hexval; 3832*0Sstevel@tonic-gate } else { 3833*0Sstevel@tonic-gate goto digit; 3834*0Sstevel@tonic-gate } 3835*0Sstevel@tonic-gate } else { 3836*0Sstevel@tonic-gate digit: 3837*0Sstevel@tonic-gate while (isdigit(ch)) { 3838*0Sstevel@tonic-gate *np++ = (char)ch; 3839*0Sstevel@tonic-gate ch = *cp++; 3840*0Sstevel@tonic-gate } 3841*0Sstevel@tonic-gate *np = 0; 3842*0Sstevel@tonic-gate cp--; 3843*0Sstevel@tonic-gate goto decval; 3844*0Sstevel@tonic-gate } 3845*0Sstevel@tonic-gate } else 3846*0Sstevel@tonic-gate return (NULL); 3847*0Sstevel@tonic-gate 3848*0Sstevel@tonic-gate hexval: 3849*0Sstevel@tonic-gate for (np = numbuf; *np; np++) { 3850*0Sstevel@tonic-gate if (*np >= 'a' && *np <= 'f') 3851*0Sstevel@tonic-gate offset = 'a' - 10; 3852*0Sstevel@tonic-gate else if (*np >= 'A' && *np <= 'F') 3853*0Sstevel@tonic-gate offset = 'A' - 10; 3854*0Sstevel@tonic-gate else if (*np >= '0' && *np <= '9') 3855*0Sstevel@tonic-gate offset = '0'; 3856*0Sstevel@tonic-gate value *= 16; 3857*0Sstevel@tonic-gate value += *np - offset; 3858*0Sstevel@tonic-gate } 3859*0Sstevel@tonic-gate *valp = value; 3860*0Sstevel@tonic-gate return (cp); 3861*0Sstevel@tonic-gate 3862*0Sstevel@tonic-gate decval: 3863*0Sstevel@tonic-gate offset = '0'; 3864*0Sstevel@tonic-gate for (np = numbuf; *np; np++) { 3865*0Sstevel@tonic-gate value *= 10; 3866*0Sstevel@tonic-gate value += *np - offset; 3867*0Sstevel@tonic-gate } 3868*0Sstevel@tonic-gate *valp = value; 3869*0Sstevel@tonic-gate return (cp); 3870*0Sstevel@tonic-gate } 3871*0Sstevel@tonic-gate 3872*0Sstevel@tonic-gate /* 3873*0Sstevel@tonic-gate * Set max (previously documented as "normal") power. 3874*0Sstevel@tonic-gate */ 3875*0Sstevel@tonic-gate static void 3876*0Sstevel@tonic-gate e_pm_set_max_power(dev_info_t *dip, int component_number, int level) 3877*0Sstevel@tonic-gate { 3878*0Sstevel@tonic-gate PM_CP(dip, component_number)->pmc_norm_pwr = level; 3879*0Sstevel@tonic-gate } 3880*0Sstevel@tonic-gate 3881*0Sstevel@tonic-gate /* 3882*0Sstevel@tonic-gate * Internal routine for destroying components 3883*0Sstevel@tonic-gate * It is called even when there might not be any, so it must be forgiving. 3884*0Sstevel@tonic-gate */ 3885*0Sstevel@tonic-gate static void 3886*0Sstevel@tonic-gate e_pm_destroy_components(dev_info_t *dip) 3887*0Sstevel@tonic-gate { 3888*0Sstevel@tonic-gate int i; 3889*0Sstevel@tonic-gate struct pm_component *cp; 3890*0Sstevel@tonic-gate 3891*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3892*0Sstevel@tonic-gate if (PM_NUMCMPTS(dip) == 0) 3893*0Sstevel@tonic-gate return; 3894*0Sstevel@tonic-gate cp = DEVI(dip)->devi_pm_components; 3895*0Sstevel@tonic-gate ASSERT(cp); 3896*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++, cp++) { 3897*0Sstevel@tonic-gate int nlevels = cp->pmc_comp.pmc_numlevels; 3898*0Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lvals, nlevels * sizeof (int)); 3899*0Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_thresh, nlevels * sizeof (int)); 3900*0Sstevel@tonic-gate /* 3901*0Sstevel@tonic-gate * For BC nodes, the rest is static in bc_comp, so skip it 3902*0Sstevel@tonic-gate */ 3903*0Sstevel@tonic-gate if (PM_ISBC(dip)) 3904*0Sstevel@tonic-gate continue; 3905*0Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_name, cp->pmc_comp.pmc_name_sz); 3906*0Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lnames, nlevels * sizeof (char *)); 3907*0Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lname_buf, 3908*0Sstevel@tonic-gate cp->pmc_comp.pmc_lnames_sz); 3909*0Sstevel@tonic-gate } 3910*0Sstevel@tonic-gate kmem_free(DEVI(dip)->devi_pm_components, DEVI(dip)->devi_pm_comp_size); 3911*0Sstevel@tonic-gate DEVI(dip)->devi_pm_components = NULL; 3912*0Sstevel@tonic-gate DEVI(dip)->devi_pm_num_components = 0; 3913*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= 3914*0Sstevel@tonic-gate ~(PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED); 3915*0Sstevel@tonic-gate } 3916*0Sstevel@tonic-gate 3917*0Sstevel@tonic-gate /* 3918*0Sstevel@tonic-gate * Read the pm-components property (if there is one) and use it to set up 3919*0Sstevel@tonic-gate * components. Returns a pointer to an array of component structures if 3920*0Sstevel@tonic-gate * pm-components found and successfully parsed, else returns NULL. 3921*0Sstevel@tonic-gate * Sets error return *errp to true to indicate a failure (as opposed to no 3922*0Sstevel@tonic-gate * property being present). 3923*0Sstevel@tonic-gate */ 3924*0Sstevel@tonic-gate pm_comp_t * 3925*0Sstevel@tonic-gate pm_autoconfig(dev_info_t *dip, int *errp) 3926*0Sstevel@tonic-gate { 3927*0Sstevel@tonic-gate PMD_FUNC(pmf, "autoconfig") 3928*0Sstevel@tonic-gate uint_t nelems; 3929*0Sstevel@tonic-gate char **pp; 3930*0Sstevel@tonic-gate pm_comp_t *compp = NULL; 3931*0Sstevel@tonic-gate int i, j, level, components = 0; 3932*0Sstevel@tonic-gate size_t size = 0; 3933*0Sstevel@tonic-gate struct pm_comp_pkg *p, *ptail; 3934*0Sstevel@tonic-gate struct pm_comp_pkg *phead = NULL; 3935*0Sstevel@tonic-gate int *lvals = NULL; 3936*0Sstevel@tonic-gate int *lszs = NULL; 3937*0Sstevel@tonic-gate int *np = NULL; 3938*0Sstevel@tonic-gate int npi = 0; 3939*0Sstevel@tonic-gate char **lnames = NULL; 3940*0Sstevel@tonic-gate char *cp, *tp; 3941*0Sstevel@tonic-gate pm_comp_t *ret = NULL; 3942*0Sstevel@tonic-gate 3943*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 3944*0Sstevel@tonic-gate *errp = 0; /* assume success */ 3945*0Sstevel@tonic-gate if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 3946*0Sstevel@tonic-gate (char *)pmcompstr, &pp, &nelems) != DDI_PROP_SUCCESS) { 3947*0Sstevel@tonic-gate return (NULL); 3948*0Sstevel@tonic-gate } 3949*0Sstevel@tonic-gate 3950*0Sstevel@tonic-gate if (nelems < 3) { /* need at least one name and two levels */ 3951*0Sstevel@tonic-gate goto errout; 3952*0Sstevel@tonic-gate } 3953*0Sstevel@tonic-gate 3954*0Sstevel@tonic-gate /* 3955*0Sstevel@tonic-gate * pm_create_components is no longer allowed 3956*0Sstevel@tonic-gate */ 3957*0Sstevel@tonic-gate if (PM_NUMCMPTS(dip) != 0) { 3958*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) has %d comps\n", 3959*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_NUMCMPTS(dip))) 3960*0Sstevel@tonic-gate goto errout; 3961*0Sstevel@tonic-gate } 3962*0Sstevel@tonic-gate 3963*0Sstevel@tonic-gate lvals = kmem_alloc(nelems * sizeof (int), KM_SLEEP); 3964*0Sstevel@tonic-gate lszs = kmem_alloc(nelems * sizeof (int), KM_SLEEP); 3965*0Sstevel@tonic-gate lnames = kmem_alloc(nelems * sizeof (char *), KM_SLEEP); 3966*0Sstevel@tonic-gate np = kmem_alloc(nelems * sizeof (int), KM_SLEEP); 3967*0Sstevel@tonic-gate 3968*0Sstevel@tonic-gate level = 0; 3969*0Sstevel@tonic-gate phead = NULL; 3970*0Sstevel@tonic-gate for (i = 0; i < nelems; i++) { 3971*0Sstevel@tonic-gate cp = pp[i]; 3972*0Sstevel@tonic-gate if (!isdigit(*cp)) { /* must be name */ 3973*0Sstevel@tonic-gate if (strncmp(cp, namestr, nameln) != 0) { 3974*0Sstevel@tonic-gate goto errout; 3975*0Sstevel@tonic-gate } 3976*0Sstevel@tonic-gate if (i != 0) { 3977*0Sstevel@tonic-gate if (level == 0) { /* no level spec'd */ 3978*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: no level spec'd\n", 3979*0Sstevel@tonic-gate pmf)) 3980*0Sstevel@tonic-gate goto errout; 3981*0Sstevel@tonic-gate } 3982*0Sstevel@tonic-gate np[npi++] = lvals[level - 1]; 3983*0Sstevel@tonic-gate /* finish up previous component levels */ 3984*0Sstevel@tonic-gate FINISH_COMP; 3985*0Sstevel@tonic-gate } 3986*0Sstevel@tonic-gate cp += nameln; 3987*0Sstevel@tonic-gate if (!*cp) { 3988*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: nsa\n", pmf)) 3989*0Sstevel@tonic-gate goto errout; 3990*0Sstevel@tonic-gate } 3991*0Sstevel@tonic-gate p = kmem_zalloc(sizeof (*phead), KM_SLEEP); 3992*0Sstevel@tonic-gate if (phead == NULL) { 3993*0Sstevel@tonic-gate phead = ptail = p; 3994*0Sstevel@tonic-gate } else { 3995*0Sstevel@tonic-gate ptail->next = p; 3996*0Sstevel@tonic-gate ptail = p; 3997*0Sstevel@tonic-gate } 3998*0Sstevel@tonic-gate compp = p->comp = kmem_zalloc(sizeof (pm_comp_t), 3999*0Sstevel@tonic-gate KM_SLEEP); 4000*0Sstevel@tonic-gate compp->pmc_name_sz = strlen(cp) + 1; 4001*0Sstevel@tonic-gate compp->pmc_name = kmem_zalloc(compp->pmc_name_sz, 4002*0Sstevel@tonic-gate KM_SLEEP); 4003*0Sstevel@tonic-gate (void) strncpy(compp->pmc_name, cp, compp->pmc_name_sz); 4004*0Sstevel@tonic-gate components++; 4005*0Sstevel@tonic-gate level = 0; 4006*0Sstevel@tonic-gate } else { /* better be power level <num>=<name> */ 4007*0Sstevel@tonic-gate #ifdef DEBUG 4008*0Sstevel@tonic-gate tp = cp; 4009*0Sstevel@tonic-gate #endif 4010*0Sstevel@tonic-gate if (i == 0 || 4011*0Sstevel@tonic-gate (cp = pm_parsenum(cp, &lvals[level])) == NULL) { 4012*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: parsenum(%s)\n", pmf, tp)) 4013*0Sstevel@tonic-gate goto errout; 4014*0Sstevel@tonic-gate } 4015*0Sstevel@tonic-gate #ifdef DEBUG 4016*0Sstevel@tonic-gate tp = cp; 4017*0Sstevel@tonic-gate #endif 4018*0Sstevel@tonic-gate if (*cp++ != '=' || !*cp) { 4019*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: ex =, got %s\n", pmf, tp)) 4020*0Sstevel@tonic-gate goto errout; 4021*0Sstevel@tonic-gate } 4022*0Sstevel@tonic-gate 4023*0Sstevel@tonic-gate lszs[level] = strlen(cp) + 1; 4024*0Sstevel@tonic-gate size += lszs[level]; 4025*0Sstevel@tonic-gate lnames[level] = cp; /* points into prop string */ 4026*0Sstevel@tonic-gate level++; 4027*0Sstevel@tonic-gate } 4028*0Sstevel@tonic-gate } 4029*0Sstevel@tonic-gate np[npi++] = lvals[level - 1]; 4030*0Sstevel@tonic-gate if (level == 0) { /* ended with a name */ 4031*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: ewn\n", pmf)) 4032*0Sstevel@tonic-gate goto errout; 4033*0Sstevel@tonic-gate } 4034*0Sstevel@tonic-gate FINISH_COMP; 4035*0Sstevel@tonic-gate 4036*0Sstevel@tonic-gate 4037*0Sstevel@tonic-gate /* 4038*0Sstevel@tonic-gate * Now we have a list of components--we have to return instead an 4039*0Sstevel@tonic-gate * array of them, but we can just copy the top level and leave 4040*0Sstevel@tonic-gate * the rest as is 4041*0Sstevel@tonic-gate */ 4042*0Sstevel@tonic-gate (void) e_pm_create_components(dip, components); 4043*0Sstevel@tonic-gate for (i = 0; i < components; i++) 4044*0Sstevel@tonic-gate e_pm_set_max_power(dip, i, np[i]); 4045*0Sstevel@tonic-gate 4046*0Sstevel@tonic-gate ret = kmem_zalloc(components * sizeof (pm_comp_t), KM_SLEEP); 4047*0Sstevel@tonic-gate for (i = 0, p = phead; i < components; i++) { 4048*0Sstevel@tonic-gate ASSERT(p); 4049*0Sstevel@tonic-gate /* 4050*0Sstevel@tonic-gate * Now sanity-check values: levels must be monotonically 4051*0Sstevel@tonic-gate * increasing 4052*0Sstevel@tonic-gate */ 4053*0Sstevel@tonic-gate if (p->comp->pmc_numlevels < 2) { 4054*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) only %d " 4055*0Sstevel@tonic-gate "levels\n", pmf, 4056*0Sstevel@tonic-gate p->comp->pmc_name, PM_DEVICE(dip), 4057*0Sstevel@tonic-gate p->comp->pmc_numlevels)) 4058*0Sstevel@tonic-gate goto errout; 4059*0Sstevel@tonic-gate } 4060*0Sstevel@tonic-gate for (j = 0; j < p->comp->pmc_numlevels; j++) { 4061*0Sstevel@tonic-gate if ((p->comp->pmc_lvals[j] < 0) || ((j > 0) && 4062*0Sstevel@tonic-gate (p->comp->pmc_lvals[j] <= 4063*0Sstevel@tonic-gate p->comp->pmc_lvals[j - 1]))) { 4064*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) " 4065*0Sstevel@tonic-gate "not mono. incr, %d follows %d\n", pmf, 4066*0Sstevel@tonic-gate p->comp->pmc_name, PM_DEVICE(dip), 4067*0Sstevel@tonic-gate p->comp->pmc_lvals[j], 4068*0Sstevel@tonic-gate p->comp->pmc_lvals[j - 1])) 4069*0Sstevel@tonic-gate goto errout; 4070*0Sstevel@tonic-gate } 4071*0Sstevel@tonic-gate } 4072*0Sstevel@tonic-gate ret[i] = *p->comp; /* struct assignment */ 4073*0Sstevel@tonic-gate for (j = 0; j < i; j++) { 4074*0Sstevel@tonic-gate /* 4075*0Sstevel@tonic-gate * Test for unique component names 4076*0Sstevel@tonic-gate */ 4077*0Sstevel@tonic-gate if (strcmp(ret[j].pmc_name, ret[i].pmc_name) == 0) { 4078*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s of %s@%s(%s#%d) not " 4079*0Sstevel@tonic-gate "unique\n", pmf, ret[j].pmc_name, 4080*0Sstevel@tonic-gate PM_DEVICE(dip))) 4081*0Sstevel@tonic-gate goto errout; 4082*0Sstevel@tonic-gate } 4083*0Sstevel@tonic-gate } 4084*0Sstevel@tonic-gate ptail = p; 4085*0Sstevel@tonic-gate p = p->next; 4086*0Sstevel@tonic-gate phead = p; /* errout depends on phead making sense */ 4087*0Sstevel@tonic-gate kmem_free(ptail->comp, sizeof (*ptail->comp)); 4088*0Sstevel@tonic-gate kmem_free(ptail, sizeof (*ptail)); 4089*0Sstevel@tonic-gate } 4090*0Sstevel@tonic-gate out: 4091*0Sstevel@tonic-gate ddi_prop_free(pp); 4092*0Sstevel@tonic-gate if (lvals) 4093*0Sstevel@tonic-gate kmem_free(lvals, nelems * sizeof (int)); 4094*0Sstevel@tonic-gate if (lszs) 4095*0Sstevel@tonic-gate kmem_free(lszs, nelems * sizeof (int)); 4096*0Sstevel@tonic-gate if (lnames) 4097*0Sstevel@tonic-gate kmem_free(lnames, nelems * sizeof (char *)); 4098*0Sstevel@tonic-gate if (np) 4099*0Sstevel@tonic-gate kmem_free(np, nelems * sizeof (int)); 4100*0Sstevel@tonic-gate return (ret); 4101*0Sstevel@tonic-gate 4102*0Sstevel@tonic-gate errout: 4103*0Sstevel@tonic-gate e_pm_destroy_components(dip); 4104*0Sstevel@tonic-gate *errp = 1; /* signal failure */ 4105*0Sstevel@tonic-gate cmn_err(CE_CONT, "!pm: %s property ", pmcompstr); 4106*0Sstevel@tonic-gate for (i = 0; i < nelems - 1; i++) 4107*0Sstevel@tonic-gate cmn_err(CE_CONT, "!'%s', ", pp[i]); 4108*0Sstevel@tonic-gate if (nelems != 0) 4109*0Sstevel@tonic-gate cmn_err(CE_CONT, "!'%s'", pp[nelems - 1]); 4110*0Sstevel@tonic-gate cmn_err(CE_CONT, "! for %s@%s(%s#%d) is ill-formed.\n", PM_DEVICE(dip)); 4111*0Sstevel@tonic-gate for (p = phead; p; ) { 4112*0Sstevel@tonic-gate pm_comp_t *pp; 4113*0Sstevel@tonic-gate int n; 4114*0Sstevel@tonic-gate 4115*0Sstevel@tonic-gate ptail = p; 4116*0Sstevel@tonic-gate /* 4117*0Sstevel@tonic-gate * Free component data structures 4118*0Sstevel@tonic-gate */ 4119*0Sstevel@tonic-gate pp = p->comp; 4120*0Sstevel@tonic-gate n = pp->pmc_numlevels; 4121*0Sstevel@tonic-gate if (pp->pmc_name_sz) { 4122*0Sstevel@tonic-gate kmem_free(pp->pmc_name, pp->pmc_name_sz); 4123*0Sstevel@tonic-gate } 4124*0Sstevel@tonic-gate if (pp->pmc_lnames_sz) { 4125*0Sstevel@tonic-gate kmem_free(pp->pmc_lname_buf, pp->pmc_lnames_sz); 4126*0Sstevel@tonic-gate } 4127*0Sstevel@tonic-gate if (pp->pmc_lnames) { 4128*0Sstevel@tonic-gate kmem_free(pp->pmc_lnames, n * (sizeof (char *))); 4129*0Sstevel@tonic-gate } 4130*0Sstevel@tonic-gate if (pp->pmc_thresh) { 4131*0Sstevel@tonic-gate kmem_free(pp->pmc_thresh, n * (sizeof (int))); 4132*0Sstevel@tonic-gate } 4133*0Sstevel@tonic-gate if (pp->pmc_lvals) { 4134*0Sstevel@tonic-gate kmem_free(pp->pmc_lvals, n * (sizeof (int))); 4135*0Sstevel@tonic-gate } 4136*0Sstevel@tonic-gate p = ptail->next; 4137*0Sstevel@tonic-gate kmem_free(ptail, sizeof (*ptail)); 4138*0Sstevel@tonic-gate } 4139*0Sstevel@tonic-gate if (ret != NULL) 4140*0Sstevel@tonic-gate kmem_free(ret, components * sizeof (pm_comp_t)); 4141*0Sstevel@tonic-gate ret = NULL; 4142*0Sstevel@tonic-gate goto out; 4143*0Sstevel@tonic-gate } 4144*0Sstevel@tonic-gate 4145*0Sstevel@tonic-gate /* 4146*0Sstevel@tonic-gate * Set threshold values for a devices components by dividing the target 4147*0Sstevel@tonic-gate * threshold (base) by the number of transitions and assign each transition 4148*0Sstevel@tonic-gate * that threshold. This will get the entire device down in the target time if 4149*0Sstevel@tonic-gate * all components are idle and even if there are dependencies among components. 4150*0Sstevel@tonic-gate * 4151*0Sstevel@tonic-gate * Devices may well get powered all the way down before the target time, but 4152*0Sstevel@tonic-gate * at least the EPA will be happy. 4153*0Sstevel@tonic-gate */ 4154*0Sstevel@tonic-gate void 4155*0Sstevel@tonic-gate pm_set_device_threshold(dev_info_t *dip, int base, int flag) 4156*0Sstevel@tonic-gate { 4157*0Sstevel@tonic-gate PMD_FUNC(pmf, "set_device_threshold") 4158*0Sstevel@tonic-gate int target_threshold = (base * 95) / 100; 4159*0Sstevel@tonic-gate int level, comp; /* loop counters */ 4160*0Sstevel@tonic-gate int transitions = 0; 4161*0Sstevel@tonic-gate int ncomp = PM_NUMCMPTS(dip); 4162*0Sstevel@tonic-gate int thresh; 4163*0Sstevel@tonic-gate int remainder; 4164*0Sstevel@tonic-gate pm_comp_t *pmc; 4165*0Sstevel@tonic-gate int i, circ; 4166*0Sstevel@tonic-gate 4167*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 4168*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 4169*0Sstevel@tonic-gate /* 4170*0Sstevel@tonic-gate * First we handle the easy one. If we're setting the default 4171*0Sstevel@tonic-gate * threshold for a node with children, then we set it to the 4172*0Sstevel@tonic-gate * default nexus threshold (currently 0) and mark it as default 4173*0Sstevel@tonic-gate * nexus threshold instead 4174*0Sstevel@tonic-gate */ 4175*0Sstevel@tonic-gate if (PM_IS_NEXUS(dip)) { 4176*0Sstevel@tonic-gate if (flag == PMC_DEF_THRESH) { 4177*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: [%s@%s(%s#%d) NEXDEF]\n", pmf, 4178*0Sstevel@tonic-gate PM_DEVICE(dip))) 4179*0Sstevel@tonic-gate thresh = pm_default_nexus_threshold; 4180*0Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4181*0Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4182*0Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; 4183*0Sstevel@tonic-gate level++) { 4184*0Sstevel@tonic-gate pmc->pmc_thresh[level] = thresh; 4185*0Sstevel@tonic-gate } 4186*0Sstevel@tonic-gate } 4187*0Sstevel@tonic-gate DEVI(dip)->devi_pm_dev_thresh = 4188*0Sstevel@tonic-gate pm_default_nexus_threshold; 4189*0Sstevel@tonic-gate /* 4190*0Sstevel@tonic-gate * If the nexus node is being reconfigured back to 4191*0Sstevel@tonic-gate * the default threshold, adjust the notlowest count. 4192*0Sstevel@tonic-gate */ 4193*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & 4194*0Sstevel@tonic-gate (PMC_DEV_THRESH|PMC_COMP_THRESH)) { 4195*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 4196*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 4197*0Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == 0) 4198*0Sstevel@tonic-gate continue; 4199*0Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); 4200*0Sstevel@tonic-gate ASSERT(pm_comps_notlowest); 4201*0Sstevel@tonic-gate pm_comps_notlowest--; 4202*0Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr " 4203*0Sstevel@tonic-gate "notlowest to %d\n", pmf, 4204*0Sstevel@tonic-gate PM_DEVICE(dip), pm_comps_notlowest)) 4205*0Sstevel@tonic-gate if (pm_comps_notlowest == 0) 4206*0Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, 4207*0Sstevel@tonic-gate PM_ALL_LOWEST); 4208*0Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); 4209*0Sstevel@tonic-gate } 4210*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 4211*0Sstevel@tonic-gate } 4212*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE; 4213*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NEXDEF_THRESH; 4214*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 4215*0Sstevel@tonic-gate return; 4216*0Sstevel@tonic-gate } else if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) { 4217*0Sstevel@tonic-gate /* 4218*0Sstevel@tonic-gate * If the nexus node is being configured for a 4219*0Sstevel@tonic-gate * non-default threshold, include that node in 4220*0Sstevel@tonic-gate * the notlowest accounting. 4221*0Sstevel@tonic-gate */ 4222*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 4223*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 4224*0Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == 0) 4225*0Sstevel@tonic-gate continue; 4226*0Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); 4227*0Sstevel@tonic-gate if (pm_comps_notlowest == 0) 4228*0Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, 4229*0Sstevel@tonic-gate PM_NOT_ALL_LOWEST); 4230*0Sstevel@tonic-gate pm_comps_notlowest++; 4231*0Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr " 4232*0Sstevel@tonic-gate "notlowest to %d\n", pmf, 4233*0Sstevel@tonic-gate PM_DEVICE(dip), pm_comps_notlowest)) 4234*0Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); 4235*0Sstevel@tonic-gate } 4236*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 4237*0Sstevel@tonic-gate } 4238*0Sstevel@tonic-gate } 4239*0Sstevel@tonic-gate /* 4240*0Sstevel@tonic-gate * Compute the total number of transitions for all components 4241*0Sstevel@tonic-gate * of the device. Distribute the threshold evenly over them 4242*0Sstevel@tonic-gate */ 4243*0Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4244*0Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4245*0Sstevel@tonic-gate ASSERT(pmc->pmc_numlevels > 1); 4246*0Sstevel@tonic-gate transitions += pmc->pmc_numlevels - 1; 4247*0Sstevel@tonic-gate } 4248*0Sstevel@tonic-gate ASSERT(transitions); 4249*0Sstevel@tonic-gate thresh = target_threshold / transitions; 4250*0Sstevel@tonic-gate 4251*0Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4252*0Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4253*0Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) { 4254*0Sstevel@tonic-gate pmc->pmc_thresh[level] = thresh; 4255*0Sstevel@tonic-gate } 4256*0Sstevel@tonic-gate } 4257*0Sstevel@tonic-gate 4258*0Sstevel@tonic-gate #ifdef DEBUG 4259*0Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4260*0Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4261*0Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) { 4262*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: thresh before %s@%s(%s#%d) " 4263*0Sstevel@tonic-gate "comp=%d, level=%d, %d\n", pmf, PM_DEVICE(dip), 4264*0Sstevel@tonic-gate comp, level, pmc->pmc_thresh[level])) 4265*0Sstevel@tonic-gate } 4266*0Sstevel@tonic-gate } 4267*0Sstevel@tonic-gate #endif 4268*0Sstevel@tonic-gate /* 4269*0Sstevel@tonic-gate * Distribute any remainder till they are all gone 4270*0Sstevel@tonic-gate */ 4271*0Sstevel@tonic-gate remainder = target_threshold - thresh * transitions; 4272*0Sstevel@tonic-gate level = 1; 4273*0Sstevel@tonic-gate #ifdef DEBUG 4274*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: remainder=%d target_threshold=%d thresh=%d " 4275*0Sstevel@tonic-gate "trans=%d\n", pmf, remainder, target_threshold, thresh, 4276*0Sstevel@tonic-gate transitions)) 4277*0Sstevel@tonic-gate #endif 4278*0Sstevel@tonic-gate while (remainder > 0) { 4279*0Sstevel@tonic-gate comp = 0; 4280*0Sstevel@tonic-gate while (remainder && (comp < ncomp)) { 4281*0Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4282*0Sstevel@tonic-gate if (level < pmc->pmc_numlevels) { 4283*0Sstevel@tonic-gate pmc->pmc_thresh[level] += 1; 4284*0Sstevel@tonic-gate remainder--; 4285*0Sstevel@tonic-gate } 4286*0Sstevel@tonic-gate comp++; 4287*0Sstevel@tonic-gate } 4288*0Sstevel@tonic-gate level++; 4289*0Sstevel@tonic-gate } 4290*0Sstevel@tonic-gate #ifdef DEBUG 4291*0Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) { 4292*0Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp; 4293*0Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) { 4294*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: thresh after %s@%s(%s#%d) " 4295*0Sstevel@tonic-gate "comp=%d level=%d, %d\n", pmf, PM_DEVICE(dip), 4296*0Sstevel@tonic-gate comp, level, pmc->pmc_thresh[level])) 4297*0Sstevel@tonic-gate } 4298*0Sstevel@tonic-gate } 4299*0Sstevel@tonic-gate #endif 4300*0Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip)); 4301*0Sstevel@tonic-gate DEVI(dip)->devi_pm_dev_thresh = base; 4302*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE; 4303*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= flag; 4304*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 4305*0Sstevel@tonic-gate } 4306*0Sstevel@tonic-gate 4307*0Sstevel@tonic-gate /* 4308*0Sstevel@tonic-gate * Called when there is no old-style platform power management driver 4309*0Sstevel@tonic-gate */ 4310*0Sstevel@tonic-gate static int 4311*0Sstevel@tonic-gate ddi_no_platform_power(power_req_t *req) 4312*0Sstevel@tonic-gate { 4313*0Sstevel@tonic-gate _NOTE(ARGUNUSED(req)) 4314*0Sstevel@tonic-gate return (DDI_FAILURE); 4315*0Sstevel@tonic-gate } 4316*0Sstevel@tonic-gate 4317*0Sstevel@tonic-gate /* 4318*0Sstevel@tonic-gate * This function calls the entry point supplied by the platform-specific 4319*0Sstevel@tonic-gate * pm driver to bring the device component 'pm_cmpt' to power level 'pm_level'. 4320*0Sstevel@tonic-gate * The use of global for getting the function name from platform-specific 4321*0Sstevel@tonic-gate * pm driver is not ideal, but it is simple and efficient. 4322*0Sstevel@tonic-gate * The previous property lookup was being done in the idle loop on swift 4323*0Sstevel@tonic-gate * systems without pmc chips and hurt deskbench performance as well as 4324*0Sstevel@tonic-gate * violating scheduler locking rules 4325*0Sstevel@tonic-gate */ 4326*0Sstevel@tonic-gate int (*pm_platform_power)(power_req_t *) = ddi_no_platform_power; 4327*0Sstevel@tonic-gate 4328*0Sstevel@tonic-gate /* 4329*0Sstevel@tonic-gate * Old obsolete interface for a device to request a power change (but only 4330*0Sstevel@tonic-gate * an increase in power) 4331*0Sstevel@tonic-gate */ 4332*0Sstevel@tonic-gate int 4333*0Sstevel@tonic-gate ddi_dev_is_needed(dev_info_t *dip, int cmpt, int level) 4334*0Sstevel@tonic-gate { 4335*0Sstevel@tonic-gate return (pm_raise_power(dip, cmpt, level)); 4336*0Sstevel@tonic-gate } 4337*0Sstevel@tonic-gate 4338*0Sstevel@tonic-gate /* 4339*0Sstevel@tonic-gate * The old obsolete interface to platform power management. Only used by 4340*0Sstevel@tonic-gate * Gypsy platform and APM on X86. 4341*0Sstevel@tonic-gate */ 4342*0Sstevel@tonic-gate int 4343*0Sstevel@tonic-gate ddi_power(dev_info_t *dip, int pm_cmpt, int pm_level) 4344*0Sstevel@tonic-gate { 4345*0Sstevel@tonic-gate power_req_t request; 4346*0Sstevel@tonic-gate 4347*0Sstevel@tonic-gate request.request_type = PMR_SET_POWER; 4348*0Sstevel@tonic-gate request.req.set_power_req.who = dip; 4349*0Sstevel@tonic-gate request.req.set_power_req.cmpt = pm_cmpt; 4350*0Sstevel@tonic-gate request.req.set_power_req.level = pm_level; 4351*0Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL)); 4352*0Sstevel@tonic-gate } 4353*0Sstevel@tonic-gate 4354*0Sstevel@tonic-gate /* 4355*0Sstevel@tonic-gate * A driver can invoke this from its detach routine when DDI_SUSPEND is 4356*0Sstevel@tonic-gate * passed. Returns true if subsequent processing could result in power being 4357*0Sstevel@tonic-gate * removed from the device. The arg is not currently used because it is 4358*0Sstevel@tonic-gate * implicit in the operation of cpr/DR. 4359*0Sstevel@tonic-gate */ 4360*0Sstevel@tonic-gate int 4361*0Sstevel@tonic-gate ddi_removing_power(dev_info_t *dip) 4362*0Sstevel@tonic-gate { 4363*0Sstevel@tonic-gate _NOTE(ARGUNUSED(dip)) 4364*0Sstevel@tonic-gate return (pm_powering_down); 4365*0Sstevel@tonic-gate } 4366*0Sstevel@tonic-gate 4367*0Sstevel@tonic-gate /* 4368*0Sstevel@tonic-gate * Returns true if a device indicates that its parent handles suspend/resume 4369*0Sstevel@tonic-gate * processing for it. 4370*0Sstevel@tonic-gate */ 4371*0Sstevel@tonic-gate int 4372*0Sstevel@tonic-gate e_ddi_parental_suspend_resume(dev_info_t *dip) 4373*0Sstevel@tonic-gate { 4374*0Sstevel@tonic-gate return (DEVI(dip)->devi_pm_flags & PMC_PARENTAL_SR); 4375*0Sstevel@tonic-gate } 4376*0Sstevel@tonic-gate 4377*0Sstevel@tonic-gate /* 4378*0Sstevel@tonic-gate * Called for devices which indicate that their parent does suspend/resume 4379*0Sstevel@tonic-gate * handling for them 4380*0Sstevel@tonic-gate */ 4381*0Sstevel@tonic-gate int 4382*0Sstevel@tonic-gate e_ddi_suspend(dev_info_t *dip, ddi_detach_cmd_t cmd) 4383*0Sstevel@tonic-gate { 4384*0Sstevel@tonic-gate power_req_t request; 4385*0Sstevel@tonic-gate request.request_type = PMR_SUSPEND; 4386*0Sstevel@tonic-gate request.req.suspend_req.who = dip; 4387*0Sstevel@tonic-gate request.req.suspend_req.cmd = cmd; 4388*0Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL)); 4389*0Sstevel@tonic-gate } 4390*0Sstevel@tonic-gate 4391*0Sstevel@tonic-gate /* 4392*0Sstevel@tonic-gate * Called for devices which indicate that their parent does suspend/resume 4393*0Sstevel@tonic-gate * handling for them 4394*0Sstevel@tonic-gate */ 4395*0Sstevel@tonic-gate int 4396*0Sstevel@tonic-gate e_ddi_resume(dev_info_t *dip, ddi_attach_cmd_t cmd) 4397*0Sstevel@tonic-gate { 4398*0Sstevel@tonic-gate power_req_t request; 4399*0Sstevel@tonic-gate request.request_type = PMR_RESUME; 4400*0Sstevel@tonic-gate request.req.resume_req.who = dip; 4401*0Sstevel@tonic-gate request.req.resume_req.cmd = cmd; 4402*0Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL)); 4403*0Sstevel@tonic-gate } 4404*0Sstevel@tonic-gate 4405*0Sstevel@tonic-gate /* 4406*0Sstevel@tonic-gate * Old obsolete exported interface for drivers to create components. 4407*0Sstevel@tonic-gate * This is now handled by exporting the pm-components property. 4408*0Sstevel@tonic-gate */ 4409*0Sstevel@tonic-gate int 4410*0Sstevel@tonic-gate pm_create_components(dev_info_t *dip, int num_components) 4411*0Sstevel@tonic-gate { 4412*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_create_components") 4413*0Sstevel@tonic-gate 4414*0Sstevel@tonic-gate if (num_components < 1) 4415*0Sstevel@tonic-gate return (DDI_FAILURE); 4416*0Sstevel@tonic-gate 4417*0Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) { 4418*0Sstevel@tonic-gate return (DDI_FAILURE); 4419*0Sstevel@tonic-gate } 4420*0Sstevel@tonic-gate 4421*0Sstevel@tonic-gate /* don't need to lock dip because attach is single threaded */ 4422*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_components) { 4423*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) already has %d\n", pmf, 4424*0Sstevel@tonic-gate PM_DEVICE(dip), PM_NUMCMPTS(dip))) 4425*0Sstevel@tonic-gate return (DDI_FAILURE); 4426*0Sstevel@tonic-gate } 4427*0Sstevel@tonic-gate e_pm_create_components(dip, num_components); 4428*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_BC; 4429*0Sstevel@tonic-gate e_pm_default_components(dip, num_components); 4430*0Sstevel@tonic-gate return (DDI_SUCCESS); 4431*0Sstevel@tonic-gate } 4432*0Sstevel@tonic-gate 4433*0Sstevel@tonic-gate /* 4434*0Sstevel@tonic-gate * Obsolete interface previously called by drivers to destroy their components 4435*0Sstevel@tonic-gate * at detach time. This is now done automatically. However, we need to keep 4436*0Sstevel@tonic-gate * this for the old drivers. 4437*0Sstevel@tonic-gate */ 4438*0Sstevel@tonic-gate void 4439*0Sstevel@tonic-gate pm_destroy_components(dev_info_t *dip) 4440*0Sstevel@tonic-gate { 4441*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_destroy_components") 4442*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 4443*0Sstevel@tonic-gate 4444*0Sstevel@tonic-gate PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, 4445*0Sstevel@tonic-gate PM_DEVICE(dip))) 4446*0Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip)); 4447*0Sstevel@tonic-gate #ifdef DEBUG 4448*0Sstevel@tonic-gate if (!PM_ISBC(dip)) 4449*0Sstevel@tonic-gate cmn_err(CE_WARN, "!driver exporting pm-components property " 4450*0Sstevel@tonic-gate "(%s@%s) calls pm_destroy_components", PM_NAME(dip), 4451*0Sstevel@tonic-gate PM_ADDR(dip)); 4452*0Sstevel@tonic-gate #endif 4453*0Sstevel@tonic-gate /* 4454*0Sstevel@tonic-gate * We ignore this unless this is an old-style driver, except for 4455*0Sstevel@tonic-gate * printing the message above 4456*0Sstevel@tonic-gate */ 4457*0Sstevel@tonic-gate if (PM_NUMCMPTS(dip) == 0 || !PM_ISBC(dip)) { 4458*0Sstevel@tonic-gate PMD(PMD_REMDEV, ("%s: ignore %s@%s(%s#%d)\n", pmf, 4459*0Sstevel@tonic-gate PM_DEVICE(dip))) 4460*0Sstevel@tonic-gate return; 4461*0Sstevel@tonic-gate } 4462*0Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip)); 4463*0Sstevel@tonic-gate 4464*0Sstevel@tonic-gate /* 4465*0Sstevel@tonic-gate * pm_unmanage will clear info pointer later, after dealing with 4466*0Sstevel@tonic-gate * dependencies 4467*0Sstevel@tonic-gate */ 4468*0Sstevel@tonic-gate ASSERT(!PM_GET_PM_SCAN(dip)); /* better be gone already */ 4469*0Sstevel@tonic-gate /* 4470*0Sstevel@tonic-gate * Now adjust parent's kidsupcnt. We check only comp 0. 4471*0Sstevel@tonic-gate * Parents that get notification are not adjusted because their 4472*0Sstevel@tonic-gate * kidsupcnt is always 0 (or 1 during probe and attach). 4473*0Sstevel@tonic-gate */ 4474*0Sstevel@tonic-gate if ((PM_CURPOWER(dip, 0) != 0) && pdip && !PM_WANTS_NOTIFICATION(pdip)) 4475*0Sstevel@tonic-gate pm_rele_power(pdip); 4476*0Sstevel@tonic-gate #ifdef DEBUG 4477*0Sstevel@tonic-gate else { 4478*0Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: kuc stays %s@%s(%s#%d) comps gone\n", 4479*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 4480*0Sstevel@tonic-gate } 4481*0Sstevel@tonic-gate #endif 4482*0Sstevel@tonic-gate e_pm_destroy_components(dip); 4483*0Sstevel@tonic-gate /* 4484*0Sstevel@tonic-gate * Forget we ever knew anything about the components of this device 4485*0Sstevel@tonic-gate */ 4486*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= 4487*0Sstevel@tonic-gate ~(PMC_BC | PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED); 4488*0Sstevel@tonic-gate } 4489*0Sstevel@tonic-gate 4490*0Sstevel@tonic-gate /* 4491*0Sstevel@tonic-gate * Exported interface for a driver to set a component busy. 4492*0Sstevel@tonic-gate */ 4493*0Sstevel@tonic-gate int 4494*0Sstevel@tonic-gate pm_busy_component(dev_info_t *dip, int cmpt) 4495*0Sstevel@tonic-gate { 4496*0Sstevel@tonic-gate struct pm_component *cp; 4497*0Sstevel@tonic-gate 4498*0Sstevel@tonic-gate ASSERT(dip != NULL); 4499*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp)) 4500*0Sstevel@tonic-gate return (DDI_FAILURE); 4501*0Sstevel@tonic-gate PM_LOCK_BUSY(dip); 4502*0Sstevel@tonic-gate cp->pmc_busycount++; 4503*0Sstevel@tonic-gate cp->pmc_timestamp = 0; 4504*0Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 4505*0Sstevel@tonic-gate return (DDI_SUCCESS); 4506*0Sstevel@tonic-gate } 4507*0Sstevel@tonic-gate 4508*0Sstevel@tonic-gate /* 4509*0Sstevel@tonic-gate * Exported interface for a driver to set a component idle. 4510*0Sstevel@tonic-gate */ 4511*0Sstevel@tonic-gate int 4512*0Sstevel@tonic-gate pm_idle_component(dev_info_t *dip, int cmpt) 4513*0Sstevel@tonic-gate { 4514*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_idle_component") 4515*0Sstevel@tonic-gate struct pm_component *cp; 4516*0Sstevel@tonic-gate pm_scan_t *scanp = PM_GET_PM_SCAN(dip); 4517*0Sstevel@tonic-gate 4518*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp)) 4519*0Sstevel@tonic-gate return (DDI_FAILURE); 4520*0Sstevel@tonic-gate 4521*0Sstevel@tonic-gate PM_LOCK_BUSY(dip); 4522*0Sstevel@tonic-gate if (cp->pmc_busycount) { 4523*0Sstevel@tonic-gate if (--(cp->pmc_busycount) == 0) 4524*0Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 4525*0Sstevel@tonic-gate } else { 4526*0Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 4527*0Sstevel@tonic-gate } 4528*0Sstevel@tonic-gate 4529*0Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 4530*0Sstevel@tonic-gate 4531*0Sstevel@tonic-gate /* 4532*0Sstevel@tonic-gate * if device becomes idle during idle down period, try scan it down 4533*0Sstevel@tonic-gate */ 4534*0Sstevel@tonic-gate if (scanp && PM_IS_PID(dip)) { 4535*0Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d) idle.\n", pmf, 4536*0Sstevel@tonic-gate PM_DEVICE(dip))) 4537*0Sstevel@tonic-gate pm_rescan(dip); 4538*0Sstevel@tonic-gate return (DDI_SUCCESS); 4539*0Sstevel@tonic-gate } 4540*0Sstevel@tonic-gate 4541*0Sstevel@tonic-gate /* 4542*0Sstevel@tonic-gate * handle scan not running with nexus threshold == 0 4543*0Sstevel@tonic-gate */ 4544*0Sstevel@tonic-gate 4545*0Sstevel@tonic-gate if (PM_IS_NEXUS(dip) && (cp->pmc_busycount == 0)) { 4546*0Sstevel@tonic-gate pm_rescan(dip); 4547*0Sstevel@tonic-gate } 4548*0Sstevel@tonic-gate 4549*0Sstevel@tonic-gate return (DDI_SUCCESS); 4550*0Sstevel@tonic-gate } 4551*0Sstevel@tonic-gate 4552*0Sstevel@tonic-gate /* 4553*0Sstevel@tonic-gate * This is the old obsolete interface called by drivers to set their normal 4554*0Sstevel@tonic-gate * power. Thus we can't fix its behavior or return a value. 4555*0Sstevel@tonic-gate * This functionality is replaced by the pm-component property. 4556*0Sstevel@tonic-gate * We'll only get components destroyed while no power management is 4557*0Sstevel@tonic-gate * going on (and the device is detached), so we don't need a mutex here 4558*0Sstevel@tonic-gate */ 4559*0Sstevel@tonic-gate void 4560*0Sstevel@tonic-gate pm_set_normal_power(dev_info_t *dip, int comp, int level) 4561*0Sstevel@tonic-gate { 4562*0Sstevel@tonic-gate PMD_FUNC(pmf, "set_normal_power") 4563*0Sstevel@tonic-gate #ifdef DEBUG 4564*0Sstevel@tonic-gate if (!PM_ISBC(dip)) 4565*0Sstevel@tonic-gate cmn_err(CE_WARN, "!call to pm_set_normal_power() by %s@%s " 4566*0Sstevel@tonic-gate "(driver exporting pm-components property) ignored", 4567*0Sstevel@tonic-gate PM_NAME(dip), PM_ADDR(dip)); 4568*0Sstevel@tonic-gate #endif 4569*0Sstevel@tonic-gate if (PM_ISBC(dip)) { 4570*0Sstevel@tonic-gate PMD(PMD_NORM, ("%s: %s@%s(%s#%d) set normal power comp=%d, " 4571*0Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level)) 4572*0Sstevel@tonic-gate e_pm_set_max_power(dip, comp, level); 4573*0Sstevel@tonic-gate e_pm_default_levels(dip, PM_CP(dip, comp), level); 4574*0Sstevel@tonic-gate } 4575*0Sstevel@tonic-gate } 4576*0Sstevel@tonic-gate 4577*0Sstevel@tonic-gate /* 4578*0Sstevel@tonic-gate * Called on a successfully detached driver to free pm resources 4579*0Sstevel@tonic-gate */ 4580*0Sstevel@tonic-gate static void 4581*0Sstevel@tonic-gate pm_stop(dev_info_t *dip) 4582*0Sstevel@tonic-gate { 4583*0Sstevel@tonic-gate PMD_FUNC(pmf, "stop") 4584*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 4585*0Sstevel@tonic-gate 4586*0Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip)); 4587*0Sstevel@tonic-gate /* stopping scan, destroy scan data structure */ 4588*0Sstevel@tonic-gate if (!PM_ISBC(dip)) { 4589*0Sstevel@tonic-gate pm_scan_stop(dip); 4590*0Sstevel@tonic-gate pm_scan_fini(dip); 4591*0Sstevel@tonic-gate } 4592*0Sstevel@tonic-gate 4593*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) != NULL) { 4594*0Sstevel@tonic-gate if (pm_unmanage(dip) == DDI_SUCCESS) { 4595*0Sstevel@tonic-gate /* 4596*0Sstevel@tonic-gate * Old style driver may have called 4597*0Sstevel@tonic-gate * pm_destroy_components already, but just in case ... 4598*0Sstevel@tonic-gate */ 4599*0Sstevel@tonic-gate e_pm_destroy_components(dip); 4600*0Sstevel@tonic-gate } else { 4601*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't pm_unmanage %s@%s(%s#%d)\n", 4602*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 4603*0Sstevel@tonic-gate } 4604*0Sstevel@tonic-gate } else { 4605*0Sstevel@tonic-gate if (PM_NUMCMPTS(dip)) 4606*0Sstevel@tonic-gate e_pm_destroy_components(dip); 4607*0Sstevel@tonic-gate else { 4608*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NOPMKID) { 4609*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_NOPMKID; 4610*0Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 4611*0Sstevel@tonic-gate pm_rele_power(pdip); 4612*0Sstevel@tonic-gate } else if (pdip && MDI_VHCI(pdip)) { 4613*0Sstevel@tonic-gate (void) mdi_power(pdip, 4614*0Sstevel@tonic-gate MDI_PM_RELE_POWER, 4615*0Sstevel@tonic-gate (void *)dip, NULL, 0); 4616*0Sstevel@tonic-gate } 4617*0Sstevel@tonic-gate } 4618*0Sstevel@tonic-gate } 4619*0Sstevel@tonic-gate } 4620*0Sstevel@tonic-gate } 4621*0Sstevel@tonic-gate 4622*0Sstevel@tonic-gate /* 4623*0Sstevel@tonic-gate * The node is the subject of a reparse pm props ioctl. Throw away the old 4624*0Sstevel@tonic-gate * info and start over. 4625*0Sstevel@tonic-gate */ 4626*0Sstevel@tonic-gate int 4627*0Sstevel@tonic-gate e_new_pm_props(dev_info_t *dip) 4628*0Sstevel@tonic-gate { 4629*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) != NULL) { 4630*0Sstevel@tonic-gate pm_stop(dip); 4631*0Sstevel@tonic-gate 4632*0Sstevel@tonic-gate if (e_pm_manage(dip, PM_STYLE_NEW) != DDI_SUCCESS) { 4633*0Sstevel@tonic-gate return (DDI_FAILURE); 4634*0Sstevel@tonic-gate } 4635*0Sstevel@tonic-gate } 4636*0Sstevel@tonic-gate e_pm_props(dip); 4637*0Sstevel@tonic-gate return (DDI_SUCCESS); 4638*0Sstevel@tonic-gate } 4639*0Sstevel@tonic-gate 4640*0Sstevel@tonic-gate /* 4641*0Sstevel@tonic-gate * Device has been attached, so process its pm properties 4642*0Sstevel@tonic-gate */ 4643*0Sstevel@tonic-gate void 4644*0Sstevel@tonic-gate e_pm_props(dev_info_t *dip) 4645*0Sstevel@tonic-gate { 4646*0Sstevel@tonic-gate char *pp; 4647*0Sstevel@tonic-gate int len; 4648*0Sstevel@tonic-gate int flags = 0; 4649*0Sstevel@tonic-gate int propflag = DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP; 4650*0Sstevel@tonic-gate 4651*0Sstevel@tonic-gate /* 4652*0Sstevel@tonic-gate * It doesn't matter if we do this more than once, we should always 4653*0Sstevel@tonic-gate * get the same answers, and if not, then the last one in is the 4654*0Sstevel@tonic-gate * best one. 4655*0Sstevel@tonic-gate */ 4656*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-hardware-state", 4657*0Sstevel@tonic-gate (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) { 4658*0Sstevel@tonic-gate if (strcmp(pp, "needs-suspend-resume") == 0) { 4659*0Sstevel@tonic-gate flags = PMC_NEEDS_SR; 4660*0Sstevel@tonic-gate } else if (strcmp(pp, "no-suspend-resume") == 0) { 4661*0Sstevel@tonic-gate flags = PMC_NO_SR; 4662*0Sstevel@tonic-gate } else if (strcmp(pp, "parental-suspend-resume") == 0) { 4663*0Sstevel@tonic-gate flags = PMC_PARENTAL_SR; 4664*0Sstevel@tonic-gate } else { 4665*0Sstevel@tonic-gate cmn_err(CE_NOTE, "!device %s@%s has unrecognized " 4666*0Sstevel@tonic-gate "%s property value '%s'", PM_NAME(dip), 4667*0Sstevel@tonic-gate PM_ADDR(dip), "pm-hardware-state", pp); 4668*0Sstevel@tonic-gate } 4669*0Sstevel@tonic-gate kmem_free(pp, len); 4670*0Sstevel@tonic-gate } 4671*0Sstevel@tonic-gate /* 4672*0Sstevel@tonic-gate * This next segment (PMC_WANTS_NOTIFY) is in 4673*0Sstevel@tonic-gate * support of nexus drivers which will want to be involved in 4674*0Sstevel@tonic-gate * (or at least notified of) their child node's power level transitions. 4675*0Sstevel@tonic-gate * "pm-want-child-notification?" is defined by the parent. 4676*0Sstevel@tonic-gate */ 4677*0Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag, 4678*0Sstevel@tonic-gate "pm-want-child-notification?") && PM_HAS_BUS_POWER(dip)) 4679*0Sstevel@tonic-gate flags |= PMC_WANTS_NOTIFY; 4680*0Sstevel@tonic-gate ASSERT(PM_HAS_BUS_POWER(dip) || !ddi_prop_exists(DDI_DEV_T_ANY, 4681*0Sstevel@tonic-gate dip, propflag, "pm-want-child-notification?")); 4682*0Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag, 4683*0Sstevel@tonic-gate "no-involuntary-power-cycles")) 4684*0Sstevel@tonic-gate flags |= PMC_NO_INVOL; 4685*0Sstevel@tonic-gate /* devfs single threads us */ 4686*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= flags; 4687*0Sstevel@tonic-gate } 4688*0Sstevel@tonic-gate 4689*0Sstevel@tonic-gate /* 4690*0Sstevel@tonic-gate * This is the DDI_CTLOPS_POWER handler that is used when there is no ppm 4691*0Sstevel@tonic-gate * driver which has claimed a node. 4692*0Sstevel@tonic-gate * Sets old_power in arg struct. 4693*0Sstevel@tonic-gate */ 4694*0Sstevel@tonic-gate static int 4695*0Sstevel@tonic-gate pm_default_ctlops(dev_info_t *dip, dev_info_t *rdip, 4696*0Sstevel@tonic-gate ddi_ctl_enum_t ctlop, void *arg, void *result) 4697*0Sstevel@tonic-gate { 4698*0Sstevel@tonic-gate _NOTE(ARGUNUSED(dip)) 4699*0Sstevel@tonic-gate PMD_FUNC(pmf, "ctlops") 4700*0Sstevel@tonic-gate power_req_t *reqp = (power_req_t *)arg; 4701*0Sstevel@tonic-gate int retval; 4702*0Sstevel@tonic-gate dev_info_t *target_dip; 4703*0Sstevel@tonic-gate int new_level, old_level, cmpt; 4704*0Sstevel@tonic-gate #ifdef DEBUG 4705*0Sstevel@tonic-gate char *format; 4706*0Sstevel@tonic-gate #endif 4707*0Sstevel@tonic-gate 4708*0Sstevel@tonic-gate /* 4709*0Sstevel@tonic-gate * The interface for doing the actual power level changes is now 4710*0Sstevel@tonic-gate * through the DDI_CTLOPS_POWER bus_ctl, so that we can plug in 4711*0Sstevel@tonic-gate * different platform-specific power control drivers. 4712*0Sstevel@tonic-gate * 4713*0Sstevel@tonic-gate * This driver implements the "default" version of this interface. 4714*0Sstevel@tonic-gate * If no ppm driver has been installed then this interface is called 4715*0Sstevel@tonic-gate * instead. 4716*0Sstevel@tonic-gate */ 4717*0Sstevel@tonic-gate ASSERT(dip == NULL); 4718*0Sstevel@tonic-gate switch (ctlop) { 4719*0Sstevel@tonic-gate case DDI_CTLOPS_POWER: 4720*0Sstevel@tonic-gate switch (reqp->request_type) { 4721*0Sstevel@tonic-gate case PMR_PPM_SET_POWER: 4722*0Sstevel@tonic-gate { 4723*0Sstevel@tonic-gate target_dip = reqp->req.ppm_set_power_req.who; 4724*0Sstevel@tonic-gate ASSERT(target_dip == rdip); 4725*0Sstevel@tonic-gate new_level = reqp->req.ppm_set_power_req.new_level; 4726*0Sstevel@tonic-gate cmpt = reqp->req.ppm_set_power_req.cmpt; 4727*0Sstevel@tonic-gate /* pass back old power for the PM_LEVEL_UNKNOWN case */ 4728*0Sstevel@tonic-gate old_level = PM_CURPOWER(target_dip, cmpt); 4729*0Sstevel@tonic-gate reqp->req.ppm_set_power_req.old_level = old_level; 4730*0Sstevel@tonic-gate retval = pm_power(target_dip, cmpt, new_level); 4731*0Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PPM_SET_POWER %s@%s(%s#%d)[%d] %d->" 4732*0Sstevel@tonic-gate "%d %s\n", pmf, PM_DEVICE(target_dip), cmpt, 4733*0Sstevel@tonic-gate old_level, new_level, (retval == DDI_SUCCESS ? 4734*0Sstevel@tonic-gate "chd" : "no chg"))) 4735*0Sstevel@tonic-gate return (retval); 4736*0Sstevel@tonic-gate } 4737*0Sstevel@tonic-gate 4738*0Sstevel@tonic-gate case PMR_PPM_PRE_DETACH: 4739*0Sstevel@tonic-gate case PMR_PPM_POST_DETACH: 4740*0Sstevel@tonic-gate case PMR_PPM_PRE_ATTACH: 4741*0Sstevel@tonic-gate case PMR_PPM_POST_ATTACH: 4742*0Sstevel@tonic-gate case PMR_PPM_PRE_PROBE: 4743*0Sstevel@tonic-gate case PMR_PPM_POST_PROBE: 4744*0Sstevel@tonic-gate case PMR_PPM_PRE_RESUME: 4745*0Sstevel@tonic-gate case PMR_PPM_INIT_CHILD: 4746*0Sstevel@tonic-gate case PMR_PPM_UNINIT_CHILD: 4747*0Sstevel@tonic-gate #ifdef DEBUG 4748*0Sstevel@tonic-gate switch (reqp->request_type) { 4749*0Sstevel@tonic-gate case PMR_PPM_PRE_DETACH: 4750*0Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_DETACH " 4751*0Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4752*0Sstevel@tonic-gate break; 4753*0Sstevel@tonic-gate case PMR_PPM_POST_DETACH: 4754*0Sstevel@tonic-gate format = "%s: PMR_PPM_POST_DETACH " 4755*0Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4756*0Sstevel@tonic-gate break; 4757*0Sstevel@tonic-gate case PMR_PPM_PRE_ATTACH: 4758*0Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_ATTACH " 4759*0Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4760*0Sstevel@tonic-gate break; 4761*0Sstevel@tonic-gate case PMR_PPM_POST_ATTACH: 4762*0Sstevel@tonic-gate format = "%s: PMR_PPM_POST_ATTACH " 4763*0Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4764*0Sstevel@tonic-gate break; 4765*0Sstevel@tonic-gate case PMR_PPM_PRE_PROBE: 4766*0Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_PROBE " 4767*0Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4768*0Sstevel@tonic-gate break; 4769*0Sstevel@tonic-gate case PMR_PPM_POST_PROBE: 4770*0Sstevel@tonic-gate format = "%s: PMR_PPM_POST_PROBE " 4771*0Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4772*0Sstevel@tonic-gate break; 4773*0Sstevel@tonic-gate case PMR_PPM_PRE_RESUME: 4774*0Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_RESUME " 4775*0Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n"; 4776*0Sstevel@tonic-gate break; 4777*0Sstevel@tonic-gate case PMR_PPM_INIT_CHILD: 4778*0Sstevel@tonic-gate format = "%s: PMR_PPM_INIT_CHILD " 4779*0Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4780*0Sstevel@tonic-gate break; 4781*0Sstevel@tonic-gate case PMR_PPM_UNINIT_CHILD: 4782*0Sstevel@tonic-gate format = "%s: PMR_PPM_UNINIT_CHILD " 4783*0Sstevel@tonic-gate "%s@%s(%s#%d)\n"; 4784*0Sstevel@tonic-gate break; 4785*0Sstevel@tonic-gate default: 4786*0Sstevel@tonic-gate break; 4787*0Sstevel@tonic-gate } 4788*0Sstevel@tonic-gate PMD(PMD_PPM, (format, pmf, PM_DEVICE(rdip), 4789*0Sstevel@tonic-gate reqp->req.ppm_config_req.result)) 4790*0Sstevel@tonic-gate #endif 4791*0Sstevel@tonic-gate return (DDI_SUCCESS); 4792*0Sstevel@tonic-gate 4793*0Sstevel@tonic-gate case PMR_PPM_POWER_CHANGE_NOTIFY: 4794*0Sstevel@tonic-gate /* 4795*0Sstevel@tonic-gate * Nothing for us to do 4796*0Sstevel@tonic-gate */ 4797*0Sstevel@tonic-gate ASSERT(reqp->req.ppm_notify_level_req.who == rdip); 4798*0Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PMR_PPM_POWER_CHANGE_NOTIFY " 4799*0Sstevel@tonic-gate "%s@%s(%s#%d)[%d] %d->%d\n", pmf, 4800*0Sstevel@tonic-gate PM_DEVICE(reqp->req.ppm_notify_level_req.who), 4801*0Sstevel@tonic-gate reqp->req.ppm_notify_level_req.cmpt, 4802*0Sstevel@tonic-gate PM_CURPOWER(reqp->req.ppm_notify_level_req.who, 4803*0Sstevel@tonic-gate reqp->req.ppm_notify_level_req.cmpt), 4804*0Sstevel@tonic-gate reqp->req.ppm_notify_level_req.new_level)) 4805*0Sstevel@tonic-gate return (DDI_SUCCESS); 4806*0Sstevel@tonic-gate 4807*0Sstevel@tonic-gate case PMR_PPM_UNMANAGE: 4808*0Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PMR_PPM_UNMANAGE %s@%s(%s#%d)\n", 4809*0Sstevel@tonic-gate pmf, PM_DEVICE(rdip))) 4810*0Sstevel@tonic-gate return (DDI_SUCCESS); 4811*0Sstevel@tonic-gate 4812*0Sstevel@tonic-gate case PMR_PPM_LOCK_POWER: 4813*0Sstevel@tonic-gate pm_lock_power_single(reqp->req.ppm_lock_power_req.who, 4814*0Sstevel@tonic-gate reqp->req.ppm_lock_power_req.circp); 4815*0Sstevel@tonic-gate return (DDI_SUCCESS); 4816*0Sstevel@tonic-gate 4817*0Sstevel@tonic-gate case PMR_PPM_UNLOCK_POWER: 4818*0Sstevel@tonic-gate pm_unlock_power_single( 4819*0Sstevel@tonic-gate reqp->req.ppm_unlock_power_req.who, 4820*0Sstevel@tonic-gate reqp->req.ppm_unlock_power_req.circ); 4821*0Sstevel@tonic-gate return (DDI_SUCCESS); 4822*0Sstevel@tonic-gate 4823*0Sstevel@tonic-gate case PMR_PPM_TRY_LOCK_POWER: 4824*0Sstevel@tonic-gate *(int *)result = pm_try_locking_power_single( 4825*0Sstevel@tonic-gate reqp->req.ppm_lock_power_req.who, 4826*0Sstevel@tonic-gate reqp->req.ppm_lock_power_req.circp); 4827*0Sstevel@tonic-gate return (DDI_SUCCESS); 4828*0Sstevel@tonic-gate 4829*0Sstevel@tonic-gate case PMR_PPM_POWER_LOCK_OWNER: 4830*0Sstevel@tonic-gate target_dip = reqp->req.ppm_power_lock_owner_req.who; 4831*0Sstevel@tonic-gate ASSERT(target_dip == rdip); 4832*0Sstevel@tonic-gate reqp->req.ppm_power_lock_owner_req.owner = 4833*0Sstevel@tonic-gate DEVI(rdip)->devi_busy_thread; 4834*0Sstevel@tonic-gate return (DDI_SUCCESS); 4835*0Sstevel@tonic-gate default: 4836*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: default!\n", pmf)) 4837*0Sstevel@tonic-gate return (DDI_FAILURE); 4838*0Sstevel@tonic-gate } 4839*0Sstevel@tonic-gate 4840*0Sstevel@tonic-gate default: 4841*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: unknown\n", pmf)) 4842*0Sstevel@tonic-gate return (DDI_FAILURE); 4843*0Sstevel@tonic-gate } 4844*0Sstevel@tonic-gate } 4845*0Sstevel@tonic-gate 4846*0Sstevel@tonic-gate /* 4847*0Sstevel@tonic-gate * We overload the bus_ctl ops here--perhaps we ought to have a distinct 4848*0Sstevel@tonic-gate * power_ops struct for this functionality instead? 4849*0Sstevel@tonic-gate * However, we only ever do this on a ppm driver. 4850*0Sstevel@tonic-gate */ 4851*0Sstevel@tonic-gate int 4852*0Sstevel@tonic-gate pm_ctlops(dev_info_t *d, dev_info_t *r, ddi_ctl_enum_t op, void *a, void *v) 4853*0Sstevel@tonic-gate { 4854*0Sstevel@tonic-gate int (*fp)(); 4855*0Sstevel@tonic-gate 4856*0Sstevel@tonic-gate /* if no ppm handler, call the default routine */ 4857*0Sstevel@tonic-gate if (d == NULL) { 4858*0Sstevel@tonic-gate return (pm_default_ctlops(d, r, op, a, v)); 4859*0Sstevel@tonic-gate } 4860*0Sstevel@tonic-gate if (!d || !r) 4861*0Sstevel@tonic-gate return (DDI_FAILURE); 4862*0Sstevel@tonic-gate ASSERT(DEVI(d)->devi_ops && DEVI(d)->devi_ops->devo_bus_ops && 4863*0Sstevel@tonic-gate DEVI(d)->devi_ops->devo_bus_ops->bus_ctl); 4864*0Sstevel@tonic-gate 4865*0Sstevel@tonic-gate fp = DEVI(d)->devi_ops->devo_bus_ops->bus_ctl; 4866*0Sstevel@tonic-gate return ((*fp)(d, r, op, a, v)); 4867*0Sstevel@tonic-gate } 4868*0Sstevel@tonic-gate 4869*0Sstevel@tonic-gate /* 4870*0Sstevel@tonic-gate * Called on a node when attach completes or the driver makes its first pm 4871*0Sstevel@tonic-gate * call (whichever comes first). 4872*0Sstevel@tonic-gate * In the attach case, device may not be power manageable at all. 4873*0Sstevel@tonic-gate * Don't need to lock the dip because we're single threaded by the devfs code 4874*0Sstevel@tonic-gate */ 4875*0Sstevel@tonic-gate static int 4876*0Sstevel@tonic-gate pm_start(dev_info_t *dip) 4877*0Sstevel@tonic-gate { 4878*0Sstevel@tonic-gate PMD_FUNC(pmf, "start") 4879*0Sstevel@tonic-gate int ret; 4880*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 4881*0Sstevel@tonic-gate int e_pm_manage(dev_info_t *, int); 4882*0Sstevel@tonic-gate void pm_noinvol_specd(dev_info_t *dip); 4883*0Sstevel@tonic-gate 4884*0Sstevel@tonic-gate e_pm_props(dip); 4885*0Sstevel@tonic-gate pm_noinvol_specd(dip); 4886*0Sstevel@tonic-gate /* 4887*0Sstevel@tonic-gate * If this dip has already been processed, don't mess with it 4888*0Sstevel@tonic-gate * (but decrement the speculative count we did above, as whatever 4889*0Sstevel@tonic-gate * code put it under pm already will have dealt with it) 4890*0Sstevel@tonic-gate */ 4891*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip)) { 4892*0Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: pm already done for %s@%s(%s#%d)\n", 4893*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 4894*0Sstevel@tonic-gate return (0); 4895*0Sstevel@tonic-gate } 4896*0Sstevel@tonic-gate ret = e_pm_manage(dip, PM_STYLE_UNKNOWN); 4897*0Sstevel@tonic-gate 4898*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) { 4899*0Sstevel@tonic-gate /* 4900*0Sstevel@tonic-gate * keep the kidsupcount increment as is 4901*0Sstevel@tonic-gate */ 4902*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NOPMKID; 4903*0Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) { 4904*0Sstevel@tonic-gate pm_hold_power(pdip); 4905*0Sstevel@tonic-gate } else if (pdip && MDI_VHCI(pdip)) { 4906*0Sstevel@tonic-gate (void) mdi_power(pdip, MDI_PM_HOLD_POWER, 4907*0Sstevel@tonic-gate (void *)dip, NULL, 0); 4908*0Sstevel@tonic-gate } 4909*0Sstevel@tonic-gate 4910*0Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: pm of %s@%s(%s#%d) failed, parent " 4911*0Sstevel@tonic-gate "left up\n", pmf, PM_DEVICE(dip))) 4912*0Sstevel@tonic-gate } 4913*0Sstevel@tonic-gate 4914*0Sstevel@tonic-gate return (ret); 4915*0Sstevel@tonic-gate } 4916*0Sstevel@tonic-gate 4917*0Sstevel@tonic-gate /* 4918*0Sstevel@tonic-gate * Keep a list of recorded thresholds. For now we just keep a list and 4919*0Sstevel@tonic-gate * search it linearly. We don't expect too many entries. Can always hash it 4920*0Sstevel@tonic-gate * later if we need to. 4921*0Sstevel@tonic-gate */ 4922*0Sstevel@tonic-gate void 4923*0Sstevel@tonic-gate pm_record_thresh(pm_thresh_rec_t *rp) 4924*0Sstevel@tonic-gate { 4925*0Sstevel@tonic-gate pm_thresh_rec_t *pptr, *ptr; 4926*0Sstevel@tonic-gate 4927*0Sstevel@tonic-gate ASSERT(*rp->ptr_physpath); 4928*0Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER); 4929*0Sstevel@tonic-gate for (pptr = NULL, ptr = pm_thresh_head; 4930*0Sstevel@tonic-gate ptr; pptr = ptr, ptr = ptr->ptr_next) { 4931*0Sstevel@tonic-gate if (strcmp(rp->ptr_physpath, ptr->ptr_physpath) == 0) { 4932*0Sstevel@tonic-gate /* replace this one */ 4933*0Sstevel@tonic-gate rp->ptr_next = ptr->ptr_next; 4934*0Sstevel@tonic-gate if (pptr) { 4935*0Sstevel@tonic-gate pptr->ptr_next = rp; 4936*0Sstevel@tonic-gate } else { 4937*0Sstevel@tonic-gate pm_thresh_head = rp; 4938*0Sstevel@tonic-gate } 4939*0Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 4940*0Sstevel@tonic-gate kmem_free(ptr, ptr->ptr_size); 4941*0Sstevel@tonic-gate return; 4942*0Sstevel@tonic-gate } 4943*0Sstevel@tonic-gate continue; 4944*0Sstevel@tonic-gate } 4945*0Sstevel@tonic-gate /* 4946*0Sstevel@tonic-gate * There was not a match in the list, insert this one in front 4947*0Sstevel@tonic-gate */ 4948*0Sstevel@tonic-gate if (pm_thresh_head) { 4949*0Sstevel@tonic-gate rp->ptr_next = pm_thresh_head; 4950*0Sstevel@tonic-gate pm_thresh_head = rp; 4951*0Sstevel@tonic-gate } else { 4952*0Sstevel@tonic-gate rp->ptr_next = NULL; 4953*0Sstevel@tonic-gate pm_thresh_head = rp; 4954*0Sstevel@tonic-gate } 4955*0Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 4956*0Sstevel@tonic-gate } 4957*0Sstevel@tonic-gate 4958*0Sstevel@tonic-gate /* 4959*0Sstevel@tonic-gate * Create a new dependency record and hang a new dependency entry off of it 4960*0Sstevel@tonic-gate */ 4961*0Sstevel@tonic-gate pm_pdr_t * 4962*0Sstevel@tonic-gate newpdr(char *kept, char *keeps, int isprop) 4963*0Sstevel@tonic-gate { 4964*0Sstevel@tonic-gate size_t size = strlen(kept) + strlen(keeps) + 2 + sizeof (pm_pdr_t); 4965*0Sstevel@tonic-gate pm_pdr_t *p = kmem_zalloc(size, KM_SLEEP); 4966*0Sstevel@tonic-gate p->pdr_size = size; 4967*0Sstevel@tonic-gate p->pdr_isprop = isprop; 4968*0Sstevel@tonic-gate p->pdr_kept_paths = NULL; 4969*0Sstevel@tonic-gate p->pdr_kept_count = 0; 4970*0Sstevel@tonic-gate p->pdr_kept = (char *)((intptr_t)p + sizeof (pm_pdr_t)); 4971*0Sstevel@tonic-gate (void) strcpy(p->pdr_kept, kept); 4972*0Sstevel@tonic-gate p->pdr_keeper = (char *)((intptr_t)p->pdr_kept + strlen(kept) + 1); 4973*0Sstevel@tonic-gate (void) strcpy(p->pdr_keeper, keeps); 4974*0Sstevel@tonic-gate ASSERT((intptr_t)p->pdr_keeper + strlen(p->pdr_keeper) + 1 <= 4975*0Sstevel@tonic-gate (intptr_t)p + size); 4976*0Sstevel@tonic-gate ASSERT((intptr_t)p->pdr_kept + strlen(p->pdr_kept) + 1 <= 4977*0Sstevel@tonic-gate (intptr_t)p + size); 4978*0Sstevel@tonic-gate return (p); 4979*0Sstevel@tonic-gate } 4980*0Sstevel@tonic-gate 4981*0Sstevel@tonic-gate /* 4982*0Sstevel@tonic-gate * Keep a list of recorded dependencies. We only keep the 4983*0Sstevel@tonic-gate * keeper -> kept list for simplification. At this point We do not 4984*0Sstevel@tonic-gate * care about whether the devices are attached or not yet, 4985*0Sstevel@tonic-gate * this would be done in pm_keeper() and pm_kept(). 4986*0Sstevel@tonic-gate * If a PM_RESET_PM happens, then we tear down and forget the dependencies, 4987*0Sstevel@tonic-gate * and it is up to the user to issue the ioctl again if they want it 4988*0Sstevel@tonic-gate * (e.g. pmconfig) 4989*0Sstevel@tonic-gate * Returns true if dependency already exists in the list. 4990*0Sstevel@tonic-gate */ 4991*0Sstevel@tonic-gate int 4992*0Sstevel@tonic-gate pm_record_keeper(char *kept, char *keeper, int isprop) 4993*0Sstevel@tonic-gate { 4994*0Sstevel@tonic-gate PMD_FUNC(pmf, "record_keeper") 4995*0Sstevel@tonic-gate pm_pdr_t *npdr, *ppdr, *pdr; 4996*0Sstevel@tonic-gate 4997*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s, %s\n", pmf, kept, keeper)) 4998*0Sstevel@tonic-gate ASSERT(kept && keeper); 4999*0Sstevel@tonic-gate #ifdef DEBUG 5000*0Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5001*0Sstevel@tonic-gate prdeps("pm_record_keeper entry"); 5002*0Sstevel@tonic-gate #endif 5003*0Sstevel@tonic-gate for (ppdr = NULL, pdr = pm_dep_head; pdr; 5004*0Sstevel@tonic-gate ppdr = pdr, pdr = pdr->pdr_next) { 5005*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: check %s, %s\n", pmf, pdr->pdr_kept, 5006*0Sstevel@tonic-gate pdr->pdr_keeper)) 5007*0Sstevel@tonic-gate if (strcmp(kept, pdr->pdr_kept) == 0 && 5008*0Sstevel@tonic-gate strcmp(keeper, pdr->pdr_keeper) == 0) { 5009*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: match\n", pmf)) 5010*0Sstevel@tonic-gate return (1); 5011*0Sstevel@tonic-gate } 5012*0Sstevel@tonic-gate } 5013*0Sstevel@tonic-gate /* 5014*0Sstevel@tonic-gate * We did not find any match, so we have to make an entry 5015*0Sstevel@tonic-gate */ 5016*0Sstevel@tonic-gate npdr = newpdr(kept, keeper, isprop); 5017*0Sstevel@tonic-gate if (ppdr) { 5018*0Sstevel@tonic-gate ASSERT(ppdr->pdr_next == NULL); 5019*0Sstevel@tonic-gate ppdr->pdr_next = npdr; 5020*0Sstevel@tonic-gate } else { 5021*0Sstevel@tonic-gate ASSERT(pm_dep_head == NULL); 5022*0Sstevel@tonic-gate pm_dep_head = npdr; 5023*0Sstevel@tonic-gate } 5024*0Sstevel@tonic-gate #ifdef DEBUG 5025*0Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5026*0Sstevel@tonic-gate prdeps("pm_record_keeper after new record"); 5027*0Sstevel@tonic-gate #endif 5028*0Sstevel@tonic-gate if (!isprop) 5029*0Sstevel@tonic-gate pm_unresolved_deps++; 5030*0Sstevel@tonic-gate else 5031*0Sstevel@tonic-gate pm_prop_deps++; 5032*0Sstevel@tonic-gate return (0); 5033*0Sstevel@tonic-gate } 5034*0Sstevel@tonic-gate 5035*0Sstevel@tonic-gate /* 5036*0Sstevel@tonic-gate * Look up this device in the set of devices we've seen ioctls for 5037*0Sstevel@tonic-gate * to see if we are holding a threshold spec for it. If so, make it so. 5038*0Sstevel@tonic-gate * At ioctl time, we were given the physical path of the device. 5039*0Sstevel@tonic-gate */ 5040*0Sstevel@tonic-gate int 5041*0Sstevel@tonic-gate pm_thresh_specd(dev_info_t *dip) 5042*0Sstevel@tonic-gate { 5043*0Sstevel@tonic-gate void pm_apply_recorded_thresh(dev_info_t *, pm_thresh_rec_t *); 5044*0Sstevel@tonic-gate char *path = 0; 5045*0Sstevel@tonic-gate char pathbuf[MAXNAMELEN]; 5046*0Sstevel@tonic-gate pm_thresh_rec_t *rp; 5047*0Sstevel@tonic-gate 5048*0Sstevel@tonic-gate path = ddi_pathname(dip, pathbuf); 5049*0Sstevel@tonic-gate 5050*0Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_READER); 5051*0Sstevel@tonic-gate for (rp = pm_thresh_head; rp; rp = rp->ptr_next) { 5052*0Sstevel@tonic-gate if (strcmp(rp->ptr_physpath, path) != 0) 5053*0Sstevel@tonic-gate continue; 5054*0Sstevel@tonic-gate pm_apply_recorded_thresh(dip, rp); 5055*0Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 5056*0Sstevel@tonic-gate return (1); 5057*0Sstevel@tonic-gate } 5058*0Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 5059*0Sstevel@tonic-gate return (0); 5060*0Sstevel@tonic-gate } 5061*0Sstevel@tonic-gate 5062*0Sstevel@tonic-gate static int 5063*0Sstevel@tonic-gate pm_set_keeping(dev_info_t *keeper, dev_info_t *kept) 5064*0Sstevel@tonic-gate { 5065*0Sstevel@tonic-gate PMD_FUNC(pmf, "set_keeping") 5066*0Sstevel@tonic-gate pm_info_t *kept_info; 5067*0Sstevel@tonic-gate int j, up = 0, circ; 5068*0Sstevel@tonic-gate void prdeps(char *); 5069*0Sstevel@tonic-gate 5070*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), kept=%s@%s(%s#%d)\n", pmf, 5071*0Sstevel@tonic-gate PM_DEVICE(keeper), PM_DEVICE(kept))) 5072*0Sstevel@tonic-gate #ifdef DEBUG 5073*0Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5074*0Sstevel@tonic-gate prdeps("Before PAD\n"); 5075*0Sstevel@tonic-gate #endif 5076*0Sstevel@tonic-gate ASSERT(keeper != kept); 5077*0Sstevel@tonic-gate if (PM_GET_PM_INFO(keeper) == NULL) { 5078*0Sstevel@tonic-gate cmn_err(CE_CONT, "!device %s@%s(%s#%d) keeps up device " 5079*0Sstevel@tonic-gate "%s@%s(%s#%d), but the latter is not power managed", 5080*0Sstevel@tonic-gate PM_DEVICE(keeper), PM_DEVICE(kept)); 5081*0Sstevel@tonic-gate PMD((PMD_FAIL | PMD_KEEPS), ("%s: keeper %s@%s(%s#%d) is not" 5082*0Sstevel@tonic-gate "power managed\n", pmf, PM_DEVICE(keeper))) 5083*0Sstevel@tonic-gate return (0); 5084*0Sstevel@tonic-gate } 5085*0Sstevel@tonic-gate kept_info = PM_GET_PM_INFO(kept); 5086*0Sstevel@tonic-gate ASSERT(kept_info); 5087*0Sstevel@tonic-gate PM_LOCK_POWER(keeper, &circ); 5088*0Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(keeper); j++) { 5089*0Sstevel@tonic-gate if (PM_CURPOWER(keeper, j)) { 5090*0Sstevel@tonic-gate up++; 5091*0Sstevel@tonic-gate break; 5092*0Sstevel@tonic-gate } 5093*0Sstevel@tonic-gate } 5094*0Sstevel@tonic-gate if (up) { 5095*0Sstevel@tonic-gate /* Bringup and maintain a hold on the kept */ 5096*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: place a hold on kept %s@%s(%s#%d)\n", pmf, 5097*0Sstevel@tonic-gate PM_DEVICE(kept))) 5098*0Sstevel@tonic-gate bring_pmdep_up(kept, 1); 5099*0Sstevel@tonic-gate } 5100*0Sstevel@tonic-gate PM_UNLOCK_POWER(keeper, circ); 5101*0Sstevel@tonic-gate #ifdef DEBUG 5102*0Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) 5103*0Sstevel@tonic-gate prdeps("After PAD\n"); 5104*0Sstevel@tonic-gate #endif 5105*0Sstevel@tonic-gate return (1); 5106*0Sstevel@tonic-gate } 5107*0Sstevel@tonic-gate 5108*0Sstevel@tonic-gate /* 5109*0Sstevel@tonic-gate * Should this device keep up another device? 5110*0Sstevel@tonic-gate * Look up this device in the set of devices we've seen ioctls for 5111*0Sstevel@tonic-gate * to see if we are holding a dependency spec for it. If so, make it so. 5112*0Sstevel@tonic-gate * Because we require the kept device to be attached already in order to 5113*0Sstevel@tonic-gate * make the list entry (and hold it), we only need to look for keepers. 5114*0Sstevel@tonic-gate * At ioctl time, we were given the physical path of the device. 5115*0Sstevel@tonic-gate */ 5116*0Sstevel@tonic-gate int 5117*0Sstevel@tonic-gate pm_keeper(char *keeper) 5118*0Sstevel@tonic-gate { 5119*0Sstevel@tonic-gate PMD_FUNC(pmf, "keeper") 5120*0Sstevel@tonic-gate int pm_apply_recorded_dep(dev_info_t *, pm_pdr_t *); 5121*0Sstevel@tonic-gate dev_info_t *dip; 5122*0Sstevel@tonic-gate pm_pdr_t *dp; 5123*0Sstevel@tonic-gate dev_info_t *kept = NULL; 5124*0Sstevel@tonic-gate int ret = 0; 5125*0Sstevel@tonic-gate int i; 5126*0Sstevel@tonic-gate 5127*0Sstevel@tonic-gate if (!pm_unresolved_deps && !pm_prop_deps) 5128*0Sstevel@tonic-gate return (0); 5129*0Sstevel@tonic-gate ASSERT(keeper != NULL); 5130*0Sstevel@tonic-gate dip = pm_name_to_dip(keeper, 1); 5131*0Sstevel@tonic-gate if (dip == NULL) 5132*0Sstevel@tonic-gate return (0); 5133*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s\n", pmf, keeper)) 5134*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 5135*0Sstevel@tonic-gate if (!dp->pdr_isprop) { 5136*0Sstevel@tonic-gate if (!pm_unresolved_deps) 5137*0Sstevel@tonic-gate continue; 5138*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper %s\n", pmf, dp->pdr_keeper)) 5139*0Sstevel@tonic-gate if (dp->pdr_satisfied) { 5140*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: satisfied\n", pmf)) 5141*0Sstevel@tonic-gate continue; 5142*0Sstevel@tonic-gate } 5143*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) == 0) { 5144*0Sstevel@tonic-gate ret += pm_apply_recorded_dep(dip, dp); 5145*0Sstevel@tonic-gate } 5146*0Sstevel@tonic-gate } else { 5147*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0) 5148*0Sstevel@tonic-gate continue; 5149*0Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 5150*0Sstevel@tonic-gate if (dp->pdr_kept_paths[i] == NULL) 5151*0Sstevel@tonic-gate continue; 5152*0Sstevel@tonic-gate kept = pm_name_to_dip(dp->pdr_kept_paths[i], 1); 5153*0Sstevel@tonic-gate if (kept == NULL) 5154*0Sstevel@tonic-gate continue; 5155*0Sstevel@tonic-gate ASSERT(ddi_prop_exists(DDI_DEV_T_ANY, kept, 5156*0Sstevel@tonic-gate DDI_PROP_DONTPASS, dp->pdr_kept)); 5157*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), " 5158*0Sstevel@tonic-gate "kept=%s@%s(%s#%d) keptcnt=%d\n", 5159*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_DEVICE(kept), 5160*0Sstevel@tonic-gate dp->pdr_kept_count)) 5161*0Sstevel@tonic-gate if (kept != dip) { 5162*0Sstevel@tonic-gate ret += pm_set_keeping(dip, kept); 5163*0Sstevel@tonic-gate } 5164*0Sstevel@tonic-gate ddi_release_devi(kept); 5165*0Sstevel@tonic-gate } 5166*0Sstevel@tonic-gate 5167*0Sstevel@tonic-gate } 5168*0Sstevel@tonic-gate } 5169*0Sstevel@tonic-gate ddi_release_devi(dip); 5170*0Sstevel@tonic-gate return (ret); 5171*0Sstevel@tonic-gate } 5172*0Sstevel@tonic-gate 5173*0Sstevel@tonic-gate /* 5174*0Sstevel@tonic-gate * Should this device be kept up by another device? 5175*0Sstevel@tonic-gate * Look up all dependency recorded from PM_ADD_DEPENDENT and 5176*0Sstevel@tonic-gate * PM_ADD_DEPENDENT_PROPERTY ioctls. Record down on the keeper's 5177*0Sstevel@tonic-gate * kept device lists. 5178*0Sstevel@tonic-gate */ 5179*0Sstevel@tonic-gate static int 5180*0Sstevel@tonic-gate pm_kept(char *keptp) 5181*0Sstevel@tonic-gate { 5182*0Sstevel@tonic-gate PMD_FUNC(pmf, "kept") 5183*0Sstevel@tonic-gate pm_pdr_t *dp; 5184*0Sstevel@tonic-gate int found = 0; 5185*0Sstevel@tonic-gate int ret = 0; 5186*0Sstevel@tonic-gate dev_info_t *keeper; 5187*0Sstevel@tonic-gate dev_info_t *kept; 5188*0Sstevel@tonic-gate size_t length; 5189*0Sstevel@tonic-gate int i; 5190*0Sstevel@tonic-gate char **paths; 5191*0Sstevel@tonic-gate char *path; 5192*0Sstevel@tonic-gate 5193*0Sstevel@tonic-gate ASSERT(keptp != NULL); 5194*0Sstevel@tonic-gate kept = pm_name_to_dip(keptp, 1); 5195*0Sstevel@tonic-gate if (kept == NULL) 5196*0Sstevel@tonic-gate return (0); 5197*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(kept))) 5198*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 5199*0Sstevel@tonic-gate if (dp->pdr_isprop) { 5200*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: property %s\n", pmf, dp->pdr_kept)) 5201*0Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, kept, 5202*0Sstevel@tonic-gate DDI_PROP_DONTPASS, dp->pdr_kept)) { 5203*0Sstevel@tonic-gate /* 5204*0Sstevel@tonic-gate * Dont allow self dependency. 5205*0Sstevel@tonic-gate */ 5206*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keptp) == 0) 5207*0Sstevel@tonic-gate continue; 5208*0Sstevel@tonic-gate keeper = pm_name_to_dip(dp->pdr_keeper, 1); 5209*0Sstevel@tonic-gate if (keeper == NULL) 5210*0Sstevel@tonic-gate continue; 5211*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: adding to kepts path list " 5212*0Sstevel@tonic-gate "%p\n", pmf, (void *)kept)) 5213*0Sstevel@tonic-gate #ifdef DEBUG 5214*0Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5215*0Sstevel@tonic-gate prdeps("Before Adding from pm_kept\n"); 5216*0Sstevel@tonic-gate #endif 5217*0Sstevel@tonic-gate /* 5218*0Sstevel@tonic-gate * Add ourselves to the dip list. 5219*0Sstevel@tonic-gate */ 5220*0Sstevel@tonic-gate if (dp->pdr_kept_count == 0) { 5221*0Sstevel@tonic-gate length = strlen(keptp) + 1; 5222*0Sstevel@tonic-gate path = 5223*0Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP); 5224*0Sstevel@tonic-gate paths = kmem_alloc(sizeof (char **), 5225*0Sstevel@tonic-gate KM_SLEEP); 5226*0Sstevel@tonic-gate (void) strcpy(path, keptp); 5227*0Sstevel@tonic-gate paths[0] = path; 5228*0Sstevel@tonic-gate dp->pdr_kept_paths = paths; 5229*0Sstevel@tonic-gate dp->pdr_kept_count++; 5230*0Sstevel@tonic-gate } else { 5231*0Sstevel@tonic-gate /* Check to see if already on list */ 5232*0Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; 5233*0Sstevel@tonic-gate i++) { 5234*0Sstevel@tonic-gate if (strcmp(keptp, 5235*0Sstevel@tonic-gate dp->pdr_kept_paths[i]) 5236*0Sstevel@tonic-gate == 0) { 5237*0Sstevel@tonic-gate found++; 5238*0Sstevel@tonic-gate break; 5239*0Sstevel@tonic-gate } 5240*0Sstevel@tonic-gate } 5241*0Sstevel@tonic-gate if (found) { 5242*0Sstevel@tonic-gate ddi_release_devi(keeper); 5243*0Sstevel@tonic-gate continue; 5244*0Sstevel@tonic-gate } 5245*0Sstevel@tonic-gate length = dp->pdr_kept_count * 5246*0Sstevel@tonic-gate sizeof (char **); 5247*0Sstevel@tonic-gate paths = kmem_alloc( 5248*0Sstevel@tonic-gate length + sizeof (char **), 5249*0Sstevel@tonic-gate KM_SLEEP); 5250*0Sstevel@tonic-gate if (dp->pdr_kept_count) { 5251*0Sstevel@tonic-gate bcopy(dp->pdr_kept_paths, 5252*0Sstevel@tonic-gate paths, length); 5253*0Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, 5254*0Sstevel@tonic-gate length); 5255*0Sstevel@tonic-gate } 5256*0Sstevel@tonic-gate dp->pdr_kept_paths = paths; 5257*0Sstevel@tonic-gate length = strlen(keptp) + 1; 5258*0Sstevel@tonic-gate path = 5259*0Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP); 5260*0Sstevel@tonic-gate (void) strcpy(path, keptp); 5261*0Sstevel@tonic-gate dp->pdr_kept_paths[i] = path; 5262*0Sstevel@tonic-gate dp->pdr_kept_count++; 5263*0Sstevel@tonic-gate } 5264*0Sstevel@tonic-gate #ifdef DEBUG 5265*0Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5266*0Sstevel@tonic-gate prdeps("After from pm_kept\n"); 5267*0Sstevel@tonic-gate #endif 5268*0Sstevel@tonic-gate if (keeper) { 5269*0Sstevel@tonic-gate ret += pm_set_keeping(keeper, kept); 5270*0Sstevel@tonic-gate ddi_release_devi(keeper); 5271*0Sstevel@tonic-gate } 5272*0Sstevel@tonic-gate } 5273*0Sstevel@tonic-gate } else { 5274*0Sstevel@tonic-gate /* 5275*0Sstevel@tonic-gate * pm_keeper would be called later to do 5276*0Sstevel@tonic-gate * the actual pm_set_keeping. 5277*0Sstevel@tonic-gate */ 5278*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: adding to kepts path list %p\n", 5279*0Sstevel@tonic-gate pmf, (void *)kept)) 5280*0Sstevel@tonic-gate #ifdef DEBUG 5281*0Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5282*0Sstevel@tonic-gate prdeps("Before Adding from pm_kept\n"); 5283*0Sstevel@tonic-gate #endif 5284*0Sstevel@tonic-gate if (strcmp(keptp, dp->pdr_kept) == 0) { 5285*0Sstevel@tonic-gate if (dp->pdr_kept_paths == NULL) { 5286*0Sstevel@tonic-gate length = strlen(keptp) + 1; 5287*0Sstevel@tonic-gate path = 5288*0Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP); 5289*0Sstevel@tonic-gate paths = kmem_alloc(sizeof (char **), 5290*0Sstevel@tonic-gate KM_SLEEP); 5291*0Sstevel@tonic-gate (void) strcpy(path, keptp); 5292*0Sstevel@tonic-gate paths[0] = path; 5293*0Sstevel@tonic-gate dp->pdr_kept_paths = paths; 5294*0Sstevel@tonic-gate dp->pdr_kept_count++; 5295*0Sstevel@tonic-gate } 5296*0Sstevel@tonic-gate } 5297*0Sstevel@tonic-gate #ifdef DEBUG 5298*0Sstevel@tonic-gate if (pm_debug & PMD_DEP) 5299*0Sstevel@tonic-gate prdeps("After from pm_kept\n"); 5300*0Sstevel@tonic-gate #endif 5301*0Sstevel@tonic-gate } 5302*0Sstevel@tonic-gate } 5303*0Sstevel@tonic-gate ddi_release_devi(kept); 5304*0Sstevel@tonic-gate return (ret); 5305*0Sstevel@tonic-gate } 5306*0Sstevel@tonic-gate 5307*0Sstevel@tonic-gate /* 5308*0Sstevel@tonic-gate * Apply a recorded dependency. dp specifies the dependency, and 5309*0Sstevel@tonic-gate * keeper is already known to be the device that keeps up the other (kept) one. 5310*0Sstevel@tonic-gate * We have to the whole tree for the "kept" device, then apply 5311*0Sstevel@tonic-gate * the dependency (which may already be applied). 5312*0Sstevel@tonic-gate */ 5313*0Sstevel@tonic-gate int 5314*0Sstevel@tonic-gate pm_apply_recorded_dep(dev_info_t *keeper, pm_pdr_t *dp) 5315*0Sstevel@tonic-gate { 5316*0Sstevel@tonic-gate PMD_FUNC(pmf, "apply_recorded_dep") 5317*0Sstevel@tonic-gate dev_info_t *kept = NULL; 5318*0Sstevel@tonic-gate int ret = 0; 5319*0Sstevel@tonic-gate char *keptp = NULL; 5320*0Sstevel@tonic-gate 5321*0Sstevel@tonic-gate /* 5322*0Sstevel@tonic-gate * Device to Device dependency can only be 1 to 1. 5323*0Sstevel@tonic-gate */ 5324*0Sstevel@tonic-gate if (dp->pdr_kept_paths == NULL) 5325*0Sstevel@tonic-gate return (0); 5326*0Sstevel@tonic-gate keptp = dp->pdr_kept_paths[0]; 5327*0Sstevel@tonic-gate if (keptp == NULL) 5328*0Sstevel@tonic-gate return (0); 5329*0Sstevel@tonic-gate ASSERT(*keptp != '\0'); 5330*0Sstevel@tonic-gate kept = pm_name_to_dip(keptp, 1); 5331*0Sstevel@tonic-gate if (kept == NULL) 5332*0Sstevel@tonic-gate return (0); 5333*0Sstevel@tonic-gate if (kept) { 5334*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, 5335*0Sstevel@tonic-gate dp->pdr_keeper, keptp)) 5336*0Sstevel@tonic-gate if (pm_set_keeping(keeper, kept)) { 5337*0Sstevel@tonic-gate ASSERT(dp->pdr_satisfied == 0); 5338*0Sstevel@tonic-gate dp->pdr_satisfied = 1; 5339*0Sstevel@tonic-gate ASSERT(pm_unresolved_deps); 5340*0Sstevel@tonic-gate pm_unresolved_deps--; 5341*0Sstevel@tonic-gate ret++; 5342*0Sstevel@tonic-gate } 5343*0Sstevel@tonic-gate } 5344*0Sstevel@tonic-gate ddi_release_devi(kept); 5345*0Sstevel@tonic-gate 5346*0Sstevel@tonic-gate return (ret); 5347*0Sstevel@tonic-gate } 5348*0Sstevel@tonic-gate 5349*0Sstevel@tonic-gate /* 5350*0Sstevel@tonic-gate * Called from common/io/pm.c 5351*0Sstevel@tonic-gate */ 5352*0Sstevel@tonic-gate int 5353*0Sstevel@tonic-gate pm_cur_power(pm_component_t *cp) 5354*0Sstevel@tonic-gate { 5355*0Sstevel@tonic-gate return (cur_power(cp)); 5356*0Sstevel@tonic-gate } 5357*0Sstevel@tonic-gate 5358*0Sstevel@tonic-gate /* 5359*0Sstevel@tonic-gate * External interface to sanity-check a power level. 5360*0Sstevel@tonic-gate */ 5361*0Sstevel@tonic-gate int 5362*0Sstevel@tonic-gate pm_valid_power(dev_info_t *dip, int comp, int level) 5363*0Sstevel@tonic-gate { 5364*0Sstevel@tonic-gate PMD_FUNC(pmf, "valid_power") 5365*0Sstevel@tonic-gate 5366*0Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip) && level >= 0) 5367*0Sstevel@tonic-gate return (e_pm_valid_power(dip, comp, level)); 5368*0Sstevel@tonic-gate else { 5369*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: comp=%d, ncomp=%d, level=%d\n", 5370*0Sstevel@tonic-gate pmf, comp, PM_NUMCMPTS(dip), level)) 5371*0Sstevel@tonic-gate return (0); 5372*0Sstevel@tonic-gate } 5373*0Sstevel@tonic-gate } 5374*0Sstevel@tonic-gate 5375*0Sstevel@tonic-gate /* 5376*0Sstevel@tonic-gate * Called when a device that is direct power managed needs to change state. 5377*0Sstevel@tonic-gate * This routine arranges to block the request until the process managing 5378*0Sstevel@tonic-gate * the device makes the change (or some other incompatible change) or 5379*0Sstevel@tonic-gate * the process closes /dev/pm. 5380*0Sstevel@tonic-gate */ 5381*0Sstevel@tonic-gate static int 5382*0Sstevel@tonic-gate pm_block(dev_info_t *dip, int comp, int newpower, int oldpower) 5383*0Sstevel@tonic-gate { 5384*0Sstevel@tonic-gate pm_rsvp_t *new = kmem_zalloc(sizeof (*new), KM_SLEEP); 5385*0Sstevel@tonic-gate int ret = 0; 5386*0Sstevel@tonic-gate void pm_dequeue_blocked(pm_rsvp_t *); 5387*0Sstevel@tonic-gate void pm_enqueue_blocked(pm_rsvp_t *); 5388*0Sstevel@tonic-gate 5389*0Sstevel@tonic-gate ASSERT(!pm_processes_stopped); 5390*0Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip)); 5391*0Sstevel@tonic-gate new->pr_dip = dip; 5392*0Sstevel@tonic-gate new->pr_comp = comp; 5393*0Sstevel@tonic-gate new->pr_newlevel = newpower; 5394*0Sstevel@tonic-gate new->pr_oldlevel = oldpower; 5395*0Sstevel@tonic-gate cv_init(&new->pr_cv, NULL, CV_DEFAULT, NULL); 5396*0Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 5397*0Sstevel@tonic-gate pm_enqueue_blocked(new); 5398*0Sstevel@tonic-gate pm_enqueue_notify(PSC_PENDING_CHANGE, dip, comp, newpower, oldpower, 5399*0Sstevel@tonic-gate PM_CANBLOCK_BLOCK); 5400*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5401*0Sstevel@tonic-gate /* 5402*0Sstevel@tonic-gate * truss may make the cv_wait_sig return prematurely 5403*0Sstevel@tonic-gate */ 5404*0Sstevel@tonic-gate while (ret == 0) { 5405*0Sstevel@tonic-gate /* 5406*0Sstevel@tonic-gate * Normally there will be no user context involved, but if 5407*0Sstevel@tonic-gate * there is (e.g. we are here via an ioctl call to a driver) 5408*0Sstevel@tonic-gate * then we should allow the process to abort the request, 5409*0Sstevel@tonic-gate * or we get an unkillable process if the same thread does 5410*0Sstevel@tonic-gate * PM_DIRECT_PM and pm_raise_power 5411*0Sstevel@tonic-gate */ 5412*0Sstevel@tonic-gate if (cv_wait_sig(&new->pr_cv, &pm_rsvp_lock) == 0) { 5413*0Sstevel@tonic-gate ret = PMP_FAIL; 5414*0Sstevel@tonic-gate } else { 5415*0Sstevel@tonic-gate ret = new->pr_retval; 5416*0Sstevel@tonic-gate } 5417*0Sstevel@tonic-gate } 5418*0Sstevel@tonic-gate pm_dequeue_blocked(new); 5419*0Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 5420*0Sstevel@tonic-gate cv_destroy(&new->pr_cv); 5421*0Sstevel@tonic-gate kmem_free(new, sizeof (*new)); 5422*0Sstevel@tonic-gate return (ret); 5423*0Sstevel@tonic-gate } 5424*0Sstevel@tonic-gate 5425*0Sstevel@tonic-gate /* 5426*0Sstevel@tonic-gate * Returns true if the process is interested in power level changes (has issued 5427*0Sstevel@tonic-gate * PM_GET_STATE_CHANGE ioctl). 5428*0Sstevel@tonic-gate */ 5429*0Sstevel@tonic-gate int 5430*0Sstevel@tonic-gate pm_interest_registered(int clone) 5431*0Sstevel@tonic-gate { 5432*0Sstevel@tonic-gate ASSERT(clone >= 0 && clone < PM_MAX_CLONE - 1); 5433*0Sstevel@tonic-gate return (pm_interest[clone]); 5434*0Sstevel@tonic-gate } 5435*0Sstevel@tonic-gate 5436*0Sstevel@tonic-gate /* 5437*0Sstevel@tonic-gate * Process with clone has just done PM_DIRECT_PM on dip, or has asked to 5438*0Sstevel@tonic-gate * watch all state transitions (dip == NULL). Set up data 5439*0Sstevel@tonic-gate * structs to communicate with process about state changes. 5440*0Sstevel@tonic-gate */ 5441*0Sstevel@tonic-gate void 5442*0Sstevel@tonic-gate pm_register_watcher(int clone, dev_info_t *dip) 5443*0Sstevel@tonic-gate { 5444*0Sstevel@tonic-gate pscc_t *p; 5445*0Sstevel@tonic-gate psce_t *psce; 5446*0Sstevel@tonic-gate static void pm_enqueue_pscc(pscc_t *, pscc_t **); 5447*0Sstevel@tonic-gate 5448*0Sstevel@tonic-gate /* 5449*0Sstevel@tonic-gate * We definitely need a control struct, then we have to search to see 5450*0Sstevel@tonic-gate * there is already an entries struct (in the dip != NULL case). 5451*0Sstevel@tonic-gate */ 5452*0Sstevel@tonic-gate pscc_t *pscc = kmem_zalloc(sizeof (*pscc), KM_SLEEP); 5453*0Sstevel@tonic-gate pscc->pscc_clone = clone; 5454*0Sstevel@tonic-gate pscc->pscc_dip = dip; 5455*0Sstevel@tonic-gate 5456*0Sstevel@tonic-gate if (dip) { 5457*0Sstevel@tonic-gate int found = 0; 5458*0Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_WRITER); 5459*0Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = p->pscc_next) { 5460*0Sstevel@tonic-gate /* 5461*0Sstevel@tonic-gate * Already an entry for this clone, so just use it 5462*0Sstevel@tonic-gate * for the new one (for the case where a single 5463*0Sstevel@tonic-gate * process is watching multiple devices) 5464*0Sstevel@tonic-gate */ 5465*0Sstevel@tonic-gate if (p->pscc_clone == clone) { 5466*0Sstevel@tonic-gate ASSERT(p->pscc_dip != dip); 5467*0Sstevel@tonic-gate pscc->pscc_entries = p->pscc_entries; 5468*0Sstevel@tonic-gate pscc->pscc_entries->psce_references++; 5469*0Sstevel@tonic-gate found++; 5470*0Sstevel@tonic-gate } 5471*0Sstevel@tonic-gate } 5472*0Sstevel@tonic-gate if (!found) { /* create a new one */ 5473*0Sstevel@tonic-gate psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP); 5474*0Sstevel@tonic-gate mutex_init(&psce->psce_lock, NULL, MUTEX_DEFAULT, NULL); 5475*0Sstevel@tonic-gate psce->psce_first = 5476*0Sstevel@tonic-gate kmem_zalloc(sizeof (pm_state_change_t) * PSCCOUNT, 5477*0Sstevel@tonic-gate KM_SLEEP); 5478*0Sstevel@tonic-gate psce->psce_in = psce->psce_out = psce->psce_first; 5479*0Sstevel@tonic-gate psce->psce_last = &psce->psce_first[PSCCOUNT - 1]; 5480*0Sstevel@tonic-gate psce->psce_references = 1; 5481*0Sstevel@tonic-gate pscc->pscc_entries = psce; 5482*0Sstevel@tonic-gate } 5483*0Sstevel@tonic-gate pm_enqueue_pscc(pscc, &pm_pscc_direct); 5484*0Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 5485*0Sstevel@tonic-gate } else { 5486*0Sstevel@tonic-gate ASSERT(!pm_interest_registered(clone)); 5487*0Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_WRITER); 5488*0Sstevel@tonic-gate #ifdef DEBUG 5489*0Sstevel@tonic-gate for (p = pm_pscc_interest; p; p = p->pscc_next) { 5490*0Sstevel@tonic-gate /* 5491*0Sstevel@tonic-gate * Should not be an entry for this clone! 5492*0Sstevel@tonic-gate */ 5493*0Sstevel@tonic-gate ASSERT(p->pscc_clone != clone); 5494*0Sstevel@tonic-gate } 5495*0Sstevel@tonic-gate #endif 5496*0Sstevel@tonic-gate psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP); 5497*0Sstevel@tonic-gate psce->psce_first = kmem_zalloc(sizeof (pm_state_change_t) * 5498*0Sstevel@tonic-gate PSCCOUNT, KM_SLEEP); 5499*0Sstevel@tonic-gate psce->psce_in = psce->psce_out = psce->psce_first; 5500*0Sstevel@tonic-gate psce->psce_last = &psce->psce_first[PSCCOUNT - 1]; 5501*0Sstevel@tonic-gate psce->psce_references = 1; 5502*0Sstevel@tonic-gate pscc->pscc_entries = psce; 5503*0Sstevel@tonic-gate pm_enqueue_pscc(pscc, &pm_pscc_interest); 5504*0Sstevel@tonic-gate pm_interest[clone] = 1; 5505*0Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock); 5506*0Sstevel@tonic-gate } 5507*0Sstevel@tonic-gate } 5508*0Sstevel@tonic-gate 5509*0Sstevel@tonic-gate /* 5510*0Sstevel@tonic-gate * Remove the given entry from the blocked list 5511*0Sstevel@tonic-gate */ 5512*0Sstevel@tonic-gate void 5513*0Sstevel@tonic-gate pm_dequeue_blocked(pm_rsvp_t *p) 5514*0Sstevel@tonic-gate { 5515*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 5516*0Sstevel@tonic-gate if (pm_blocked_list == p) { 5517*0Sstevel@tonic-gate ASSERT(p->pr_prev == NULL); 5518*0Sstevel@tonic-gate if (p->pr_next != NULL) 5519*0Sstevel@tonic-gate p->pr_next->pr_prev = NULL; 5520*0Sstevel@tonic-gate pm_blocked_list = p->pr_next; 5521*0Sstevel@tonic-gate } else { 5522*0Sstevel@tonic-gate ASSERT(p->pr_prev != NULL); 5523*0Sstevel@tonic-gate p->pr_prev->pr_next = p->pr_next; 5524*0Sstevel@tonic-gate if (p->pr_next != NULL) 5525*0Sstevel@tonic-gate p->pr_next->pr_prev = p->pr_prev; 5526*0Sstevel@tonic-gate } 5527*0Sstevel@tonic-gate } 5528*0Sstevel@tonic-gate 5529*0Sstevel@tonic-gate /* 5530*0Sstevel@tonic-gate * Remove the given control struct from the given list 5531*0Sstevel@tonic-gate */ 5532*0Sstevel@tonic-gate static void 5533*0Sstevel@tonic-gate pm_dequeue_pscc(pscc_t *p, pscc_t **list) 5534*0Sstevel@tonic-gate { 5535*0Sstevel@tonic-gate if (*list == p) { 5536*0Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL); 5537*0Sstevel@tonic-gate if (p->pscc_next != NULL) 5538*0Sstevel@tonic-gate p->pscc_next->pscc_prev = NULL; 5539*0Sstevel@tonic-gate *list = p->pscc_next; 5540*0Sstevel@tonic-gate } else { 5541*0Sstevel@tonic-gate ASSERT(p->pscc_prev != NULL); 5542*0Sstevel@tonic-gate p->pscc_prev->pscc_next = p->pscc_next; 5543*0Sstevel@tonic-gate if (p->pscc_next != NULL) 5544*0Sstevel@tonic-gate p->pscc_next->pscc_prev = p->pscc_prev; 5545*0Sstevel@tonic-gate } 5546*0Sstevel@tonic-gate } 5547*0Sstevel@tonic-gate 5548*0Sstevel@tonic-gate /* 5549*0Sstevel@tonic-gate * Stick the control struct specified on the front of the list 5550*0Sstevel@tonic-gate */ 5551*0Sstevel@tonic-gate static void 5552*0Sstevel@tonic-gate pm_enqueue_pscc(pscc_t *p, pscc_t **list) 5553*0Sstevel@tonic-gate { 5554*0Sstevel@tonic-gate pscc_t *h; /* entry at head of list */ 5555*0Sstevel@tonic-gate if ((h = *list) == NULL) { 5556*0Sstevel@tonic-gate *list = p; 5557*0Sstevel@tonic-gate ASSERT(p->pscc_next == NULL); 5558*0Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL); 5559*0Sstevel@tonic-gate } else { 5560*0Sstevel@tonic-gate p->pscc_next = h; 5561*0Sstevel@tonic-gate ASSERT(h->pscc_prev == NULL); 5562*0Sstevel@tonic-gate h->pscc_prev = p; 5563*0Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL); 5564*0Sstevel@tonic-gate *list = p; 5565*0Sstevel@tonic-gate } 5566*0Sstevel@tonic-gate } 5567*0Sstevel@tonic-gate 5568*0Sstevel@tonic-gate /* 5569*0Sstevel@tonic-gate * If dip is NULL, process is closing "clone" clean up all its registrations. 5570*0Sstevel@tonic-gate * Otherwise only clean up those for dip because process is just giving up 5571*0Sstevel@tonic-gate * control of a direct device. 5572*0Sstevel@tonic-gate */ 5573*0Sstevel@tonic-gate void 5574*0Sstevel@tonic-gate pm_deregister_watcher(int clone, dev_info_t *dip) 5575*0Sstevel@tonic-gate { 5576*0Sstevel@tonic-gate pscc_t *p, *pn; 5577*0Sstevel@tonic-gate psce_t *psce; 5578*0Sstevel@tonic-gate int found = 0; 5579*0Sstevel@tonic-gate 5580*0Sstevel@tonic-gate if (dip == NULL) { 5581*0Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_WRITER); 5582*0Sstevel@tonic-gate for (p = pm_pscc_interest; p; p = pn) { 5583*0Sstevel@tonic-gate pn = p->pscc_next; 5584*0Sstevel@tonic-gate if (p->pscc_clone == clone) { 5585*0Sstevel@tonic-gate pm_dequeue_pscc(p, &pm_pscc_interest); 5586*0Sstevel@tonic-gate psce = p->pscc_entries; 5587*0Sstevel@tonic-gate ASSERT(psce->psce_references == 1); 5588*0Sstevel@tonic-gate mutex_destroy(&psce->psce_lock); 5589*0Sstevel@tonic-gate kmem_free(psce->psce_first, 5590*0Sstevel@tonic-gate sizeof (pm_state_change_t) * PSCCOUNT); 5591*0Sstevel@tonic-gate kmem_free(psce, sizeof (*psce)); 5592*0Sstevel@tonic-gate kmem_free(p, sizeof (*p)); 5593*0Sstevel@tonic-gate } 5594*0Sstevel@tonic-gate } 5595*0Sstevel@tonic-gate pm_interest[clone] = 0; 5596*0Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock); 5597*0Sstevel@tonic-gate } 5598*0Sstevel@tonic-gate found = 0; 5599*0Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_WRITER); 5600*0Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = pn) { 5601*0Sstevel@tonic-gate pn = p->pscc_next; 5602*0Sstevel@tonic-gate if ((dip && p->pscc_dip == dip) || 5603*0Sstevel@tonic-gate (dip == NULL && clone == p->pscc_clone)) { 5604*0Sstevel@tonic-gate ASSERT(clone == p->pscc_clone); 5605*0Sstevel@tonic-gate found++; 5606*0Sstevel@tonic-gate /* 5607*0Sstevel@tonic-gate * Remove from control list 5608*0Sstevel@tonic-gate */ 5609*0Sstevel@tonic-gate pm_dequeue_pscc(p, &pm_pscc_direct); 5610*0Sstevel@tonic-gate /* 5611*0Sstevel@tonic-gate * If we're the last reference, free the 5612*0Sstevel@tonic-gate * entries struct. 5613*0Sstevel@tonic-gate */ 5614*0Sstevel@tonic-gate psce = p->pscc_entries; 5615*0Sstevel@tonic-gate ASSERT(psce); 5616*0Sstevel@tonic-gate if (psce->psce_references == 1) { 5617*0Sstevel@tonic-gate kmem_free(psce->psce_first, 5618*0Sstevel@tonic-gate PSCCOUNT * sizeof (pm_state_change_t)); 5619*0Sstevel@tonic-gate kmem_free(psce, sizeof (*psce)); 5620*0Sstevel@tonic-gate } else { 5621*0Sstevel@tonic-gate psce->psce_references--; 5622*0Sstevel@tonic-gate } 5623*0Sstevel@tonic-gate kmem_free(p, sizeof (*p)); 5624*0Sstevel@tonic-gate } 5625*0Sstevel@tonic-gate } 5626*0Sstevel@tonic-gate ASSERT(dip == NULL || found); 5627*0Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock); 5628*0Sstevel@tonic-gate } 5629*0Sstevel@tonic-gate 5630*0Sstevel@tonic-gate /* 5631*0Sstevel@tonic-gate * Search the indicated list for an entry that matches clone, and return a 5632*0Sstevel@tonic-gate * pointer to it. To be interesting, the entry must have something ready to 5633*0Sstevel@tonic-gate * be passed up to the controlling process. 5634*0Sstevel@tonic-gate * The returned entry will be locked upon return from this call. 5635*0Sstevel@tonic-gate */ 5636*0Sstevel@tonic-gate static psce_t * 5637*0Sstevel@tonic-gate pm_psc_find_clone(int clone, pscc_t **list, krwlock_t *lock) 5638*0Sstevel@tonic-gate { 5639*0Sstevel@tonic-gate pscc_t *p; 5640*0Sstevel@tonic-gate psce_t *psce; 5641*0Sstevel@tonic-gate rw_enter(lock, RW_READER); 5642*0Sstevel@tonic-gate for (p = *list; p; p = p->pscc_next) { 5643*0Sstevel@tonic-gate if (clone == p->pscc_clone) { 5644*0Sstevel@tonic-gate psce = p->pscc_entries; 5645*0Sstevel@tonic-gate mutex_enter(&psce->psce_lock); 5646*0Sstevel@tonic-gate if (psce->psce_out->size) { 5647*0Sstevel@tonic-gate rw_exit(lock); 5648*0Sstevel@tonic-gate return (psce); 5649*0Sstevel@tonic-gate } else { 5650*0Sstevel@tonic-gate mutex_exit(&psce->psce_lock); 5651*0Sstevel@tonic-gate } 5652*0Sstevel@tonic-gate } 5653*0Sstevel@tonic-gate } 5654*0Sstevel@tonic-gate rw_exit(lock); 5655*0Sstevel@tonic-gate return (NULL); 5656*0Sstevel@tonic-gate } 5657*0Sstevel@tonic-gate 5658*0Sstevel@tonic-gate /* 5659*0Sstevel@tonic-gate * Find an entry for a particular clone in the direct list. 5660*0Sstevel@tonic-gate */ 5661*0Sstevel@tonic-gate psce_t * 5662*0Sstevel@tonic-gate pm_psc_clone_to_direct(int clone) 5663*0Sstevel@tonic-gate { 5664*0Sstevel@tonic-gate static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *); 5665*0Sstevel@tonic-gate return (pm_psc_find_clone(clone, &pm_pscc_direct, 5666*0Sstevel@tonic-gate &pm_pscc_direct_rwlock)); 5667*0Sstevel@tonic-gate } 5668*0Sstevel@tonic-gate 5669*0Sstevel@tonic-gate /* 5670*0Sstevel@tonic-gate * Find an entry for a particular clone in the interest list. 5671*0Sstevel@tonic-gate */ 5672*0Sstevel@tonic-gate psce_t * 5673*0Sstevel@tonic-gate pm_psc_clone_to_interest(int clone) 5674*0Sstevel@tonic-gate { 5675*0Sstevel@tonic-gate static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *); 5676*0Sstevel@tonic-gate return (pm_psc_find_clone(clone, &pm_pscc_interest, 5677*0Sstevel@tonic-gate &pm_pscc_interest_rwlock)); 5678*0Sstevel@tonic-gate } 5679*0Sstevel@tonic-gate 5680*0Sstevel@tonic-gate /* 5681*0Sstevel@tonic-gate * Put the given entry at the head of the blocked list 5682*0Sstevel@tonic-gate */ 5683*0Sstevel@tonic-gate void 5684*0Sstevel@tonic-gate pm_enqueue_blocked(pm_rsvp_t *p) 5685*0Sstevel@tonic-gate { 5686*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock)); 5687*0Sstevel@tonic-gate ASSERT(p->pr_next == NULL); 5688*0Sstevel@tonic-gate ASSERT(p->pr_prev == NULL); 5689*0Sstevel@tonic-gate if (pm_blocked_list != NULL) { 5690*0Sstevel@tonic-gate p->pr_next = pm_blocked_list; 5691*0Sstevel@tonic-gate ASSERT(pm_blocked_list->pr_prev == NULL); 5692*0Sstevel@tonic-gate pm_blocked_list->pr_prev = p; 5693*0Sstevel@tonic-gate pm_blocked_list = p; 5694*0Sstevel@tonic-gate } else { 5695*0Sstevel@tonic-gate pm_blocked_list = p; 5696*0Sstevel@tonic-gate } 5697*0Sstevel@tonic-gate } 5698*0Sstevel@tonic-gate 5699*0Sstevel@tonic-gate /* 5700*0Sstevel@tonic-gate * Sets every power managed device back to its default threshold 5701*0Sstevel@tonic-gate */ 5702*0Sstevel@tonic-gate void 5703*0Sstevel@tonic-gate pm_all_to_default_thresholds(void) 5704*0Sstevel@tonic-gate { 5705*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_set_dev_thr_walk, 5706*0Sstevel@tonic-gate (void *) &pm_system_idle_threshold); 5707*0Sstevel@tonic-gate } 5708*0Sstevel@tonic-gate 5709*0Sstevel@tonic-gate static int 5710*0Sstevel@tonic-gate pm_set_dev_thr_walk(dev_info_t *dip, void *arg) 5711*0Sstevel@tonic-gate { 5712*0Sstevel@tonic-gate int thr = (int)(*(int *)arg); 5713*0Sstevel@tonic-gate 5714*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) 5715*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 5716*0Sstevel@tonic-gate pm_set_device_threshold(dip, thr, PMC_DEF_THRESH); 5717*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 5718*0Sstevel@tonic-gate } 5719*0Sstevel@tonic-gate 5720*0Sstevel@tonic-gate /* 5721*0Sstevel@tonic-gate * Returns the current threshold value (in seconds) for the indicated component 5722*0Sstevel@tonic-gate */ 5723*0Sstevel@tonic-gate int 5724*0Sstevel@tonic-gate pm_current_threshold(dev_info_t *dip, int comp, int *threshp) 5725*0Sstevel@tonic-gate { 5726*0Sstevel@tonic-gate if (comp < 0 || comp >= PM_NUMCMPTS(dip)) { 5727*0Sstevel@tonic-gate return (DDI_FAILURE); 5728*0Sstevel@tonic-gate } else { 5729*0Sstevel@tonic-gate *threshp = cur_threshold(dip, comp); 5730*0Sstevel@tonic-gate return (DDI_SUCCESS); 5731*0Sstevel@tonic-gate } 5732*0Sstevel@tonic-gate } 5733*0Sstevel@tonic-gate 5734*0Sstevel@tonic-gate /* 5735*0Sstevel@tonic-gate * To be called when changing the power level of a component of a device. 5736*0Sstevel@tonic-gate * On some platforms, changing power on one device may require that power 5737*0Sstevel@tonic-gate * be changed on other, related devices in the same transaction. Thus, we 5738*0Sstevel@tonic-gate * always pass this request to the platform power manager so that all the 5739*0Sstevel@tonic-gate * affected devices will be locked. 5740*0Sstevel@tonic-gate */ 5741*0Sstevel@tonic-gate void 5742*0Sstevel@tonic-gate pm_lock_power(dev_info_t *dip, int *circp) 5743*0Sstevel@tonic-gate { 5744*0Sstevel@tonic-gate power_req_t power_req; 5745*0Sstevel@tonic-gate int result; 5746*0Sstevel@tonic-gate 5747*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_LOCK_POWER; 5748*0Sstevel@tonic-gate power_req.req.ppm_lock_power_req.who = dip; 5749*0Sstevel@tonic-gate power_req.req.ppm_lock_power_req.circp = circp; 5750*0Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result); 5751*0Sstevel@tonic-gate } 5752*0Sstevel@tonic-gate 5753*0Sstevel@tonic-gate /* 5754*0Sstevel@tonic-gate * Release the lock (or locks) acquired to change the power of a device. 5755*0Sstevel@tonic-gate * See comments for pm_lock_power. 5756*0Sstevel@tonic-gate */ 5757*0Sstevel@tonic-gate void 5758*0Sstevel@tonic-gate pm_unlock_power(dev_info_t *dip, int circ) 5759*0Sstevel@tonic-gate { 5760*0Sstevel@tonic-gate power_req_t power_req; 5761*0Sstevel@tonic-gate int result; 5762*0Sstevel@tonic-gate 5763*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNLOCK_POWER; 5764*0Sstevel@tonic-gate power_req.req.ppm_unlock_power_req.who = dip; 5765*0Sstevel@tonic-gate power_req.req.ppm_unlock_power_req.circ = circ; 5766*0Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result); 5767*0Sstevel@tonic-gate } 5768*0Sstevel@tonic-gate 5769*0Sstevel@tonic-gate 5770*0Sstevel@tonic-gate /* 5771*0Sstevel@tonic-gate * Attempt (without blocking) to acquire the lock(s) needed to change the 5772*0Sstevel@tonic-gate * power of a component of a device. See comments for pm_lock_power. 5773*0Sstevel@tonic-gate * 5774*0Sstevel@tonic-gate * Return: 1 if lock(s) acquired, 0 if not. 5775*0Sstevel@tonic-gate */ 5776*0Sstevel@tonic-gate int 5777*0Sstevel@tonic-gate pm_try_locking_power(dev_info_t *dip, int *circp) 5778*0Sstevel@tonic-gate { 5779*0Sstevel@tonic-gate power_req_t power_req; 5780*0Sstevel@tonic-gate int result; 5781*0Sstevel@tonic-gate 5782*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_TRY_LOCK_POWER; 5783*0Sstevel@tonic-gate power_req.req.ppm_lock_power_req.who = dip; 5784*0Sstevel@tonic-gate power_req.req.ppm_lock_power_req.circp = circp; 5785*0Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result); 5786*0Sstevel@tonic-gate return (result); 5787*0Sstevel@tonic-gate } 5788*0Sstevel@tonic-gate 5789*0Sstevel@tonic-gate 5790*0Sstevel@tonic-gate /* 5791*0Sstevel@tonic-gate * Lock power state of a device. 5792*0Sstevel@tonic-gate * 5793*0Sstevel@tonic-gate * The implementation handles a special case where another thread may have 5794*0Sstevel@tonic-gate * acquired the lock and created/launched this thread to do the work. If 5795*0Sstevel@tonic-gate * the lock cannot be acquired immediately, we check to see if this thread 5796*0Sstevel@tonic-gate * is registered as a borrower of the lock. If so, we may proceed without 5797*0Sstevel@tonic-gate * the lock. This assumes that the lending thread blocks on the completion 5798*0Sstevel@tonic-gate * of this thread. 5799*0Sstevel@tonic-gate * 5800*0Sstevel@tonic-gate * Note 1: for use by ppm only. 5801*0Sstevel@tonic-gate * 5802*0Sstevel@tonic-gate * Note 2: On failing to get the lock immediately, we search lock_loan list 5803*0Sstevel@tonic-gate * for curthread (as borrower of the lock). On a hit, we check that the 5804*0Sstevel@tonic-gate * lending thread already owns the lock we want. It is safe to compare 5805*0Sstevel@tonic-gate * devi_busy_thread and thread id of the lender because in the == case (the 5806*0Sstevel@tonic-gate * only one we care about) we know that the owner is blocked. Similarly, 5807*0Sstevel@tonic-gate * If we find that curthread isn't registered as a lock borrower, it is safe 5808*0Sstevel@tonic-gate * to use the blocking call (ndi_devi_enter) because we know that if we 5809*0Sstevel@tonic-gate * weren't already listed as a borrower (upstream on the call stack) we won't 5810*0Sstevel@tonic-gate * become one. 5811*0Sstevel@tonic-gate */ 5812*0Sstevel@tonic-gate void 5813*0Sstevel@tonic-gate pm_lock_power_single(dev_info_t *dip, int *circp) 5814*0Sstevel@tonic-gate { 5815*0Sstevel@tonic-gate lock_loan_t *cur; 5816*0Sstevel@tonic-gate 5817*0Sstevel@tonic-gate /* if the lock is available, we are done. */ 5818*0Sstevel@tonic-gate if (ndi_devi_tryenter(dip, circp)) 5819*0Sstevel@tonic-gate return; 5820*0Sstevel@tonic-gate 5821*0Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 5822*0Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */ 5823*0Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next) 5824*0Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 5825*0Sstevel@tonic-gate break; 5826*0Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 5827*0Sstevel@tonic-gate 5828*0Sstevel@tonic-gate /* if this thread not already registered, it is safe to block */ 5829*0Sstevel@tonic-gate if (cur == NULL) 5830*0Sstevel@tonic-gate ndi_devi_enter(dip, circp); 5831*0Sstevel@tonic-gate else { 5832*0Sstevel@tonic-gate /* registered: does lender own the lock we want? */ 5833*0Sstevel@tonic-gate if (cur->pmlk_lender == DEVI(dip)->devi_busy_thread) { 5834*0Sstevel@tonic-gate ASSERT(cur->pmlk_dip == NULL || cur->pmlk_dip == dip); 5835*0Sstevel@tonic-gate cur->pmlk_dip = dip; 5836*0Sstevel@tonic-gate } else /* no: just block for it */ 5837*0Sstevel@tonic-gate ndi_devi_enter(dip, circp); 5838*0Sstevel@tonic-gate 5839*0Sstevel@tonic-gate } 5840*0Sstevel@tonic-gate } 5841*0Sstevel@tonic-gate 5842*0Sstevel@tonic-gate /* 5843*0Sstevel@tonic-gate * Drop the lock on the device's power state. See comment for 5844*0Sstevel@tonic-gate * pm_lock_power_single() for special implementation considerations. 5845*0Sstevel@tonic-gate * 5846*0Sstevel@tonic-gate * Note: for use by ppm only. 5847*0Sstevel@tonic-gate */ 5848*0Sstevel@tonic-gate void 5849*0Sstevel@tonic-gate pm_unlock_power_single(dev_info_t *dip, int circ) 5850*0Sstevel@tonic-gate { 5851*0Sstevel@tonic-gate lock_loan_t *cur; 5852*0Sstevel@tonic-gate 5853*0Sstevel@tonic-gate /* optimization: mutex not needed to check empty list */ 5854*0Sstevel@tonic-gate if (lock_loan_head.pmlk_next == NULL) { 5855*0Sstevel@tonic-gate ndi_devi_exit(dip, circ); 5856*0Sstevel@tonic-gate return; 5857*0Sstevel@tonic-gate } 5858*0Sstevel@tonic-gate 5859*0Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 5860*0Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */ 5861*0Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next) 5862*0Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 5863*0Sstevel@tonic-gate break; 5864*0Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 5865*0Sstevel@tonic-gate 5866*0Sstevel@tonic-gate if (cur == NULL || cur->pmlk_dip != dip) 5867*0Sstevel@tonic-gate /* we acquired the lock directly, so return it */ 5868*0Sstevel@tonic-gate ndi_devi_exit(dip, circ); 5869*0Sstevel@tonic-gate } 5870*0Sstevel@tonic-gate 5871*0Sstevel@tonic-gate /* 5872*0Sstevel@tonic-gate * Try to take the lock for changing the power level of a component. 5873*0Sstevel@tonic-gate * 5874*0Sstevel@tonic-gate * Note: for use by ppm only. 5875*0Sstevel@tonic-gate */ 5876*0Sstevel@tonic-gate int 5877*0Sstevel@tonic-gate pm_try_locking_power_single(dev_info_t *dip, int *circp) 5878*0Sstevel@tonic-gate { 5879*0Sstevel@tonic-gate return (ndi_devi_tryenter(dip, circp)); 5880*0Sstevel@tonic-gate } 5881*0Sstevel@tonic-gate 5882*0Sstevel@tonic-gate #ifdef DEBUG 5883*0Sstevel@tonic-gate /* 5884*0Sstevel@tonic-gate * The following are used only to print out data structures for debugging 5885*0Sstevel@tonic-gate */ 5886*0Sstevel@tonic-gate void 5887*0Sstevel@tonic-gate prdeps(char *msg) 5888*0Sstevel@tonic-gate { 5889*0Sstevel@tonic-gate 5890*0Sstevel@tonic-gate pm_pdr_t *rp; 5891*0Sstevel@tonic-gate int i; 5892*0Sstevel@tonic-gate 5893*0Sstevel@tonic-gate pm_log("pm_dep_head %s %p\n", msg, (void *)pm_dep_head); 5894*0Sstevel@tonic-gate for (rp = pm_dep_head; rp; rp = rp->pdr_next) { 5895*0Sstevel@tonic-gate pm_log("%p: %s keeper %s, kept %s, kept count %d, next %p\n", 5896*0Sstevel@tonic-gate (void *)rp, (rp->pdr_isprop ? "property" : "device"), 5897*0Sstevel@tonic-gate rp->pdr_keeper, rp->pdr_kept, rp->pdr_kept_count, 5898*0Sstevel@tonic-gate (void *)rp->pdr_next); 5899*0Sstevel@tonic-gate if (rp->pdr_kept_count != 0) { 5900*0Sstevel@tonic-gate pm_log("kept list = "); 5901*0Sstevel@tonic-gate i = 0; 5902*0Sstevel@tonic-gate while (i < rp->pdr_kept_count) { 5903*0Sstevel@tonic-gate pm_log("%s ", rp->pdr_kept_paths[i]); 5904*0Sstevel@tonic-gate i++; 5905*0Sstevel@tonic-gate } 5906*0Sstevel@tonic-gate pm_log("\n"); 5907*0Sstevel@tonic-gate } 5908*0Sstevel@tonic-gate } 5909*0Sstevel@tonic-gate } 5910*0Sstevel@tonic-gate 5911*0Sstevel@tonic-gate void 5912*0Sstevel@tonic-gate pr_noinvol(char *hdr) 5913*0Sstevel@tonic-gate { 5914*0Sstevel@tonic-gate pm_noinvol_t *ip; 5915*0Sstevel@tonic-gate 5916*0Sstevel@tonic-gate pm_log("%s\n", hdr); 5917*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 5918*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) 5919*0Sstevel@tonic-gate pm_log("\tmaj %d, flags %x, noinvolpm %d %s\n", 5920*0Sstevel@tonic-gate ip->ni_major, ip->ni_flags, ip->ni_noinvolpm, ip->ni_path); 5921*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 5922*0Sstevel@tonic-gate } 5923*0Sstevel@tonic-gate #endif 5924*0Sstevel@tonic-gate 5925*0Sstevel@tonic-gate /* 5926*0Sstevel@tonic-gate * Attempt to apply the thresholds indicated by rp to the node specified by 5927*0Sstevel@tonic-gate * dip. 5928*0Sstevel@tonic-gate */ 5929*0Sstevel@tonic-gate void 5930*0Sstevel@tonic-gate pm_apply_recorded_thresh(dev_info_t *dip, pm_thresh_rec_t *rp) 5931*0Sstevel@tonic-gate { 5932*0Sstevel@tonic-gate PMD_FUNC(pmf, "apply_recorded_thresh") 5933*0Sstevel@tonic-gate int i, j; 5934*0Sstevel@tonic-gate int comps = PM_NUMCMPTS(dip); 5935*0Sstevel@tonic-gate struct pm_component *cp; 5936*0Sstevel@tonic-gate pm_pte_t *ep; 5937*0Sstevel@tonic-gate int pm_valid_thresh(dev_info_t *, pm_thresh_rec_t *); 5938*0Sstevel@tonic-gate 5939*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: part: %s@%s(%s#%d), rp %p, %s\n", pmf, 5940*0Sstevel@tonic-gate PM_DEVICE(dip), (void *)rp, rp->ptr_physpath)) 5941*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 5942*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip) || !pm_valid_thresh(dip, rp)) { 5943*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_GET_PM_INFO %p\n", 5944*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), (void*)PM_GET_PM_INFO(dip))) 5945*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_ISBC %d\n", 5946*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_ISBC(dip))) 5947*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) pm_valid_thresh %d\n", 5948*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_valid_thresh(dip, rp))) 5949*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5950*0Sstevel@tonic-gate return; 5951*0Sstevel@tonic-gate } 5952*0Sstevel@tonic-gate 5953*0Sstevel@tonic-gate ep = rp->ptr_entries; 5954*0Sstevel@tonic-gate /* 5955*0Sstevel@tonic-gate * Here we do the special case of a device threshold 5956*0Sstevel@tonic-gate */ 5957*0Sstevel@tonic-gate if (rp->ptr_numcomps == 0) { /* PM_SET_DEVICE_THRESHOLD product */ 5958*0Sstevel@tonic-gate ASSERT(ep && ep->pte_numthresh == 1); 5959*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: set dev thr %s@%s(%s#%d) to 0x%x\n", 5960*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), ep->pte_thresh[0])) 5961*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5962*0Sstevel@tonic-gate pm_set_device_threshold(dip, ep->pte_thresh[0], PMC_DEV_THRESH); 5963*0Sstevel@tonic-gate if (autopm_enabled) 5964*0Sstevel@tonic-gate pm_rescan(dip); 5965*0Sstevel@tonic-gate return; 5966*0Sstevel@tonic-gate } 5967*0Sstevel@tonic-gate for (i = 0; i < comps; i++) { 5968*0Sstevel@tonic-gate cp = PM_CP(dip, i); 5969*0Sstevel@tonic-gate for (j = 0; j < ep->pte_numthresh; j++) { 5970*0Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: set thr %d for %s@%s(%s#%d)[%d] " 5971*0Sstevel@tonic-gate "to %x\n", pmf, j, PM_DEVICE(dip), 5972*0Sstevel@tonic-gate i, ep->pte_thresh[j])) 5973*0Sstevel@tonic-gate cp->pmc_comp.pmc_thresh[j + 1] = ep->pte_thresh[j]; 5974*0Sstevel@tonic-gate } 5975*0Sstevel@tonic-gate ep++; 5976*0Sstevel@tonic-gate } 5977*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE; 5978*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMP_THRESH; 5979*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 5980*0Sstevel@tonic-gate 5981*0Sstevel@tonic-gate if (autopm_enabled) 5982*0Sstevel@tonic-gate pm_rescan(dip); 5983*0Sstevel@tonic-gate } 5984*0Sstevel@tonic-gate 5985*0Sstevel@tonic-gate /* 5986*0Sstevel@tonic-gate * Returns true if the threshold specified by rp could be applied to dip 5987*0Sstevel@tonic-gate * (that is, the number of components and transitions are the same) 5988*0Sstevel@tonic-gate */ 5989*0Sstevel@tonic-gate int 5990*0Sstevel@tonic-gate pm_valid_thresh(dev_info_t *dip, pm_thresh_rec_t *rp) 5991*0Sstevel@tonic-gate { 5992*0Sstevel@tonic-gate PMD_FUNC(pmf, "valid_thresh") 5993*0Sstevel@tonic-gate int comps, i; 5994*0Sstevel@tonic-gate pm_component_t *cp; 5995*0Sstevel@tonic-gate pm_pte_t *ep; 5996*0Sstevel@tonic-gate 5997*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) { 5998*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s: no pm_info or BC\n", pmf, 5999*0Sstevel@tonic-gate rp->ptr_physpath)) 6000*0Sstevel@tonic-gate return (0); 6001*0Sstevel@tonic-gate } 6002*0Sstevel@tonic-gate /* 6003*0Sstevel@tonic-gate * Special case: we represent the PM_SET_DEVICE_THRESHOLD case by 6004*0Sstevel@tonic-gate * an entry with numcomps == 0, (since we don't know how many 6005*0Sstevel@tonic-gate * components there are in advance). This is always a valid 6006*0Sstevel@tonic-gate * spec. 6007*0Sstevel@tonic-gate */ 6008*0Sstevel@tonic-gate if (rp->ptr_numcomps == 0) { 6009*0Sstevel@tonic-gate ASSERT(rp->ptr_entries && rp->ptr_entries->pte_numthresh == 1); 6010*0Sstevel@tonic-gate return (1); 6011*0Sstevel@tonic-gate } 6012*0Sstevel@tonic-gate if (rp->ptr_numcomps != (comps = PM_NUMCMPTS(dip))) { 6013*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp # mm (dip %d cmd %d) for %s\n", 6014*0Sstevel@tonic-gate pmf, PM_NUMCMPTS(dip), rp->ptr_numcomps, rp->ptr_physpath)) 6015*0Sstevel@tonic-gate return (0); 6016*0Sstevel@tonic-gate } 6017*0Sstevel@tonic-gate ep = rp->ptr_entries; 6018*0Sstevel@tonic-gate for (i = 0; i < comps; i++) { 6019*0Sstevel@tonic-gate cp = PM_CP(dip, i); 6020*0Sstevel@tonic-gate if ((ep + i)->pte_numthresh != 6021*0Sstevel@tonic-gate cp->pmc_comp.pmc_numlevels - 1) { 6022*0Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s[%d]: thresh=%d, record=%d\n", 6023*0Sstevel@tonic-gate pmf, rp->ptr_physpath, i, 6024*0Sstevel@tonic-gate cp->pmc_comp.pmc_numlevels - 1, 6025*0Sstevel@tonic-gate (ep + i)->pte_numthresh)) 6026*0Sstevel@tonic-gate return (0); 6027*0Sstevel@tonic-gate } 6028*0Sstevel@tonic-gate } 6029*0Sstevel@tonic-gate return (1); 6030*0Sstevel@tonic-gate } 6031*0Sstevel@tonic-gate 6032*0Sstevel@tonic-gate /* 6033*0Sstevel@tonic-gate * Remove any recorded threshold for device physpath 6034*0Sstevel@tonic-gate * We know there will be at most one. 6035*0Sstevel@tonic-gate */ 6036*0Sstevel@tonic-gate void 6037*0Sstevel@tonic-gate pm_unrecord_threshold(char *physpath) 6038*0Sstevel@tonic-gate { 6039*0Sstevel@tonic-gate pm_thresh_rec_t *pptr, *ptr; 6040*0Sstevel@tonic-gate 6041*0Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER); 6042*0Sstevel@tonic-gate for (pptr = NULL, ptr = pm_thresh_head; ptr; ptr = ptr->ptr_next) { 6043*0Sstevel@tonic-gate if (strcmp(physpath, ptr->ptr_physpath) == 0) { 6044*0Sstevel@tonic-gate if (pptr) { 6045*0Sstevel@tonic-gate pptr->ptr_next = ptr->ptr_next; 6046*0Sstevel@tonic-gate } else { 6047*0Sstevel@tonic-gate ASSERT(pm_thresh_head == ptr); 6048*0Sstevel@tonic-gate pm_thresh_head = ptr->ptr_next; 6049*0Sstevel@tonic-gate } 6050*0Sstevel@tonic-gate kmem_free(ptr, ptr->ptr_size); 6051*0Sstevel@tonic-gate break; 6052*0Sstevel@tonic-gate } 6053*0Sstevel@tonic-gate pptr = ptr; 6054*0Sstevel@tonic-gate } 6055*0Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 6056*0Sstevel@tonic-gate } 6057*0Sstevel@tonic-gate 6058*0Sstevel@tonic-gate /* 6059*0Sstevel@tonic-gate * Discard all recorded thresholds. We are returning to the default pm state. 6060*0Sstevel@tonic-gate */ 6061*0Sstevel@tonic-gate void 6062*0Sstevel@tonic-gate pm_discard_thresholds(void) 6063*0Sstevel@tonic-gate { 6064*0Sstevel@tonic-gate pm_thresh_rec_t *rp; 6065*0Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER); 6066*0Sstevel@tonic-gate while (pm_thresh_head) { 6067*0Sstevel@tonic-gate rp = pm_thresh_head; 6068*0Sstevel@tonic-gate pm_thresh_head = rp->ptr_next; 6069*0Sstevel@tonic-gate kmem_free(rp, rp->ptr_size); 6070*0Sstevel@tonic-gate } 6071*0Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock); 6072*0Sstevel@tonic-gate } 6073*0Sstevel@tonic-gate 6074*0Sstevel@tonic-gate /* 6075*0Sstevel@tonic-gate * Discard all recorded dependencies. We are returning to the default pm state. 6076*0Sstevel@tonic-gate */ 6077*0Sstevel@tonic-gate void 6078*0Sstevel@tonic-gate pm_discard_dependencies(void) 6079*0Sstevel@tonic-gate { 6080*0Sstevel@tonic-gate pm_pdr_t *rp; 6081*0Sstevel@tonic-gate int i; 6082*0Sstevel@tonic-gate size_t length; 6083*0Sstevel@tonic-gate 6084*0Sstevel@tonic-gate #ifdef DEBUG 6085*0Sstevel@tonic-gate if (pm_debug & PMD_DEP) 6086*0Sstevel@tonic-gate prdeps("Before discard\n"); 6087*0Sstevel@tonic-gate #endif 6088*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_discard_dep_walk, NULL); 6089*0Sstevel@tonic-gate 6090*0Sstevel@tonic-gate #ifdef DEBUG 6091*0Sstevel@tonic-gate if (pm_debug & PMD_DEP) 6092*0Sstevel@tonic-gate prdeps("After discard\n"); 6093*0Sstevel@tonic-gate #endif 6094*0Sstevel@tonic-gate while (pm_dep_head) { 6095*0Sstevel@tonic-gate rp = pm_dep_head; 6096*0Sstevel@tonic-gate if (!rp->pdr_isprop) { 6097*0Sstevel@tonic-gate ASSERT(rp->pdr_satisfied == 0); 6098*0Sstevel@tonic-gate ASSERT(pm_unresolved_deps); 6099*0Sstevel@tonic-gate pm_unresolved_deps--; 6100*0Sstevel@tonic-gate } else { 6101*0Sstevel@tonic-gate ASSERT(pm_prop_deps); 6102*0Sstevel@tonic-gate pm_prop_deps--; 6103*0Sstevel@tonic-gate } 6104*0Sstevel@tonic-gate pm_dep_head = rp->pdr_next; 6105*0Sstevel@tonic-gate if (rp->pdr_kept_count) { 6106*0Sstevel@tonic-gate for (i = 0; i < rp->pdr_kept_count; i++) { 6107*0Sstevel@tonic-gate length = strlen(rp->pdr_kept_paths[i]) + 1; 6108*0Sstevel@tonic-gate kmem_free(rp->pdr_kept_paths[i], length); 6109*0Sstevel@tonic-gate } 6110*0Sstevel@tonic-gate kmem_free(rp->pdr_kept_paths, 6111*0Sstevel@tonic-gate rp->pdr_kept_count * sizeof (char **)); 6112*0Sstevel@tonic-gate } 6113*0Sstevel@tonic-gate kmem_free(rp, rp->pdr_size); 6114*0Sstevel@tonic-gate } 6115*0Sstevel@tonic-gate } 6116*0Sstevel@tonic-gate 6117*0Sstevel@tonic-gate 6118*0Sstevel@tonic-gate static int 6119*0Sstevel@tonic-gate pm_discard_dep_walk(dev_info_t *dip, void *arg) 6120*0Sstevel@tonic-gate { 6121*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 6122*0Sstevel@tonic-gate char *pathbuf; 6123*0Sstevel@tonic-gate 6124*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) 6125*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6126*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 6127*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 6128*0Sstevel@tonic-gate pm_free_keeper(pathbuf, 0); 6129*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 6130*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6131*0Sstevel@tonic-gate } 6132*0Sstevel@tonic-gate 6133*0Sstevel@tonic-gate static int 6134*0Sstevel@tonic-gate pm_kept_walk(dev_info_t *dip, void *arg) 6135*0Sstevel@tonic-gate { 6136*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 6137*0Sstevel@tonic-gate char *pathbuf; 6138*0Sstevel@tonic-gate 6139*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 6140*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 6141*0Sstevel@tonic-gate (void) pm_kept(pathbuf); 6142*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 6143*0Sstevel@tonic-gate 6144*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6145*0Sstevel@tonic-gate } 6146*0Sstevel@tonic-gate 6147*0Sstevel@tonic-gate static int 6148*0Sstevel@tonic-gate pm_keeper_walk(dev_info_t *dip, void *arg) 6149*0Sstevel@tonic-gate { 6150*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 6151*0Sstevel@tonic-gate char *pathbuf; 6152*0Sstevel@tonic-gate 6153*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 6154*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 6155*0Sstevel@tonic-gate (void) pm_keeper(pathbuf); 6156*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 6157*0Sstevel@tonic-gate 6158*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 6159*0Sstevel@tonic-gate } 6160*0Sstevel@tonic-gate 6161*0Sstevel@tonic-gate static char * 6162*0Sstevel@tonic-gate pdw_type_decode(int type) 6163*0Sstevel@tonic-gate { 6164*0Sstevel@tonic-gate switch (type) { 6165*0Sstevel@tonic-gate case PM_DEP_WK_POWER_ON: 6166*0Sstevel@tonic-gate return ("power on"); 6167*0Sstevel@tonic-gate case PM_DEP_WK_POWER_OFF: 6168*0Sstevel@tonic-gate return ("power off"); 6169*0Sstevel@tonic-gate case PM_DEP_WK_DETACH: 6170*0Sstevel@tonic-gate return ("detach"); 6171*0Sstevel@tonic-gate case PM_DEP_WK_REMOVE_DEP: 6172*0Sstevel@tonic-gate return ("remove dep"); 6173*0Sstevel@tonic-gate case PM_DEP_WK_BRINGUP_SELF: 6174*0Sstevel@tonic-gate return ("bringup self"); 6175*0Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER: 6176*0Sstevel@tonic-gate return ("add dependent"); 6177*0Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER_PROP: 6178*0Sstevel@tonic-gate return ("add dependent property"); 6179*0Sstevel@tonic-gate case PM_DEP_WK_KEPT: 6180*0Sstevel@tonic-gate return ("kept"); 6181*0Sstevel@tonic-gate case PM_DEP_WK_KEEPER: 6182*0Sstevel@tonic-gate return ("keeper"); 6183*0Sstevel@tonic-gate case PM_DEP_WK_ATTACH: 6184*0Sstevel@tonic-gate return ("attach"); 6185*0Sstevel@tonic-gate case PM_DEP_WK_CHECK_KEPT: 6186*0Sstevel@tonic-gate return ("check kept"); 6187*0Sstevel@tonic-gate case PM_DEP_WK_CPR_SUSPEND: 6188*0Sstevel@tonic-gate return ("suspend"); 6189*0Sstevel@tonic-gate case PM_DEP_WK_CPR_RESUME: 6190*0Sstevel@tonic-gate return ("resume"); 6191*0Sstevel@tonic-gate default: 6192*0Sstevel@tonic-gate return ("unknown"); 6193*0Sstevel@tonic-gate } 6194*0Sstevel@tonic-gate 6195*0Sstevel@tonic-gate } 6196*0Sstevel@tonic-gate 6197*0Sstevel@tonic-gate static void 6198*0Sstevel@tonic-gate pm_rele_dep(char *keeper) 6199*0Sstevel@tonic-gate { 6200*0Sstevel@tonic-gate PMD_FUNC(pmf, "rele_dep") 6201*0Sstevel@tonic-gate pm_pdr_t *dp; 6202*0Sstevel@tonic-gate char *kept_path = NULL; 6203*0Sstevel@tonic-gate dev_info_t *kept = NULL; 6204*0Sstevel@tonic-gate int count = 0; 6205*0Sstevel@tonic-gate 6206*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 6207*0Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0) 6208*0Sstevel@tonic-gate continue; 6209*0Sstevel@tonic-gate for (count = 0; count < dp->pdr_kept_count; count++) { 6210*0Sstevel@tonic-gate kept_path = dp->pdr_kept_paths[count]; 6211*0Sstevel@tonic-gate if (kept_path == NULL) 6212*0Sstevel@tonic-gate continue; 6213*0Sstevel@tonic-gate kept = pm_name_to_dip(kept_path, 1); 6214*0Sstevel@tonic-gate if (kept) { 6215*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: release kept=%s@%s(%s#%d) " 6216*0Sstevel@tonic-gate "of keeper=%s\n", pmf, PM_DEVICE(kept), 6217*0Sstevel@tonic-gate keeper)) 6218*0Sstevel@tonic-gate ASSERT(DEVI(kept)->devi_pm_kidsupcnt > 0); 6219*0Sstevel@tonic-gate pm_rele_power(kept); 6220*0Sstevel@tonic-gate ddi_release_devi(kept); 6221*0Sstevel@tonic-gate } 6222*0Sstevel@tonic-gate } 6223*0Sstevel@tonic-gate } 6224*0Sstevel@tonic-gate } 6225*0Sstevel@tonic-gate 6226*0Sstevel@tonic-gate /* 6227*0Sstevel@tonic-gate * Called when we are just released from direct PM. Bring ourself up 6228*0Sstevel@tonic-gate * if our keeper is up since dependency is not honored while a kept 6229*0Sstevel@tonic-gate * device is under direct PM. 6230*0Sstevel@tonic-gate */ 6231*0Sstevel@tonic-gate static void 6232*0Sstevel@tonic-gate pm_bring_self_up(char *keptpath) 6233*0Sstevel@tonic-gate { 6234*0Sstevel@tonic-gate PMD_FUNC(pmf, "bring_self_up") 6235*0Sstevel@tonic-gate dev_info_t *kept; 6236*0Sstevel@tonic-gate dev_info_t *keeper; 6237*0Sstevel@tonic-gate pm_pdr_t *dp; 6238*0Sstevel@tonic-gate int i, j; 6239*0Sstevel@tonic-gate int up = 0, circ; 6240*0Sstevel@tonic-gate 6241*0Sstevel@tonic-gate kept = pm_name_to_dip(keptpath, 1); 6242*0Sstevel@tonic-gate if (kept == NULL) 6243*0Sstevel@tonic-gate return; 6244*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: kept=%s@%s(%s#%d)\n", pmf, PM_DEVICE(kept))) 6245*0Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) { 6246*0Sstevel@tonic-gate if (dp->pdr_kept_count == 0) 6247*0Sstevel@tonic-gate continue; 6248*0Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) { 6249*0Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], keptpath) != 0) 6250*0Sstevel@tonic-gate continue; 6251*0Sstevel@tonic-gate keeper = pm_name_to_dip(dp->pdr_keeper, 1); 6252*0Sstevel@tonic-gate if (keeper) { 6253*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d)\n", 6254*0Sstevel@tonic-gate pmf, PM_DEVICE(keeper))) 6255*0Sstevel@tonic-gate PM_LOCK_POWER(keeper, &circ); 6256*0Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(keeper); 6257*0Sstevel@tonic-gate j++) { 6258*0Sstevel@tonic-gate if (PM_CURPOWER(keeper, j)) { 6259*0Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: comp=" 6260*0Sstevel@tonic-gate "%d is up\n", pmf, j)) 6261*0Sstevel@tonic-gate up++; 6262*0Sstevel@tonic-gate } 6263*0Sstevel@tonic-gate } 6264*0Sstevel@tonic-gate if (up) { 6265*0Sstevel@tonic-gate if (PM_SKBU(kept)) 6266*0Sstevel@tonic-gate DEVI(kept)->devi_pm_flags &= 6267*0Sstevel@tonic-gate ~PMC_SKIP_BRINGUP; 6268*0Sstevel@tonic-gate bring_pmdep_up(kept, 1); 6269*0Sstevel@tonic-gate } 6270*0Sstevel@tonic-gate PM_UNLOCK_POWER(keeper, circ); 6271*0Sstevel@tonic-gate ddi_release_devi(keeper); 6272*0Sstevel@tonic-gate } 6273*0Sstevel@tonic-gate } 6274*0Sstevel@tonic-gate } 6275*0Sstevel@tonic-gate ddi_release_devi(kept); 6276*0Sstevel@tonic-gate } 6277*0Sstevel@tonic-gate 6278*0Sstevel@tonic-gate static void 6279*0Sstevel@tonic-gate pm_process_dep_request(pm_dep_wk_t *work) 6280*0Sstevel@tonic-gate { 6281*0Sstevel@tonic-gate PMD_FUNC(pmf, "dep_req") 6282*0Sstevel@tonic-gate int ret; 6283*0Sstevel@tonic-gate 6284*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: work=%s\n", pmf, 6285*0Sstevel@tonic-gate pdw_type_decode(work->pdw_type))) 6286*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: keeper=%s, kept=%s\n", pmf, 6287*0Sstevel@tonic-gate (work->pdw_keeper ? work->pdw_keeper : "NULL"), 6288*0Sstevel@tonic-gate (work->pdw_kept ? work->pdw_kept : "NULL"))) 6289*0Sstevel@tonic-gate 6290*0Sstevel@tonic-gate switch (work->pdw_type) { 6291*0Sstevel@tonic-gate case PM_DEP_WK_POWER_ON: 6292*0Sstevel@tonic-gate /* Bring up the kept devices and put a hold on them */ 6293*0Sstevel@tonic-gate bring_wekeeps_up(work->pdw_keeper); 6294*0Sstevel@tonic-gate break; 6295*0Sstevel@tonic-gate case PM_DEP_WK_POWER_OFF: 6296*0Sstevel@tonic-gate /* Release the kept devices */ 6297*0Sstevel@tonic-gate pm_rele_dep(work->pdw_keeper); 6298*0Sstevel@tonic-gate break; 6299*0Sstevel@tonic-gate case PM_DEP_WK_DETACH: 6300*0Sstevel@tonic-gate pm_free_keeps(work->pdw_keeper, work->pdw_pwr); 6301*0Sstevel@tonic-gate break; 6302*0Sstevel@tonic-gate case PM_DEP_WK_REMOVE_DEP: 6303*0Sstevel@tonic-gate pm_discard_dependencies(); 6304*0Sstevel@tonic-gate break; 6305*0Sstevel@tonic-gate case PM_DEP_WK_BRINGUP_SELF: 6306*0Sstevel@tonic-gate /* 6307*0Sstevel@tonic-gate * We deferred satisfying our dependency till now, so satisfy 6308*0Sstevel@tonic-gate * it again and bring ourselves up. 6309*0Sstevel@tonic-gate */ 6310*0Sstevel@tonic-gate pm_bring_self_up(work->pdw_kept); 6311*0Sstevel@tonic-gate break; 6312*0Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER: 6313*0Sstevel@tonic-gate (void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 0); 6314*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL); 6315*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL); 6316*0Sstevel@tonic-gate break; 6317*0Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER_PROP: 6318*0Sstevel@tonic-gate (void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 1); 6319*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL); 6320*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL); 6321*0Sstevel@tonic-gate break; 6322*0Sstevel@tonic-gate case PM_DEP_WK_KEPT: 6323*0Sstevel@tonic-gate ret = pm_kept(work->pdw_kept); 6324*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_KEPT: pm_kept returns %d\n", pmf, 6325*0Sstevel@tonic-gate ret)) 6326*0Sstevel@tonic-gate break; 6327*0Sstevel@tonic-gate case PM_DEP_WK_KEEPER: 6328*0Sstevel@tonic-gate ret = pm_keeper(work->pdw_keeper); 6329*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_KEEPER: pm_keeper returns %d\n", 6330*0Sstevel@tonic-gate pmf, ret)) 6331*0Sstevel@tonic-gate break; 6332*0Sstevel@tonic-gate case PM_DEP_WK_ATTACH: 6333*0Sstevel@tonic-gate ret = pm_keeper(work->pdw_keeper); 6334*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_keeper returns %d\n", 6335*0Sstevel@tonic-gate pmf, ret)) 6336*0Sstevel@tonic-gate ret = pm_kept(work->pdw_kept); 6337*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_kept returns %d\n", 6338*0Sstevel@tonic-gate pmf, ret)) 6339*0Sstevel@tonic-gate break; 6340*0Sstevel@tonic-gate case PM_DEP_WK_CHECK_KEPT: 6341*0Sstevel@tonic-gate ret = pm_is_kept(work->pdw_kept); 6342*0Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_CHECK_KEPT: kept=%s, ret=%d\n", 6343*0Sstevel@tonic-gate pmf, work->pdw_kept, ret)) 6344*0Sstevel@tonic-gate break; 6345*0Sstevel@tonic-gate case PM_DEP_WK_CPR_SUSPEND: 6346*0Sstevel@tonic-gate pm_discard_dependencies(); 6347*0Sstevel@tonic-gate break; 6348*0Sstevel@tonic-gate case PM_DEP_WK_CPR_RESUME: 6349*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL); 6350*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL); 6351*0Sstevel@tonic-gate break; 6352*0Sstevel@tonic-gate default: 6353*0Sstevel@tonic-gate ASSERT(0); 6354*0Sstevel@tonic-gate break; 6355*0Sstevel@tonic-gate } 6356*0Sstevel@tonic-gate /* 6357*0Sstevel@tonic-gate * Free the work structure if the requester is not waiting 6358*0Sstevel@tonic-gate * Otherwise it is the requester's responsiblity to free it. 6359*0Sstevel@tonic-gate */ 6360*0Sstevel@tonic-gate if (!work->pdw_wait) { 6361*0Sstevel@tonic-gate if (work->pdw_keeper) 6362*0Sstevel@tonic-gate kmem_free(work->pdw_keeper, 6363*0Sstevel@tonic-gate strlen(work->pdw_keeper) + 1); 6364*0Sstevel@tonic-gate if (work->pdw_kept) 6365*0Sstevel@tonic-gate kmem_free(work->pdw_kept, strlen(work->pdw_kept) + 1); 6366*0Sstevel@tonic-gate kmem_free(work, sizeof (pm_dep_wk_t)); 6367*0Sstevel@tonic-gate } else { 6368*0Sstevel@tonic-gate /* 6369*0Sstevel@tonic-gate * Notify requester if it is waiting for it. 6370*0Sstevel@tonic-gate */ 6371*0Sstevel@tonic-gate work->pdw_ret = ret; 6372*0Sstevel@tonic-gate work->pdw_done = 1; 6373*0Sstevel@tonic-gate cv_signal(&work->pdw_cv); 6374*0Sstevel@tonic-gate } 6375*0Sstevel@tonic-gate } 6376*0Sstevel@tonic-gate 6377*0Sstevel@tonic-gate /* 6378*0Sstevel@tonic-gate * Process PM dependency requests. 6379*0Sstevel@tonic-gate */ 6380*0Sstevel@tonic-gate static void 6381*0Sstevel@tonic-gate pm_dep_thread(void) 6382*0Sstevel@tonic-gate { 6383*0Sstevel@tonic-gate pm_dep_wk_t *work; 6384*0Sstevel@tonic-gate callb_cpr_t cprinfo; 6385*0Sstevel@tonic-gate 6386*0Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &pm_dep_thread_lock, callb_generic_cpr, 6387*0Sstevel@tonic-gate "pm_dep_thread"); 6388*0Sstevel@tonic-gate for (;;) { 6389*0Sstevel@tonic-gate mutex_enter(&pm_dep_thread_lock); 6390*0Sstevel@tonic-gate if (pm_dep_thread_workq == NULL) { 6391*0Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 6392*0Sstevel@tonic-gate cv_wait(&pm_dep_thread_cv, &pm_dep_thread_lock); 6393*0Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, &pm_dep_thread_lock); 6394*0Sstevel@tonic-gate } 6395*0Sstevel@tonic-gate work = pm_dep_thread_workq; 6396*0Sstevel@tonic-gate pm_dep_thread_workq = work->pdw_next; 6397*0Sstevel@tonic-gate if (pm_dep_thread_tail == work) 6398*0Sstevel@tonic-gate pm_dep_thread_tail = work->pdw_next; 6399*0Sstevel@tonic-gate mutex_exit(&pm_dep_thread_lock); 6400*0Sstevel@tonic-gate pm_process_dep_request(work); 6401*0Sstevel@tonic-gate 6402*0Sstevel@tonic-gate } 6403*0Sstevel@tonic-gate /*NOTREACHED*/ 6404*0Sstevel@tonic-gate } 6405*0Sstevel@tonic-gate 6406*0Sstevel@tonic-gate /* 6407*0Sstevel@tonic-gate * Set the power level of the indicated device to unknown (if it is not a 6408*0Sstevel@tonic-gate * backwards compatible device), as it has just been resumed, and it won't 6409*0Sstevel@tonic-gate * know if the power was removed or not. Adjust parent's kidsupcnt if necessary. 6410*0Sstevel@tonic-gate */ 6411*0Sstevel@tonic-gate void 6412*0Sstevel@tonic-gate pm_forget_power_level(dev_info_t *dip) 6413*0Sstevel@tonic-gate { 6414*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 6415*0Sstevel@tonic-gate int i, count = 0; 6416*0Sstevel@tonic-gate 6417*0Sstevel@tonic-gate if (!PM_ISBC(dip)) { 6418*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 6419*0Sstevel@tonic-gate count += (PM_CURPOWER(dip, i) == 0); 6420*0Sstevel@tonic-gate 6421*0Sstevel@tonic-gate if (count && pdip && !PM_WANTS_NOTIFICATION(pdip)) 6422*0Sstevel@tonic-gate e_pm_hold_rele_power(pdip, count); 6423*0Sstevel@tonic-gate 6424*0Sstevel@tonic-gate /* 6425*0Sstevel@tonic-gate * Count this as a power cycle if we care 6426*0Sstevel@tonic-gate */ 6427*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd && 6428*0Sstevel@tonic-gate PM_CP(dip, 0)->pmc_cur_pwr == 0) 6429*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0; 6430*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 6431*0Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN); 6432*0Sstevel@tonic-gate } 6433*0Sstevel@tonic-gate } 6434*0Sstevel@tonic-gate 6435*0Sstevel@tonic-gate /* 6436*0Sstevel@tonic-gate * This function advises the caller whether it should make a power-off 6437*0Sstevel@tonic-gate * transition at this time or not. If the transition is not advised 6438*0Sstevel@tonic-gate * at this time, the time that the next power-off transition can 6439*0Sstevel@tonic-gate * be made from now is returned through "intervalp" pointer. 6440*0Sstevel@tonic-gate * This function returns: 6441*0Sstevel@tonic-gate * 6442*0Sstevel@tonic-gate * 1 power-off advised 6443*0Sstevel@tonic-gate * 0 power-off not advised, intervalp will point to seconds from 6444*0Sstevel@tonic-gate * now that a power-off is advised. If it is passed the number 6445*0Sstevel@tonic-gate * of years that policy specifies the device should last, 6446*0Sstevel@tonic-gate * a large number is returned as the time interval. 6447*0Sstevel@tonic-gate * -1 error 6448*0Sstevel@tonic-gate */ 6449*0Sstevel@tonic-gate int 6450*0Sstevel@tonic-gate pm_trans_check(struct pm_trans_data *datap, time_t *intervalp) 6451*0Sstevel@tonic-gate { 6452*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_trans_check") 6453*0Sstevel@tonic-gate char dbuf[DC_SCSI_MFR_LEN]; 6454*0Sstevel@tonic-gate struct pm_scsi_cycles *scp; 6455*0Sstevel@tonic-gate int service_years, service_weeks, full_years; 6456*0Sstevel@tonic-gate time_t now, service_seconds, tdiff; 6457*0Sstevel@tonic-gate time_t within_year, when_allowed; 6458*0Sstevel@tonic-gate char *ptr; 6459*0Sstevel@tonic-gate int lower_bound_cycles, upper_bound_cycles, cycles_allowed; 6460*0Sstevel@tonic-gate int cycles_diff, cycles_over; 6461*0Sstevel@tonic-gate 6462*0Sstevel@tonic-gate if (datap == NULL) { 6463*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: NULL data pointer!\n", pmf)) 6464*0Sstevel@tonic-gate return (-1); 6465*0Sstevel@tonic-gate } 6466*0Sstevel@tonic-gate 6467*0Sstevel@tonic-gate if (datap->format == DC_SCSI_FORMAT) { 6468*0Sstevel@tonic-gate /* 6469*0Sstevel@tonic-gate * Power cycles of the scsi drives are distributed 6470*0Sstevel@tonic-gate * over 5 years with the following percentage ratio: 6471*0Sstevel@tonic-gate * 6472*0Sstevel@tonic-gate * 30%, 25%, 20%, 15%, and 10% 6473*0Sstevel@tonic-gate * 6474*0Sstevel@tonic-gate * The power cycle quota for each year is distributed 6475*0Sstevel@tonic-gate * linearly through out the year. The equation for 6476*0Sstevel@tonic-gate * determining the expected cycles is: 6477*0Sstevel@tonic-gate * 6478*0Sstevel@tonic-gate * e = a * (n / y) 6479*0Sstevel@tonic-gate * 6480*0Sstevel@tonic-gate * e = expected cycles 6481*0Sstevel@tonic-gate * a = allocated cycles for this year 6482*0Sstevel@tonic-gate * n = number of seconds since beginning of this year 6483*0Sstevel@tonic-gate * y = number of seconds in a year 6484*0Sstevel@tonic-gate * 6485*0Sstevel@tonic-gate * Note that beginning of the year starts the day that 6486*0Sstevel@tonic-gate * the drive has been put on service. 6487*0Sstevel@tonic-gate * 6488*0Sstevel@tonic-gate * If the drive has passed its expected cycles, we 6489*0Sstevel@tonic-gate * can determine when it can start to power cycle 6490*0Sstevel@tonic-gate * again to keep it on track to meet the 5-year 6491*0Sstevel@tonic-gate * life expectancy. The equation for determining 6492*0Sstevel@tonic-gate * when to power cycle is: 6493*0Sstevel@tonic-gate * 6494*0Sstevel@tonic-gate * w = y * (c / a) 6495*0Sstevel@tonic-gate * 6496*0Sstevel@tonic-gate * w = when it can power cycle again 6497*0Sstevel@tonic-gate * y = number of seconds in a year 6498*0Sstevel@tonic-gate * c = current number of cycles 6499*0Sstevel@tonic-gate * a = allocated cycles for the year 6500*0Sstevel@tonic-gate * 6501*0Sstevel@tonic-gate */ 6502*0Sstevel@tonic-gate char pcnt[DC_SCSI_NPY] = { 30, 55, 75, 90, 100 }; 6503*0Sstevel@tonic-gate 6504*0Sstevel@tonic-gate scp = &datap->un.scsi_cycles; 6505*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: format=%d, lifemax=%d, ncycles=%d, " 6506*0Sstevel@tonic-gate "svc_date=%s, svc_flag=%d\n", pmf, datap->format, 6507*0Sstevel@tonic-gate scp->lifemax, scp->ncycles, scp->svc_date, scp->flag)) 6508*0Sstevel@tonic-gate if (scp->ncycles < 0 || scp->flag != 0) { 6509*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: ncycles < 0 || flag != 0\n", pmf)) 6510*0Sstevel@tonic-gate return (-1); 6511*0Sstevel@tonic-gate } 6512*0Sstevel@tonic-gate 6513*0Sstevel@tonic-gate if (scp->ncycles > scp->lifemax) { 6514*0Sstevel@tonic-gate *intervalp = (LONG_MAX / hz); 6515*0Sstevel@tonic-gate return (0); 6516*0Sstevel@tonic-gate } 6517*0Sstevel@tonic-gate 6518*0Sstevel@tonic-gate /* 6519*0Sstevel@tonic-gate * convert service date to time_t 6520*0Sstevel@tonic-gate */ 6521*0Sstevel@tonic-gate bcopy(scp->svc_date, dbuf, DC_SCSI_YEAR_LEN); 6522*0Sstevel@tonic-gate dbuf[DC_SCSI_YEAR_LEN] = '\0'; 6523*0Sstevel@tonic-gate ptr = dbuf; 6524*0Sstevel@tonic-gate service_years = stoi(&ptr) - EPOCH_YEAR; 6525*0Sstevel@tonic-gate bcopy(&scp->svc_date[DC_SCSI_YEAR_LEN], dbuf, 6526*0Sstevel@tonic-gate DC_SCSI_WEEK_LEN); 6527*0Sstevel@tonic-gate dbuf[DC_SCSI_WEEK_LEN] = '\0'; 6528*0Sstevel@tonic-gate 6529*0Sstevel@tonic-gate /* 6530*0Sstevel@tonic-gate * scsi standard does not specify WW data, 6531*0Sstevel@tonic-gate * could be (00-51) or (01-52) 6532*0Sstevel@tonic-gate */ 6533*0Sstevel@tonic-gate ptr = dbuf; 6534*0Sstevel@tonic-gate service_weeks = stoi(&ptr); 6535*0Sstevel@tonic-gate if (service_years < 0 || 6536*0Sstevel@tonic-gate service_weeks < 0 || service_weeks > 52) { 6537*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: service year %d and week %d\n", 6538*0Sstevel@tonic-gate pmf, service_years, service_weeks)) 6539*0Sstevel@tonic-gate return (-1); 6540*0Sstevel@tonic-gate } 6541*0Sstevel@tonic-gate 6542*0Sstevel@tonic-gate /* 6543*0Sstevel@tonic-gate * calculate service date in seconds-since-epoch, 6544*0Sstevel@tonic-gate * adding one day for each leap-year. 6545*0Sstevel@tonic-gate * 6546*0Sstevel@tonic-gate * (years-since-epoch + 2) fixes integer truncation, 6547*0Sstevel@tonic-gate * example: (8) leap-years during [1972, 2000] 6548*0Sstevel@tonic-gate * (2000 - 1970) = 30; and (30 + 2) / 4 = 8; 6549*0Sstevel@tonic-gate */ 6550*0Sstevel@tonic-gate service_seconds = (service_years * DC_SPY) + 6551*0Sstevel@tonic-gate (service_weeks * DC_SPW) + 6552*0Sstevel@tonic-gate (((service_years + 2) / 4) * DC_SPD); 6553*0Sstevel@tonic-gate 6554*0Sstevel@tonic-gate now = gethrestime_sec(); 6555*0Sstevel@tonic-gate /* 6556*0Sstevel@tonic-gate * since the granularity of 'svc_date' is day not second, 6557*0Sstevel@tonic-gate * 'now' should be rounded up to full day. 6558*0Sstevel@tonic-gate */ 6559*0Sstevel@tonic-gate now = ((now + DC_SPD -1) / DC_SPD) * DC_SPD; 6560*0Sstevel@tonic-gate if (service_seconds > now) { 6561*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: service date (%ld) later " 6562*0Sstevel@tonic-gate "than now (%ld)!\n", pmf, service_seconds, now)) 6563*0Sstevel@tonic-gate return (-1); 6564*0Sstevel@tonic-gate } 6565*0Sstevel@tonic-gate 6566*0Sstevel@tonic-gate tdiff = now - service_seconds; 6567*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: age is %ld sec\n", pmf, tdiff)) 6568*0Sstevel@tonic-gate 6569*0Sstevel@tonic-gate /* 6570*0Sstevel@tonic-gate * NOTE - Leap years are not considered in the calculations 6571*0Sstevel@tonic-gate * below. 6572*0Sstevel@tonic-gate */ 6573*0Sstevel@tonic-gate full_years = (tdiff / DC_SPY); 6574*0Sstevel@tonic-gate if ((full_years >= DC_SCSI_NPY) && 6575*0Sstevel@tonic-gate (scp->ncycles <= scp->lifemax)) 6576*0Sstevel@tonic-gate return (1); 6577*0Sstevel@tonic-gate 6578*0Sstevel@tonic-gate /* 6579*0Sstevel@tonic-gate * Determine what is the normal cycle usage for the 6580*0Sstevel@tonic-gate * device at the beginning and the end of this year. 6581*0Sstevel@tonic-gate */ 6582*0Sstevel@tonic-gate lower_bound_cycles = (!full_years) ? 0 : 6583*0Sstevel@tonic-gate ((scp->lifemax * pcnt[full_years - 1]) / 100); 6584*0Sstevel@tonic-gate upper_bound_cycles = (scp->lifemax * pcnt[full_years]) / 100; 6585*0Sstevel@tonic-gate 6586*0Sstevel@tonic-gate if (scp->ncycles <= lower_bound_cycles) 6587*0Sstevel@tonic-gate return (1); 6588*0Sstevel@tonic-gate 6589*0Sstevel@tonic-gate /* 6590*0Sstevel@tonic-gate * The linear slope that determines how many cycles 6591*0Sstevel@tonic-gate * are allowed this year is number of seconds 6592*0Sstevel@tonic-gate * passed this year over total number of seconds in a year. 6593*0Sstevel@tonic-gate */ 6594*0Sstevel@tonic-gate cycles_diff = (upper_bound_cycles - lower_bound_cycles); 6595*0Sstevel@tonic-gate within_year = (tdiff % DC_SPY); 6596*0Sstevel@tonic-gate cycles_allowed = lower_bound_cycles + 6597*0Sstevel@tonic-gate (((uint64_t)cycles_diff * (uint64_t)within_year) / DC_SPY); 6598*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: lived %d yrs and %ld secs\n", pmf, 6599*0Sstevel@tonic-gate full_years, within_year)) 6600*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: # of cycles allowed %d\n", pmf, 6601*0Sstevel@tonic-gate cycles_allowed)) 6602*0Sstevel@tonic-gate 6603*0Sstevel@tonic-gate if (scp->ncycles <= cycles_allowed) 6604*0Sstevel@tonic-gate return (1); 6605*0Sstevel@tonic-gate 6606*0Sstevel@tonic-gate /* 6607*0Sstevel@tonic-gate * The transition is not advised now but we can 6608*0Sstevel@tonic-gate * determine when the next transition can be made. 6609*0Sstevel@tonic-gate * 6610*0Sstevel@tonic-gate * Depending on how many cycles the device has been 6611*0Sstevel@tonic-gate * over-used, we may need to skip years with 6612*0Sstevel@tonic-gate * different percentage quota in order to determine 6613*0Sstevel@tonic-gate * when the next transition can be made. 6614*0Sstevel@tonic-gate */ 6615*0Sstevel@tonic-gate cycles_over = (scp->ncycles - lower_bound_cycles); 6616*0Sstevel@tonic-gate while (cycles_over > cycles_diff) { 6617*0Sstevel@tonic-gate full_years++; 6618*0Sstevel@tonic-gate if (full_years >= DC_SCSI_NPY) { 6619*0Sstevel@tonic-gate *intervalp = (LONG_MAX / hz); 6620*0Sstevel@tonic-gate return (0); 6621*0Sstevel@tonic-gate } 6622*0Sstevel@tonic-gate cycles_over -= cycles_diff; 6623*0Sstevel@tonic-gate lower_bound_cycles = upper_bound_cycles; 6624*0Sstevel@tonic-gate upper_bound_cycles = 6625*0Sstevel@tonic-gate (scp->lifemax * pcnt[full_years]) / 100; 6626*0Sstevel@tonic-gate cycles_diff = (upper_bound_cycles - lower_bound_cycles); 6627*0Sstevel@tonic-gate } 6628*0Sstevel@tonic-gate 6629*0Sstevel@tonic-gate /* 6630*0Sstevel@tonic-gate * The linear slope that determines when the next transition 6631*0Sstevel@tonic-gate * can be made is the relative position of used cycles within a 6632*0Sstevel@tonic-gate * year over total number of cycles within that year. 6633*0Sstevel@tonic-gate */ 6634*0Sstevel@tonic-gate when_allowed = service_seconds + (full_years * DC_SPY) + 6635*0Sstevel@tonic-gate (((uint64_t)DC_SPY * (uint64_t)cycles_over) / cycles_diff); 6636*0Sstevel@tonic-gate *intervalp = (when_allowed - now); 6637*0Sstevel@tonic-gate if (*intervalp > (LONG_MAX / hz)) 6638*0Sstevel@tonic-gate *intervalp = (LONG_MAX / hz); 6639*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: no cycle is allowed in %ld secs\n", pmf, 6640*0Sstevel@tonic-gate *intervalp)) 6641*0Sstevel@tonic-gate return (0); 6642*0Sstevel@tonic-gate } 6643*0Sstevel@tonic-gate 6644*0Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: unknown format!\n", pmf)) 6645*0Sstevel@tonic-gate return (-1); 6646*0Sstevel@tonic-gate } 6647*0Sstevel@tonic-gate 6648*0Sstevel@tonic-gate /* 6649*0Sstevel@tonic-gate * Nexus drivers call into pm framework to indicate which child driver is about 6650*0Sstevel@tonic-gate * to be installed. In some platforms, ppm may need to configure the hardware 6651*0Sstevel@tonic-gate * for successful installation of a driver. 6652*0Sstevel@tonic-gate */ 6653*0Sstevel@tonic-gate int 6654*0Sstevel@tonic-gate pm_init_child(dev_info_t *dip) 6655*0Sstevel@tonic-gate { 6656*0Sstevel@tonic-gate power_req_t power_req; 6657*0Sstevel@tonic-gate 6658*0Sstevel@tonic-gate ASSERT(ddi_binding_name(dip)); 6659*0Sstevel@tonic-gate ASSERT(ddi_get_name_addr(dip)); 6660*0Sstevel@tonic-gate pm_ppm_claim(dip); 6661*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */ 6662*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_INIT_CHILD; 6663*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6664*0Sstevel@tonic-gate ASSERT(PPM(dip) != NULL); 6665*0Sstevel@tonic-gate return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, 6666*0Sstevel@tonic-gate NULL)); 6667*0Sstevel@tonic-gate } else { 6668*0Sstevel@tonic-gate #ifdef DEBUG 6669*0Sstevel@tonic-gate /* pass it to the default handler so we can debug things */ 6670*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_INIT_CHILD; 6671*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6672*0Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 6673*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, NULL); 6674*0Sstevel@tonic-gate #endif 6675*0Sstevel@tonic-gate } 6676*0Sstevel@tonic-gate return (DDI_SUCCESS); 6677*0Sstevel@tonic-gate } 6678*0Sstevel@tonic-gate 6679*0Sstevel@tonic-gate /* 6680*0Sstevel@tonic-gate * Bring parent of a node that is about to be probed up to full power, and 6681*0Sstevel@tonic-gate * arrange for it to stay up until pm_post_probe() or pm_post_attach() decide 6682*0Sstevel@tonic-gate * it is time to let it go down again 6683*0Sstevel@tonic-gate */ 6684*0Sstevel@tonic-gate void 6685*0Sstevel@tonic-gate pm_pre_probe(dev_info_t *dip, pm_ppm_cookie_t *cp) 6686*0Sstevel@tonic-gate { 6687*0Sstevel@tonic-gate int result; 6688*0Sstevel@tonic-gate power_req_t power_req; 6689*0Sstevel@tonic-gate 6690*0Sstevel@tonic-gate bzero(cp, sizeof (*cp)); 6691*0Sstevel@tonic-gate cp->ppc_dip = dip; 6692*0Sstevel@tonic-gate 6693*0Sstevel@tonic-gate pm_ppm_claim(dip); 6694*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */ 6695*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_PROBE; 6696*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6697*0Sstevel@tonic-gate ASSERT(PPM(dip) != NULL); 6698*0Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, 6699*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6700*0Sstevel@tonic-gate cp->ppc_ppm = PPM(dip); 6701*0Sstevel@tonic-gate } else { 6702*0Sstevel@tonic-gate #ifdef DEBUG 6703*0Sstevel@tonic-gate /* pass it to the default handler so we can debug things */ 6704*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_PROBE; 6705*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6706*0Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 6707*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6708*0Sstevel@tonic-gate #endif 6709*0Sstevel@tonic-gate cp->ppc_ppm = NULL; 6710*0Sstevel@tonic-gate } 6711*0Sstevel@tonic-gate } 6712*0Sstevel@tonic-gate 6713*0Sstevel@tonic-gate int 6714*0Sstevel@tonic-gate pm_pre_config(dev_info_t *dip, char *devnm) 6715*0Sstevel@tonic-gate { 6716*0Sstevel@tonic-gate PMD_FUNC(pmf, "pre_config") 6717*0Sstevel@tonic-gate int ret; 6718*0Sstevel@tonic-gate 6719*0Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6720*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6721*0Sstevel@tonic-gate ret = mdi_power(dip, MDI_PM_PRE_CONFIG, NULL, devnm, 0); 6722*0Sstevel@tonic-gate return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 6723*0Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6724*0Sstevel@tonic-gate return (DDI_SUCCESS); 6725*0Sstevel@tonic-gate 6726*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6727*0Sstevel@tonic-gate pm_hold_power(dip); 6728*0Sstevel@tonic-gate ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK); 6729*0Sstevel@tonic-gate if (ret != DDI_SUCCESS) 6730*0Sstevel@tonic-gate pm_rele_power(dip); 6731*0Sstevel@tonic-gate return (ret); 6732*0Sstevel@tonic-gate } 6733*0Sstevel@tonic-gate 6734*0Sstevel@tonic-gate /* 6735*0Sstevel@tonic-gate * This routine is called by devfs during its walk to unconfigue a node. 6736*0Sstevel@tonic-gate * If the call is due to auto mod_unloads and the dip is not at its 6737*0Sstevel@tonic-gate * full power, we return DDI_FAILURE to terminate the walk, otherwise 6738*0Sstevel@tonic-gate * return DDI_SUCCESS. 6739*0Sstevel@tonic-gate */ 6740*0Sstevel@tonic-gate int 6741*0Sstevel@tonic-gate pm_pre_unconfig(dev_info_t *dip, int flags, int *held, char *devnm) 6742*0Sstevel@tonic-gate { 6743*0Sstevel@tonic-gate PMD_FUNC(pmf, "pre_unconfig") 6744*0Sstevel@tonic-gate int ret; 6745*0Sstevel@tonic-gate 6746*0Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6747*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, 6748*0Sstevel@tonic-gate PM_DEVICE(dip), flags)) 6749*0Sstevel@tonic-gate ret = mdi_power(dip, MDI_PM_PRE_UNCONFIG, held, devnm, flags); 6750*0Sstevel@tonic-gate return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 6751*0Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6752*0Sstevel@tonic-gate return (DDI_SUCCESS); 6753*0Sstevel@tonic-gate 6754*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, PM_DEVICE(dip), 6755*0Sstevel@tonic-gate flags)) 6756*0Sstevel@tonic-gate *held = 0; 6757*0Sstevel@tonic-gate 6758*0Sstevel@tonic-gate /* 6759*0Sstevel@tonic-gate * If the dip is a leaf node, don't power it up. 6760*0Sstevel@tonic-gate */ 6761*0Sstevel@tonic-gate if (!ddi_get_child(dip)) 6762*0Sstevel@tonic-gate return (DDI_SUCCESS); 6763*0Sstevel@tonic-gate 6764*0Sstevel@tonic-gate /* 6765*0Sstevel@tonic-gate * Do not power up the node if it is called due to auto-modunload. 6766*0Sstevel@tonic-gate */ 6767*0Sstevel@tonic-gate if ((flags & NDI_AUTODETACH) && !pm_all_at_normal(dip)) 6768*0Sstevel@tonic-gate return (DDI_FAILURE); 6769*0Sstevel@tonic-gate 6770*0Sstevel@tonic-gate pm_hold_power(dip); 6771*0Sstevel@tonic-gate *held = 1; 6772*0Sstevel@tonic-gate ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK); 6773*0Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 6774*0Sstevel@tonic-gate pm_rele_power(dip); 6775*0Sstevel@tonic-gate *held = 0; 6776*0Sstevel@tonic-gate } 6777*0Sstevel@tonic-gate return (ret); 6778*0Sstevel@tonic-gate } 6779*0Sstevel@tonic-gate 6780*0Sstevel@tonic-gate /* 6781*0Sstevel@tonic-gate * Notify ppm of attach action. Parent is already held at full power by 6782*0Sstevel@tonic-gate * probe action. 6783*0Sstevel@tonic-gate */ 6784*0Sstevel@tonic-gate void 6785*0Sstevel@tonic-gate pm_pre_attach(dev_info_t *dip, pm_ppm_cookie_t *cp, ddi_attach_cmd_t cmd) 6786*0Sstevel@tonic-gate { 6787*0Sstevel@tonic-gate static char *me = "pm_pre_attach"; 6788*0Sstevel@tonic-gate power_req_t power_req; 6789*0Sstevel@tonic-gate int result; 6790*0Sstevel@tonic-gate 6791*0Sstevel@tonic-gate /* 6792*0Sstevel@tonic-gate * Initialize and fill in the PPM cookie 6793*0Sstevel@tonic-gate */ 6794*0Sstevel@tonic-gate bzero(cp, sizeof (*cp)); 6795*0Sstevel@tonic-gate cp->ppc_cmd = (int)cmd; 6796*0Sstevel@tonic-gate cp->ppc_ppm = PPM(dip); 6797*0Sstevel@tonic-gate cp->ppc_dip = dip; 6798*0Sstevel@tonic-gate 6799*0Sstevel@tonic-gate /* 6800*0Sstevel@tonic-gate * DDI_ATTACH and DDI_RESUME cmds need to call platform specific 6801*0Sstevel@tonic-gate * Power Management stuff. DDI_RESUME also has to purge it's 6802*0Sstevel@tonic-gate * powerlevel information. 6803*0Sstevel@tonic-gate */ 6804*0Sstevel@tonic-gate switch (cmd) { 6805*0Sstevel@tonic-gate case DDI_ATTACH: 6806*0Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6807*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_ATTACH; 6808*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6809*0Sstevel@tonic-gate ASSERT(PPM(dip)); 6810*0Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, dip, DDI_CTLOPS_POWER, 6811*0Sstevel@tonic-gate &power_req, &result); 6812*0Sstevel@tonic-gate } 6813*0Sstevel@tonic-gate #ifdef DEBUG 6814*0Sstevel@tonic-gate else { 6815*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_ATTACH; 6816*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6817*0Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 6818*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6819*0Sstevel@tonic-gate } 6820*0Sstevel@tonic-gate #endif 6821*0Sstevel@tonic-gate break; 6822*0Sstevel@tonic-gate case DDI_RESUME: 6823*0Sstevel@tonic-gate pm_forget_power_level(dip); 6824*0Sstevel@tonic-gate 6825*0Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6826*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_RESUME; 6827*0Sstevel@tonic-gate power_req.req.resume_req.who = cp->ppc_dip; 6828*0Sstevel@tonic-gate power_req.req.resume_req.cmd = 6829*0Sstevel@tonic-gate (ddi_attach_cmd_t)cp->ppc_cmd; 6830*0Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 6831*0Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, 6832*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6833*0Sstevel@tonic-gate } 6834*0Sstevel@tonic-gate #ifdef DEBUG 6835*0Sstevel@tonic-gate else { 6836*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_RESUME; 6837*0Sstevel@tonic-gate power_req.req.resume_req.who = cp->ppc_dip; 6838*0Sstevel@tonic-gate power_req.req.resume_req.cmd = 6839*0Sstevel@tonic-gate (ddi_attach_cmd_t)cp->ppc_cmd; 6840*0Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, 6841*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6842*0Sstevel@tonic-gate } 6843*0Sstevel@tonic-gate #endif 6844*0Sstevel@tonic-gate break; 6845*0Sstevel@tonic-gate 6846*0Sstevel@tonic-gate case DDI_PM_RESUME: 6847*0Sstevel@tonic-gate break; 6848*0Sstevel@tonic-gate 6849*0Sstevel@tonic-gate default: 6850*0Sstevel@tonic-gate panic(me); 6851*0Sstevel@tonic-gate } 6852*0Sstevel@tonic-gate } 6853*0Sstevel@tonic-gate 6854*0Sstevel@tonic-gate /* 6855*0Sstevel@tonic-gate * Nexus drivers call into pm framework to indicate which child driver is 6856*0Sstevel@tonic-gate * being uninstalled. In some platforms, ppm may need to reconfigure the 6857*0Sstevel@tonic-gate * hardware since the device driver is no longer installed. 6858*0Sstevel@tonic-gate */ 6859*0Sstevel@tonic-gate int 6860*0Sstevel@tonic-gate pm_uninit_child(dev_info_t *dip) 6861*0Sstevel@tonic-gate { 6862*0Sstevel@tonic-gate power_req_t power_req; 6863*0Sstevel@tonic-gate 6864*0Sstevel@tonic-gate ASSERT(ddi_binding_name(dip)); 6865*0Sstevel@tonic-gate ASSERT(ddi_get_name_addr(dip)); 6866*0Sstevel@tonic-gate pm_ppm_claim(dip); 6867*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */ 6868*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNINIT_CHILD; 6869*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6870*0Sstevel@tonic-gate ASSERT(PPM(dip)); 6871*0Sstevel@tonic-gate return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, 6872*0Sstevel@tonic-gate NULL)); 6873*0Sstevel@tonic-gate } else { 6874*0Sstevel@tonic-gate #ifdef DEBUG 6875*0Sstevel@tonic-gate /* pass it to the default handler so we can debug things */ 6876*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNINIT_CHILD; 6877*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 6878*0Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, DDI_CTLOPS_POWER, &power_req, NULL); 6879*0Sstevel@tonic-gate #endif 6880*0Sstevel@tonic-gate } 6881*0Sstevel@tonic-gate return (DDI_SUCCESS); 6882*0Sstevel@tonic-gate } 6883*0Sstevel@tonic-gate /* 6884*0Sstevel@tonic-gate * Decrement kidsupcnt so scan can turn the parent back off if it is idle 6885*0Sstevel@tonic-gate * Also notify ppm of result of probe if there is a ppm that cares 6886*0Sstevel@tonic-gate */ 6887*0Sstevel@tonic-gate void 6888*0Sstevel@tonic-gate pm_post_probe(pm_ppm_cookie_t *cp, int ret, int probe_failed) 6889*0Sstevel@tonic-gate { 6890*0Sstevel@tonic-gate _NOTE(ARGUNUSED(probe_failed)) 6891*0Sstevel@tonic-gate int result; 6892*0Sstevel@tonic-gate power_req_t power_req; 6893*0Sstevel@tonic-gate 6894*0Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6895*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_PROBE; 6896*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6897*0Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6898*0Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 6899*0Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, DDI_CTLOPS_POWER, 6900*0Sstevel@tonic-gate &power_req, &result); 6901*0Sstevel@tonic-gate } 6902*0Sstevel@tonic-gate #ifdef DEBUG 6903*0Sstevel@tonic-gate else { 6904*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_PROBE; 6905*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6906*0Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6907*0Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, DDI_CTLOPS_POWER, 6908*0Sstevel@tonic-gate &power_req, &result); 6909*0Sstevel@tonic-gate } 6910*0Sstevel@tonic-gate #endif 6911*0Sstevel@tonic-gate } 6912*0Sstevel@tonic-gate 6913*0Sstevel@tonic-gate void 6914*0Sstevel@tonic-gate pm_post_config(dev_info_t *dip, char *devnm) 6915*0Sstevel@tonic-gate { 6916*0Sstevel@tonic-gate PMD_FUNC(pmf, "post_config") 6917*0Sstevel@tonic-gate 6918*0Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6919*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6920*0Sstevel@tonic-gate (void) mdi_power(dip, MDI_PM_POST_CONFIG, NULL, devnm, 0); 6921*0Sstevel@tonic-gate return; 6922*0Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6923*0Sstevel@tonic-gate return; 6924*0Sstevel@tonic-gate 6925*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 6926*0Sstevel@tonic-gate pm_rele_power(dip); 6927*0Sstevel@tonic-gate } 6928*0Sstevel@tonic-gate 6929*0Sstevel@tonic-gate void 6930*0Sstevel@tonic-gate pm_post_unconfig(dev_info_t *dip, int held, char *devnm) 6931*0Sstevel@tonic-gate { 6932*0Sstevel@tonic-gate PMD_FUNC(pmf, "post_unconfig") 6933*0Sstevel@tonic-gate 6934*0Sstevel@tonic-gate if (MDI_VHCI(dip)) { 6935*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, 6936*0Sstevel@tonic-gate PM_DEVICE(dip), held)) 6937*0Sstevel@tonic-gate (void) mdi_power(dip, MDI_PM_POST_UNCONFIG, &held, devnm, 0); 6938*0Sstevel@tonic-gate return; 6939*0Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip)) 6940*0Sstevel@tonic-gate return; 6941*0Sstevel@tonic-gate 6942*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, PM_DEVICE(dip), 6943*0Sstevel@tonic-gate held)) 6944*0Sstevel@tonic-gate if (!held) 6945*0Sstevel@tonic-gate return; 6946*0Sstevel@tonic-gate /* 6947*0Sstevel@tonic-gate * We have held power in pre_unconfig, release it here. 6948*0Sstevel@tonic-gate */ 6949*0Sstevel@tonic-gate pm_rele_power(dip); 6950*0Sstevel@tonic-gate } 6951*0Sstevel@tonic-gate 6952*0Sstevel@tonic-gate /* 6953*0Sstevel@tonic-gate * Notify ppm of result of attach if there is a ppm that cares 6954*0Sstevel@tonic-gate */ 6955*0Sstevel@tonic-gate void 6956*0Sstevel@tonic-gate pm_post_attach(pm_ppm_cookie_t *cp, int ret) 6957*0Sstevel@tonic-gate { 6958*0Sstevel@tonic-gate int result; 6959*0Sstevel@tonic-gate power_req_t power_req; 6960*0Sstevel@tonic-gate dev_info_t *dip; 6961*0Sstevel@tonic-gate 6962*0Sstevel@tonic-gate if (cp->ppc_cmd != DDI_ATTACH) 6963*0Sstevel@tonic-gate return; 6964*0Sstevel@tonic-gate 6965*0Sstevel@tonic-gate dip = cp->ppc_dip; 6966*0Sstevel@tonic-gate 6967*0Sstevel@tonic-gate if (ret == DDI_SUCCESS) { 6968*0Sstevel@tonic-gate /* 6969*0Sstevel@tonic-gate * Attach succeeded, so proceed to doing post-attach pm tasks 6970*0Sstevel@tonic-gate */ 6971*0Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) 6972*0Sstevel@tonic-gate (void) pm_start(dip); 6973*0Sstevel@tonic-gate } else { 6974*0Sstevel@tonic-gate /* 6975*0Sstevel@tonic-gate * Attach may have got pm started before failing 6976*0Sstevel@tonic-gate */ 6977*0Sstevel@tonic-gate pm_stop(dip); 6978*0Sstevel@tonic-gate } 6979*0Sstevel@tonic-gate 6980*0Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 6981*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_ATTACH; 6982*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6983*0Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6984*0Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 6985*0Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, 6986*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6987*0Sstevel@tonic-gate } 6988*0Sstevel@tonic-gate #ifdef DEBUG 6989*0Sstevel@tonic-gate else { 6990*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_ATTACH; 6991*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 6992*0Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 6993*0Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, 6994*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 6995*0Sstevel@tonic-gate } 6996*0Sstevel@tonic-gate #endif 6997*0Sstevel@tonic-gate } 6998*0Sstevel@tonic-gate 6999*0Sstevel@tonic-gate /* 7000*0Sstevel@tonic-gate * Notify ppm of attach action. Parent is already held at full power by 7001*0Sstevel@tonic-gate * probe action. 7002*0Sstevel@tonic-gate */ 7003*0Sstevel@tonic-gate void 7004*0Sstevel@tonic-gate pm_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, pm_ppm_cookie_t *cp) 7005*0Sstevel@tonic-gate { 7006*0Sstevel@tonic-gate int result; 7007*0Sstevel@tonic-gate power_req_t power_req; 7008*0Sstevel@tonic-gate 7009*0Sstevel@tonic-gate bzero(cp, sizeof (*cp)); 7010*0Sstevel@tonic-gate cp->ppc_dip = dip; 7011*0Sstevel@tonic-gate cp->ppc_cmd = (int)cmd; 7012*0Sstevel@tonic-gate 7013*0Sstevel@tonic-gate switch (cmd) { 7014*0Sstevel@tonic-gate case DDI_DETACH: 7015*0Sstevel@tonic-gate pm_detaching(dip); /* suspend pm while detaching */ 7016*0Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims node */ 7017*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_DETACH; 7018*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 7019*0Sstevel@tonic-gate ASSERT(PPM(dip)); 7020*0Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, 7021*0Sstevel@tonic-gate &power_req, &result); 7022*0Sstevel@tonic-gate cp->ppc_ppm = PPM(dip); 7023*0Sstevel@tonic-gate } else { 7024*0Sstevel@tonic-gate #ifdef DEBUG 7025*0Sstevel@tonic-gate /* pass to the default handler so we can debug things */ 7026*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_DETACH; 7027*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip; 7028*0Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, 7029*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 7030*0Sstevel@tonic-gate #endif 7031*0Sstevel@tonic-gate cp->ppc_ppm = NULL; 7032*0Sstevel@tonic-gate } 7033*0Sstevel@tonic-gate break; 7034*0Sstevel@tonic-gate 7035*0Sstevel@tonic-gate default: 7036*0Sstevel@tonic-gate break; 7037*0Sstevel@tonic-gate } 7038*0Sstevel@tonic-gate } 7039*0Sstevel@tonic-gate 7040*0Sstevel@tonic-gate /* 7041*0Sstevel@tonic-gate * Dip is either a leaf node that exported "no-involuntary-power-cycles" prop., 7042*0Sstevel@tonic-gate * (if devi_pm_noinvol count is 0) or an ancestor of such a node. We need to 7043*0Sstevel@tonic-gate * make an entry to record the details, which includes certain flag settings. 7044*0Sstevel@tonic-gate */ 7045*0Sstevel@tonic-gate static void 7046*0Sstevel@tonic-gate pm_record_invol_path(char *path, int flags, int noinvolpm, int volpmd, 7047*0Sstevel@tonic-gate int wasvolpmd, major_t major) 7048*0Sstevel@tonic-gate { 7049*0Sstevel@tonic-gate PMD_FUNC(pmf, "record_invol_path") 7050*0Sstevel@tonic-gate major_t pm_path_to_major(char *); 7051*0Sstevel@tonic-gate size_t plen; 7052*0Sstevel@tonic-gate pm_noinvol_t *ip, *np, *pp; 7053*0Sstevel@tonic-gate pp = NULL; 7054*0Sstevel@tonic-gate 7055*0Sstevel@tonic-gate plen = strlen(path) + 1; 7056*0Sstevel@tonic-gate np = kmem_zalloc(sizeof (*np), KM_SLEEP); 7057*0Sstevel@tonic-gate np->ni_size = plen; 7058*0Sstevel@tonic-gate np->ni_path = kmem_alloc(plen, KM_SLEEP); 7059*0Sstevel@tonic-gate np->ni_noinvolpm = noinvolpm; 7060*0Sstevel@tonic-gate np->ni_volpmd = volpmd; 7061*0Sstevel@tonic-gate np->ni_wasvolpmd = wasvolpmd; 7062*0Sstevel@tonic-gate np->ni_flags = flags; 7063*0Sstevel@tonic-gate (void) strcpy(np->ni_path, path); 7064*0Sstevel@tonic-gate /* 7065*0Sstevel@tonic-gate * If we haven't actually seen the node attached, it is hard to figure 7066*0Sstevel@tonic-gate * out its major. If we could hold the node by path, we would be much 7067*0Sstevel@tonic-gate * happier here. 7068*0Sstevel@tonic-gate */ 7069*0Sstevel@tonic-gate if (major == (major_t)-1) { 7070*0Sstevel@tonic-gate np->ni_major = pm_path_to_major(path); 7071*0Sstevel@tonic-gate } else { 7072*0Sstevel@tonic-gate np->ni_major = major; 7073*0Sstevel@tonic-gate } 7074*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 7075*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7076*0Sstevel@tonic-gate int comp = strcmp(path, ip->ni_path); 7077*0Sstevel@tonic-gate if (comp < 0) { 7078*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s insert before %s\n", 7079*0Sstevel@tonic-gate pmf, path, ip->ni_path)) 7080*0Sstevel@tonic-gate /* insert before current entry */ 7081*0Sstevel@tonic-gate np->ni_next = ip; 7082*0Sstevel@tonic-gate if (pp) { 7083*0Sstevel@tonic-gate pp->ni_next = np; 7084*0Sstevel@tonic-gate } else { 7085*0Sstevel@tonic-gate pm_noinvol_head = np; 7086*0Sstevel@tonic-gate } 7087*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7088*0Sstevel@tonic-gate #ifdef DEBUG 7089*0Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL) 7090*0Sstevel@tonic-gate pr_noinvol("record_invol_path exit0"); 7091*0Sstevel@tonic-gate #endif 7092*0Sstevel@tonic-gate return; 7093*0Sstevel@tonic-gate } else if (comp == 0) { 7094*0Sstevel@tonic-gate panic("%s already in pm_noinvol list", path); 7095*0Sstevel@tonic-gate } 7096*0Sstevel@tonic-gate } 7097*0Sstevel@tonic-gate /* 7098*0Sstevel@tonic-gate * If we did not find an entry in the list that this should go before, 7099*0Sstevel@tonic-gate * then it must go at the end 7100*0Sstevel@tonic-gate */ 7101*0Sstevel@tonic-gate if (pp) { 7102*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s append after %s\n", pmf, path, 7103*0Sstevel@tonic-gate pp->ni_path)) 7104*0Sstevel@tonic-gate ASSERT(pp->ni_next == 0); 7105*0Sstevel@tonic-gate pp->ni_next = np; 7106*0Sstevel@tonic-gate } else { 7107*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s added to end-of-list\n", pmf, path)) 7108*0Sstevel@tonic-gate ASSERT(!pm_noinvol_head); 7109*0Sstevel@tonic-gate pm_noinvol_head = np; 7110*0Sstevel@tonic-gate } 7111*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7112*0Sstevel@tonic-gate #ifdef DEBUG 7113*0Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL) 7114*0Sstevel@tonic-gate pr_noinvol("record_invol_path exit"); 7115*0Sstevel@tonic-gate #endif 7116*0Sstevel@tonic-gate } 7117*0Sstevel@tonic-gate 7118*0Sstevel@tonic-gate void 7119*0Sstevel@tonic-gate pm_record_invol(dev_info_t *dip) 7120*0Sstevel@tonic-gate { 7121*0Sstevel@tonic-gate char *pathbuf; 7122*0Sstevel@tonic-gate int pm_all_components_off(dev_info_t *); 7123*0Sstevel@tonic-gate int volpmd = (PM_NUMCMPTS(dip) > 0) && pm_all_components_off(dip); 7124*0Sstevel@tonic-gate 7125*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 7126*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 7127*0Sstevel@tonic-gate 7128*0Sstevel@tonic-gate pm_record_invol_path(pathbuf, (DEVI(dip)->devi_pm_flags & 7129*0Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)), DEVI(dip)->devi_pm_noinvolpm, 7130*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, volpmd, PM_MAJOR(dip)); 7131*0Sstevel@tonic-gate 7132*0Sstevel@tonic-gate /* 7133*0Sstevel@tonic-gate * If this child's detach will be holding up its ancestors, then we 7134*0Sstevel@tonic-gate * allow for an exception to that if all children of this type have 7135*0Sstevel@tonic-gate * gone down voluntarily. 7136*0Sstevel@tonic-gate * Now walk down the tree incrementing devi_pm_noinvolpm 7137*0Sstevel@tonic-gate */ 7138*0Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_DETACH, 0, volpmd, pathbuf, 7139*0Sstevel@tonic-gate dip); 7140*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7141*0Sstevel@tonic-gate } 7142*0Sstevel@tonic-gate 7143*0Sstevel@tonic-gate void 7144*0Sstevel@tonic-gate pm_post_detach(pm_ppm_cookie_t *cp, int ret) 7145*0Sstevel@tonic-gate { 7146*0Sstevel@tonic-gate dev_info_t *dip = cp->ppc_dip; 7147*0Sstevel@tonic-gate int result; 7148*0Sstevel@tonic-gate power_req_t power_req; 7149*0Sstevel@tonic-gate 7150*0Sstevel@tonic-gate switch (cp->ppc_cmd) { 7151*0Sstevel@tonic-gate case DDI_DETACH: 7152*0Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */ 7153*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_DETACH; 7154*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 7155*0Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 7156*0Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm); 7157*0Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, 7158*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 7159*0Sstevel@tonic-gate } 7160*0Sstevel@tonic-gate #ifdef DEBUG 7161*0Sstevel@tonic-gate else { 7162*0Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_DETACH; 7163*0Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip; 7164*0Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret; 7165*0Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, 7166*0Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result); 7167*0Sstevel@tonic-gate } 7168*0Sstevel@tonic-gate #endif 7169*0Sstevel@tonic-gate if (ret == DDI_SUCCESS) { 7170*0Sstevel@tonic-gate /* 7171*0Sstevel@tonic-gate * For hotplug detach we assume it is *really* gone 7172*0Sstevel@tonic-gate */ 7173*0Sstevel@tonic-gate if (cp->ppc_cmd == DDI_DETACH && 7174*0Sstevel@tonic-gate ((DEVI(dip)->devi_pm_flags & 7175*0Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)) || 7176*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm)) 7177*0Sstevel@tonic-gate pm_record_invol(dip); 7178*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= 7179*0Sstevel@tonic-gate ~(PMC_NO_INVOL | PMC_NOINVOL_DONE); 7180*0Sstevel@tonic-gate 7181*0Sstevel@tonic-gate /* 7182*0Sstevel@tonic-gate * If console fb is detaching, then we don't need to 7183*0Sstevel@tonic-gate * worry any more about it going off (pm_detaching has 7184*0Sstevel@tonic-gate * brought up all components) 7185*0Sstevel@tonic-gate */ 7186*0Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 7187*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7188*0Sstevel@tonic-gate ASSERT(cfb_dip_detaching); 7189*0Sstevel@tonic-gate ASSERT(cfb_dip == NULL); 7190*0Sstevel@tonic-gate ASSERT(pm_cfb_comps_off == 0); 7191*0Sstevel@tonic-gate cfb_dip_detaching = NULL; 7192*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7193*0Sstevel@tonic-gate } 7194*0Sstevel@tonic-gate pm_stop(dip); /* make it permanent */ 7195*0Sstevel@tonic-gate } else { 7196*0Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 7197*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7198*0Sstevel@tonic-gate ASSERT(cfb_dip_detaching); 7199*0Sstevel@tonic-gate ASSERT(cfb_dip == NULL); 7200*0Sstevel@tonic-gate ASSERT(pm_cfb_comps_off == 0); 7201*0Sstevel@tonic-gate cfb_dip = cfb_dip_detaching; 7202*0Sstevel@tonic-gate cfb_dip_detaching = NULL; 7203*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7204*0Sstevel@tonic-gate } 7205*0Sstevel@tonic-gate pm_detach_failed(dip); /* resume power management */ 7206*0Sstevel@tonic-gate } 7207*0Sstevel@tonic-gate break; 7208*0Sstevel@tonic-gate case DDI_PM_SUSPEND: 7209*0Sstevel@tonic-gate break; 7210*0Sstevel@tonic-gate case DDI_SUSPEND: 7211*0Sstevel@tonic-gate break; /* legal, but nothing to do */ 7212*0Sstevel@tonic-gate default: 7213*0Sstevel@tonic-gate #ifdef DEBUG 7214*0Sstevel@tonic-gate panic("pm_post_detach: unrecognized cmd %d for detach", 7215*0Sstevel@tonic-gate cp->ppc_cmd); 7216*0Sstevel@tonic-gate /*NOTREACHED*/ 7217*0Sstevel@tonic-gate #else 7218*0Sstevel@tonic-gate break; 7219*0Sstevel@tonic-gate #endif 7220*0Sstevel@tonic-gate } 7221*0Sstevel@tonic-gate } 7222*0Sstevel@tonic-gate 7223*0Sstevel@tonic-gate /* 7224*0Sstevel@tonic-gate * Called after vfs_mountroot has got the clock started to fix up timestamps 7225*0Sstevel@tonic-gate * that were set when root bush drivers attached. hresttime was 0 then, so the 7226*0Sstevel@tonic-gate * devices look busy but have a 0 busycnt 7227*0Sstevel@tonic-gate */ 7228*0Sstevel@tonic-gate int 7229*0Sstevel@tonic-gate pm_adjust_timestamps(dev_info_t *dip, void *arg) 7230*0Sstevel@tonic-gate { 7231*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 7232*0Sstevel@tonic-gate 7233*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 7234*0Sstevel@tonic-gate struct pm_component *cp; 7235*0Sstevel@tonic-gate int i; 7236*0Sstevel@tonic-gate 7237*0Sstevel@tonic-gate if (!info) 7238*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7239*0Sstevel@tonic-gate PM_LOCK_BUSY(dip); 7240*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 7241*0Sstevel@tonic-gate cp = PM_CP(dip, i); 7242*0Sstevel@tonic-gate if (cp->pmc_timestamp == 0 && cp->pmc_busycount == 0) 7243*0Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec(); 7244*0Sstevel@tonic-gate } 7245*0Sstevel@tonic-gate PM_UNLOCK_BUSY(dip); 7246*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7247*0Sstevel@tonic-gate } 7248*0Sstevel@tonic-gate 7249*0Sstevel@tonic-gate /* 7250*0Sstevel@tonic-gate * Called at attach time to see if the device being attached has a record in 7251*0Sstevel@tonic-gate * the no involuntary power cycles list. If so, we do some bookkeeping on the 7252*0Sstevel@tonic-gate * parents and set a flag in the dip 7253*0Sstevel@tonic-gate */ 7254*0Sstevel@tonic-gate void 7255*0Sstevel@tonic-gate pm_noinvol_specd(dev_info_t *dip) 7256*0Sstevel@tonic-gate { 7257*0Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_specd") 7258*0Sstevel@tonic-gate char *pathbuf; 7259*0Sstevel@tonic-gate pm_noinvol_t *ip, *pp = NULL; 7260*0Sstevel@tonic-gate int wasvolpmd; 7261*0Sstevel@tonic-gate int found = 0; 7262*0Sstevel@tonic-gate 7263*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NOINVOL_DONE) 7264*0Sstevel@tonic-gate return; 7265*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NOINVOL_DONE; 7266*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 7267*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 7268*0Sstevel@tonic-gate 7269*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 7270*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0; 7271*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm = 0; 7272*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 7273*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7274*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n", 7275*0Sstevel@tonic-gate pmf, pathbuf, ip->ni_path)) 7276*0Sstevel@tonic-gate if (strcmp(pathbuf, ip->ni_path) == 0) { 7277*0Sstevel@tonic-gate found++; 7278*0Sstevel@tonic-gate break; 7279*0Sstevel@tonic-gate } 7280*0Sstevel@tonic-gate } 7281*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7282*0Sstevel@tonic-gate if (!found) { 7283*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 7284*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7285*0Sstevel@tonic-gate return; 7286*0Sstevel@tonic-gate } 7287*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 7288*0Sstevel@tonic-gate pp = NULL; 7289*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7290*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n", 7291*0Sstevel@tonic-gate pmf, pathbuf, ip->ni_path)) 7292*0Sstevel@tonic-gate if (strcmp(pathbuf, ip->ni_path) == 0) { 7293*0Sstevel@tonic-gate ip->ni_flags &= ~PMC_DRIVER_REMOVED; 7294*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= ip->ni_flags; 7295*0Sstevel@tonic-gate /* 7296*0Sstevel@tonic-gate * Handle special case of console fb 7297*0Sstevel@tonic-gate */ 7298*0Sstevel@tonic-gate if (PM_IS_CFB(dip)) { 7299*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7300*0Sstevel@tonic-gate cfb_dip = dip; 7301*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) setting " 7302*0Sstevel@tonic-gate "cfb_dip\n", pmf, PM_DEVICE(dip))) 7303*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7304*0Sstevel@tonic-gate } 7305*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm = ip->ni_noinvolpm; 7306*0Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags & 7307*0Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)) || 7308*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm); 7309*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = ip->ni_volpmd; 7310*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: noinvol=%d, volpmd=%d, " 7311*0Sstevel@tonic-gate "wasvolpmd=%d, flags=%x, path=%s\n", pmf, 7312*0Sstevel@tonic-gate ip->ni_noinvolpm, ip->ni_volpmd, 7313*0Sstevel@tonic-gate ip->ni_wasvolpmd, ip->ni_flags, ip->ni_path)) 7314*0Sstevel@tonic-gate /* 7315*0Sstevel@tonic-gate * free the entry in hopes the list will now be empty 7316*0Sstevel@tonic-gate * and we won't have to search it any more until the 7317*0Sstevel@tonic-gate * device detaches 7318*0Sstevel@tonic-gate */ 7319*0Sstevel@tonic-gate if (pp) { 7320*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s, prev %s\n", 7321*0Sstevel@tonic-gate pmf, ip->ni_path, pp->ni_path)) 7322*0Sstevel@tonic-gate pp->ni_next = ip->ni_next; 7323*0Sstevel@tonic-gate } else { 7324*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s head\n", 7325*0Sstevel@tonic-gate pmf, ip->ni_path)) 7326*0Sstevel@tonic-gate ASSERT(pm_noinvol_head == ip); 7327*0Sstevel@tonic-gate pm_noinvol_head = ip->ni_next; 7328*0Sstevel@tonic-gate } 7329*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 7330*0Sstevel@tonic-gate wasvolpmd = ip->ni_wasvolpmd; 7331*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7332*0Sstevel@tonic-gate kmem_free(ip->ni_path, ip->ni_size); 7333*0Sstevel@tonic-gate kmem_free(ip, sizeof (*ip)); 7334*0Sstevel@tonic-gate /* 7335*0Sstevel@tonic-gate * Now walk up the tree decrementing devi_pm_noinvolpm 7336*0Sstevel@tonic-gate * (and volpmd if appropriate) 7337*0Sstevel@tonic-gate */ 7338*0Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_ATTACH, 0, 7339*0Sstevel@tonic-gate wasvolpmd, pathbuf, dip); 7340*0Sstevel@tonic-gate #ifdef DEBUG 7341*0Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL) 7342*0Sstevel@tonic-gate pr_noinvol("noinvol_specd exit"); 7343*0Sstevel@tonic-gate #endif 7344*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7345*0Sstevel@tonic-gate return; 7346*0Sstevel@tonic-gate } 7347*0Sstevel@tonic-gate } 7348*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 7349*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7350*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 7351*0Sstevel@tonic-gate } 7352*0Sstevel@tonic-gate 7353*0Sstevel@tonic-gate int 7354*0Sstevel@tonic-gate pm_all_components_off(dev_info_t *dip) 7355*0Sstevel@tonic-gate { 7356*0Sstevel@tonic-gate int i; 7357*0Sstevel@tonic-gate pm_component_t *cp; 7358*0Sstevel@tonic-gate 7359*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 7360*0Sstevel@tonic-gate cp = PM_CP(dip, i); 7361*0Sstevel@tonic-gate if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN || 7362*0Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]) 7363*0Sstevel@tonic-gate return (0); 7364*0Sstevel@tonic-gate } 7365*0Sstevel@tonic-gate return (1); /* all off */ 7366*0Sstevel@tonic-gate } 7367*0Sstevel@tonic-gate 7368*0Sstevel@tonic-gate /* 7369*0Sstevel@tonic-gate * Make sure that all "no involuntary power cycles" devices are attached. 7370*0Sstevel@tonic-gate * Called before doing a cpr suspend to make sure the driver has a say about 7371*0Sstevel@tonic-gate * the power cycle 7372*0Sstevel@tonic-gate */ 7373*0Sstevel@tonic-gate int 7374*0Sstevel@tonic-gate pm_reattach_noinvol(void) 7375*0Sstevel@tonic-gate { 7376*0Sstevel@tonic-gate PMD_FUNC(pmf, "reattach_noinvol") 7377*0Sstevel@tonic-gate pm_noinvol_t *ip; 7378*0Sstevel@tonic-gate char *path; 7379*0Sstevel@tonic-gate dev_info_t *dip; 7380*0Sstevel@tonic-gate 7381*0Sstevel@tonic-gate /* 7382*0Sstevel@tonic-gate * Prevent the modunload thread from unloading any modules until we 7383*0Sstevel@tonic-gate * have completely stopped all kernel threads. 7384*0Sstevel@tonic-gate */ 7385*0Sstevel@tonic-gate modunload_disable(); 7386*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 7387*0Sstevel@tonic-gate /* 7388*0Sstevel@tonic-gate * Forget we'v ever seen any entry 7389*0Sstevel@tonic-gate */ 7390*0Sstevel@tonic-gate ip->ni_persistent = 0; 7391*0Sstevel@tonic-gate } 7392*0Sstevel@tonic-gate restart: 7393*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 7394*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 7395*0Sstevel@tonic-gate major_t maj; 7396*0Sstevel@tonic-gate maj = ip->ni_major; 7397*0Sstevel@tonic-gate path = ip->ni_path; 7398*0Sstevel@tonic-gate if (path != NULL && !(ip->ni_flags & PMC_DRIVER_REMOVED)) { 7399*0Sstevel@tonic-gate if (ip->ni_persistent) { 7400*0Sstevel@tonic-gate /* 7401*0Sstevel@tonic-gate * If we weren't able to make this entry 7402*0Sstevel@tonic-gate * go away, then we give up, as 7403*0Sstevel@tonic-gate * holding/attaching the driver ought to have 7404*0Sstevel@tonic-gate * resulted in this entry being deleted 7405*0Sstevel@tonic-gate */ 7406*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: can't reattach %s " 7407*0Sstevel@tonic-gate "(%s|%d)\n", pmf, ip->ni_path, 7408*0Sstevel@tonic-gate ddi_major_to_name(maj), (int)maj)) 7409*0Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable to reattach %s ", 7410*0Sstevel@tonic-gate ip->ni_path); 7411*0Sstevel@tonic-gate modunload_enable(); 7412*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7413*0Sstevel@tonic-gate return (0); 7414*0Sstevel@tonic-gate } 7415*0Sstevel@tonic-gate ip->ni_persistent++; 7416*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7417*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: holding %s\n", pmf, path)) 7418*0Sstevel@tonic-gate dip = e_ddi_hold_devi_by_path(path, 0); 7419*0Sstevel@tonic-gate if (dip == NULL) { 7420*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: can't hold (%s|%d)\n", 7421*0Sstevel@tonic-gate pmf, path, (int)maj)) 7422*0Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable to hold %s " 7423*0Sstevel@tonic-gate "driver", path); 7424*0Sstevel@tonic-gate modunload_enable(); 7425*0Sstevel@tonic-gate return (0); 7426*0Sstevel@tonic-gate } else { 7427*0Sstevel@tonic-gate PMD(PMD_DHR, ("%s: release %s\n", pmf, path)) 7428*0Sstevel@tonic-gate /* 7429*0Sstevel@tonic-gate * Since the modunload thread is stopped, we 7430*0Sstevel@tonic-gate * don't have to keep the driver held, which 7431*0Sstevel@tonic-gate * saves a ton of bookkeeping 7432*0Sstevel@tonic-gate */ 7433*0Sstevel@tonic-gate ddi_release_devi(dip); 7434*0Sstevel@tonic-gate goto restart; 7435*0Sstevel@tonic-gate } 7436*0Sstevel@tonic-gate } else { 7437*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: skip %s; unknown major\n", 7438*0Sstevel@tonic-gate pmf, ip->ni_path)) 7439*0Sstevel@tonic-gate continue; 7440*0Sstevel@tonic-gate } 7441*0Sstevel@tonic-gate } 7442*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7443*0Sstevel@tonic-gate return (1); 7444*0Sstevel@tonic-gate } 7445*0Sstevel@tonic-gate 7446*0Sstevel@tonic-gate void 7447*0Sstevel@tonic-gate pm_reattach_noinvol_fini(void) 7448*0Sstevel@tonic-gate { 7449*0Sstevel@tonic-gate modunload_enable(); 7450*0Sstevel@tonic-gate } 7451*0Sstevel@tonic-gate 7452*0Sstevel@tonic-gate /* 7453*0Sstevel@tonic-gate * Display pm support code 7454*0Sstevel@tonic-gate */ 7455*0Sstevel@tonic-gate 7456*0Sstevel@tonic-gate 7457*0Sstevel@tonic-gate /* 7458*0Sstevel@tonic-gate * console frame-buffer power-mgmt gets enabled when debugging 7459*0Sstevel@tonic-gate * services are not present or console fbpm override is set 7460*0Sstevel@tonic-gate */ 7461*0Sstevel@tonic-gate void 7462*0Sstevel@tonic-gate pm_cfb_setup(const char *stdout_path) 7463*0Sstevel@tonic-gate { 7464*0Sstevel@tonic-gate PMD_FUNC(pmf, "cfb_setup") 7465*0Sstevel@tonic-gate extern int obpdebug; 7466*0Sstevel@tonic-gate char *devname; 7467*0Sstevel@tonic-gate dev_info_t *dip; 7468*0Sstevel@tonic-gate int devname_len; 7469*0Sstevel@tonic-gate extern dev_info_t *fbdip; 7470*0Sstevel@tonic-gate 7471*0Sstevel@tonic-gate /* 7472*0Sstevel@tonic-gate * By virtue of this function being called (from consconfig), 7473*0Sstevel@tonic-gate * we know stdout is a framebuffer. 7474*0Sstevel@tonic-gate */ 7475*0Sstevel@tonic-gate stdout_is_framebuffer = 1; 7476*0Sstevel@tonic-gate 7477*0Sstevel@tonic-gate if (obpdebug || (boothowto & RB_DEBUG)) { 7478*0Sstevel@tonic-gate if (pm_cfb_override == 0) { 7479*0Sstevel@tonic-gate /* 7480*0Sstevel@tonic-gate * Console is frame buffer, but we want to suppress 7481*0Sstevel@tonic-gate * pm on it because of debugging setup 7482*0Sstevel@tonic-gate */ 7483*0Sstevel@tonic-gate pm_cfb_enabled = 0; 7484*0Sstevel@tonic-gate cmn_err(CE_NOTE, "Kernel debugger present: disabling " 7485*0Sstevel@tonic-gate "console power management."); 7486*0Sstevel@tonic-gate /* 7487*0Sstevel@tonic-gate * however, we still need to know which is the console 7488*0Sstevel@tonic-gate * fb in order to suppress pm on it 7489*0Sstevel@tonic-gate */ 7490*0Sstevel@tonic-gate } else { 7491*0Sstevel@tonic-gate cmn_err(CE_WARN, "Kernel debugger present: see " 7492*0Sstevel@tonic-gate "kmdb(1M) for interaction with power management."); 7493*0Sstevel@tonic-gate } 7494*0Sstevel@tonic-gate } 7495*0Sstevel@tonic-gate #ifdef DEBUG 7496*0Sstevel@tonic-gate /* 7497*0Sstevel@tonic-gate * IF console is fb and is power managed, don't do prom_printfs from 7498*0Sstevel@tonic-gate * pm debug macro 7499*0Sstevel@tonic-gate */ 7500*0Sstevel@tonic-gate if (pm_cfb_enabled) { 7501*0Sstevel@tonic-gate if (pm_debug) 7502*0Sstevel@tonic-gate prom_printf("pm debug output will be to log only\n"); 7503*0Sstevel@tonic-gate pm_divertdebug++; 7504*0Sstevel@tonic-gate } 7505*0Sstevel@tonic-gate #endif 7506*0Sstevel@tonic-gate devname = i_ddi_strdup((char *)stdout_path, KM_SLEEP); 7507*0Sstevel@tonic-gate devname_len = strlen(devname) + 1; 7508*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: stripped %s\n", pmf, devname)) 7509*0Sstevel@tonic-gate /* if the driver is attached */ 7510*0Sstevel@tonic-gate if ((dip = fbdip) != NULL) { 7511*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: attached: %s@%s(%s#%d)\n", pmf, 7512*0Sstevel@tonic-gate PM_DEVICE(dip))) 7513*0Sstevel@tonic-gate /* 7514*0Sstevel@tonic-gate * We set up here as if the driver were power manageable in case 7515*0Sstevel@tonic-gate * we get a later attach of a pm'able driver (which would result 7516*0Sstevel@tonic-gate * in a panic later) 7517*0Sstevel@tonic-gate */ 7518*0Sstevel@tonic-gate cfb_dip = dip; 7519*0Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= (PMC_CONSOLE_FB | PMC_NO_INVOL); 7520*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: cfb_dip -> %s@%s(%s#%d)\n", pmf, 7521*0Sstevel@tonic-gate PM_DEVICE(dip))) 7522*0Sstevel@tonic-gate #ifdef DEBUG 7523*0Sstevel@tonic-gate if (!(PM_GET_PM_INFO(dip) != NULL && PM_NUMCMPTS(dip))) { 7524*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) not power-managed\n", 7525*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 7526*0Sstevel@tonic-gate } 7527*0Sstevel@tonic-gate #endif 7528*0Sstevel@tonic-gate } else { 7529*0Sstevel@tonic-gate char *ep; 7530*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: pntd %s failed\n", pmf, devname)) 7531*0Sstevel@tonic-gate pm_record_invol_path(devname, 7532*0Sstevel@tonic-gate (PMC_CONSOLE_FB | PMC_NO_INVOL), 1, 0, 0, 7533*0Sstevel@tonic-gate (major_t)-1); 7534*0Sstevel@tonic-gate for (ep = strrchr(devname, '/'); ep != devname; 7535*0Sstevel@tonic-gate ep = strrchr(devname, '/')) { 7536*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: devname %s\n", pmf, devname)) 7537*0Sstevel@tonic-gate *ep = '\0'; 7538*0Sstevel@tonic-gate dip = pm_name_to_dip(devname, 0); 7539*0Sstevel@tonic-gate if (dip != NULL) { 7540*0Sstevel@tonic-gate /* 7541*0Sstevel@tonic-gate * Walk up the tree incrementing 7542*0Sstevel@tonic-gate * devi_pm_noinvolpm 7543*0Sstevel@tonic-gate */ 7544*0Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_CFB, 7545*0Sstevel@tonic-gate 0, 0, devname, dip); 7546*0Sstevel@tonic-gate break; 7547*0Sstevel@tonic-gate } else { 7548*0Sstevel@tonic-gate pm_record_invol_path(devname, 7549*0Sstevel@tonic-gate PMC_NO_INVOL, 1, 0, 0, (major_t)-1); 7550*0Sstevel@tonic-gate } 7551*0Sstevel@tonic-gate } 7552*0Sstevel@tonic-gate } 7553*0Sstevel@tonic-gate kmem_free(devname, devname_len); 7554*0Sstevel@tonic-gate } 7555*0Sstevel@tonic-gate 7556*0Sstevel@tonic-gate void 7557*0Sstevel@tonic-gate pm_cfb_rele(void) 7558*0Sstevel@tonic-gate { 7559*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7560*0Sstevel@tonic-gate /* 7561*0Sstevel@tonic-gate * this call isn't using the console any more, it is ok to take it 7562*0Sstevel@tonic-gate * down if the count goes to 0 7563*0Sstevel@tonic-gate */ 7564*0Sstevel@tonic-gate cfb_inuse--; 7565*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7566*0Sstevel@tonic-gate } 7567*0Sstevel@tonic-gate 7568*0Sstevel@tonic-gate /* 7569*0Sstevel@tonic-gate * software interrupt handler for fbpm; this function exists because we can't 7570*0Sstevel@tonic-gate * bring up the frame buffer power from above lock level. So if we need to, 7571*0Sstevel@tonic-gate * we instead schedule a softint that runs this routine and takes us into 7572*0Sstevel@tonic-gate * debug_enter (a bit delayed from the original request, but avoiding a panic). 7573*0Sstevel@tonic-gate */ 7574*0Sstevel@tonic-gate static uint_t 7575*0Sstevel@tonic-gate pm_cfb_softint(caddr_t int_handler_arg) 7576*0Sstevel@tonic-gate { 7577*0Sstevel@tonic-gate _NOTE(ARGUNUSED(int_handler_arg)) 7578*0Sstevel@tonic-gate int rval = DDI_INTR_UNCLAIMED; 7579*0Sstevel@tonic-gate 7580*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7581*0Sstevel@tonic-gate if (pm_soft_pending) { 7582*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7583*0Sstevel@tonic-gate debug_enter((char *)NULL); 7584*0Sstevel@tonic-gate /* acquired in debug_enter before calling pm_cfb_trigger */ 7585*0Sstevel@tonic-gate pm_cfb_rele(); 7586*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7587*0Sstevel@tonic-gate pm_soft_pending = 0; 7588*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7589*0Sstevel@tonic-gate rval = DDI_INTR_CLAIMED; 7590*0Sstevel@tonic-gate } else 7591*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7592*0Sstevel@tonic-gate 7593*0Sstevel@tonic-gate return (rval); 7594*0Sstevel@tonic-gate } 7595*0Sstevel@tonic-gate 7596*0Sstevel@tonic-gate void 7597*0Sstevel@tonic-gate pm_cfb_setup_intr(void) 7598*0Sstevel@tonic-gate { 7599*0Sstevel@tonic-gate PMD_FUNC(pmf, "cfb_setup_intr") 7600*0Sstevel@tonic-gate extern void prom_set_outfuncs(void (*)(void), void (*)(void)); 7601*0Sstevel@tonic-gate void pm_cfb_check_and_powerup(void); 7602*0Sstevel@tonic-gate 7603*0Sstevel@tonic-gate if (!stdout_is_framebuffer) { 7604*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: console not fb\n", pmf)) 7605*0Sstevel@tonic-gate return; 7606*0Sstevel@tonic-gate } 7607*0Sstevel@tonic-gate mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8)); 7608*0Sstevel@tonic-gate #ifdef DEBUG 7609*0Sstevel@tonic-gate mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8)); 7610*0Sstevel@tonic-gate #endif 7611*0Sstevel@tonic-gate /* 7612*0Sstevel@tonic-gate * setup software interrupt handler 7613*0Sstevel@tonic-gate */ 7614*0Sstevel@tonic-gate if (ddi_add_softintr(ddi_root_node(), DDI_SOFTINT_HIGH, &pm_soft_id, 7615*0Sstevel@tonic-gate NULL, NULL, pm_cfb_softint, NULL) != DDI_SUCCESS) 7616*0Sstevel@tonic-gate panic("pm: unable to register soft intr."); 7617*0Sstevel@tonic-gate 7618*0Sstevel@tonic-gate prom_set_outfuncs(pm_cfb_check_and_powerup, pm_cfb_rele); 7619*0Sstevel@tonic-gate } 7620*0Sstevel@tonic-gate 7621*0Sstevel@tonic-gate /* 7622*0Sstevel@tonic-gate * Checks to see if it is safe to write to the console wrt power management 7623*0Sstevel@tonic-gate * (i.e. if the console is a framebuffer, then it must be at full power) 7624*0Sstevel@tonic-gate * returns 1 when power is off (power-up is needed) 7625*0Sstevel@tonic-gate * returns 0 when power is on (power-up not needed) 7626*0Sstevel@tonic-gate */ 7627*0Sstevel@tonic-gate int 7628*0Sstevel@tonic-gate pm_cfb_check_and_hold(void) 7629*0Sstevel@tonic-gate { 7630*0Sstevel@tonic-gate /* 7631*0Sstevel@tonic-gate * cfb_dip is set iff console is a power manageable frame buffer 7632*0Sstevel@tonic-gate * device 7633*0Sstevel@tonic-gate */ 7634*0Sstevel@tonic-gate extern int modrootloaded; 7635*0Sstevel@tonic-gate 7636*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7637*0Sstevel@tonic-gate cfb_inuse++; 7638*0Sstevel@tonic-gate ASSERT(cfb_inuse); /* wrap? */ 7639*0Sstevel@tonic-gate if (modrootloaded && cfb_dip) { 7640*0Sstevel@tonic-gate /* 7641*0Sstevel@tonic-gate * don't power down the frame buffer, the prom is using it 7642*0Sstevel@tonic-gate */ 7643*0Sstevel@tonic-gate if (pm_cfb_comps_off) { 7644*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7645*0Sstevel@tonic-gate return (1); 7646*0Sstevel@tonic-gate } 7647*0Sstevel@tonic-gate } 7648*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7649*0Sstevel@tonic-gate return (0); 7650*0Sstevel@tonic-gate } 7651*0Sstevel@tonic-gate 7652*0Sstevel@tonic-gate /* 7653*0Sstevel@tonic-gate * turn on cfb power (which is known to be off). 7654*0Sstevel@tonic-gate * Must be called below lock level! 7655*0Sstevel@tonic-gate */ 7656*0Sstevel@tonic-gate void 7657*0Sstevel@tonic-gate pm_cfb_powerup(void) 7658*0Sstevel@tonic-gate { 7659*0Sstevel@tonic-gate pm_info_t *info; 7660*0Sstevel@tonic-gate int norm; 7661*0Sstevel@tonic-gate int ccount, ci; 7662*0Sstevel@tonic-gate int unused; 7663*0Sstevel@tonic-gate #ifdef DEBUG 7664*0Sstevel@tonic-gate /* 7665*0Sstevel@tonic-gate * Can't reenter prom_prekern, so suppress pm debug messages 7666*0Sstevel@tonic-gate * (still go to circular buffer). 7667*0Sstevel@tonic-gate */ 7668*0Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 7669*0Sstevel@tonic-gate pm_divertdebug++; 7670*0Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 7671*0Sstevel@tonic-gate #endif 7672*0Sstevel@tonic-gate info = PM_GET_PM_INFO(cfb_dip); 7673*0Sstevel@tonic-gate ASSERT(info); 7674*0Sstevel@tonic-gate 7675*0Sstevel@tonic-gate ccount = PM_NUMCMPTS(cfb_dip); 7676*0Sstevel@tonic-gate for (ci = 0; ci < ccount; ci++) { 7677*0Sstevel@tonic-gate norm = pm_get_normal_power(cfb_dip, ci); 7678*0Sstevel@tonic-gate (void) pm_set_power(cfb_dip, ci, norm, PM_LEVEL_UPONLY, 7679*0Sstevel@tonic-gate PM_CANBLOCK_BYPASS, 0, &unused); 7680*0Sstevel@tonic-gate } 7681*0Sstevel@tonic-gate #ifdef DEBUG 7682*0Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 7683*0Sstevel@tonic-gate pm_divertdebug--; 7684*0Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 7685*0Sstevel@tonic-gate #endif 7686*0Sstevel@tonic-gate } 7687*0Sstevel@tonic-gate 7688*0Sstevel@tonic-gate /* 7689*0Sstevel@tonic-gate * Check if the console framebuffer is powered up. If not power it up. 7690*0Sstevel@tonic-gate * Note: Calling pm_cfb_check_and_hold has put a hold on the power state which 7691*0Sstevel@tonic-gate * must be released by calling pm_cfb_rele when the console fb operation 7692*0Sstevel@tonic-gate * is completed. 7693*0Sstevel@tonic-gate */ 7694*0Sstevel@tonic-gate void 7695*0Sstevel@tonic-gate pm_cfb_check_and_powerup(void) 7696*0Sstevel@tonic-gate { 7697*0Sstevel@tonic-gate if (pm_cfb_check_and_hold()) 7698*0Sstevel@tonic-gate pm_cfb_powerup(); 7699*0Sstevel@tonic-gate } 7700*0Sstevel@tonic-gate 7701*0Sstevel@tonic-gate /* 7702*0Sstevel@tonic-gate * Trigger a low level interrupt to power up console frame buffer. 7703*0Sstevel@tonic-gate */ 7704*0Sstevel@tonic-gate void 7705*0Sstevel@tonic-gate pm_cfb_trigger(void) 7706*0Sstevel@tonic-gate { 7707*0Sstevel@tonic-gate if (cfb_dip == NULL) 7708*0Sstevel@tonic-gate return; 7709*0Sstevel@tonic-gate 7710*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 7711*0Sstevel@tonic-gate /* 7712*0Sstevel@tonic-gate * If machine appears to be hung, pulling the keyboard connector of 7713*0Sstevel@tonic-gate * the console will cause a high level interrupt and go to debug_enter. 7714*0Sstevel@tonic-gate * But, if the fb is powered down, this routine will be called to bring 7715*0Sstevel@tonic-gate * it up (by generating a softint to do the work). If soft interrupts 7716*0Sstevel@tonic-gate * are not running, and the keyboard connector is pulled again, the 7717*0Sstevel@tonic-gate * following code detects this condition and calls panic which allows 7718*0Sstevel@tonic-gate * the fb to be brought up from high level. 7719*0Sstevel@tonic-gate * 7720*0Sstevel@tonic-gate * If two nearly simultaneous calls to debug_enter occur (both from 7721*0Sstevel@tonic-gate * high level) the code described above will cause a panic. 7722*0Sstevel@tonic-gate */ 7723*0Sstevel@tonic-gate if (lbolt <= pm_soft_pending) { 7724*0Sstevel@tonic-gate panicstr = "pm_cfb_trigger: lbolt not advancing"; 7725*0Sstevel@tonic-gate panic(panicstr); /* does a power up at any intr level */ 7726*0Sstevel@tonic-gate /* NOTREACHED */ 7727*0Sstevel@tonic-gate } 7728*0Sstevel@tonic-gate pm_soft_pending = lbolt; 7729*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 7730*0Sstevel@tonic-gate ddi_trigger_softintr(pm_soft_id); 7731*0Sstevel@tonic-gate } 7732*0Sstevel@tonic-gate 7733*0Sstevel@tonic-gate major_t 7734*0Sstevel@tonic-gate pm_path_to_major(char *path) 7735*0Sstevel@tonic-gate { 7736*0Sstevel@tonic-gate PMD_FUNC(pmf, "path_to_major") 7737*0Sstevel@tonic-gate char *np, *ap, *bp; 7738*0Sstevel@tonic-gate major_t ret; 7739*0Sstevel@tonic-gate size_t len; 7740*0Sstevel@tonic-gate static major_t i_path_to_major(char *, char *); 7741*0Sstevel@tonic-gate 7742*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s\n", pmf, path)) 7743*0Sstevel@tonic-gate 7744*0Sstevel@tonic-gate np = strrchr(path, '/'); 7745*0Sstevel@tonic-gate if (np != NULL) 7746*0Sstevel@tonic-gate np++; 7747*0Sstevel@tonic-gate else 7748*0Sstevel@tonic-gate np = path; 7749*0Sstevel@tonic-gate len = strlen(np) + 1; 7750*0Sstevel@tonic-gate bp = kmem_alloc(len, KM_SLEEP); 7751*0Sstevel@tonic-gate (void) strcpy(bp, np); 7752*0Sstevel@tonic-gate if ((ap = strchr(bp, '@')) != NULL) { 7753*0Sstevel@tonic-gate *ap = '\0'; 7754*0Sstevel@tonic-gate } 7755*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %d\n", pmf, ddi_name_to_major(np))) 7756*0Sstevel@tonic-gate ret = i_path_to_major(path, np); 7757*0Sstevel@tonic-gate kmem_free(bp, len); 7758*0Sstevel@tonic-gate return (ret); 7759*0Sstevel@tonic-gate } 7760*0Sstevel@tonic-gate 7761*0Sstevel@tonic-gate #ifdef DEBUG 7762*0Sstevel@tonic-gate 7763*0Sstevel@tonic-gate char *pm_msgp; 7764*0Sstevel@tonic-gate char *pm_bufend; 7765*0Sstevel@tonic-gate char *pm_msgbuf = NULL; 7766*0Sstevel@tonic-gate int pm_logpages = 2; 7767*0Sstevel@tonic-gate 7768*0Sstevel@tonic-gate #define PMLOGPGS pm_logpages 7769*0Sstevel@tonic-gate 7770*0Sstevel@tonic-gate /*PRINTFLIKE1*/ 7771*0Sstevel@tonic-gate void 7772*0Sstevel@tonic-gate pm_log(const char *fmt, ...) 7773*0Sstevel@tonic-gate { 7774*0Sstevel@tonic-gate va_list adx; 7775*0Sstevel@tonic-gate size_t size; 7776*0Sstevel@tonic-gate 7777*0Sstevel@tonic-gate mutex_enter(&pm_debug_lock); 7778*0Sstevel@tonic-gate if (pm_msgbuf == NULL) { 7779*0Sstevel@tonic-gate pm_msgbuf = kmem_zalloc(mmu_ptob(PMLOGPGS), KM_SLEEP); 7780*0Sstevel@tonic-gate pm_bufend = pm_msgbuf + mmu_ptob(PMLOGPGS) - 1; 7781*0Sstevel@tonic-gate pm_msgp = pm_msgbuf; 7782*0Sstevel@tonic-gate } 7783*0Sstevel@tonic-gate va_start(adx, fmt); 7784*0Sstevel@tonic-gate size = vsnprintf(NULL, 0, fmt, adx) + 1; 7785*0Sstevel@tonic-gate va_end(adx); 7786*0Sstevel@tonic-gate va_start(adx, fmt); 7787*0Sstevel@tonic-gate if (size > (pm_bufend - pm_msgp)) { /* wraps */ 7788*0Sstevel@tonic-gate bzero(pm_msgp, pm_bufend - pm_msgp); 7789*0Sstevel@tonic-gate (void) vsnprintf(pm_msgbuf, size, fmt, adx); 7790*0Sstevel@tonic-gate if (!pm_divertdebug) 7791*0Sstevel@tonic-gate prom_printf("%s", pm_msgp); 7792*0Sstevel@tonic-gate pm_msgp = pm_msgbuf + size; 7793*0Sstevel@tonic-gate } else { 7794*0Sstevel@tonic-gate (void) vsnprintf(pm_msgp, size, fmt, adx); 7795*0Sstevel@tonic-gate if (!pm_divertdebug) 7796*0Sstevel@tonic-gate prom_printf("%s", pm_msgp); 7797*0Sstevel@tonic-gate pm_msgp += size; 7798*0Sstevel@tonic-gate } 7799*0Sstevel@tonic-gate va_end(adx); 7800*0Sstevel@tonic-gate mutex_exit(&pm_debug_lock); 7801*0Sstevel@tonic-gate } 7802*0Sstevel@tonic-gate #endif /* DEBUG */ 7803*0Sstevel@tonic-gate 7804*0Sstevel@tonic-gate /* 7805*0Sstevel@tonic-gate * We want to save the state of any directly pm'd devices over the suspend/ 7806*0Sstevel@tonic-gate * resume process so that we can put them back the way the controlling 7807*0Sstevel@tonic-gate * process left them. 7808*0Sstevel@tonic-gate */ 7809*0Sstevel@tonic-gate void 7810*0Sstevel@tonic-gate pm_save_direct_levels(void) 7811*0Sstevel@tonic-gate { 7812*0Sstevel@tonic-gate pm_processes_stopped = 1; 7813*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_save_direct_lvl_walk, 0); 7814*0Sstevel@tonic-gate } 7815*0Sstevel@tonic-gate 7816*0Sstevel@tonic-gate static int 7817*0Sstevel@tonic-gate pm_save_direct_lvl_walk(dev_info_t *dip, void *arg) 7818*0Sstevel@tonic-gate { 7819*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 7820*0Sstevel@tonic-gate int i; 7821*0Sstevel@tonic-gate int *ip; 7822*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 7823*0Sstevel@tonic-gate 7824*0Sstevel@tonic-gate if (!info) 7825*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7826*0Sstevel@tonic-gate 7827*0Sstevel@tonic-gate if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) { 7828*0Sstevel@tonic-gate if (PM_NUMCMPTS(dip) > 2) { 7829*0Sstevel@tonic-gate info->pmi_lp = kmem_alloc(PM_NUMCMPTS(dip) * 7830*0Sstevel@tonic-gate sizeof (int), KM_SLEEP); 7831*0Sstevel@tonic-gate ip = info->pmi_lp; 7832*0Sstevel@tonic-gate } else { 7833*0Sstevel@tonic-gate ip = info->pmi_levels; 7834*0Sstevel@tonic-gate } 7835*0Sstevel@tonic-gate /* autopm and processes are stopped, ok not to lock power */ 7836*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) 7837*0Sstevel@tonic-gate *ip++ = PM_CURPOWER(dip, i); 7838*0Sstevel@tonic-gate /* 7839*0Sstevel@tonic-gate * There is a small window between stopping the 7840*0Sstevel@tonic-gate * processes and setting pm_processes_stopped where 7841*0Sstevel@tonic-gate * a driver could get hung up in a pm_raise_power() 7842*0Sstevel@tonic-gate * call. Free any such driver now. 7843*0Sstevel@tonic-gate */ 7844*0Sstevel@tonic-gate pm_proceed(dip, PMP_RELEASE, -1, -1); 7845*0Sstevel@tonic-gate } 7846*0Sstevel@tonic-gate 7847*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7848*0Sstevel@tonic-gate } 7849*0Sstevel@tonic-gate 7850*0Sstevel@tonic-gate void 7851*0Sstevel@tonic-gate pm_restore_direct_levels(void) 7852*0Sstevel@tonic-gate { 7853*0Sstevel@tonic-gate /* 7854*0Sstevel@tonic-gate * If cpr didn't call pm_save_direct_levels, (because stopping user 7855*0Sstevel@tonic-gate * threads failed) then we don't want to try to restore them 7856*0Sstevel@tonic-gate */ 7857*0Sstevel@tonic-gate if (!pm_processes_stopped) 7858*0Sstevel@tonic-gate return; 7859*0Sstevel@tonic-gate 7860*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_restore_direct_lvl_walk, 0); 7861*0Sstevel@tonic-gate pm_processes_stopped = 0; 7862*0Sstevel@tonic-gate } 7863*0Sstevel@tonic-gate 7864*0Sstevel@tonic-gate static int 7865*0Sstevel@tonic-gate pm_restore_direct_lvl_walk(dev_info_t *dip, void *arg) 7866*0Sstevel@tonic-gate { 7867*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 7868*0Sstevel@tonic-gate PMD_FUNC(pmf, "restore_direct_lvl_walk") 7869*0Sstevel@tonic-gate int i, nc, result; 7870*0Sstevel@tonic-gate int *ip; 7871*0Sstevel@tonic-gate 7872*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 7873*0Sstevel@tonic-gate if (!info) 7874*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7875*0Sstevel@tonic-gate 7876*0Sstevel@tonic-gate if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) { 7877*0Sstevel@tonic-gate if ((nc = PM_NUMCMPTS(dip)) > 2) { 7878*0Sstevel@tonic-gate ip = &info->pmi_lp[nc - 1]; 7879*0Sstevel@tonic-gate } else { 7880*0Sstevel@tonic-gate ip = &info->pmi_levels[nc - 1]; 7881*0Sstevel@tonic-gate } 7882*0Sstevel@tonic-gate /* 7883*0Sstevel@tonic-gate * Because fb drivers fail attempts to turn off the 7884*0Sstevel@tonic-gate * fb when the monitor is on, but treat a request to 7885*0Sstevel@tonic-gate * turn on the monitor as a request to turn on the 7886*0Sstevel@tonic-gate * fb too, we process components in descending order 7887*0Sstevel@tonic-gate * Because autopm is disabled and processes aren't 7888*0Sstevel@tonic-gate * running, it is ok to examine current power outside 7889*0Sstevel@tonic-gate * of the power lock 7890*0Sstevel@tonic-gate */ 7891*0Sstevel@tonic-gate for (i = nc - 1; i >= 0; i--, ip--) { 7892*0Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == *ip) 7893*0Sstevel@tonic-gate continue; 7894*0Sstevel@tonic-gate if (pm_set_power(dip, i, *ip, PM_LEVEL_EXACT, 7895*0Sstevel@tonic-gate PM_CANBLOCK_BYPASS, 0, &result) != 7896*0Sstevel@tonic-gate DDI_SUCCESS) { 7897*0Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable " 7898*0Sstevel@tonic-gate "to restore power level of " 7899*0Sstevel@tonic-gate "component %d of directly " 7900*0Sstevel@tonic-gate "power manged device %s@%s" 7901*0Sstevel@tonic-gate " to %d", 7902*0Sstevel@tonic-gate i, PM_NAME(dip), 7903*0Sstevel@tonic-gate PM_ADDR(dip), *ip); 7904*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to restore " 7905*0Sstevel@tonic-gate "%s@%s(%s#%d)[%d] exact(%d)->%d, " 7906*0Sstevel@tonic-gate "errno %d\n", pmf, PM_DEVICE(dip), i, 7907*0Sstevel@tonic-gate PM_CURPOWER(dip, i), *ip, result)) 7908*0Sstevel@tonic-gate } 7909*0Sstevel@tonic-gate } 7910*0Sstevel@tonic-gate if (nc > 2) { 7911*0Sstevel@tonic-gate kmem_free(info->pmi_lp, nc * sizeof (int)); 7912*0Sstevel@tonic-gate info->pmi_lp = NULL; 7913*0Sstevel@tonic-gate } 7914*0Sstevel@tonic-gate } 7915*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 7916*0Sstevel@tonic-gate } 7917*0Sstevel@tonic-gate 7918*0Sstevel@tonic-gate /* 7919*0Sstevel@tonic-gate * Stolen from the bootdev module 7920*0Sstevel@tonic-gate * attempt to convert a path to a major number 7921*0Sstevel@tonic-gate */ 7922*0Sstevel@tonic-gate static major_t 7923*0Sstevel@tonic-gate i_path_to_major(char *path, char *leaf_name) 7924*0Sstevel@tonic-gate { 7925*0Sstevel@tonic-gate extern major_t path_to_major(char *pathname); 7926*0Sstevel@tonic-gate major_t maj; 7927*0Sstevel@tonic-gate 7928*0Sstevel@tonic-gate if ((maj = path_to_major(path)) == (major_t)-1) { 7929*0Sstevel@tonic-gate maj = ddi_name_to_major(leaf_name); 7930*0Sstevel@tonic-gate } 7931*0Sstevel@tonic-gate 7932*0Sstevel@tonic-gate return (maj); 7933*0Sstevel@tonic-gate } 7934*0Sstevel@tonic-gate 7935*0Sstevel@tonic-gate /* 7936*0Sstevel@tonic-gate * When user calls rem_drv, we need to forget no-involuntary-power-cycles state 7937*0Sstevel@tonic-gate * An entry in the list means that the device is detached, so we need to 7938*0Sstevel@tonic-gate * adjust its ancestors as if they had just seen this attach, and any detached 7939*0Sstevel@tonic-gate * ancestors need to have their list entries adjusted. 7940*0Sstevel@tonic-gate */ 7941*0Sstevel@tonic-gate void 7942*0Sstevel@tonic-gate pm_driver_removed(major_t major) 7943*0Sstevel@tonic-gate { 7944*0Sstevel@tonic-gate static void i_pm_driver_removed(major_t major); 7945*0Sstevel@tonic-gate 7946*0Sstevel@tonic-gate /* 7947*0Sstevel@tonic-gate * Serialize removal of drivers. This is to keep ancestors of 7948*0Sstevel@tonic-gate * a node that is being deleted from getting deleted and added back 7949*0Sstevel@tonic-gate * with different counters. 7950*0Sstevel@tonic-gate */ 7951*0Sstevel@tonic-gate mutex_enter(&pm_remdrv_lock); 7952*0Sstevel@tonic-gate i_pm_driver_removed(major); 7953*0Sstevel@tonic-gate mutex_exit(&pm_remdrv_lock); 7954*0Sstevel@tonic-gate } 7955*0Sstevel@tonic-gate 7956*0Sstevel@tonic-gate /* 7957*0Sstevel@tonic-gate * This routine is called recursively by pm_noinvol_process_ancestors() 7958*0Sstevel@tonic-gate */ 7959*0Sstevel@tonic-gate static void 7960*0Sstevel@tonic-gate i_pm_driver_removed(major_t major) 7961*0Sstevel@tonic-gate { 7962*0Sstevel@tonic-gate PMD_FUNC(pmf, "driver_removed") 7963*0Sstevel@tonic-gate static void adjust_ancestors(char *, int); 7964*0Sstevel@tonic-gate static int pm_is_noinvol_ancestor(pm_noinvol_t *); 7965*0Sstevel@tonic-gate static void pm_noinvol_process_ancestors(char *); 7966*0Sstevel@tonic-gate pm_noinvol_t *ip, *pp = NULL; 7967*0Sstevel@tonic-gate int wasvolpmd; 7968*0Sstevel@tonic-gate ASSERT(major != (major_t)-1); 7969*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s\n", pmf, ddi_major_to_name(major))) 7970*0Sstevel@tonic-gate again: 7971*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 7972*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) { 7973*0Sstevel@tonic-gate if (major != ip->ni_major) 7974*0Sstevel@tonic-gate continue; 7975*0Sstevel@tonic-gate /* 7976*0Sstevel@tonic-gate * If it is an ancestor of no-invol node, which is 7977*0Sstevel@tonic-gate * not removed, skip it. This is to cover the case of 7978*0Sstevel@tonic-gate * ancestor removed without removing its descendants. 7979*0Sstevel@tonic-gate */ 7980*0Sstevel@tonic-gate if (pm_is_noinvol_ancestor(ip)) { 7981*0Sstevel@tonic-gate ip->ni_flags |= PMC_DRIVER_REMOVED; 7982*0Sstevel@tonic-gate continue; 7983*0Sstevel@tonic-gate } 7984*0Sstevel@tonic-gate wasvolpmd = ip->ni_wasvolpmd; 7985*0Sstevel@tonic-gate /* 7986*0Sstevel@tonic-gate * remove the entry from the list 7987*0Sstevel@tonic-gate */ 7988*0Sstevel@tonic-gate if (pp) { 7989*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: freeing %s, prev is %s\n", 7990*0Sstevel@tonic-gate pmf, ip->ni_path, pp->ni_path)) 7991*0Sstevel@tonic-gate pp->ni_next = ip->ni_next; 7992*0Sstevel@tonic-gate } else { 7993*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s head\n", pmf, 7994*0Sstevel@tonic-gate ip->ni_path)) 7995*0Sstevel@tonic-gate ASSERT(pm_noinvol_head == ip); 7996*0Sstevel@tonic-gate pm_noinvol_head = ip->ni_next; 7997*0Sstevel@tonic-gate } 7998*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 7999*0Sstevel@tonic-gate adjust_ancestors(ip->ni_path, wasvolpmd); 8000*0Sstevel@tonic-gate /* 8001*0Sstevel@tonic-gate * Had an ancestor been removed before this node, it would have 8002*0Sstevel@tonic-gate * been skipped. Adjust the no-invol counters for such skipped 8003*0Sstevel@tonic-gate * ancestors. 8004*0Sstevel@tonic-gate */ 8005*0Sstevel@tonic-gate pm_noinvol_process_ancestors(ip->ni_path); 8006*0Sstevel@tonic-gate kmem_free(ip->ni_path, ip->ni_size); 8007*0Sstevel@tonic-gate kmem_free(ip, sizeof (*ip)); 8008*0Sstevel@tonic-gate goto again; 8009*0Sstevel@tonic-gate } 8010*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8011*0Sstevel@tonic-gate } 8012*0Sstevel@tonic-gate 8013*0Sstevel@tonic-gate /* 8014*0Sstevel@tonic-gate * returns 1, if *aip is a ancestor of a no-invol node 8015*0Sstevel@tonic-gate * 0, otherwise 8016*0Sstevel@tonic-gate */ 8017*0Sstevel@tonic-gate static int 8018*0Sstevel@tonic-gate pm_is_noinvol_ancestor(pm_noinvol_t *aip) 8019*0Sstevel@tonic-gate { 8020*0Sstevel@tonic-gate pm_noinvol_t *ip; 8021*0Sstevel@tonic-gate 8022*0Sstevel@tonic-gate ASSERT(strlen(aip->ni_path) != 0); 8023*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 8024*0Sstevel@tonic-gate if (ip == aip) 8025*0Sstevel@tonic-gate continue; 8026*0Sstevel@tonic-gate /* 8027*0Sstevel@tonic-gate * To be an ancestor, the path must be an initial substring of 8028*0Sstevel@tonic-gate * the descendent, and end just before a '/' in the 8029*0Sstevel@tonic-gate * descendent's path. 8030*0Sstevel@tonic-gate */ 8031*0Sstevel@tonic-gate if ((strstr(ip->ni_path, aip->ni_path) == ip->ni_path) && 8032*0Sstevel@tonic-gate (ip->ni_path[strlen(aip->ni_path)] == '/')) 8033*0Sstevel@tonic-gate return (1); 8034*0Sstevel@tonic-gate } 8035*0Sstevel@tonic-gate return (0); 8036*0Sstevel@tonic-gate } 8037*0Sstevel@tonic-gate 8038*0Sstevel@tonic-gate #define PM_MAJOR(dip) ddi_name_to_major(ddi_binding_name(dip)) 8039*0Sstevel@tonic-gate /* 8040*0Sstevel@tonic-gate * scan through the pm_noinvolpm list adjusting ancestors of the current 8041*0Sstevel@tonic-gate * node; Modifies string *path. 8042*0Sstevel@tonic-gate */ 8043*0Sstevel@tonic-gate static void 8044*0Sstevel@tonic-gate adjust_ancestors(char *path, int wasvolpmd) 8045*0Sstevel@tonic-gate { 8046*0Sstevel@tonic-gate PMD_FUNC(pmf, "adjust_ancestors") 8047*0Sstevel@tonic-gate char *cp; 8048*0Sstevel@tonic-gate pm_noinvol_t *lp; 8049*0Sstevel@tonic-gate pm_noinvol_t *pp = NULL; 8050*0Sstevel@tonic-gate major_t locked = (major_t)UINT_MAX; 8051*0Sstevel@tonic-gate dev_info_t *dip; 8052*0Sstevel@tonic-gate char *pathbuf; 8053*0Sstevel@tonic-gate size_t pathbuflen = strlen(path) + 1; 8054*0Sstevel@tonic-gate 8055*0Sstevel@tonic-gate /* 8056*0Sstevel@tonic-gate * First we look up the ancestor's dip. If we find it, then we 8057*0Sstevel@tonic-gate * adjust counts up the tree 8058*0Sstevel@tonic-gate */ 8059*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s wasvolpmd %d\n", pmf, path, wasvolpmd)) 8060*0Sstevel@tonic-gate pathbuf = kmem_alloc(pathbuflen, KM_SLEEP); 8061*0Sstevel@tonic-gate (void) strcpy(pathbuf, path); 8062*0Sstevel@tonic-gate cp = strrchr(pathbuf, '/'); 8063*0Sstevel@tonic-gate if (cp == NULL) { 8064*0Sstevel@tonic-gate /* if no ancestors, then nothing to do */ 8065*0Sstevel@tonic-gate kmem_free(pathbuf, pathbuflen); 8066*0Sstevel@tonic-gate return; 8067*0Sstevel@tonic-gate } 8068*0Sstevel@tonic-gate *cp = '\0'; 8069*0Sstevel@tonic-gate dip = pm_name_to_dip(pathbuf, 1); 8070*0Sstevel@tonic-gate if (dip != NULL) { 8071*0Sstevel@tonic-gate locked = PM_MAJOR(dip); 8072*0Sstevel@tonic-gate 8073*0Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_REMDRV, 0, wasvolpmd, 8074*0Sstevel@tonic-gate path, dip); 8075*0Sstevel@tonic-gate 8076*0Sstevel@tonic-gate if (locked != (major_t)UINT_MAX) 8077*0Sstevel@tonic-gate ddi_release_devi(dip); 8078*0Sstevel@tonic-gate } else { 8079*0Sstevel@tonic-gate char *apath; 8080*0Sstevel@tonic-gate size_t len = strlen(pathbuf) + 1; 8081*0Sstevel@tonic-gate int lock_held = 1; 8082*0Sstevel@tonic-gate 8083*0Sstevel@tonic-gate /* 8084*0Sstevel@tonic-gate * Now check for ancestors that exist only in the list 8085*0Sstevel@tonic-gate */ 8086*0Sstevel@tonic-gate apath = kmem_alloc(len, KM_SLEEP); 8087*0Sstevel@tonic-gate (void) strcpy(apath, pathbuf); 8088*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER); 8089*0Sstevel@tonic-gate for (lp = pm_noinvol_head; lp; pp = lp, lp = lp->ni_next) { 8090*0Sstevel@tonic-gate /* 8091*0Sstevel@tonic-gate * This can only happen once. Since we have to drop 8092*0Sstevel@tonic-gate * the lock, we need to extract the relevant info. 8093*0Sstevel@tonic-gate */ 8094*0Sstevel@tonic-gate if (strcmp(pathbuf, lp->ni_path) == 0) { 8095*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s no %d -> %d\n", pmf, 8096*0Sstevel@tonic-gate lp->ni_path, lp->ni_noinvolpm, 8097*0Sstevel@tonic-gate lp->ni_noinvolpm - 1)) 8098*0Sstevel@tonic-gate lp->ni_noinvolpm--; 8099*0Sstevel@tonic-gate if (wasvolpmd && lp->ni_volpmd) { 8100*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s vol %d -> " 8101*0Sstevel@tonic-gate "%d\n", pmf, lp->ni_path, 8102*0Sstevel@tonic-gate lp->ni_volpmd, lp->ni_volpmd - 1)) 8103*0Sstevel@tonic-gate lp->ni_volpmd--; 8104*0Sstevel@tonic-gate } 8105*0Sstevel@tonic-gate /* 8106*0Sstevel@tonic-gate * remove the entry from the list, if there 8107*0Sstevel@tonic-gate * are no more no-invol descendants and node 8108*0Sstevel@tonic-gate * itself is not a no-invol node. 8109*0Sstevel@tonic-gate */ 8110*0Sstevel@tonic-gate if (!(lp->ni_noinvolpm || 8111*0Sstevel@tonic-gate (lp->ni_flags & PMC_NO_INVOL))) { 8112*0Sstevel@tonic-gate ASSERT(lp->ni_volpmd == 0); 8113*0Sstevel@tonic-gate if (pp) { 8114*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: freeing " 8115*0Sstevel@tonic-gate "%s, prev is %s\n", pmf, 8116*0Sstevel@tonic-gate lp->ni_path, pp->ni_path)) 8117*0Sstevel@tonic-gate pp->ni_next = lp->ni_next; 8118*0Sstevel@tonic-gate } else { 8119*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s " 8120*0Sstevel@tonic-gate "head\n", pmf, lp->ni_path)) 8121*0Sstevel@tonic-gate ASSERT(pm_noinvol_head == lp); 8122*0Sstevel@tonic-gate pm_noinvol_head = lp->ni_next; 8123*0Sstevel@tonic-gate } 8124*0Sstevel@tonic-gate lock_held = 0; 8125*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8126*0Sstevel@tonic-gate adjust_ancestors(apath, wasvolpmd); 8127*0Sstevel@tonic-gate /* restore apath */ 8128*0Sstevel@tonic-gate (void) strcpy(apath, pathbuf); 8129*0Sstevel@tonic-gate kmem_free(lp->ni_path, lp->ni_size); 8130*0Sstevel@tonic-gate kmem_free(lp, sizeof (*lp)); 8131*0Sstevel@tonic-gate } 8132*0Sstevel@tonic-gate break; 8133*0Sstevel@tonic-gate } 8134*0Sstevel@tonic-gate } 8135*0Sstevel@tonic-gate if (lock_held) 8136*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8137*0Sstevel@tonic-gate adjust_ancestors(apath, wasvolpmd); 8138*0Sstevel@tonic-gate kmem_free(apath, len); 8139*0Sstevel@tonic-gate } 8140*0Sstevel@tonic-gate kmem_free(pathbuf, pathbuflen); 8141*0Sstevel@tonic-gate } 8142*0Sstevel@tonic-gate 8143*0Sstevel@tonic-gate /* 8144*0Sstevel@tonic-gate * Do no-invol processing for any ancestors i.e. adjust counters of ancestors, 8145*0Sstevel@tonic-gate * which were skipped even though their drivers were removed. 8146*0Sstevel@tonic-gate */ 8147*0Sstevel@tonic-gate static void 8148*0Sstevel@tonic-gate pm_noinvol_process_ancestors(char *path) 8149*0Sstevel@tonic-gate { 8150*0Sstevel@tonic-gate pm_noinvol_t *lp; 8151*0Sstevel@tonic-gate 8152*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 8153*0Sstevel@tonic-gate for (lp = pm_noinvol_head; lp; lp = lp->ni_next) { 8154*0Sstevel@tonic-gate if (strstr(path, lp->ni_path) && 8155*0Sstevel@tonic-gate (lp->ni_flags & PMC_DRIVER_REMOVED)) { 8156*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8157*0Sstevel@tonic-gate i_pm_driver_removed(lp->ni_major); 8158*0Sstevel@tonic-gate return; 8159*0Sstevel@tonic-gate } 8160*0Sstevel@tonic-gate } 8161*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8162*0Sstevel@tonic-gate } 8163*0Sstevel@tonic-gate 8164*0Sstevel@tonic-gate /* 8165*0Sstevel@tonic-gate * Returns true if (detached) device needs to be kept up because it exported the 8166*0Sstevel@tonic-gate * "no-involuntary-power-cycles" property or we're pretending it did (console 8167*0Sstevel@tonic-gate * fb case) or it is an ancestor of such a device and has used up the "one 8168*0Sstevel@tonic-gate * free cycle" allowed when all such leaf nodes have voluntarily powered down 8169*0Sstevel@tonic-gate * upon detach. In any event, we need an exact hit on the path or we return 8170*0Sstevel@tonic-gate * false. 8171*0Sstevel@tonic-gate */ 8172*0Sstevel@tonic-gate int 8173*0Sstevel@tonic-gate pm_noinvol_detached(char *path) 8174*0Sstevel@tonic-gate { 8175*0Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_detached") 8176*0Sstevel@tonic-gate pm_noinvol_t *ip; 8177*0Sstevel@tonic-gate int ret = 0; 8178*0Sstevel@tonic-gate 8179*0Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER); 8180*0Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) { 8181*0Sstevel@tonic-gate if (strcmp(path, ip->ni_path) == 0) { 8182*0Sstevel@tonic-gate if (ip->ni_flags & PMC_CONSOLE_FB) { 8183*0Sstevel@tonic-gate PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB " 8184*0Sstevel@tonic-gate "%s\n", pmf, path)) 8185*0Sstevel@tonic-gate ret = 1; 8186*0Sstevel@tonic-gate break; 8187*0Sstevel@tonic-gate } 8188*0Sstevel@tonic-gate #ifdef DEBUG 8189*0Sstevel@tonic-gate if (ip->ni_noinvolpm != ip->ni_volpmd) 8190*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s" 8191*0Sstevel@tonic-gate "\n", pmf, ip->ni_noinvolpm, ip->ni_volpmd, 8192*0Sstevel@tonic-gate path)) 8193*0Sstevel@tonic-gate #endif 8194*0Sstevel@tonic-gate ret = (ip->ni_noinvolpm != ip->ni_volpmd); 8195*0Sstevel@tonic-gate break; 8196*0Sstevel@tonic-gate } 8197*0Sstevel@tonic-gate } 8198*0Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock); 8199*0Sstevel@tonic-gate return (ret); 8200*0Sstevel@tonic-gate } 8201*0Sstevel@tonic-gate 8202*0Sstevel@tonic-gate int 8203*0Sstevel@tonic-gate pm_is_cfb(dev_info_t *dip) 8204*0Sstevel@tonic-gate { 8205*0Sstevel@tonic-gate return (dip == cfb_dip); 8206*0Sstevel@tonic-gate } 8207*0Sstevel@tonic-gate 8208*0Sstevel@tonic-gate #ifdef DEBUG 8209*0Sstevel@tonic-gate /* 8210*0Sstevel@tonic-gate * Return true if all components of the console frame buffer are at 8211*0Sstevel@tonic-gate * "normal" power, i.e., fully on. For the case where the console is not 8212*0Sstevel@tonic-gate * a framebuffer, we also return true 8213*0Sstevel@tonic-gate */ 8214*0Sstevel@tonic-gate int 8215*0Sstevel@tonic-gate pm_cfb_is_up(void) 8216*0Sstevel@tonic-gate { 8217*0Sstevel@tonic-gate return (pm_cfb_comps_off == 0); 8218*0Sstevel@tonic-gate } 8219*0Sstevel@tonic-gate #endif 8220*0Sstevel@tonic-gate 8221*0Sstevel@tonic-gate /* 8222*0Sstevel@tonic-gate * Preventing scan from powering down the node by incrementing the 8223*0Sstevel@tonic-gate * kidsupcnt. 8224*0Sstevel@tonic-gate */ 8225*0Sstevel@tonic-gate void 8226*0Sstevel@tonic-gate pm_hold_power(dev_info_t *dip) 8227*0Sstevel@tonic-gate { 8228*0Sstevel@tonic-gate e_pm_hold_rele_power(dip, 1); 8229*0Sstevel@tonic-gate } 8230*0Sstevel@tonic-gate 8231*0Sstevel@tonic-gate /* 8232*0Sstevel@tonic-gate * Releasing the hold by decrementing the kidsupcnt allowing scan 8233*0Sstevel@tonic-gate * to power down the node if all conditions are met. 8234*0Sstevel@tonic-gate */ 8235*0Sstevel@tonic-gate void 8236*0Sstevel@tonic-gate pm_rele_power(dev_info_t *dip) 8237*0Sstevel@tonic-gate { 8238*0Sstevel@tonic-gate e_pm_hold_rele_power(dip, -1); 8239*0Sstevel@tonic-gate } 8240*0Sstevel@tonic-gate 8241*0Sstevel@tonic-gate /* 8242*0Sstevel@tonic-gate * A wrapper of pm_all_to_normal() to power up a dip 8243*0Sstevel@tonic-gate * to its normal level 8244*0Sstevel@tonic-gate */ 8245*0Sstevel@tonic-gate int 8246*0Sstevel@tonic-gate pm_powerup(dev_info_t *dip) 8247*0Sstevel@tonic-gate { 8248*0Sstevel@tonic-gate PMD_FUNC(pmf, "pm_powerup") 8249*0Sstevel@tonic-gate 8250*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 8251*0Sstevel@tonic-gate ASSERT(!(servicing_interrupt())); 8252*0Sstevel@tonic-gate 8253*0Sstevel@tonic-gate /* 8254*0Sstevel@tonic-gate * in case this node is not already participating pm 8255*0Sstevel@tonic-gate */ 8256*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) { 8257*0Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) 8258*0Sstevel@tonic-gate return (DDI_SUCCESS); 8259*0Sstevel@tonic-gate if (pm_start(dip) != DDI_SUCCESS) 8260*0Sstevel@tonic-gate return (DDI_FAILURE); 8261*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) 8262*0Sstevel@tonic-gate return (DDI_SUCCESS); 8263*0Sstevel@tonic-gate } 8264*0Sstevel@tonic-gate 8265*0Sstevel@tonic-gate return (pm_all_to_normal(dip, PM_CANBLOCK_BLOCK)); 8266*0Sstevel@tonic-gate } 8267*0Sstevel@tonic-gate 8268*0Sstevel@tonic-gate int 8269*0Sstevel@tonic-gate pm_rescan_walk(dev_info_t *dip, void *arg) 8270*0Sstevel@tonic-gate { 8271*0Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 8272*0Sstevel@tonic-gate 8273*0Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) 8274*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 8275*0Sstevel@tonic-gate 8276*0Sstevel@tonic-gate /* 8277*0Sstevel@tonic-gate * Currently pm_cpr_callb/resume code is the only caller 8278*0Sstevel@tonic-gate * and it needs to make sure that stopped scan get 8279*0Sstevel@tonic-gate * reactivated. Otherwise, rescan walk needn't reactive 8280*0Sstevel@tonic-gate * stopped scan. 8281*0Sstevel@tonic-gate */ 8282*0Sstevel@tonic-gate pm_scan_init(dip); 8283*0Sstevel@tonic-gate 8284*0Sstevel@tonic-gate (void) pm_rescan(dip); 8285*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 8286*0Sstevel@tonic-gate } 8287*0Sstevel@tonic-gate 8288*0Sstevel@tonic-gate static dev_info_t * 8289*0Sstevel@tonic-gate pm_get_next_descendent(dev_info_t *dip, dev_info_t *tdip) 8290*0Sstevel@tonic-gate { 8291*0Sstevel@tonic-gate dev_info_t *wdip, *pdip; 8292*0Sstevel@tonic-gate 8293*0Sstevel@tonic-gate for (wdip = tdip; wdip != dip; wdip = pdip) { 8294*0Sstevel@tonic-gate pdip = ddi_get_parent(wdip); 8295*0Sstevel@tonic-gate if (pdip == dip) 8296*0Sstevel@tonic-gate return (wdip); 8297*0Sstevel@tonic-gate } 8298*0Sstevel@tonic-gate return (NULL); 8299*0Sstevel@tonic-gate } 8300*0Sstevel@tonic-gate 8301*0Sstevel@tonic-gate int 8302*0Sstevel@tonic-gate pm_busop_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 8303*0Sstevel@tonic-gate void *arg, void *result) 8304*0Sstevel@tonic-gate { 8305*0Sstevel@tonic-gate PMD_FUNC(pmf, "bp_bus_power") 8306*0Sstevel@tonic-gate dev_info_t *cdip; 8307*0Sstevel@tonic-gate pm_info_t *cinfo; 8308*0Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc; 8309*0Sstevel@tonic-gate pm_sp_misc_t *pspm; 8310*0Sstevel@tonic-gate pm_bp_nexus_pwrup_t *bpn; 8311*0Sstevel@tonic-gate pm_bp_child_pwrchg_t new_bpc; 8312*0Sstevel@tonic-gate pm_bp_noinvol_t *bpi; 8313*0Sstevel@tonic-gate dev_info_t *tdip; 8314*0Sstevel@tonic-gate char *pathbuf; 8315*0Sstevel@tonic-gate int ret = DDI_SUCCESS; 8316*0Sstevel@tonic-gate int errno = 0; 8317*0Sstevel@tonic-gate pm_component_t *cp; 8318*0Sstevel@tonic-gate 8319*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip), 8320*0Sstevel@tonic-gate pm_decode_op(op))) 8321*0Sstevel@tonic-gate switch (op) { 8322*0Sstevel@tonic-gate case BUS_POWER_CHILD_PWRCHG: 8323*0Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg; 8324*0Sstevel@tonic-gate pspm = (pm_sp_misc_t *)bpc->bpc_private; 8325*0Sstevel@tonic-gate tdip = bpc->bpc_dip; 8326*0Sstevel@tonic-gate cdip = pm_get_next_descendent(dip, tdip); 8327*0Sstevel@tonic-gate cinfo = PM_GET_PM_INFO(cdip); 8328*0Sstevel@tonic-gate if (cdip != tdip) { 8329*0Sstevel@tonic-gate /* 8330*0Sstevel@tonic-gate * If the node is an involved parent, it needs to 8331*0Sstevel@tonic-gate * power up the node as it is needed. There is nothing 8332*0Sstevel@tonic-gate * else the framework can do here. 8333*0Sstevel@tonic-gate */ 8334*0Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(cdip)) { 8335*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: call bus_power for " 8336*0Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(cdip))) 8337*0Sstevel@tonic-gate return ((*PM_BUS_POWER_FUNC(cdip))(cdip, 8338*0Sstevel@tonic-gate impl_arg, op, arg, result)); 8339*0Sstevel@tonic-gate } 8340*0Sstevel@tonic-gate ASSERT(pspm->pspm_direction == PM_LEVEL_UPONLY || 8341*0Sstevel@tonic-gate pspm->pspm_direction == PM_LEVEL_DOWNONLY || 8342*0Sstevel@tonic-gate pspm->pspm_direction == PM_LEVEL_EXACT); 8343*0Sstevel@tonic-gate /* 8344*0Sstevel@tonic-gate * we presume that the parent needs to be up in 8345*0Sstevel@tonic-gate * order for the child to change state (either 8346*0Sstevel@tonic-gate * because it must already be on if the child is on 8347*0Sstevel@tonic-gate * (and the pm_all_to_normal_nexus() will be a nop) 8348*0Sstevel@tonic-gate * or because it will need to be on for the child 8349*0Sstevel@tonic-gate * to come on; so we make the call regardless 8350*0Sstevel@tonic-gate */ 8351*0Sstevel@tonic-gate pm_hold_power(cdip); 8352*0Sstevel@tonic-gate if (cinfo) { 8353*0Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock; 8354*0Sstevel@tonic-gate ret = pm_all_to_normal_nexus(cdip, canblock); 8355*0Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 8356*0Sstevel@tonic-gate pm_rele_power(cdip); 8357*0Sstevel@tonic-gate return (ret); 8358*0Sstevel@tonic-gate } 8359*0Sstevel@tonic-gate } 8360*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: walk down to %s@%s(%s#%d)\n", pmf, 8361*0Sstevel@tonic-gate PM_DEVICE(cdip))) 8362*0Sstevel@tonic-gate ret = pm_busop_bus_power(cdip, impl_arg, op, arg, 8363*0Sstevel@tonic-gate result); 8364*0Sstevel@tonic-gate pm_rele_power(cdip); 8365*0Sstevel@tonic-gate } else { 8366*0Sstevel@tonic-gate ret = pm_busop_set_power(cdip, impl_arg, op, arg, 8367*0Sstevel@tonic-gate result); 8368*0Sstevel@tonic-gate } 8369*0Sstevel@tonic-gate return (ret); 8370*0Sstevel@tonic-gate 8371*0Sstevel@tonic-gate case BUS_POWER_NEXUS_PWRUP: 8372*0Sstevel@tonic-gate bpn = (pm_bp_nexus_pwrup_t *)arg; 8373*0Sstevel@tonic-gate pspm = (pm_sp_misc_t *)bpn->bpn_private; 8374*0Sstevel@tonic-gate 8375*0Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || 8376*0Sstevel@tonic-gate !e_pm_valid_comp(dip, bpn->bpn_comp, &cp) || 8377*0Sstevel@tonic-gate !e_pm_valid_power(dip, bpn->bpn_comp, bpn->bpn_level)) { 8378*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) has no pm info; EIO\n", 8379*0Sstevel@tonic-gate pmf, PM_DEVICE(dip))) 8380*0Sstevel@tonic-gate *pspm->pspm_errnop = EIO; 8381*0Sstevel@tonic-gate *(int *)result = DDI_FAILURE; 8382*0Sstevel@tonic-gate return (DDI_FAILURE); 8383*0Sstevel@tonic-gate } 8384*0Sstevel@tonic-gate 8385*0Sstevel@tonic-gate ASSERT(bpn->bpn_dip == dip); 8386*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: nexus powerup for %s@%s(%s#%d)\n", pmf, 8387*0Sstevel@tonic-gate PM_DEVICE(dip))) 8388*0Sstevel@tonic-gate new_bpc.bpc_dip = dip; 8389*0Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 8390*0Sstevel@tonic-gate new_bpc.bpc_path = ddi_pathname(dip, pathbuf); 8391*0Sstevel@tonic-gate new_bpc.bpc_comp = bpn->bpn_comp; 8392*0Sstevel@tonic-gate new_bpc.bpc_olevel = PM_CURPOWER(dip, bpn->bpn_comp); 8393*0Sstevel@tonic-gate new_bpc.bpc_nlevel = bpn->bpn_level; 8394*0Sstevel@tonic-gate new_bpc.bpc_private = bpn->bpn_private; 8395*0Sstevel@tonic-gate ((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_direction = 8396*0Sstevel@tonic-gate PM_LEVEL_UPONLY; 8397*0Sstevel@tonic-gate ((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_errnop = 8398*0Sstevel@tonic-gate &errno; 8399*0Sstevel@tonic-gate ret = pm_busop_set_power(dip, impl_arg, BUS_POWER_CHILD_PWRCHG, 8400*0Sstevel@tonic-gate (void *)&new_bpc, result); 8401*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 8402*0Sstevel@tonic-gate return (ret); 8403*0Sstevel@tonic-gate 8404*0Sstevel@tonic-gate case BUS_POWER_NOINVOL: 8405*0Sstevel@tonic-gate bpi = (pm_bp_noinvol_t *)arg; 8406*0Sstevel@tonic-gate tdip = bpi->bpni_dip; 8407*0Sstevel@tonic-gate cdip = pm_get_next_descendent(dip, tdip); 8408*0Sstevel@tonic-gate 8409*0Sstevel@tonic-gate /* In case of rem_drv, the leaf node has been removed */ 8410*0Sstevel@tonic-gate if (cdip == NULL) 8411*0Sstevel@tonic-gate return (DDI_SUCCESS); 8412*0Sstevel@tonic-gate 8413*0Sstevel@tonic-gate cinfo = PM_GET_PM_INFO(cdip); 8414*0Sstevel@tonic-gate if (cdip != tdip) { 8415*0Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(cdip)) { 8416*0Sstevel@tonic-gate PMD(PMD_NOINVOL, 8417*0Sstevel@tonic-gate ("%s: call bus_power for %s@%s(%s#%d)\n", 8418*0Sstevel@tonic-gate pmf, PM_DEVICE(cdip))) 8419*0Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(cdip)) 8420*0Sstevel@tonic-gate (cdip, NULL, op, arg, result); 8421*0Sstevel@tonic-gate if ((cinfo) && (ret == DDI_SUCCESS)) 8422*0Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, 8423*0Sstevel@tonic-gate bpi); 8424*0Sstevel@tonic-gate return (ret); 8425*0Sstevel@tonic-gate } else { 8426*0Sstevel@tonic-gate PMD(PMD_NOINVOL, 8427*0Sstevel@tonic-gate ("%s: walk down to %s@%s(%s#%d)\n", pmf, 8428*0Sstevel@tonic-gate PM_DEVICE(cdip))) 8429*0Sstevel@tonic-gate ret = pm_busop_bus_power(cdip, NULL, op, 8430*0Sstevel@tonic-gate arg, result); 8431*0Sstevel@tonic-gate /* 8432*0Sstevel@tonic-gate * Update the current node. 8433*0Sstevel@tonic-gate */ 8434*0Sstevel@tonic-gate if ((cinfo) && (ret == DDI_SUCCESS)) 8435*0Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, 8436*0Sstevel@tonic-gate bpi); 8437*0Sstevel@tonic-gate return (ret); 8438*0Sstevel@tonic-gate } 8439*0Sstevel@tonic-gate } else { 8440*0Sstevel@tonic-gate /* 8441*0Sstevel@tonic-gate * For attach, detach, power up: 8442*0Sstevel@tonic-gate * Do nothing for leaf node since its 8443*0Sstevel@tonic-gate * counts are already updated. 8444*0Sstevel@tonic-gate * For CFB and driver removal, since the 8445*0Sstevel@tonic-gate * path and the target dip passed in is up to and incl. 8446*0Sstevel@tonic-gate * the immediate ancestor, need to do the update. 8447*0Sstevel@tonic-gate */ 8448*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: target %s@%s(%s#%d) is " 8449*0Sstevel@tonic-gate "reached\n", pmf, PM_DEVICE(cdip))) 8450*0Sstevel@tonic-gate if (cinfo && ((bpi->bpni_cmd == PM_BP_NOINVOL_REMDRV) || 8451*0Sstevel@tonic-gate (bpi->bpni_cmd == PM_BP_NOINVOL_CFB))) 8452*0Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, bpi); 8453*0Sstevel@tonic-gate return (DDI_SUCCESS); 8454*0Sstevel@tonic-gate } 8455*0Sstevel@tonic-gate 8456*0Sstevel@tonic-gate default: 8457*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: operation %d is not supported!\n", pmf, op)) 8458*0Sstevel@tonic-gate return (DDI_FAILURE); 8459*0Sstevel@tonic-gate } 8460*0Sstevel@tonic-gate } 8461*0Sstevel@tonic-gate 8462*0Sstevel@tonic-gate static int 8463*0Sstevel@tonic-gate pm_busop_set_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op, 8464*0Sstevel@tonic-gate void *arg, void *resultp) 8465*0Sstevel@tonic-gate { 8466*0Sstevel@tonic-gate _NOTE(ARGUNUSED(impl_arg)) 8467*0Sstevel@tonic-gate PMD_FUNC(pmf, "bp_set_power") 8468*0Sstevel@tonic-gate pm_ppm_devlist_t *devl; 8469*0Sstevel@tonic-gate int clevel, circ; 8470*0Sstevel@tonic-gate #ifdef DEBUG 8471*0Sstevel@tonic-gate int circ_db, ccirc_db; 8472*0Sstevel@tonic-gate #endif 8473*0Sstevel@tonic-gate int ret = DDI_SUCCESS; 8474*0Sstevel@tonic-gate dev_info_t *cdip; 8475*0Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg; 8476*0Sstevel@tonic-gate pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private; 8477*0Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock; 8478*0Sstevel@tonic-gate int scan = pspm->pspm_scan; 8479*0Sstevel@tonic-gate int comp = bpc->bpc_comp; 8480*0Sstevel@tonic-gate int olevel = bpc->bpc_olevel; 8481*0Sstevel@tonic-gate int nlevel = bpc->bpc_nlevel; 8482*0Sstevel@tonic-gate int comps_off_incr = 0; 8483*0Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip); 8484*0Sstevel@tonic-gate int dodeps; 8485*0Sstevel@tonic-gate int direction = pspm->pspm_direction; 8486*0Sstevel@tonic-gate int *errnop = pspm->pspm_errnop; 8487*0Sstevel@tonic-gate char *dir = pm_decode_direction(direction); 8488*0Sstevel@tonic-gate int *iresp = (int *)resultp; 8489*0Sstevel@tonic-gate time_t idletime, thresh; 8490*0Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, comp); 8491*0Sstevel@tonic-gate int work_type; 8492*0Sstevel@tonic-gate 8493*0Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8494*0Sstevel@tonic-gate *errnop = 0; 8495*0Sstevel@tonic-gate ASSERT(op == BUS_POWER_CHILD_PWRCHG); 8496*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip), 8497*0Sstevel@tonic-gate pm_decode_op(op))) 8498*0Sstevel@tonic-gate 8499*0Sstevel@tonic-gate /* 8500*0Sstevel@tonic-gate * The following set of conditions indicate we are here to handle a 8501*0Sstevel@tonic-gate * driver's pm_[raise|lower]_power request, but the device is being 8502*0Sstevel@tonic-gate * power managed (PM_DIRECT_PM) by a user process. For that case 8503*0Sstevel@tonic-gate * we want to pm_block and pass a status back to the caller based 8504*0Sstevel@tonic-gate * on whether the controlling process's next activity on the device 8505*0Sstevel@tonic-gate * matches the current request or not. This distinction tells 8506*0Sstevel@tonic-gate * downstream functions to avoid calling into a driver or changing 8507*0Sstevel@tonic-gate * the framework's power state. To actually block, we need: 8508*0Sstevel@tonic-gate * 8509*0Sstevel@tonic-gate * PM_ISDIRECT(dip) 8510*0Sstevel@tonic-gate * no reason to block unless a process is directly controlling dev 8511*0Sstevel@tonic-gate * direction != PM_LEVEL_EXACT 8512*0Sstevel@tonic-gate * EXACT is used by controlling proc's PM_SET_CURRENT_POWER ioctl 8513*0Sstevel@tonic-gate * !pm_processes_stopped 8514*0Sstevel@tonic-gate * don't block if controlling proc already be stopped for cpr 8515*0Sstevel@tonic-gate * canblock != PM_CANBLOCK_BYPASS 8516*0Sstevel@tonic-gate * our caller must not have explicitly prevented blocking 8517*0Sstevel@tonic-gate */ 8518*0Sstevel@tonic-gate if (direction != PM_LEVEL_EXACT && canblock != PM_CANBLOCK_BYPASS) { 8519*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 8520*0Sstevel@tonic-gate while (PM_ISDIRECT(dip) && !pm_processes_stopped) { 8521*0Sstevel@tonic-gate /* releases dip lock */ 8522*0Sstevel@tonic-gate ret = pm_busop_match_request(dip, bpc); 8523*0Sstevel@tonic-gate if (ret == EAGAIN) { 8524*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 8525*0Sstevel@tonic-gate continue; 8526*0Sstevel@tonic-gate } 8527*0Sstevel@tonic-gate return (*iresp = ret); 8528*0Sstevel@tonic-gate } 8529*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8530*0Sstevel@tonic-gate } 8531*0Sstevel@tonic-gate /* BC device is never scanned, so power will stick until we are done */ 8532*0Sstevel@tonic-gate if (PM_ISBC(dip) && comp != 0 && nlevel != 0 && 8533*0Sstevel@tonic-gate direction != PM_LEVEL_DOWNONLY) { 8534*0Sstevel@tonic-gate int nrmpwr0 = pm_get_normal_power(dip, 0); 8535*0Sstevel@tonic-gate if (pm_set_power(dip, 0, nrmpwr0, direction, 8536*0Sstevel@tonic-gate canblock, 0, resultp) != DDI_SUCCESS) { 8537*0Sstevel@tonic-gate /* *resultp set by pm_set_power */ 8538*0Sstevel@tonic-gate return (DDI_FAILURE); 8539*0Sstevel@tonic-gate } 8540*0Sstevel@tonic-gate } 8541*0Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(pdip)) { 8542*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: pre_notify %s@%s(%s#%d) for child " 8543*0Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(pdip), PM_DEVICE(dip))) 8544*0Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL, 8545*0Sstevel@tonic-gate BUS_POWER_PRE_NOTIFICATION, bpc, resultp); 8546*0Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 8547*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: failed to pre_notify %s@%s(%s#%d)\n", 8548*0Sstevel@tonic-gate pmf, PM_DEVICE(pdip))) 8549*0Sstevel@tonic-gate return (DDI_FAILURE); 8550*0Sstevel@tonic-gate } 8551*0Sstevel@tonic-gate } else { 8552*0Sstevel@tonic-gate /* 8553*0Sstevel@tonic-gate * Since we don't know what the actual power level is, 8554*0Sstevel@tonic-gate * we place a power hold on the parent no matter what 8555*0Sstevel@tonic-gate * component and level is changing. 8556*0Sstevel@tonic-gate */ 8557*0Sstevel@tonic-gate pm_hold_power(pdip); 8558*0Sstevel@tonic-gate } 8559*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 8560*0Sstevel@tonic-gate clevel = PM_CURPOWER(dip, comp); 8561*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, olvl=%d, nlvl=%d, clvl=%d, " 8562*0Sstevel@tonic-gate "dir=%s\n", pmf, PM_DEVICE(dip), comp, bpc->bpc_olevel, nlevel, 8563*0Sstevel@tonic-gate clevel, dir)) 8564*0Sstevel@tonic-gate switch (direction) { 8565*0Sstevel@tonic-gate case PM_LEVEL_UPONLY: 8566*0Sstevel@tonic-gate /* Powering up */ 8567*0Sstevel@tonic-gate if (clevel >= nlevel) { 8568*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already " 8569*0Sstevel@tonic-gate "at or above the requested level.\n", pmf)) 8570*0Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8571*0Sstevel@tonic-gate ret = DDI_SUCCESS; 8572*0Sstevel@tonic-gate goto post_notify; 8573*0Sstevel@tonic-gate } 8574*0Sstevel@tonic-gate break; 8575*0Sstevel@tonic-gate case PM_LEVEL_EXACT: 8576*0Sstevel@tonic-gate /* specific level request */ 8577*0Sstevel@tonic-gate if (clevel == nlevel && !PM_ISBC(dip)) { 8578*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already " 8579*0Sstevel@tonic-gate "at the requested level.\n", pmf)) 8580*0Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8581*0Sstevel@tonic-gate ret = DDI_SUCCESS; 8582*0Sstevel@tonic-gate goto post_notify; 8583*0Sstevel@tonic-gate } else if (PM_IS_CFB(dip) && (nlevel < clevel)) { 8584*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: powerdown of console\n", pmf)) 8585*0Sstevel@tonic-gate if (!pm_cfb_enabled) { 8586*0Sstevel@tonic-gate PMD(PMD_ERROR | PMD_CFB, 8587*0Sstevel@tonic-gate ("%s: !pm_cfb_enabled, fails\n", pmf)) 8588*0Sstevel@tonic-gate *errnop = EINVAL; 8589*0Sstevel@tonic-gate *iresp = DDI_FAILURE; 8590*0Sstevel@tonic-gate ret = DDI_FAILURE; 8591*0Sstevel@tonic-gate goto post_notify; 8592*0Sstevel@tonic-gate } 8593*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 8594*0Sstevel@tonic-gate while (cfb_inuse) { 8595*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 8596*0Sstevel@tonic-gate if (delay_sig(1) == EINTR) { 8597*0Sstevel@tonic-gate ret = DDI_FAILURE; 8598*0Sstevel@tonic-gate *iresp = DDI_FAILURE; 8599*0Sstevel@tonic-gate *errnop = EINTR; 8600*0Sstevel@tonic-gate goto post_notify; 8601*0Sstevel@tonic-gate } 8602*0Sstevel@tonic-gate mutex_enter(&pm_cfb_lock); 8603*0Sstevel@tonic-gate } 8604*0Sstevel@tonic-gate mutex_exit(&pm_cfb_lock); 8605*0Sstevel@tonic-gate } 8606*0Sstevel@tonic-gate break; 8607*0Sstevel@tonic-gate case PM_LEVEL_DOWNONLY: 8608*0Sstevel@tonic-gate /* Powering down */ 8609*0Sstevel@tonic-gate thresh = cur_threshold(dip, comp); 8610*0Sstevel@tonic-gate idletime = gethrestime_sec() - cp->pmc_timestamp; 8611*0Sstevel@tonic-gate if (scan && ((PM_KUC(dip) != 0) || 8612*0Sstevel@tonic-gate (cp->pmc_busycount > 0) || (idletime < thresh))) { 8613*0Sstevel@tonic-gate #ifdef DEBUG 8614*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_kidsupcnt != 0) 8615*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: " 8616*0Sstevel@tonic-gate "kidsupcnt != 0\n", pmf)) 8617*0Sstevel@tonic-gate if (cp->pmc_busycount > 0) 8618*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: " 8619*0Sstevel@tonic-gate "device become busy\n", pmf)) 8620*0Sstevel@tonic-gate if (idletime < thresh) 8621*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: device " 8622*0Sstevel@tonic-gate "hasn't been idle long enough\n", pmf)) 8623*0Sstevel@tonic-gate #endif 8624*0Sstevel@tonic-gate *iresp = DDI_FAILURE; 8625*0Sstevel@tonic-gate *errnop = EBUSY; 8626*0Sstevel@tonic-gate ret = DDI_FAILURE; 8627*0Sstevel@tonic-gate goto post_notify; 8628*0Sstevel@tonic-gate } else if (clevel != PM_LEVEL_UNKNOWN && clevel <= nlevel) { 8629*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already at " 8630*0Sstevel@tonic-gate "or below the requested level.\n", pmf)) 8631*0Sstevel@tonic-gate *iresp = DDI_SUCCESS; 8632*0Sstevel@tonic-gate ret = DDI_SUCCESS; 8633*0Sstevel@tonic-gate goto post_notify; 8634*0Sstevel@tonic-gate } 8635*0Sstevel@tonic-gate break; 8636*0Sstevel@tonic-gate } 8637*0Sstevel@tonic-gate 8638*0Sstevel@tonic-gate if (PM_IS_CFB(dip) && (comps_off_incr = 8639*0Sstevel@tonic-gate calc_cfb_comps_incr(dip, comp, clevel, nlevel)) > 0) { 8640*0Sstevel@tonic-gate /* 8641*0Sstevel@tonic-gate * Pre-adjust pm_cfb_comps_off if lowering a console fb 8642*0Sstevel@tonic-gate * component from full power. Remember that we tried to 8643*0Sstevel@tonic-gate * lower power in case it fails and we need to back out 8644*0Sstevel@tonic-gate * the adjustment. 8645*0Sstevel@tonic-gate */ 8646*0Sstevel@tonic-gate update_comps_off(comps_off_incr, dip); 8647*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d cfb_comps_off->%d\n", 8648*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, clevel, nlevel, 8649*0Sstevel@tonic-gate pm_cfb_comps_off)) 8650*0Sstevel@tonic-gate } 8651*0Sstevel@tonic-gate 8652*0Sstevel@tonic-gate if ((*iresp = power_dev(dip, 8653*0Sstevel@tonic-gate comp, nlevel, clevel, canblock, &devl)) == DDI_SUCCESS) { 8654*0Sstevel@tonic-gate #ifdef DEBUG 8655*0Sstevel@tonic-gate /* 8656*0Sstevel@tonic-gate * All descendents of this node should already be powered off. 8657*0Sstevel@tonic-gate */ 8658*0Sstevel@tonic-gate if (PM_CURPOWER(dip, comp) == 0) { 8659*0Sstevel@tonic-gate pm_desc_pwrchk_t pdpchk; 8660*0Sstevel@tonic-gate pdpchk.pdpc_dip = dip; 8661*0Sstevel@tonic-gate pdpchk.pdpc_par_involved = PM_WANTS_NOTIFICATION(dip); 8662*0Sstevel@tonic-gate ndi_devi_enter(dip, &circ_db); 8663*0Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip != NULL; 8664*0Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 8665*0Sstevel@tonic-gate ndi_devi_enter(cdip, &ccirc_db); 8666*0Sstevel@tonic-gate ddi_walk_devs(cdip, pm_desc_pwrchk_walk, 8667*0Sstevel@tonic-gate (void *)&pdpchk); 8668*0Sstevel@tonic-gate ndi_devi_exit(cdip, ccirc_db); 8669*0Sstevel@tonic-gate } 8670*0Sstevel@tonic-gate ndi_devi_exit(dip, circ_db); 8671*0Sstevel@tonic-gate } 8672*0Sstevel@tonic-gate #endif 8673*0Sstevel@tonic-gate /* 8674*0Sstevel@tonic-gate * Post-adjust pm_cfb_comps_off if we brought an fb component 8675*0Sstevel@tonic-gate * back up to full power. 8676*0Sstevel@tonic-gate */ 8677*0Sstevel@tonic-gate if (PM_IS_CFB(dip) && comps_off_incr < 0) { 8678*0Sstevel@tonic-gate update_comps_off(comps_off_incr, dip); 8679*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d " 8680*0Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip), 8681*0Sstevel@tonic-gate comp, clevel, nlevel, pm_cfb_comps_off)) 8682*0Sstevel@tonic-gate } 8683*0Sstevel@tonic-gate dodeps = 0; 8684*0Sstevel@tonic-gate if (POWERING_OFF(clevel, nlevel)) { 8685*0Sstevel@tonic-gate if (PM_ISBC(dip)) { 8686*0Sstevel@tonic-gate dodeps = (comp == 0); 8687*0Sstevel@tonic-gate } else { 8688*0Sstevel@tonic-gate int i; 8689*0Sstevel@tonic-gate dodeps = 1; 8690*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 8691*0Sstevel@tonic-gate /* if some component still on */ 8692*0Sstevel@tonic-gate if (PM_CURPOWER(dip, i)) { 8693*0Sstevel@tonic-gate dodeps = 0; 8694*0Sstevel@tonic-gate break; 8695*0Sstevel@tonic-gate } 8696*0Sstevel@tonic-gate } 8697*0Sstevel@tonic-gate } 8698*0Sstevel@tonic-gate if (dodeps) 8699*0Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_OFF; 8700*0Sstevel@tonic-gate } else if (POWERING_ON(clevel, nlevel)) { 8701*0Sstevel@tonic-gate if (PM_ISBC(dip)) { 8702*0Sstevel@tonic-gate dodeps = (comp == 0); 8703*0Sstevel@tonic-gate } else { 8704*0Sstevel@tonic-gate int i; 8705*0Sstevel@tonic-gate dodeps = 1; 8706*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 8707*0Sstevel@tonic-gate if (i == comp) 8708*0Sstevel@tonic-gate continue; 8709*0Sstevel@tonic-gate if (PM_CURPOWER(dip, i) > 0) { 8710*0Sstevel@tonic-gate dodeps = 0; 8711*0Sstevel@tonic-gate break; 8712*0Sstevel@tonic-gate } 8713*0Sstevel@tonic-gate } 8714*0Sstevel@tonic-gate } 8715*0Sstevel@tonic-gate if (dodeps) 8716*0Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_ON; 8717*0Sstevel@tonic-gate } 8718*0Sstevel@tonic-gate 8719*0Sstevel@tonic-gate if (dodeps) { 8720*0Sstevel@tonic-gate char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 8721*0Sstevel@tonic-gate 8722*0Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf); 8723*0Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, NULL, 8724*0Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0); 8725*0Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN); 8726*0Sstevel@tonic-gate } 8727*0Sstevel@tonic-gate if ((PM_CURPOWER(dip, comp) == nlevel) && pm_watchers()) { 8728*0Sstevel@tonic-gate int old; 8729*0Sstevel@tonic-gate 8730*0Sstevel@tonic-gate /* If old power cached during deadlock, use it. */ 8731*0Sstevel@tonic-gate old = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ? 8732*0Sstevel@tonic-gate cp->pmc_phc_pwr : olevel); 8733*0Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock); 8734*0Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, nlevel, 8735*0Sstevel@tonic-gate old, canblock); 8736*0Sstevel@tonic-gate pm_enqueue_notify_others(&devl, canblock); 8737*0Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock); 8738*0Sstevel@tonic-gate } 8739*0Sstevel@tonic-gate 8740*0Sstevel@tonic-gate /* 8741*0Sstevel@tonic-gate * If we are coming from a scan, don't do it again, 8742*0Sstevel@tonic-gate * else we can have infinite loops. 8743*0Sstevel@tonic-gate */ 8744*0Sstevel@tonic-gate if (!scan) 8745*0Sstevel@tonic-gate pm_rescan(dip); 8746*0Sstevel@tonic-gate } else { 8747*0Sstevel@tonic-gate /* if we incremented pm_comps_off_count, but failed */ 8748*0Sstevel@tonic-gate if (comps_off_incr > 0) { 8749*0Sstevel@tonic-gate update_comps_off(-comps_off_incr, dip); 8750*0Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d " 8751*0Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip), 8752*0Sstevel@tonic-gate comp, clevel, nlevel, pm_cfb_comps_off)) 8753*0Sstevel@tonic-gate } 8754*0Sstevel@tonic-gate *errnop = EIO; 8755*0Sstevel@tonic-gate } 8756*0Sstevel@tonic-gate 8757*0Sstevel@tonic-gate post_notify: 8758*0Sstevel@tonic-gate /* 8759*0Sstevel@tonic-gate * This thread may have been in deadlock with pm_power_has_changed. 8760*0Sstevel@tonic-gate * Before releasing power lock, clear the flag which marks this 8761*0Sstevel@tonic-gate * condition. 8762*0Sstevel@tonic-gate */ 8763*0Sstevel@tonic-gate cp->pmc_flags &= ~PM_PHC_WHILE_SET_POWER; 8764*0Sstevel@tonic-gate 8765*0Sstevel@tonic-gate /* 8766*0Sstevel@tonic-gate * Update the old power level in the bus power structure with the 8767*0Sstevel@tonic-gate * actual power level before the transition was made to the new level. 8768*0Sstevel@tonic-gate * Some involved parents depend on this information to keep track of 8769*0Sstevel@tonic-gate * their children's power transition. 8770*0Sstevel@tonic-gate */ 8771*0Sstevel@tonic-gate if (*iresp != DDI_FAILURE) 8772*0Sstevel@tonic-gate bpc->bpc_olevel = clevel; 8773*0Sstevel@tonic-gate 8774*0Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(pdip)) { 8775*0Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL, 8776*0Sstevel@tonic-gate BUS_POWER_POST_NOTIFICATION, bpc, resultp); 8777*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8778*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: post_notify %s@%s(%s#%d) for " 8779*0Sstevel@tonic-gate "child %s@%s(%s#%d), ret=%d\n", pmf, PM_DEVICE(pdip), 8780*0Sstevel@tonic-gate PM_DEVICE(dip), ret)) 8781*0Sstevel@tonic-gate } else { 8782*0Sstevel@tonic-gate nlevel = cur_power(cp); /* in case phc deadlock updated pwr */ 8783*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8784*0Sstevel@tonic-gate /* 8785*0Sstevel@tonic-gate * Now that we know what power transition has occurred 8786*0Sstevel@tonic-gate * (if any), release the power hold. Leave the hold 8787*0Sstevel@tonic-gate * in effect in the case of OFF->ON transition. 8788*0Sstevel@tonic-gate */ 8789*0Sstevel@tonic-gate if (!(clevel == 0 && nlevel > 0 && 8790*0Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0))) 8791*0Sstevel@tonic-gate pm_rele_power(pdip); 8792*0Sstevel@tonic-gate /* 8793*0Sstevel@tonic-gate * If the power transition was an ON->OFF transition, 8794*0Sstevel@tonic-gate * remove the power hold from the parent. 8795*0Sstevel@tonic-gate */ 8796*0Sstevel@tonic-gate if ((clevel > 0 || clevel == PM_LEVEL_UNKNOWN) && 8797*0Sstevel@tonic-gate nlevel == 0 && (!PM_ISBC(dip) || comp == 0)) 8798*0Sstevel@tonic-gate pm_rele_power(pdip); 8799*0Sstevel@tonic-gate } 8800*0Sstevel@tonic-gate if (*iresp != DDI_SUCCESS || ret != DDI_SUCCESS) 8801*0Sstevel@tonic-gate return (DDI_FAILURE); 8802*0Sstevel@tonic-gate else 8803*0Sstevel@tonic-gate return (DDI_SUCCESS); 8804*0Sstevel@tonic-gate } 8805*0Sstevel@tonic-gate 8806*0Sstevel@tonic-gate /* 8807*0Sstevel@tonic-gate * If an app (SunVTS or Xsun) has taken control, then block until it 8808*0Sstevel@tonic-gate * gives it up or makes the requested power level change, unless 8809*0Sstevel@tonic-gate * we have other instructions about blocking. Returns DDI_SUCCESS, 8810*0Sstevel@tonic-gate * DDI_FAILURE or EAGAIN (owner released device from directpm). 8811*0Sstevel@tonic-gate */ 8812*0Sstevel@tonic-gate static int 8813*0Sstevel@tonic-gate pm_busop_match_request(dev_info_t *dip, void *arg) 8814*0Sstevel@tonic-gate { 8815*0Sstevel@tonic-gate PMD_FUNC(pmf, "bp_match_request") 8816*0Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg; 8817*0Sstevel@tonic-gate pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private; 8818*0Sstevel@tonic-gate int comp = bpc->bpc_comp; 8819*0Sstevel@tonic-gate int nlevel = bpc->bpc_nlevel; 8820*0Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock; 8821*0Sstevel@tonic-gate int direction = pspm->pspm_direction; 8822*0Sstevel@tonic-gate int clevel, circ; 8823*0Sstevel@tonic-gate 8824*0Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip)); 8825*0Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ); 8826*0Sstevel@tonic-gate clevel = PM_CURPOWER(dip, comp); 8827*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, nlvl=%d, clvl=%d\n", 8828*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, nlevel, clevel)) 8829*0Sstevel@tonic-gate if (direction == PM_LEVEL_UPONLY) { 8830*0Sstevel@tonic-gate if (clevel >= nlevel) { 8831*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8832*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8833*0Sstevel@tonic-gate return (DDI_SUCCESS); 8834*0Sstevel@tonic-gate } 8835*0Sstevel@tonic-gate } else if (clevel == nlevel) { 8836*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8837*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8838*0Sstevel@tonic-gate return (DDI_SUCCESS); 8839*0Sstevel@tonic-gate } 8840*0Sstevel@tonic-gate if (canblock == PM_CANBLOCK_FAIL) { 8841*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8842*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8843*0Sstevel@tonic-gate return (DDI_FAILURE); 8844*0Sstevel@tonic-gate } 8845*0Sstevel@tonic-gate if (canblock == PM_CANBLOCK_BLOCK) { 8846*0Sstevel@tonic-gate /* 8847*0Sstevel@tonic-gate * To avoid a deadlock, we must not hold the 8848*0Sstevel@tonic-gate * power lock when we pm_block. 8849*0Sstevel@tonic-gate */ 8850*0Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ); 8851*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: blocking\n", pmf)) 8852*0Sstevel@tonic-gate /* pm_block releases dip lock */ 8853*0Sstevel@tonic-gate switch (pm_block(dip, comp, nlevel, clevel)) { 8854*0Sstevel@tonic-gate case PMP_RELEASE: 8855*0Sstevel@tonic-gate return (EAGAIN); 8856*0Sstevel@tonic-gate case PMP_SUCCEED: 8857*0Sstevel@tonic-gate return (DDI_SUCCESS); 8858*0Sstevel@tonic-gate case PMP_FAIL: 8859*0Sstevel@tonic-gate return (DDI_FAILURE); 8860*0Sstevel@tonic-gate } 8861*0Sstevel@tonic-gate } else { 8862*0Sstevel@tonic-gate ASSERT(0); 8863*0Sstevel@tonic-gate } 8864*0Sstevel@tonic-gate _NOTE(NOTREACHED); 8865*0Sstevel@tonic-gate return (DDI_FAILURE); /* keep gcc happy */ 8866*0Sstevel@tonic-gate } 8867*0Sstevel@tonic-gate 8868*0Sstevel@tonic-gate static int 8869*0Sstevel@tonic-gate pm_all_to_normal_nexus(dev_info_t *dip, pm_canblock_t canblock) 8870*0Sstevel@tonic-gate { 8871*0Sstevel@tonic-gate PMD_FUNC(pmf, "all_to_normal_nexus") 8872*0Sstevel@tonic-gate int *normal; 8873*0Sstevel@tonic-gate int i, ncomps; 8874*0Sstevel@tonic-gate size_t size; 8875*0Sstevel@tonic-gate int changefailed = 0; 8876*0Sstevel@tonic-gate int ret, result = DDI_SUCCESS; 8877*0Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn; 8878*0Sstevel@tonic-gate pm_sp_misc_t pspm; 8879*0Sstevel@tonic-gate 8880*0Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip)); 8881*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 8882*0Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) { 8883*0Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get norm pwrs\n", pmf)) 8884*0Sstevel@tonic-gate return (DDI_FAILURE); 8885*0Sstevel@tonic-gate } 8886*0Sstevel@tonic-gate ncomps = PM_NUMCMPTS(dip); 8887*0Sstevel@tonic-gate for (i = 0; i < ncomps; i++) { 8888*0Sstevel@tonic-gate bpn.bpn_dip = dip; 8889*0Sstevel@tonic-gate bpn.bpn_comp = i; 8890*0Sstevel@tonic-gate bpn.bpn_level = normal[i]; 8891*0Sstevel@tonic-gate pspm.pspm_canblock = canblock; 8892*0Sstevel@tonic-gate pspm.pspm_scan = 0; 8893*0Sstevel@tonic-gate bpn.bpn_private = &pspm; 8894*0Sstevel@tonic-gate ret = pm_busop_bus_power(dip, NULL, BUS_POWER_NEXUS_PWRUP, 8895*0Sstevel@tonic-gate (void *)&bpn, (void *)&result); 8896*0Sstevel@tonic-gate if (ret != DDI_SUCCESS || result != DDI_SUCCESS) { 8897*0Sstevel@tonic-gate PMD(PMD_FAIL | PMD_ALLNORM, ("%s: %s@%s(%s#%d)[%d] " 8898*0Sstevel@tonic-gate "->%d failure result %d\n", pmf, PM_DEVICE(dip), 8899*0Sstevel@tonic-gate i, normal[i], result)) 8900*0Sstevel@tonic-gate changefailed++; 8901*0Sstevel@tonic-gate } 8902*0Sstevel@tonic-gate } 8903*0Sstevel@tonic-gate kmem_free(normal, size); 8904*0Sstevel@tonic-gate if (changefailed) { 8905*0Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) " 8906*0Sstevel@tonic-gate "full power\n", pmf, changefailed, PM_DEVICE(dip))) 8907*0Sstevel@tonic-gate return (DDI_FAILURE); 8908*0Sstevel@tonic-gate } 8909*0Sstevel@tonic-gate return (DDI_SUCCESS); 8910*0Sstevel@tonic-gate } 8911*0Sstevel@tonic-gate 8912*0Sstevel@tonic-gate int 8913*0Sstevel@tonic-gate pm_noinvol_update(int subcmd, int volpmd, int wasvolpmd, char *path, 8914*0Sstevel@tonic-gate dev_info_t *tdip) 8915*0Sstevel@tonic-gate { 8916*0Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_update") 8917*0Sstevel@tonic-gate pm_bp_noinvol_t args; 8918*0Sstevel@tonic-gate int ret; 8919*0Sstevel@tonic-gate int result = DDI_SUCCESS; 8920*0Sstevel@tonic-gate 8921*0Sstevel@tonic-gate args.bpni_path = path; 8922*0Sstevel@tonic-gate args.bpni_dip = tdip; 8923*0Sstevel@tonic-gate args.bpni_cmd = subcmd; 8924*0Sstevel@tonic-gate args.bpni_wasvolpmd = wasvolpmd; 8925*0Sstevel@tonic-gate args.bpni_volpmd = volpmd; 8926*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: update for path %s tdip %p subcmd %d " 8927*0Sstevel@tonic-gate "volpmd %d wasvolpmd %d\n", pmf, 8928*0Sstevel@tonic-gate path, (void *)tdip, subcmd, wasvolpmd, volpmd)) 8929*0Sstevel@tonic-gate ret = pm_busop_bus_power(ddi_root_node(), NULL, BUS_POWER_NOINVOL, 8930*0Sstevel@tonic-gate &args, &result); 8931*0Sstevel@tonic-gate return (ret); 8932*0Sstevel@tonic-gate } 8933*0Sstevel@tonic-gate 8934*0Sstevel@tonic-gate void 8935*0Sstevel@tonic-gate pm_noinvol_update_node(dev_info_t *dip, pm_bp_noinvol_t *req) 8936*0Sstevel@tonic-gate { 8937*0Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_update_node") 8938*0Sstevel@tonic-gate 8939*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 8940*0Sstevel@tonic-gate switch (req->bpni_cmd) { 8941*0Sstevel@tonic-gate case PM_BP_NOINVOL_ATTACH: 8942*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_PB_NOINVOL_ATTACH %s@%s(%s#%d) " 8943*0Sstevel@tonic-gate "noinvol %d->%d\n", pmf, PM_DEVICE(dip), 8944*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, 8945*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm - 1)) 8946*0Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_noinvolpm); 8947*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 8948*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm--; 8949*0Sstevel@tonic-gate if (req->bpni_wasvolpmd) { 8950*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_ATTACH " 8951*0Sstevel@tonic-gate "%s@%s(%s#%d) volpmd %d->%d\n", pmf, 8952*0Sstevel@tonic-gate PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd, 8953*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd - 1)) 8954*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd) 8955*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd--; 8956*0Sstevel@tonic-gate } 8957*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8958*0Sstevel@tonic-gate break; 8959*0Sstevel@tonic-gate 8960*0Sstevel@tonic-gate case PM_BP_NOINVOL_DETACH: 8961*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH %s@%s(%s#%d) " 8962*0Sstevel@tonic-gate "noinvolpm %d->%d\n", pmf, PM_DEVICE(dip), 8963*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, 8964*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm + 1)) 8965*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 8966*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm++; 8967*0Sstevel@tonic-gate if (req->bpni_wasvolpmd) { 8968*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH " 8969*0Sstevel@tonic-gate "%s@%s(%s#%d) volpmd %d->%d\n", pmf, 8970*0Sstevel@tonic-gate PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd, 8971*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd + 1)) 8972*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd++; 8973*0Sstevel@tonic-gate } 8974*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 8975*0Sstevel@tonic-gate break; 8976*0Sstevel@tonic-gate 8977*0Sstevel@tonic-gate case PM_BP_NOINVOL_REMDRV: 8978*0Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) " 8979*0Sstevel@tonic-gate "noinvol %d->%d\n", pmf, PM_DEVICE(dip), 8980*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, 8981*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm - 1)) 8982*0Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_noinvolpm); 8983*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 8984*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm--; 8985*0Sstevel@tonic-gate if (req->bpni_wasvolpmd) { 8986*0Sstevel@tonic-gate PMD(PMD_NOINVOL, 8987*0Sstevel@tonic-gate ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) " 8988*0Sstevel@tonic-gate "volpmd %d->%d\n", pmf, PM_DEVICE(dip), 8989*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, 8990*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd - 1)) 8991*0Sstevel@tonic-gate /* 8992*0Sstevel@tonic-gate * A power up could come in between and 8993*0Sstevel@tonic-gate * clear the volpmd, if that's the case, 8994*0Sstevel@tonic-gate * volpmd would be clear. 8995*0Sstevel@tonic-gate */ 8996*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd) 8997*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd--; 8998*0Sstevel@tonic-gate } 8999*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 9000*0Sstevel@tonic-gate break; 9001*0Sstevel@tonic-gate 9002*0Sstevel@tonic-gate case PM_BP_NOINVOL_CFB: 9003*0Sstevel@tonic-gate PMD(PMD_NOINVOL, 9004*0Sstevel@tonic-gate ("%s: PM_BP_NOIVOL_CFB %s@%s(%s#%d) noinvol %d->%d\n", 9005*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), DEVI(dip)->devi_pm_noinvolpm, 9006*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm + 1)) 9007*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 9008*0Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm++; 9009*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 9010*0Sstevel@tonic-gate break; 9011*0Sstevel@tonic-gate 9012*0Sstevel@tonic-gate case PM_BP_NOINVOL_POWER: 9013*0Sstevel@tonic-gate PMD(PMD_NOINVOL, 9014*0Sstevel@tonic-gate ("%s: PM_BP_NOIVOL_PWR %s@%s(%s#%d) volpmd %d->%d\n", 9015*0Sstevel@tonic-gate pmf, PM_DEVICE(dip), 9016*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, DEVI(dip)->devi_pm_volpmd - 9017*0Sstevel@tonic-gate req->bpni_volpmd)) 9018*0Sstevel@tonic-gate PM_LOCK_DIP(dip); 9019*0Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd -= req->bpni_volpmd; 9020*0Sstevel@tonic-gate PM_UNLOCK_DIP(dip); 9021*0Sstevel@tonic-gate break; 9022*0Sstevel@tonic-gate 9023*0Sstevel@tonic-gate default: 9024*0Sstevel@tonic-gate break; 9025*0Sstevel@tonic-gate } 9026*0Sstevel@tonic-gate 9027*0Sstevel@tonic-gate } 9028*0Sstevel@tonic-gate 9029*0Sstevel@tonic-gate #ifdef DEBUG 9030*0Sstevel@tonic-gate static int 9031*0Sstevel@tonic-gate pm_desc_pwrchk_walk(dev_info_t *dip, void *arg) 9032*0Sstevel@tonic-gate { 9033*0Sstevel@tonic-gate PMD_FUNC(pmf, "desc_pwrchk") 9034*0Sstevel@tonic-gate pm_desc_pwrchk_t *pdpchk = (pm_desc_pwrchk_t *)arg; 9035*0Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip); 9036*0Sstevel@tonic-gate int i, curpwr, ce_level; 9037*0Sstevel@tonic-gate 9038*0Sstevel@tonic-gate if (!info) 9039*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 9040*0Sstevel@tonic-gate 9041*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip))) 9042*0Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) { 9043*0Sstevel@tonic-gate if ((curpwr = PM_CURPOWER(dip, i)) == 0) 9044*0Sstevel@tonic-gate continue; 9045*0Sstevel@tonic-gate ce_level = (pdpchk->pdpc_par_involved == 0) ? CE_PANIC : 9046*0Sstevel@tonic-gate CE_WARN; 9047*0Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) is powered off while desc " 9048*0Sstevel@tonic-gate "%s@%s(%s#%d)[%d] is at %d\n", pmf, 9049*0Sstevel@tonic-gate PM_DEVICE(pdpchk->pdpc_dip), PM_DEVICE(dip), i, curpwr)) 9050*0Sstevel@tonic-gate cmn_err(ce_level, "!device %s@%s(%s#%d) is powered on, " 9051*0Sstevel@tonic-gate "while its ancestor, %s@%s(%s#%d), is powering off!", 9052*0Sstevel@tonic-gate PM_DEVICE(dip), PM_DEVICE(pdpchk->pdpc_dip)); 9053*0Sstevel@tonic-gate } 9054*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 9055*0Sstevel@tonic-gate } 9056*0Sstevel@tonic-gate #endif 9057*0Sstevel@tonic-gate 9058*0Sstevel@tonic-gate /* 9059*0Sstevel@tonic-gate * Record the fact that one thread is borrowing the lock on a device node. 9060*0Sstevel@tonic-gate * Use is restricted to the case where the lending thread will block until 9061*0Sstevel@tonic-gate * the borrowing thread (always curthread) completes. 9062*0Sstevel@tonic-gate */ 9063*0Sstevel@tonic-gate void 9064*0Sstevel@tonic-gate pm_borrow_lock(kthread_t *lender) 9065*0Sstevel@tonic-gate { 9066*0Sstevel@tonic-gate lock_loan_t *prev = &lock_loan_head; 9067*0Sstevel@tonic-gate lock_loan_t *cur = (lock_loan_t *)kmem_zalloc(sizeof (*cur), KM_SLEEP); 9068*0Sstevel@tonic-gate 9069*0Sstevel@tonic-gate cur->pmlk_borrower = curthread; 9070*0Sstevel@tonic-gate cur->pmlk_lender = lender; 9071*0Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 9072*0Sstevel@tonic-gate cur->pmlk_next = prev->pmlk_next; 9073*0Sstevel@tonic-gate prev->pmlk_next = cur; 9074*0Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 9075*0Sstevel@tonic-gate } 9076*0Sstevel@tonic-gate 9077*0Sstevel@tonic-gate /* 9078*0Sstevel@tonic-gate * Return the borrowed lock. A thread can borrow only one. 9079*0Sstevel@tonic-gate */ 9080*0Sstevel@tonic-gate void 9081*0Sstevel@tonic-gate pm_return_lock(void) 9082*0Sstevel@tonic-gate { 9083*0Sstevel@tonic-gate lock_loan_t *cur; 9084*0Sstevel@tonic-gate lock_loan_t *prev = &lock_loan_head; 9085*0Sstevel@tonic-gate 9086*0Sstevel@tonic-gate mutex_enter(&pm_loan_lock); 9087*0Sstevel@tonic-gate ASSERT(prev->pmlk_next != NULL); 9088*0Sstevel@tonic-gate for (cur = prev->pmlk_next; cur; prev = cur, cur = cur->pmlk_next) 9089*0Sstevel@tonic-gate if (cur->pmlk_borrower == curthread) 9090*0Sstevel@tonic-gate break; 9091*0Sstevel@tonic-gate 9092*0Sstevel@tonic-gate ASSERT(cur != NULL); 9093*0Sstevel@tonic-gate prev->pmlk_next = cur->pmlk_next; 9094*0Sstevel@tonic-gate mutex_exit(&pm_loan_lock); 9095*0Sstevel@tonic-gate kmem_free(cur, sizeof (*cur)); 9096*0Sstevel@tonic-gate } 9097