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