xref: /onnv-gate/usr/src/uts/common/os/sunpm.c (revision 2009)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51961Scth  * Common Development and Distribution License (the "License").
61961Scth  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
221316Sosaeed  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * sunpm.c builds sunpm.o	"power management framework"
300Sstevel@tonic-gate  *	kernel-resident power management code.  Implements power management
310Sstevel@tonic-gate  *	policy
320Sstevel@tonic-gate  *	Assumes: all backwards compat. device components wake up on &
330Sstevel@tonic-gate  *		 the pm_info pointer in dev_info is initially NULL
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * PM - (device) Power Management
360Sstevel@tonic-gate  *
370Sstevel@tonic-gate  * Each device may have 0 or more components.  If a device has no components,
380Sstevel@tonic-gate  * then it can't be power managed.  Each component has 2 or more
390Sstevel@tonic-gate  * power states.
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  * "Backwards Compatible" (bc) devices:
420Sstevel@tonic-gate  * There are two different types of devices from the point of view of this
430Sstevel@tonic-gate  * code.  The original type, left over from the original PM implementation on
440Sstevel@tonic-gate  * the voyager platform are known in this code as "backwards compatible"
450Sstevel@tonic-gate  * devices (PM_ISBC(dip) returns true).
460Sstevel@tonic-gate  * They are recognized by the pm code by the lack of a pm-components property
470Sstevel@tonic-gate  * and a call made by the driver to pm_create_components(9F).
480Sstevel@tonic-gate  * For these devices, component 0 is special, and represents the power state
490Sstevel@tonic-gate  * of the device.  If component 0 is to be set to power level 0 (off), then
500Sstevel@tonic-gate  * the framework must first call into the driver's detach(9E) routine with
510Sstevel@tonic-gate  * DDI_PM_SUSPEND, to get the driver to save the hardware state of the device.
520Sstevel@tonic-gate  * After setting component 0 from 0 to a non-zero power level, a call must be
530Sstevel@tonic-gate  * made into the driver's attach(9E) routine with DDI_PM_RESUME.
540Sstevel@tonic-gate  *
550Sstevel@tonic-gate  * Currently, the only way to get a bc device power managed is via a set of
560Sstevel@tonic-gate  * ioctls (PM_DIRECT_PM, PM_SET_CURRENT_POWER) issued to /dev/pm.
570Sstevel@tonic-gate  *
580Sstevel@tonic-gate  * For non-bc devices, the driver describes the components by exporting a
590Sstevel@tonic-gate  * pm-components(9P) property that tells how many components there are,
600Sstevel@tonic-gate  * tells what each component's power state values are, and provides human
610Sstevel@tonic-gate  * readable strings (currently unused) for each component name and power state.
620Sstevel@tonic-gate  * Devices which export pm-components(9P) are automatically power managed
630Sstevel@tonic-gate  * whenever autopm is enabled (via PM_START_PM ioctl issued by pmconfig(1M)
640Sstevel@tonic-gate  * after parsing power.conf(4)).
650Sstevel@tonic-gate  * For these devices, all components are considered independent of each other,
660Sstevel@tonic-gate  * and it is up to the driver to decide when a transition requires saving or
670Sstevel@tonic-gate  * restoring hardware state.
680Sstevel@tonic-gate  *
690Sstevel@tonic-gate  * Each device component also has a threshold time associated with each power
700Sstevel@tonic-gate  * transition (see power.conf(4)), and a busy/idle state maintained by the
710Sstevel@tonic-gate  * driver calling pm_idle_component(9F) and pm_busy_component(9F).
720Sstevel@tonic-gate  * Components are created idle.
730Sstevel@tonic-gate  *
740Sstevel@tonic-gate  * The PM framework provides several functions:
750Sstevel@tonic-gate  * -implement PM policy as described in power.conf(4)
760Sstevel@tonic-gate  *  Policy is set by pmconfig(1M) issuing pm ioctls based on power.conf(4).
770Sstevel@tonic-gate  *  Policies consist of:
780Sstevel@tonic-gate  *    -set threshold values (defaults if none provided by pmconfig)
790Sstevel@tonic-gate  *    -set dependencies among devices
800Sstevel@tonic-gate  *    -enable/disable autopm
810Sstevel@tonic-gate  *    -turn down idle components based on thresholds (if autopm is enabled)
820Sstevel@tonic-gate  *     (aka scanning)
830Sstevel@tonic-gate  *    -maintain power states based on dependencies among devices
840Sstevel@tonic-gate  *    -upon request, or when the frame buffer powers off, attempt to turn off
850Sstevel@tonic-gate  *     all components that are idle or become idle over the next (10 sec)
860Sstevel@tonic-gate  *     period in an attempt to get down to an EnergyStar compliant state
870Sstevel@tonic-gate  *    -prevent powering off of a device which exported the
880Sstevel@tonic-gate  *     pm-no-involuntary-power-cycles property without active involvement of
890Sstevel@tonic-gate  *     the device's driver (so no removing power when the device driver is
900Sstevel@tonic-gate  *     not attached)
910Sstevel@tonic-gate  * -provide a mechanism for a device driver to request that a device's component
920Sstevel@tonic-gate  *  be brought back to the power level necessary for the use of the device
930Sstevel@tonic-gate  * -allow a process to directly control the power levels of device components
940Sstevel@tonic-gate  *  (via ioctls issued to /dev/pm--see usr/src/uts/common/io/pm.c)
950Sstevel@tonic-gate  * -ensure that the console frame buffer is powered up before being referenced
960Sstevel@tonic-gate  *  via prom_printf() or other prom calls that might generate console output
970Sstevel@tonic-gate  * -maintain implicit dependencies (e.g. parent must be powered up if child is)
980Sstevel@tonic-gate  * -provide "backwards compatible" behavior for devices without pm-components
990Sstevel@tonic-gate  *  property
1000Sstevel@tonic-gate  *
1010Sstevel@tonic-gate  * Scanning:
1020Sstevel@tonic-gate  * Whenever autopm is enabled, the framework attempts to bring each component
1030Sstevel@tonic-gate  * of each device to its lowest power based on the threshold of idleness
1040Sstevel@tonic-gate  * associated with each transition and the busy/idle state of the component.
1050Sstevel@tonic-gate  *
1060Sstevel@tonic-gate  * The actual work of this is done by pm_scan_dev(), which cycles through each
1070Sstevel@tonic-gate  * component of a device, checking its idleness against its current threshold,
1080Sstevel@tonic-gate  * and calling pm_set_power() as appropriate to change the power level.
1090Sstevel@tonic-gate  * This function also indicates when it would next be profitable to scan the
1100Sstevel@tonic-gate  * device again, and a new scan is scheduled after that time.
1110Sstevel@tonic-gate  *
1120Sstevel@tonic-gate  * Dependencies:
1130Sstevel@tonic-gate  * It is possible to establish a dependency between the power states of two
1140Sstevel@tonic-gate  * otherwise unrelated devices.  This is currently done to ensure that the
1150Sstevel@tonic-gate  * cdrom is always up whenever the console framebuffer is up, so that the user
1160Sstevel@tonic-gate  * can insert a cdrom and see a popup as a result.
1170Sstevel@tonic-gate  *
1180Sstevel@tonic-gate  * The dependency terminology used in power.conf(4) is not easy to understand,
1190Sstevel@tonic-gate  * so we've adopted a different terminology in the implementation.  We write
1200Sstevel@tonic-gate  * of a "keeps up" and a "kept up" device.  A relationship can be established
1210Sstevel@tonic-gate  * where one device keeps up another.  That means that if the keepsup device
1220Sstevel@tonic-gate  * has any component that is at a non-zero power level, all components of the
1230Sstevel@tonic-gate  * "kept up" device must be brought to full power.  This relationship is
1240Sstevel@tonic-gate  * asynchronous.  When the keeping device is powered up, a request is queued
1250Sstevel@tonic-gate  * to a worker thread to bring up the kept device.  The caller does not wait.
1260Sstevel@tonic-gate  * Scan will not turn down a kept up device.
1270Sstevel@tonic-gate  *
1280Sstevel@tonic-gate  * Direct PM:
1290Sstevel@tonic-gate  * A device may be directly power managed by a process.  If a device is
1300Sstevel@tonic-gate  * directly pm'd, then it will not be scanned, and dependencies will not be
1310Sstevel@tonic-gate  * enforced.  * If a directly pm'd device's driver requests a power change (via
1320Sstevel@tonic-gate  * pm_raise_power(9F)), then the request is blocked and notification is sent
1330Sstevel@tonic-gate  * to the controlling process, which must issue the requested power change for
1340Sstevel@tonic-gate  * the driver to proceed.
1350Sstevel@tonic-gate  *
1360Sstevel@tonic-gate  */
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate #include <sys/types.h>
1390Sstevel@tonic-gate #include <sys/errno.h>
1400Sstevel@tonic-gate #include <sys/callb.h>		/* callback registration during CPR */
1410Sstevel@tonic-gate #include <sys/conf.h>		/* driver flags and functions */
1420Sstevel@tonic-gate #include <sys/open.h>		/* OTYP_CHR definition */
1430Sstevel@tonic-gate #include <sys/stat.h>		/* S_IFCHR definition */
1440Sstevel@tonic-gate #include <sys/pathname.h>	/* name -> dev_info xlation */
1450Sstevel@tonic-gate #include <sys/ddi_impldefs.h>	/* dev_info node fields */
1460Sstevel@tonic-gate #include <sys/kmem.h>		/* memory alloc stuff */
1470Sstevel@tonic-gate #include <sys/debug.h>
1480Sstevel@tonic-gate #include <sys/archsystm.h>
1490Sstevel@tonic-gate #include <sys/pm.h>
1500Sstevel@tonic-gate #include <sys/ddi.h>
1510Sstevel@tonic-gate #include <sys/sunddi.h>
1520Sstevel@tonic-gate #include <sys/sunndi.h>
1530Sstevel@tonic-gate #include <sys/sunpm.h>
1540Sstevel@tonic-gate #include <sys/epm.h>
1550Sstevel@tonic-gate #include <sys/vfs.h>
1560Sstevel@tonic-gate #include <sys/mode.h>
1570Sstevel@tonic-gate #include <sys/mkdev.h>
1580Sstevel@tonic-gate #include <sys/promif.h>
1590Sstevel@tonic-gate #include <sys/consdev.h>
1600Sstevel@tonic-gate #include <sys/esunddi.h>
1610Sstevel@tonic-gate #include <sys/modctl.h>
1620Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
1630Sstevel@tonic-gate #include <sys/note.h>
1640Sstevel@tonic-gate #include <sys/taskq.h>
1650Sstevel@tonic-gate #include <sys/bootconf.h>
1660Sstevel@tonic-gate #include <sys/reboot.h>
1670Sstevel@tonic-gate #include <sys/spl.h>
1680Sstevel@tonic-gate #include <sys/disp.h>
1690Sstevel@tonic-gate #include <sys/sobject.h>
1700Sstevel@tonic-gate #include <sys/sunmdi.h>
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate /*
1740Sstevel@tonic-gate  * PM LOCKING
1750Sstevel@tonic-gate  *	The list of locks:
1760Sstevel@tonic-gate  * Global pm mutex locks.
1770Sstevel@tonic-gate  *
1780Sstevel@tonic-gate  * pm_scan_lock:
1790Sstevel@tonic-gate  *		It protects the timeout id of the scan thread, and the value
1800Sstevel@tonic-gate  *		of autopm_enabled.  This lock is not held concurrently with
1810Sstevel@tonic-gate  *		any other PM locks.
1820Sstevel@tonic-gate  *
1830Sstevel@tonic-gate  * pm_clone_lock:	Protects the clone list and count of poll events
1840Sstevel@tonic-gate  *		pending for the pm driver.
1850Sstevel@tonic-gate  *		Lock ordering:
1860Sstevel@tonic-gate  *			pm_clone_lock -> pm_pscc_interest_rwlock,
1870Sstevel@tonic-gate  *			pm_clone_lock -> pm_pscc_direct_rwlock.
1880Sstevel@tonic-gate  *
1890Sstevel@tonic-gate  * pm_rsvp_lock:
1900Sstevel@tonic-gate  *		Used to synchronize the data structures used for processes
1910Sstevel@tonic-gate  *		to rendezvous with state change information when doing
1920Sstevel@tonic-gate  *		direct PM.
1930Sstevel@tonic-gate  *		Lock ordering:
1940Sstevel@tonic-gate  *			pm_rsvp_lock -> pm_pscc_interest_rwlock,
1950Sstevel@tonic-gate  *			pm_rsvp_lock -> pm_pscc_direct_rwlock,
1960Sstevel@tonic-gate  *			pm_rsvp_lock -> pm_clone_lock.
1970Sstevel@tonic-gate  *
1980Sstevel@tonic-gate  * ppm_lock:	protects the list of registered ppm drivers
1990Sstevel@tonic-gate  *		Lock ordering:
2000Sstevel@tonic-gate  *			ppm_lock -> ppm driver unit_lock
2010Sstevel@tonic-gate  *
2020Sstevel@tonic-gate  * pm_compcnt_lock:
2030Sstevel@tonic-gate  *		Protects count of components that are not at their lowest
2040Sstevel@tonic-gate  *		power level.
2050Sstevel@tonic-gate  *		Lock ordering:
2060Sstevel@tonic-gate  *			pm_compcnt_lock -> ppm_lock.
2070Sstevel@tonic-gate  *
2080Sstevel@tonic-gate  * pm_dep_thread_lock:
2090Sstevel@tonic-gate  *		Protects work list for pm_dep_thread.  Not taken concurrently
2100Sstevel@tonic-gate  *		with any other pm lock.
2110Sstevel@tonic-gate  *
2120Sstevel@tonic-gate  * pm_remdrv_lock:
2130Sstevel@tonic-gate  *		Serializes the operation of removing noinvol data structure
2140Sstevel@tonic-gate  *		entries for a branch of the tree when a driver has been
2150Sstevel@tonic-gate  *		removed from the system (modctl_rem_major).
2160Sstevel@tonic-gate  *		Lock ordering:
2170Sstevel@tonic-gate  *			pm_remdrv_lock -> pm_noinvol_rwlock.
2180Sstevel@tonic-gate  *
2190Sstevel@tonic-gate  * pm_cfb_lock: (High level spin lock)
2200Sstevel@tonic-gate  *		Protects the count of how many components of the console
2210Sstevel@tonic-gate  *		frame buffer are off (so we know if we have to bring up the
2220Sstevel@tonic-gate  *		console as a result of a prom_printf, etc.
2230Sstevel@tonic-gate  *		No other locks are taken while holding this lock.
2240Sstevel@tonic-gate  *
2250Sstevel@tonic-gate  * pm_loan_lock:
2260Sstevel@tonic-gate  *		Protects the lock_loan list.  List is used to record that one
2270Sstevel@tonic-gate  *		thread has acquired a power lock but has launched another thread
2280Sstevel@tonic-gate  *		to complete its processing.  An entry in the list indicates that
2290Sstevel@tonic-gate  *		the worker thread can borrow the lock held by the other thread,
2300Sstevel@tonic-gate  *		which must block on the completion of the worker.  Use is
2310Sstevel@tonic-gate  *		specific to module loading.
2320Sstevel@tonic-gate  *		No other locks are taken while holding this lock.
2330Sstevel@tonic-gate  *
2340Sstevel@tonic-gate  * Global PM rwlocks
2350Sstevel@tonic-gate  *
2360Sstevel@tonic-gate  * pm_thresh_rwlock:
2370Sstevel@tonic-gate  *		Protects the list of thresholds recorded for future use (when
2380Sstevel@tonic-gate  *		devices attach).
2390Sstevel@tonic-gate  *		Lock ordering:
2400Sstevel@tonic-gate  *			pm_thresh_rwlock -> devi_pm_lock
2410Sstevel@tonic-gate  *
2420Sstevel@tonic-gate  * pm_noinvol_rwlock:
2430Sstevel@tonic-gate  *		Protects list of detached nodes that had noinvol registered.
2440Sstevel@tonic-gate  *		No other PM locks are taken while holding pm_noinvol_rwlock.
2450Sstevel@tonic-gate  *
2460Sstevel@tonic-gate  * pm_pscc_direct_rwlock:
2470Sstevel@tonic-gate  *		Protects the list that maps devices being directly power
2480Sstevel@tonic-gate  *		managed to the processes that manage them.
2490Sstevel@tonic-gate  *		Lock ordering:
2500Sstevel@tonic-gate  *			pm_pscc_direct_rwlock -> psce_lock
2510Sstevel@tonic-gate  *
2520Sstevel@tonic-gate  * pm_pscc_interest_rwlock;
2530Sstevel@tonic-gate  *		Protects the list that maps state change events to processes
2540Sstevel@tonic-gate  *		that want to know about them.
2550Sstevel@tonic-gate  *		Lock ordering:
2560Sstevel@tonic-gate  *			pm_pscc_interest_rwlock -> psce_lock
2570Sstevel@tonic-gate  *
2580Sstevel@tonic-gate  * per-dip locks:
2590Sstevel@tonic-gate  *
2600Sstevel@tonic-gate  * Each node has these per-dip locks, which are only used if the device is
2610Sstevel@tonic-gate  * a candidate for power management (e.g. has pm components)
2620Sstevel@tonic-gate  *
2630Sstevel@tonic-gate  * devi_pm_lock:
2640Sstevel@tonic-gate  *		Protects all power management state of the node except for
2650Sstevel@tonic-gate  *		power level, which is protected by ndi_devi_enter().
2660Sstevel@tonic-gate  *		Encapsulated in macros PM_LOCK_DIP()/PM_UNLOCK_DIP().
2670Sstevel@tonic-gate  *		Lock ordering:
2680Sstevel@tonic-gate  *			devi_pm_lock -> pm_rsvp_lock,
2690Sstevel@tonic-gate  *			devi_pm_lock -> pm_dep_thread_lock,
2700Sstevel@tonic-gate  *			devi_pm_lock -> pm_noinvol_rwlock,
2710Sstevel@tonic-gate  *			devi_pm_lock -> power lock
2720Sstevel@tonic-gate  *
2730Sstevel@tonic-gate  * power lock (ndi_devi_enter()):
2740Sstevel@tonic-gate  *		Since changing power level is possibly a slow operation (30
2750Sstevel@tonic-gate  *		seconds to spin up a disk drive), this is locked separately.
2760Sstevel@tonic-gate  *		Since a call into the driver to change the power level of one
2770Sstevel@tonic-gate  *		component may result in a call back into the framework to change
2780Sstevel@tonic-gate  *		the power level of another, this lock allows re-entrancy by
2790Sstevel@tonic-gate  *		the same thread (ndi_devi_enter is used for this because
2800Sstevel@tonic-gate  *		the USB framework uses ndi_devi_enter in its power entry point,
2810Sstevel@tonic-gate  *		and use of any other lock would produce a deadlock.
2820Sstevel@tonic-gate  *
2830Sstevel@tonic-gate  * devi_pm_busy_lock:
2840Sstevel@tonic-gate  *		This lock protects the integrity of the busy count.  It is
2850Sstevel@tonic-gate  *		only taken by pm_busy_component() and pm_idle_component and
2860Sstevel@tonic-gate  *		some code that adjust the busy time after the timer gets set
2870Sstevel@tonic-gate  *		up or after a CPR operation.  It is per-dip to keep from
2880Sstevel@tonic-gate  *		single-threading all the disk drivers on a system.
2890Sstevel@tonic-gate  *		It could be per component instead, but most devices have
2900Sstevel@tonic-gate  *		only one component.
2910Sstevel@tonic-gate  *		No other PM locks are taken while holding this lock.
2920Sstevel@tonic-gate  *
2930Sstevel@tonic-gate  */
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate static int stdout_is_framebuffer;
2960Sstevel@tonic-gate static kmutex_t	e_pm_power_lock;
2970Sstevel@tonic-gate static kmutex_t pm_loan_lock;
2980Sstevel@tonic-gate kmutex_t	pm_scan_lock;
2990Sstevel@tonic-gate callb_id_t	pm_cpr_cb_id;
3000Sstevel@tonic-gate callb_id_t	pm_panic_cb_id;
3010Sstevel@tonic-gate callb_id_t	pm_halt_cb_id;
3020Sstevel@tonic-gate int		pm_comps_notlowest;	/* no. of comps not at lowest power */
3030Sstevel@tonic-gate int		pm_powering_down;	/* cpr is source of DDI_SUSPEND calls */
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate clock_t pm_min_scan = PM_MIN_SCAN;
3060Sstevel@tonic-gate clock_t pm_id_ticks = 5;	/* ticks to wait before scan during idle-down */
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate static int pm_busop_set_power(dev_info_t *,
3090Sstevel@tonic-gate     void *, pm_bus_power_op_t, void *, void *);
3100Sstevel@tonic-gate static int pm_busop_match_request(dev_info_t *, void *);
3110Sstevel@tonic-gate static int pm_all_to_normal_nexus(dev_info_t *, pm_canblock_t);
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate /*
3140Sstevel@tonic-gate  * Dependency Processing is done thru a seperate thread.
3150Sstevel@tonic-gate  */
3160Sstevel@tonic-gate kmutex_t	pm_dep_thread_lock;
3170Sstevel@tonic-gate kcondvar_t	pm_dep_thread_cv;
3180Sstevel@tonic-gate pm_dep_wk_t	*pm_dep_thread_workq = NULL;
3190Sstevel@tonic-gate pm_dep_wk_t	*pm_dep_thread_tail = NULL;
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate /*
3220Sstevel@tonic-gate  * Autopm  must be turned on by a PM_START_PM ioctl, so we don't end up
3230Sstevel@tonic-gate  * power managing things in single user mode that have been suppressed via
3240Sstevel@tonic-gate  * power.conf entries.  Protected by pm_scan_lock.
3250Sstevel@tonic-gate  */
3260Sstevel@tonic-gate int		autopm_enabled;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate /*
3290Sstevel@tonic-gate  * This flag is true while processes are stopped for a checkpoint/resume.
3300Sstevel@tonic-gate  * Controlling processes of direct pm'd devices are not available to
3310Sstevel@tonic-gate  * participate in power level changes, so we bypass them when this is set.
3320Sstevel@tonic-gate  */
3330Sstevel@tonic-gate static int	pm_processes_stopped;
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate #ifdef	DEBUG
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate /*
3380Sstevel@tonic-gate  * see common/sys/epm.h for PMD_* values
3390Sstevel@tonic-gate  */
3400Sstevel@tonic-gate uint_t		pm_debug = 0;
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate /*
3430Sstevel@tonic-gate  * If pm_divertdebug is set, then no prom_printf calls will be made by
3440Sstevel@tonic-gate  * PMD(), which will prevent debug output from bringing up the console
3450Sstevel@tonic-gate  * frame buffer.  Clearing this variable before setting pm_debug will result
3460Sstevel@tonic-gate  * in PMD output going to the console.
3470Sstevel@tonic-gate  *
3480Sstevel@tonic-gate  * pm_divertdebug is incremented in pm_set_power() if dip == cfb_dip to avoid
3490Sstevel@tonic-gate  * deadlocks and decremented at the end of pm_set_power()
3500Sstevel@tonic-gate  */
3510Sstevel@tonic-gate uint_t		pm_divertdebug = 1;
3520Sstevel@tonic-gate kmutex_t	pm_debug_lock;		/* protects pm_divertdebug */
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate void prdeps(char *);
3550Sstevel@tonic-gate #endif
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate /* Globals */
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate /*
3600Sstevel@tonic-gate  * List of recorded thresholds and dependencies
3610Sstevel@tonic-gate  */
3620Sstevel@tonic-gate pm_thresh_rec_t *pm_thresh_head;
3630Sstevel@tonic-gate krwlock_t pm_thresh_rwlock;
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate pm_pdr_t *pm_dep_head;
3660Sstevel@tonic-gate static int pm_unresolved_deps = 0;
3670Sstevel@tonic-gate static int pm_prop_deps = 0;
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate /*
3700Sstevel@tonic-gate  * List of devices that exported no-involuntary-power-cycles property
3710Sstevel@tonic-gate  */
3720Sstevel@tonic-gate pm_noinvol_t *pm_noinvol_head;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate /*
3750Sstevel@tonic-gate  * Locks used in noinvol processing
3760Sstevel@tonic-gate  */
3770Sstevel@tonic-gate krwlock_t pm_noinvol_rwlock;
3780Sstevel@tonic-gate kmutex_t pm_remdrv_lock;
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate int pm_default_idle_threshold = PM_DEFAULT_SYS_IDLENESS;
3810Sstevel@tonic-gate int pm_system_idle_threshold;
3820Sstevel@tonic-gate /*
3830Sstevel@tonic-gate  * By default nexus has 0 threshold, and depends on its children to keep it up
3840Sstevel@tonic-gate  */
3850Sstevel@tonic-gate int pm_default_nexus_threshold = 0;
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate /*
3880Sstevel@tonic-gate  * Data structures shared with common/io/pm.c
3890Sstevel@tonic-gate  */
3900Sstevel@tonic-gate kmutex_t	pm_clone_lock;
3910Sstevel@tonic-gate kcondvar_t	pm_clones_cv[PM_MAX_CLONE];
3920Sstevel@tonic-gate uint_t		pm_poll_cnt[PM_MAX_CLONE];	/* count of events for poll */
3930Sstevel@tonic-gate unsigned char	pm_interest[PM_MAX_CLONE];
3940Sstevel@tonic-gate struct pollhead	pm_pollhead;
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate extern int	hz;
3970Sstevel@tonic-gate extern char	*platform_module_list[];
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate  * Wrappers for use in ddi_walk_devs
4010Sstevel@tonic-gate  */
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate static int		pm_set_dev_thr_walk(dev_info_t *, void *);
4040Sstevel@tonic-gate static int		pm_restore_direct_lvl_walk(dev_info_t *, void *);
4050Sstevel@tonic-gate static int		pm_save_direct_lvl_walk(dev_info_t *, void *);
4060Sstevel@tonic-gate static int		pm_discard_dep_walk(dev_info_t *, void *);
4070Sstevel@tonic-gate #ifdef DEBUG
4080Sstevel@tonic-gate static int		pm_desc_pwrchk_walk(dev_info_t *, void *);
4090Sstevel@tonic-gate #endif
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate /*
4120Sstevel@tonic-gate  * Routines for managing noinvol devices
4130Sstevel@tonic-gate  */
4140Sstevel@tonic-gate int			pm_noinvol_update(int, int, int, char *, dev_info_t *);
4150Sstevel@tonic-gate void			pm_noinvol_update_node(dev_info_t *,
4160Sstevel@tonic-gate 			    pm_bp_noinvol_t *req);
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate kmutex_t pm_rsvp_lock;
4190Sstevel@tonic-gate kmutex_t pm_compcnt_lock;
4200Sstevel@tonic-gate krwlock_t pm_pscc_direct_rwlock;
4210Sstevel@tonic-gate krwlock_t pm_pscc_interest_rwlock;
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate #define	PSC_INTEREST	0	/* belongs to interest psc list */
4240Sstevel@tonic-gate #define	PSC_DIRECT	1	/* belongs to direct psc list */
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate pscc_t *pm_pscc_interest;
4270Sstevel@tonic-gate pscc_t *pm_pscc_direct;
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate #define	PM_MAJOR(dip) ddi_name_to_major(ddi_binding_name(dip))
4300Sstevel@tonic-gate #define	PM_IS_NEXUS(dip) NEXUS_DRV(devopsp[PM_MAJOR(dip)])
4310Sstevel@tonic-gate #define	POWERING_ON(old, new) ((old) == 0 && (new) != 0)
4320Sstevel@tonic-gate #define	POWERING_OFF(old, new) ((old) != 0 && (new) == 0)
4330Sstevel@tonic-gate #define	PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm)
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate #define	PM_INCR_NOTLOWEST(dip) {					\
4360Sstevel@tonic-gate 	mutex_enter(&pm_compcnt_lock);					\
4370Sstevel@tonic-gate 	if (!PM_IS_NEXUS(dip) ||					\
4380Sstevel@tonic-gate 	    (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\
4390Sstevel@tonic-gate 		if (pm_comps_notlowest == 0)				\
4400Sstevel@tonic-gate 			pm_ppm_notify_all_lowest(dip, PM_NOT_ALL_LOWEST);\
4410Sstevel@tonic-gate 		pm_comps_notlowest++;					\
4420Sstevel@tonic-gate 		PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr notlowest->%d\n",\
4430Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), pm_comps_notlowest))		\
4440Sstevel@tonic-gate 	}								\
4450Sstevel@tonic-gate 	mutex_exit(&pm_compcnt_lock);					\
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate #define	PM_DECR_NOTLOWEST(dip) {					\
4480Sstevel@tonic-gate 	mutex_enter(&pm_compcnt_lock);					\
4490Sstevel@tonic-gate 	if (!PM_IS_NEXUS(dip) ||					\
4500Sstevel@tonic-gate 	    (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\
4510Sstevel@tonic-gate 		ASSERT(pm_comps_notlowest);				\
4520Sstevel@tonic-gate 		pm_comps_notlowest--;					\
4530Sstevel@tonic-gate 		PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr notlowest to "	\
4540Sstevel@tonic-gate 			    "%d\n", pmf, PM_DEVICE(dip), pm_comps_notlowest))\
4550Sstevel@tonic-gate 		if (pm_comps_notlowest == 0)				\
4560Sstevel@tonic-gate 			pm_ppm_notify_all_lowest(dip, PM_ALL_LOWEST);	\
4570Sstevel@tonic-gate 	}								\
4580Sstevel@tonic-gate 	mutex_exit(&pm_compcnt_lock);					\
4590Sstevel@tonic-gate }
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate /*
4620Sstevel@tonic-gate  * console frame-buffer power-management is not enabled when
4630Sstevel@tonic-gate  * debugging services are present.  to override, set pm_cfb_override
4640Sstevel@tonic-gate  * to non-zero.
4650Sstevel@tonic-gate  */
4660Sstevel@tonic-gate uint_t pm_cfb_comps_off = 0;	/* PM_LEVEL_UNKNOWN is considered on */
4670Sstevel@tonic-gate kmutex_t pm_cfb_lock;
4680Sstevel@tonic-gate int pm_cfb_enabled = 1;		/* non-zero allows pm of console frame buffer */
4690Sstevel@tonic-gate #ifdef DEBUG
4700Sstevel@tonic-gate int pm_cfb_override = 1;	/* non-zero allows pm of cfb with debuggers */
4710Sstevel@tonic-gate #else
4720Sstevel@tonic-gate int pm_cfb_override = 0;	/* non-zero allows pm of cfb with debuggers */
4730Sstevel@tonic-gate #endif
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate static dev_info_t *cfb_dip = 0;
4760Sstevel@tonic-gate static dev_info_t *cfb_dip_detaching = 0;
4770Sstevel@tonic-gate uint_t cfb_inuse = 0;
4780Sstevel@tonic-gate static ddi_softintr_t pm_soft_id;
4790Sstevel@tonic-gate static clock_t pm_soft_pending;
4800Sstevel@tonic-gate int	pm_scans_disabled = 0;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate /*
4830Sstevel@tonic-gate  * A structure to record the fact that one thread has borrowed a lock held
4840Sstevel@tonic-gate  * by another thread.  The context requires that the lender block on the
4850Sstevel@tonic-gate  * completion of the borrower.
4860Sstevel@tonic-gate  */
4870Sstevel@tonic-gate typedef struct lock_loan {
4880Sstevel@tonic-gate 	struct lock_loan	*pmlk_next;
4890Sstevel@tonic-gate 	kthread_t		*pmlk_borrower;
4900Sstevel@tonic-gate 	kthread_t		*pmlk_lender;
4910Sstevel@tonic-gate 	dev_info_t		*pmlk_dip;
4920Sstevel@tonic-gate } lock_loan_t;
4930Sstevel@tonic-gate static lock_loan_t lock_loan_head;	/* list head is a dummy element */
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate #ifdef	DEBUG
4960Sstevel@tonic-gate #define	PMD_FUNC(func, name)	char *(func) = (name);
4970Sstevel@tonic-gate #else
4980Sstevel@tonic-gate #define	PMD_FUNC(func, name)
4990Sstevel@tonic-gate #endif
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate /*
5030Sstevel@tonic-gate  * Must be called before first device (including pseudo) attach
5040Sstevel@tonic-gate  */
5050Sstevel@tonic-gate void
5060Sstevel@tonic-gate pm_init_locks(void)
5070Sstevel@tonic-gate {
5080Sstevel@tonic-gate 	mutex_init(&pm_scan_lock, NULL, MUTEX_DRIVER, NULL);
5090Sstevel@tonic-gate 	mutex_init(&pm_rsvp_lock, NULL, MUTEX_DRIVER, NULL);
5100Sstevel@tonic-gate 	mutex_init(&pm_compcnt_lock, NULL, MUTEX_DRIVER, NULL);
5110Sstevel@tonic-gate 	mutex_init(&pm_dep_thread_lock, NULL, MUTEX_DRIVER, NULL);
5120Sstevel@tonic-gate 	mutex_init(&pm_remdrv_lock, NULL, MUTEX_DRIVER, NULL);
5130Sstevel@tonic-gate 	mutex_init(&pm_loan_lock, NULL, MUTEX_DRIVER, NULL);
5140Sstevel@tonic-gate 	rw_init(&pm_thresh_rwlock, NULL, RW_DEFAULT, NULL);
5150Sstevel@tonic-gate 	rw_init(&pm_noinvol_rwlock, NULL, RW_DEFAULT, NULL);
5160Sstevel@tonic-gate 	cv_init(&pm_dep_thread_cv, NULL, CV_DEFAULT, NULL);
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate static boolean_t
5200Sstevel@tonic-gate pm_cpr_callb(void *arg, int code)
5210Sstevel@tonic-gate {
5220Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
5230Sstevel@tonic-gate 	static int auto_save;
5240Sstevel@tonic-gate 	static int pm_reset_timestamps(dev_info_t *, void *);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	switch (code) {
5270Sstevel@tonic-gate 	case CB_CODE_CPR_CHKPT:
5280Sstevel@tonic-gate 		/*
5290Sstevel@tonic-gate 		 * Cancel scan or wait for scan in progress to finish
5300Sstevel@tonic-gate 		 * Other threads may be trying to restart the scan, so we
5310Sstevel@tonic-gate 		 * have to keep at it unil it sticks
5320Sstevel@tonic-gate 		 */
5330Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
5340Sstevel@tonic-gate 		ASSERT(!pm_scans_disabled);
5350Sstevel@tonic-gate 		pm_scans_disabled = 1;
5360Sstevel@tonic-gate 		auto_save = autopm_enabled;
5370Sstevel@tonic-gate 		autopm_enabled = 0;
5380Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
5390Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_scan_stop_walk, NULL);
5400Sstevel@tonic-gate 		break;
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	case CB_CODE_CPR_RESUME:
5430Sstevel@tonic-gate 		ASSERT(!autopm_enabled);
5440Sstevel@tonic-gate 		ASSERT(pm_scans_disabled);
5450Sstevel@tonic-gate 		pm_scans_disabled = 0;
5460Sstevel@tonic-gate 		/*
5470Sstevel@tonic-gate 		 * Call pm_reset_timestamps to reset timestamps of each
5480Sstevel@tonic-gate 		 * device to the time when the system is resumed so that their
5490Sstevel@tonic-gate 		 * idleness can be re-calculated. That's to avoid devices from
5500Sstevel@tonic-gate 		 * being powered down right after resume if the system was in
5510Sstevel@tonic-gate 		 * suspended mode long enough.
5520Sstevel@tonic-gate 		 */
5530Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_reset_timestamps, NULL);
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 		autopm_enabled = auto_save;
5560Sstevel@tonic-gate 		/*
5570Sstevel@tonic-gate 		 * If there is any auto-pm device, get the scanning
5580Sstevel@tonic-gate 		 * going. Otherwise don't bother.
5590Sstevel@tonic-gate 		 */
5600Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_rescan_walk, NULL);
5610Sstevel@tonic-gate 		break;
5620Sstevel@tonic-gate 	}
5630Sstevel@tonic-gate 	return (B_TRUE);
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate /*
5670Sstevel@tonic-gate  * This callback routine is called when there is a system panic.  This function
5680Sstevel@tonic-gate  * exists for prototype matching.
5690Sstevel@tonic-gate  */
5700Sstevel@tonic-gate static boolean_t
5710Sstevel@tonic-gate pm_panic_callb(void *arg, int code)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg, code))
5740Sstevel@tonic-gate 	void pm_cfb_check_and_powerup(void);
5750Sstevel@tonic-gate 	PMD(PMD_CFB, ("pm_panic_callb\n"))
5760Sstevel@tonic-gate 	pm_cfb_check_and_powerup();
5770Sstevel@tonic-gate 	return (B_TRUE);
5780Sstevel@tonic-gate }
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate static boolean_t
5810Sstevel@tonic-gate pm_halt_callb(void *arg, int code)
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg, code))
5840Sstevel@tonic-gate 	return (B_TRUE);	/* XXX for now */
5850Sstevel@tonic-gate }
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate /*
5880Sstevel@tonic-gate  * This needs to be called after the root and platform drivers are loaded
5890Sstevel@tonic-gate  * and be single-threaded with respect to driver attach/detach
5900Sstevel@tonic-gate  */
5910Sstevel@tonic-gate void
5920Sstevel@tonic-gate pm_init(void)
5930Sstevel@tonic-gate {
5940Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_init")
5950Sstevel@tonic-gate 	char **mod;
5960Sstevel@tonic-gate 	extern pri_t minclsyspri;
5970Sstevel@tonic-gate 	static void pm_dep_thread(void);
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	pm_comps_notlowest = 0;
6000Sstevel@tonic-gate 	pm_system_idle_threshold = pm_default_idle_threshold;
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	pm_cpr_cb_id = callb_add(pm_cpr_callb, (void *)NULL,
6030Sstevel@tonic-gate 	    CB_CL_CPR_PM, "pm_cpr");
6040Sstevel@tonic-gate 	pm_panic_cb_id = callb_add(pm_panic_callb, (void *)NULL,
6050Sstevel@tonic-gate 		    CB_CL_PANIC, "pm_panic");
6060Sstevel@tonic-gate 	pm_halt_cb_id = callb_add(pm_halt_callb, (void *)NULL,
6070Sstevel@tonic-gate 		    CB_CL_HALT, "pm_halt");
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	/*
6100Sstevel@tonic-gate 	 * Create a thread to do dependency processing.
6110Sstevel@tonic-gate 	 */
6120Sstevel@tonic-gate 	(void) thread_create(NULL, 0, (void (*)())pm_dep_thread, NULL, 0, &p0,
6130Sstevel@tonic-gate 	    TS_RUN, minclsyspri);
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	/*
6160Sstevel@tonic-gate 	 * loadrootmodules already loaded these ppm drivers, now get them
6170Sstevel@tonic-gate 	 * attached so they can claim the root drivers as they attach
6180Sstevel@tonic-gate 	 */
6190Sstevel@tonic-gate 	for (mod = platform_module_list; *mod; mod++) {
6200Sstevel@tonic-gate 		if (i_ddi_attach_hw_nodes(*mod) != DDI_SUCCESS) {
6210Sstevel@tonic-gate 			cmn_err(CE_WARN, "!cannot load platform pm driver %s\n",
6220Sstevel@tonic-gate 			    *mod);
6230Sstevel@tonic-gate 		} else {
6240Sstevel@tonic-gate 			PMD(PMD_DHR, ("%s: %s (%s)\n", pmf, *mod,
6250Sstevel@tonic-gate 			    ddi_major_to_name(ddi_name_to_major(*mod))))
6260Sstevel@tonic-gate 		}
6270Sstevel@tonic-gate 	}
6280Sstevel@tonic-gate }
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate /*
6310Sstevel@tonic-gate  * pm_scan_init - create pm scan data structure.  Called (if autopm enabled)
6320Sstevel@tonic-gate  * when device becomes power managed or after a failed detach and when autopm
6330Sstevel@tonic-gate  * is started via PM_START_PM ioctl, and after a CPR resume to get all the
6340Sstevel@tonic-gate  * devices scanning again.
6350Sstevel@tonic-gate  */
6360Sstevel@tonic-gate void
6370Sstevel@tonic-gate pm_scan_init(dev_info_t *dip)
6380Sstevel@tonic-gate {
6390Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_init")
6400Sstevel@tonic-gate 	pm_scan_t	*scanp;
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	ASSERT(!PM_ISBC(dip));
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
6450Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
6460Sstevel@tonic-gate 	if (!scanp) {
6470Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): create scan data\n",
6480Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
6490Sstevel@tonic-gate 		scanp =  kmem_zalloc(sizeof (pm_scan_t), KM_SLEEP);
6500Sstevel@tonic-gate 		DEVI(dip)->devi_pm_scan = scanp;
6510Sstevel@tonic-gate 	} else if (scanp->ps_scan_flags & PM_SCAN_STOP) {
6520Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): "
6530Sstevel@tonic-gate 		    "clear PM_SCAN_STOP flag\n", pmf, PM_DEVICE(dip)))
6540Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~PM_SCAN_STOP;
6550Sstevel@tonic-gate 	}
6560Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate /*
6600Sstevel@tonic-gate  * pm_scan_fini - remove pm scan data structure when stopping pm on the device
6610Sstevel@tonic-gate  */
6620Sstevel@tonic-gate void
6630Sstevel@tonic-gate pm_scan_fini(dev_info_t *dip)
6640Sstevel@tonic-gate {
6650Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_fini")
6660Sstevel@tonic-gate 	pm_scan_t	*scanp;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
6690Sstevel@tonic-gate 	ASSERT(!PM_ISBC(dip));
6700Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
6710Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
6720Sstevel@tonic-gate 	if (!scanp) {
6730Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
6740Sstevel@tonic-gate 		return;
6750Sstevel@tonic-gate 	}
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	ASSERT(!scanp->ps_scan_id && !(scanp->ps_scan_flags &
6780Sstevel@tonic-gate 	    (PM_SCANNING | PM_SCAN_DISPATCHED | PM_SCAN_AGAIN)));
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	kmem_free(scanp, sizeof (pm_scan_t));
6810Sstevel@tonic-gate 	DEVI(dip)->devi_pm_scan = NULL;
6820Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate /*
6860Sstevel@tonic-gate  * Given a pointer to a component struct, return the current power level
6870Sstevel@tonic-gate  * (struct contains index unless it is a continuous level).
6880Sstevel@tonic-gate  * Located here in hopes of getting both this and dev_is_needed into the
6890Sstevel@tonic-gate  * cache together
6900Sstevel@tonic-gate  */
6910Sstevel@tonic-gate static int
6920Sstevel@tonic-gate cur_power(pm_component_t *cp)
6930Sstevel@tonic-gate {
6940Sstevel@tonic-gate 	if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN)
6950Sstevel@tonic-gate 		return (cp->pmc_cur_pwr);
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 	return (cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]);
6980Sstevel@tonic-gate }
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate static char *
7010Sstevel@tonic-gate pm_decode_direction(int direction)
7020Sstevel@tonic-gate {
7030Sstevel@tonic-gate 	switch (direction) {
7040Sstevel@tonic-gate 	case PM_LEVEL_UPONLY:
7050Sstevel@tonic-gate 		return ("up");
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	case PM_LEVEL_EXACT:
7080Sstevel@tonic-gate 		return ("exact");
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	case PM_LEVEL_DOWNONLY:
7110Sstevel@tonic-gate 		return ("down");
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	default:
7140Sstevel@tonic-gate 		return ("INVALID DIRECTION");
7150Sstevel@tonic-gate 	}
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate char *
7190Sstevel@tonic-gate pm_decode_op(pm_bus_power_op_t op)
7200Sstevel@tonic-gate {
7210Sstevel@tonic-gate 	switch (op) {
7220Sstevel@tonic-gate 	case BUS_POWER_CHILD_PWRCHG:
7230Sstevel@tonic-gate 		return ("CHILD_PWRCHG");
7240Sstevel@tonic-gate 	case BUS_POWER_NEXUS_PWRUP:
7250Sstevel@tonic-gate 		return ("NEXUS_PWRUP");
7260Sstevel@tonic-gate 	case BUS_POWER_PRE_NOTIFICATION:
7270Sstevel@tonic-gate 		return ("PRE_NOTIFICATION");
7280Sstevel@tonic-gate 	case BUS_POWER_POST_NOTIFICATION:
7290Sstevel@tonic-gate 		return ("POST_NOTIFICATION");
7300Sstevel@tonic-gate 	case BUS_POWER_HAS_CHANGED:
7310Sstevel@tonic-gate 		return ("HAS_CHANGED");
7320Sstevel@tonic-gate 	case BUS_POWER_NOINVOL:
7330Sstevel@tonic-gate 		return ("NOINVOL");
7340Sstevel@tonic-gate 	default:
7350Sstevel@tonic-gate 		return ("UNKNOWN OP");
7360Sstevel@tonic-gate 	}
7370Sstevel@tonic-gate }
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate /*
7400Sstevel@tonic-gate  * Returns true if level is a possible (valid) power level for component
7410Sstevel@tonic-gate  */
7420Sstevel@tonic-gate int
7430Sstevel@tonic-gate e_pm_valid_power(dev_info_t *dip, int cmpt, int level)
7440Sstevel@tonic-gate {
7450Sstevel@tonic-gate 	PMD_FUNC(pmf, "e_pm_valid_power")
7460Sstevel@tonic-gate 	pm_component_t *cp = PM_CP(dip, cmpt);
7470Sstevel@tonic-gate 	int i;
7480Sstevel@tonic-gate 	int *ip = cp->pmc_comp.pmc_lvals;
7490Sstevel@tonic-gate 	int limit = cp->pmc_comp.pmc_numlevels;
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	if (level < 0)
7520Sstevel@tonic-gate 		return (0);
7530Sstevel@tonic-gate 	for (i = 0; i < limit; i++) {
7540Sstevel@tonic-gate 		if (level == *ip++)
7550Sstevel@tonic-gate 			return (1);
7560Sstevel@tonic-gate 	}
7570Sstevel@tonic-gate #ifdef DEBUG
7580Sstevel@tonic-gate 	if (pm_debug & PMD_FAIL) {
7590Sstevel@tonic-gate 		ip = cp->pmc_comp.pmc_lvals;
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 		for (i = 0; i < limit; i++)
7620Sstevel@tonic-gate 			PMD(PMD_FAIL, ("%s: index=%d, level=%d\n",
7630Sstevel@tonic-gate 			    pmf, i, *ip++))
7640Sstevel@tonic-gate 	}
7650Sstevel@tonic-gate #endif
7660Sstevel@tonic-gate 	return (0);
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate /*
7700Sstevel@tonic-gate  * Returns true if device is pm'd (after calling pm_start if need be)
7710Sstevel@tonic-gate  */
7720Sstevel@tonic-gate int
7730Sstevel@tonic-gate e_pm_valid_info(dev_info_t *dip, pm_info_t **infop)
7740Sstevel@tonic-gate {
7750Sstevel@tonic-gate 	pm_info_t *info;
7760Sstevel@tonic-gate 	static int pm_start(dev_info_t *dip);
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	/*
7790Sstevel@tonic-gate 	 * Check if the device is power managed if not.
7800Sstevel@tonic-gate 	 * To make the common case (device is power managed already)
7810Sstevel@tonic-gate 	 * fast, we check without the lock.  If device is not already
7820Sstevel@tonic-gate 	 * power managed, then we take the lock and the long route through
7830Sstevel@tonic-gate 	 * go get it managed.  Devices never go unmanaged until they
7840Sstevel@tonic-gate 	 * detach.
7850Sstevel@tonic-gate 	 */
7860Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
7870Sstevel@tonic-gate 	if (!info) {
7880Sstevel@tonic-gate 		if (!DEVI_IS_ATTACHING(dip)) {
7890Sstevel@tonic-gate 			return (0);
7900Sstevel@tonic-gate 		}
7910Sstevel@tonic-gate 		if (pm_start(dip) != DDI_SUCCESS) {
7920Sstevel@tonic-gate 			return (0);
7930Sstevel@tonic-gate 		}
7940Sstevel@tonic-gate 		info = PM_GET_PM_INFO(dip);
7950Sstevel@tonic-gate 	}
7960Sstevel@tonic-gate 	ASSERT(info);
7970Sstevel@tonic-gate 	if (infop != NULL)
7980Sstevel@tonic-gate 		*infop = info;
7990Sstevel@tonic-gate 	return (1);
8000Sstevel@tonic-gate }
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate int
8030Sstevel@tonic-gate e_pm_valid_comp(dev_info_t *dip, int cmpt, pm_component_t **cpp)
8040Sstevel@tonic-gate {
8050Sstevel@tonic-gate 	if (cmpt >= 0 && cmpt < PM_NUMCMPTS(dip)) {
8060Sstevel@tonic-gate 		if (cpp != NULL)
8070Sstevel@tonic-gate 			*cpp = PM_CP(dip, cmpt);
8080Sstevel@tonic-gate 		return (1);
8090Sstevel@tonic-gate 	} else {
8100Sstevel@tonic-gate 		return (0);
8110Sstevel@tonic-gate 	}
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate /*
8150Sstevel@tonic-gate  * Internal guts of ddi_dev_is_needed and pm_raise/lower_power
8160Sstevel@tonic-gate  */
8170Sstevel@tonic-gate static int
8180Sstevel@tonic-gate dev_is_needed(dev_info_t *dip, int cmpt, int level, int direction)
8190Sstevel@tonic-gate {
8200Sstevel@tonic-gate 	PMD_FUNC(pmf, "din")
8210Sstevel@tonic-gate 	pm_component_t *cp;
8220Sstevel@tonic-gate 	char *pathbuf;
8230Sstevel@tonic-gate 	int result;
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY);
8260Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp) ||
8270Sstevel@tonic-gate 	    !e_pm_valid_power(dip, cmpt, level))
8280Sstevel@tonic-gate 		return (DDI_FAILURE);
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	PMD(PMD_DIN, ("%s: %s@%s(%s#%d) cmpt=%d, dir=%s, new=%d, cur=%d\n",
8310Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), cmpt, pm_decode_direction(direction),
8320Sstevel@tonic-gate 	    level, cur_power(cp)))
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	if (pm_set_power(dip, cmpt, level,  direction,
8350Sstevel@tonic-gate 	    PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) {
8360Sstevel@tonic-gate 		if (direction == PM_LEVEL_UPONLY) {
8370Sstevel@tonic-gate 			pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
8380Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
8390Sstevel@tonic-gate 			cmn_err(CE_WARN, "Device %s failed to power up.",
8400Sstevel@tonic-gate 			    pathbuf);
8410Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
8420Sstevel@tonic-gate 		}
8430Sstevel@tonic-gate 		PMD(PMD_DIN | PMD_FAIL, ("%s: %s@%s(%s#%d) [%d] %s->%d failed, "
8440Sstevel@tonic-gate 		    "errno %d\n", pmf, PM_DEVICE(dip), cmpt,
8450Sstevel@tonic-gate 		    pm_decode_direction(direction), level, result))
8460Sstevel@tonic-gate 		return (DDI_FAILURE);
8470Sstevel@tonic-gate 	}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	PMD(PMD_RESCAN | PMD_DIN, ("%s: pm_rescan %s@%s(%s#%d)\n", pmf,
8500Sstevel@tonic-gate 	    PM_DEVICE(dip)))
8510Sstevel@tonic-gate 	pm_rescan(dip);
8520Sstevel@tonic-gate 	return (DDI_SUCCESS);
8530Sstevel@tonic-gate }
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate /*
8560Sstevel@tonic-gate  * We can get multiple pm_rescan() threads, if one of them discovers
8570Sstevel@tonic-gate  * that no scan is running at the moment, it kicks it into action.
8580Sstevel@tonic-gate  * Otherwise, it tells the current scanning thread to scan again when
8590Sstevel@tonic-gate  * it is done by asserting the PM_SCAN_AGAIN flag. The PM_SCANNING and
8600Sstevel@tonic-gate  * PM_SCAN_AGAIN flags are used to regulate scan, to make sure only one
8610Sstevel@tonic-gate  * thread at a time runs the pm_scan_dev() code.
8620Sstevel@tonic-gate  */
8630Sstevel@tonic-gate void
8640Sstevel@tonic-gate pm_rescan(void *arg)
8650Sstevel@tonic-gate {
8660Sstevel@tonic-gate 	PMD_FUNC(pmf, "rescan")
8670Sstevel@tonic-gate 	dev_info_t	*dip = (dev_info_t *)arg;
8680Sstevel@tonic-gate 	pm_info_t	*info;
8690Sstevel@tonic-gate 	pm_scan_t	*scanp;
8700Sstevel@tonic-gate 	timeout_id_t	scanid;
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
8730Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
8740Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
8750Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
8760Sstevel@tonic-gate 	if (pm_scans_disabled || !autopm_enabled || !info || !scanp ||
8770Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_STOP)) {
8780Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
8790Sstevel@tonic-gate 		return;
8800Sstevel@tonic-gate 	}
8810Sstevel@tonic-gate 	if (scanp->ps_scan_flags & PM_SCANNING) {
8820Sstevel@tonic-gate 		scanp->ps_scan_flags |= PM_SCAN_AGAIN;
8830Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
8840Sstevel@tonic-gate 		return;
8850Sstevel@tonic-gate 	} else if (scanp->ps_scan_id) {
8860Sstevel@tonic-gate 		scanid = scanp->ps_scan_id;
8870Sstevel@tonic-gate 		scanp->ps_scan_id = 0;
8880Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): cancel timeout scanid %lx\n",
8890Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), (ulong_t)scanid))
8900Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
8910Sstevel@tonic-gate 		(void) untimeout(scanid);
8920Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
8930Sstevel@tonic-gate 	}
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	/*
8960Sstevel@tonic-gate 	 * Dispatching pm_scan during attach time is risky due to the fact that
8970Sstevel@tonic-gate 	 * attach might soon fail and dip dissolved, and panic may happen while
8980Sstevel@tonic-gate 	 * attempting to stop scan. So schedule a pm_rescan instead.
8990Sstevel@tonic-gate 	 * (Note that if either of the first two terms are true, taskq_dispatch
9000Sstevel@tonic-gate 	 * will not be invoked).
9010Sstevel@tonic-gate 	 *
9020Sstevel@tonic-gate 	 * Multiple pm_scan dispatching is unecessary and costly to keep track
9030Sstevel@tonic-gate 	 * of. The PM_SCAN_DISPATCHED flag is used between pm_rescan and pm_scan
9040Sstevel@tonic-gate 	 * to regulate the dispatching.
9050Sstevel@tonic-gate 	 *
9060Sstevel@tonic-gate 	 * Scan is stopped before the device is detached (in pm_detaching())
9070Sstevel@tonic-gate 	 * but it may get re-started during the post_detach processing if the
9080Sstevel@tonic-gate 	 * driver fails to detach.
9090Sstevel@tonic-gate 	 */
9100Sstevel@tonic-gate 	if (DEVI_IS_ATTACHING(dip) ||
9110Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_DISPATCHED) ||
9120Sstevel@tonic-gate 	    !taskq_dispatch(system_taskq, pm_scan, (void *)dip, TQ_NOSLEEP)) {
9130Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): attaching, pm_scan already "
9140Sstevel@tonic-gate 		    "dispatched or dispatching failed\n", pmf, PM_DEVICE(dip)))
9150Sstevel@tonic-gate 		if (scanp->ps_scan_id) {
9160Sstevel@tonic-gate 			scanid = scanp->ps_scan_id;
9170Sstevel@tonic-gate 			scanp->ps_scan_id = 0;
9180Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
9190Sstevel@tonic-gate 			(void) untimeout(scanid);
9200Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
9210Sstevel@tonic-gate 			if (scanp->ps_scan_id) {
9220Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): a competing "
9230Sstevel@tonic-gate 				    "thread scheduled pm_rescan, scanid %lx\n",
9240Sstevel@tonic-gate 				    pmf, PM_DEVICE(dip),
9250Sstevel@tonic-gate 				    (ulong_t)scanp->ps_scan_id))
9260Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
9270Sstevel@tonic-gate 				return;
9280Sstevel@tonic-gate 			}
9290Sstevel@tonic-gate 		}
9300Sstevel@tonic-gate 		scanp->ps_scan_id = timeout(pm_rescan, (void *)dip,
9310Sstevel@tonic-gate 		    (scanp->ps_idle_down ? pm_id_ticks :
9320Sstevel@tonic-gate 		    (pm_min_scan * hz)));
9330Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): scheduled next pm_rescan, "
9340Sstevel@tonic-gate 		    "scanid %lx\n", pmf, PM_DEVICE(dip),
9350Sstevel@tonic-gate 		    (ulong_t)scanp->ps_scan_id))
9360Sstevel@tonic-gate 	} else {
9370Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: dispatched pm_scan for %s@%s(%s#%d)\n",
9380Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
9390Sstevel@tonic-gate 		scanp->ps_scan_flags |= PM_SCAN_DISPATCHED;
9400Sstevel@tonic-gate 	}
9410Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate void
9450Sstevel@tonic-gate pm_scan(void *arg)
9460Sstevel@tonic-gate {
9470Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan")
9480Sstevel@tonic-gate 	dev_info_t	*dip = (dev_info_t *)arg;
9490Sstevel@tonic-gate 	pm_scan_t	*scanp;
9500Sstevel@tonic-gate 	time_t		nextscan;
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
9550Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
9560Sstevel@tonic-gate 	ASSERT(scanp && PM_GET_PM_INFO(dip));
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	if (pm_scans_disabled || !autopm_enabled ||
9590Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_STOP)) {
9600Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~(PM_SCAN_AGAIN | PM_SCAN_DISPATCHED);
9610Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
9620Sstevel@tonic-gate 		return;
9630Sstevel@tonic-gate 	}
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	if (scanp->ps_idle_down) {
9660Sstevel@tonic-gate 		/*
9670Sstevel@tonic-gate 		 * make sure we remember idledown was in affect until
9680Sstevel@tonic-gate 		 * we've completed the scan
9690Sstevel@tonic-gate 		 */
9700Sstevel@tonic-gate 		PMID_SET_SCANS(scanp->ps_idle_down)
9710Sstevel@tonic-gate 		PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown starts "
9720Sstevel@tonic-gate 		    "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down))
9730Sstevel@tonic-gate 	}
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 	/* possible having two threads running pm_scan() */
9760Sstevel@tonic-gate 	if (scanp->ps_scan_flags & PM_SCANNING) {
9770Sstevel@tonic-gate 		scanp->ps_scan_flags |= PM_SCAN_AGAIN;
9780Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: scanning, will scan %s@%s(%s#%d) again\n",
9790Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
9800Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED;
9810Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
9820Sstevel@tonic-gate 		return;
9830Sstevel@tonic-gate 	}
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate 	scanp->ps_scan_flags |= PM_SCANNING;
9860Sstevel@tonic-gate 	scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED;
9870Sstevel@tonic-gate 	do {
9880Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~PM_SCAN_AGAIN;
9890Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
9900Sstevel@tonic-gate 		nextscan = pm_scan_dev(dip);
9910Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
9920Sstevel@tonic-gate 	} while (scanp->ps_scan_flags & PM_SCAN_AGAIN);
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 	ASSERT(scanp->ps_scan_flags & PM_SCANNING);
9950Sstevel@tonic-gate 	scanp->ps_scan_flags &= ~PM_SCANNING;
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	if (scanp->ps_idle_down) {
9980Sstevel@tonic-gate 		scanp->ps_idle_down &= ~PMID_SCANS;
9990Sstevel@tonic-gate 		PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown ends "
10000Sstevel@tonic-gate 		    "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down))
10010Sstevel@tonic-gate 	}
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate 	/* schedule for next idle check */
10040Sstevel@tonic-gate 	if (nextscan != LONG_MAX) {
10050Sstevel@tonic-gate 		if (nextscan > (LONG_MAX / hz))
10060Sstevel@tonic-gate 			nextscan = (LONG_MAX - 1) / hz;
10070Sstevel@tonic-gate 		if (scanp->ps_scan_id) {
10080Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): while scanning "
10090Sstevel@tonic-gate 			    "another rescan scheduled scanid(%lx)\n", pmf,
10100Sstevel@tonic-gate 			    PM_DEVICE(dip), (ulong_t)scanp->ps_scan_id))
10110Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
10120Sstevel@tonic-gate 			return;
10130Sstevel@tonic-gate 		} else if (!(scanp->ps_scan_flags & PM_SCAN_STOP)) {
10140Sstevel@tonic-gate 			scanp->ps_scan_id = timeout(pm_rescan, (void *)dip,
10150Sstevel@tonic-gate 			    (clock_t)(nextscan * hz));
10160Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: nextscan for %s@%s(%s#%d) in "
10170Sstevel@tonic-gate 			    "%lx sec, scanid(%lx) \n", pmf, PM_DEVICE(dip),
10180Sstevel@tonic-gate 			    (ulong_t)nextscan, (ulong_t)scanp->ps_scan_id))
10190Sstevel@tonic-gate 		}
10200Sstevel@tonic-gate 	}
10210Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate void
10250Sstevel@tonic-gate pm_get_timestamps(dev_info_t *dip, time_t *valuep)
10260Sstevel@tonic-gate {
10270Sstevel@tonic-gate 	int components = PM_NUMCMPTS(dip);
10280Sstevel@tonic-gate 	int i;
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	ASSERT(components > 0);
10310Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);	/* so we get a consistent view */
10320Sstevel@tonic-gate 	for (i = 0; i < components; i++) {
10330Sstevel@tonic-gate 		valuep[i] = PM_CP(dip, i)->pmc_timestamp;
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
10360Sstevel@tonic-gate }
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate /*
10390Sstevel@tonic-gate  * Returns true if device needs to be kept up because it exported the
10400Sstevel@tonic-gate  * "no-involuntary-power-cycles" property or we're pretending it did (console
10410Sstevel@tonic-gate  * fb case) or it is an ancestor of such a device and has used up the "one
10420Sstevel@tonic-gate  * free cycle" allowed when all such leaf nodes have voluntarily powered down
10430Sstevel@tonic-gate  * upon detach
10440Sstevel@tonic-gate  */
10450Sstevel@tonic-gate int
10460Sstevel@tonic-gate pm_noinvol(dev_info_t *dip)
10470Sstevel@tonic-gate {
10480Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol")
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 	/*
10510Sstevel@tonic-gate 	 * This doesn't change over the life of a driver, so no locking needed
10520Sstevel@tonic-gate 	 */
10530Sstevel@tonic-gate 	if (PM_IS_CFB(dip)) {
10540Sstevel@tonic-gate 		PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB %s@%s(%s#%d)\n",
10550Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
10560Sstevel@tonic-gate 		return (1);
10570Sstevel@tonic-gate 	}
10580Sstevel@tonic-gate 	/*
10590Sstevel@tonic-gate 	 * Not an issue if no such kids
10600Sstevel@tonic-gate 	 */
10610Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_noinvolpm == 0) {
10620Sstevel@tonic-gate #ifdef DEBUG
10630Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_volpmd != 0) {
10640Sstevel@tonic-gate 			dev_info_t *pdip = dip;
10650Sstevel@tonic-gate 			do {
10660Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d) noinvol %d "
10670Sstevel@tonic-gate 				    "volpmd %d\n", pmf, PM_DEVICE(pdip),
10680Sstevel@tonic-gate 				    DEVI(pdip)->devi_pm_noinvolpm,
10690Sstevel@tonic-gate 				    DEVI(pdip)->devi_pm_volpmd))
10700Sstevel@tonic-gate 				pdip = ddi_get_parent(pdip);
10710Sstevel@tonic-gate 			} while (pdip);
10720Sstevel@tonic-gate 		}
10730Sstevel@tonic-gate #endif
10740Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_volpmd == 0);
10750Sstevel@tonic-gate 		return (0);
10760Sstevel@tonic-gate 	}
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	/*
10790Sstevel@tonic-gate 	 * Since we now maintain the counts correct at every node, we no longer
10800Sstevel@tonic-gate 	 * need to look up the tree.  An ancestor cannot use up the free cycle
10810Sstevel@tonic-gate 	 * without the children getting their counts adjusted.
10820Sstevel@tonic-gate 	 */
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate #ifdef	DEBUG
10850Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd)
10860Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s@%s(%s#%d)\n", pmf,
10870Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm, DEVI(dip)->devi_pm_volpmd,
10880Sstevel@tonic-gate 		    PM_DEVICE(dip)))
10890Sstevel@tonic-gate #endif
10900Sstevel@tonic-gate 	return (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd);
10910Sstevel@tonic-gate }
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate /*
10940Sstevel@tonic-gate  * This function performs the actual scanning of the device.
10950Sstevel@tonic-gate  * It attempts to power off the indicated device's components if they have
10960Sstevel@tonic-gate  * been idle and other restrictions are met.
10970Sstevel@tonic-gate  * pm_scan_dev calculates and returns when the next scan should happen for
10980Sstevel@tonic-gate  * this device.
10990Sstevel@tonic-gate  */
11000Sstevel@tonic-gate time_t
11010Sstevel@tonic-gate pm_scan_dev(dev_info_t *dip)
11020Sstevel@tonic-gate {
11030Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_dev")
11040Sstevel@tonic-gate 	pm_scan_t	*scanp;
11050Sstevel@tonic-gate 	time_t		*timestamp, idletime, now, thresh;
11060Sstevel@tonic-gate 	time_t		timeleft = 0;
11070Sstevel@tonic-gate 	int		i, nxtpwr, curpwr, pwrndx, unused;
11080Sstevel@tonic-gate 	size_t		size;
11090Sstevel@tonic-gate 	pm_component_t	 *cp;
11100Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
11110Sstevel@tonic-gate 	int		circ;
11120Sstevel@tonic-gate 	static int	cur_threshold(dev_info_t *, int);
11130Sstevel@tonic-gate 	static int	pm_next_lower_power(pm_component_t *, int);
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	/*
11160Sstevel@tonic-gate 	 * skip attaching device
11170Sstevel@tonic-gate 	 */
11180Sstevel@tonic-gate 	if (DEVI_IS_ATTACHING(dip)) {
11190Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) is attaching, timeleft(%lx)\n",
11200Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), pm_min_scan))
11210Sstevel@tonic-gate 		return (pm_min_scan);
11220Sstevel@tonic-gate 	}
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
11250Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
11260Sstevel@tonic-gate 	ASSERT(scanp && PM_GET_PM_INFO(dip));
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
11290Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): kuc is %d\n", pmf, PM_DEVICE(dip),
11300Sstevel@tonic-gate 	    PM_KUC(dip)))
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate 	/* no scan under the following conditions */
11330Sstevel@tonic-gate 	if (pm_scans_disabled || !autopm_enabled ||
11340Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_STOP) ||
11350Sstevel@tonic-gate 	    (PM_KUC(dip) != 0) ||
11360Sstevel@tonic-gate 	    PM_ISDIRECT(dip) || pm_noinvol(dip)) {
11370Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
11380Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: [END, %s@%s(%s#%d)] no scan, "
11390Sstevel@tonic-gate 		    "scan_disabled(%d), apm_enabled(%d), kuc(%d), "
11400Sstevel@tonic-gate 		    "%s directpm, %s pm_noinvol\n", pmf, PM_DEVICE(dip),
11410Sstevel@tonic-gate 		    pm_scans_disabled, autopm_enabled, PM_KUC(dip),
11420Sstevel@tonic-gate 		    PM_ISDIRECT(dip) ? "is" : "is not",
11430Sstevel@tonic-gate 		    pm_noinvol(dip) ? "is" : "is not"))
11440Sstevel@tonic-gate 		return (LONG_MAX);
11450Sstevel@tonic-gate 	}
11460Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	if (!ndi_devi_tryenter(pdip, &circ)) {
11490Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) can't hold pdip",
11500Sstevel@tonic-gate 		    pmf, PM_DEVICE(pdip)))
11510Sstevel@tonic-gate 		return ((time_t)1);
11520Sstevel@tonic-gate 	}
11530Sstevel@tonic-gate 	now = gethrestime_sec();
11540Sstevel@tonic-gate 	size = PM_NUMCMPTS(dip) * sizeof (time_t);
11550Sstevel@tonic-gate 	timestamp = kmem_alloc(size, KM_SLEEP);
11560Sstevel@tonic-gate 	pm_get_timestamps(dip, timestamp);
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	/*
11590Sstevel@tonic-gate 	 * Since we removed support for backwards compatible devices,
11600Sstevel@tonic-gate 	 * (see big comment at top of file)
11610Sstevel@tonic-gate 	 * it is no longer required to deal with component 0 last.
11620Sstevel@tonic-gate 	 */
11630Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
11640Sstevel@tonic-gate 		/*
11650Sstevel@tonic-gate 		 * If already off (an optimization, perhaps)
11660Sstevel@tonic-gate 		 */
11670Sstevel@tonic-gate 		cp = PM_CP(dip, i);
11680Sstevel@tonic-gate 		pwrndx = cp->pmc_cur_pwr;
11690Sstevel@tonic-gate 		curpwr = (pwrndx == PM_LEVEL_UNKNOWN) ?
11700Sstevel@tonic-gate 		    PM_LEVEL_UNKNOWN :
11710Sstevel@tonic-gate 		    cp->pmc_comp.pmc_lvals[pwrndx];
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 		if (pwrndx == 0) {
11740Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d off or "
11750Sstevel@tonic-gate 			    "lowest\n", pmf, PM_DEVICE(dip), i))
11760Sstevel@tonic-gate 			/* skip device if off or at its lowest */
11770Sstevel@tonic-gate 			continue;
11780Sstevel@tonic-gate 		}
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 		thresh = cur_threshold(dip, i);		/* comp i threshold */
11810Sstevel@tonic-gate 		if ((timestamp[i] == 0) || (cp->pmc_busycount > 0)) {
11820Sstevel@tonic-gate 			/* were busy or newly became busy by another thread */
11830Sstevel@tonic-gate 			if (timeleft == 0)
11840Sstevel@tonic-gate 				timeleft = max(thresh, pm_min_scan);
11850Sstevel@tonic-gate 			else
11860Sstevel@tonic-gate 				timeleft = min(
11870Sstevel@tonic-gate 				    timeleft, max(thresh, pm_min_scan));
11880Sstevel@tonic-gate 			continue;
11890Sstevel@tonic-gate 		}
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 		idletime = now - timestamp[i];		/* idle time */
11920Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d idle time %lx\n",
11930Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), i, idletime))
11940Sstevel@tonic-gate 		if (idletime >= thresh || PM_IS_PID(dip)) {
11950Sstevel@tonic-gate 			nxtpwr = pm_next_lower_power(cp, pwrndx);
11960Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, %d->%d\n",
11970Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip), i, curpwr, nxtpwr))
11980Sstevel@tonic-gate 			if (pm_set_power(dip, i, nxtpwr, PM_LEVEL_DOWNONLY,
11990Sstevel@tonic-gate 			    PM_CANBLOCK_FAIL, 1, &unused) != DDI_SUCCESS &&
12000Sstevel@tonic-gate 			    PM_CURPOWER(dip, i) != nxtpwr) {
12010Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
12020Sstevel@tonic-gate 				    "%d->%d Failed\n", pmf, PM_DEVICE(dip),
12030Sstevel@tonic-gate 				    i, curpwr, nxtpwr))
12040Sstevel@tonic-gate 				timeleft = pm_min_scan;
12050Sstevel@tonic-gate 				continue;
12060Sstevel@tonic-gate 			} else {
12070Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
12080Sstevel@tonic-gate 				    "%d->%d, GOOD curpwr %d\n", pmf,
12090Sstevel@tonic-gate 				    PM_DEVICE(dip), i, curpwr, nxtpwr,
12100Sstevel@tonic-gate 				    cur_power(cp)))
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate 				if (nxtpwr == 0)	/* component went off */
12130Sstevel@tonic-gate 					continue;
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 				/*
12160Sstevel@tonic-gate 				 * scan to next lower level
12170Sstevel@tonic-gate 				 */
12180Sstevel@tonic-gate 				if (timeleft == 0)
12190Sstevel@tonic-gate 					timeleft = max(
12200Sstevel@tonic-gate 					    1, cur_threshold(dip, i));
12210Sstevel@tonic-gate 				else
12220Sstevel@tonic-gate 					timeleft = min(timeleft,
12230Sstevel@tonic-gate 					    max(1, cur_threshold(dip, i)));
12240Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
12250Sstevel@tonic-gate 				    "timeleft(%lx)\n", pmf, PM_DEVICE(dip),
12260Sstevel@tonic-gate 				    i, timeleft))
12270Sstevel@tonic-gate 			}
12280Sstevel@tonic-gate 		} else {	/* comp not idle long enough */
12290Sstevel@tonic-gate 			if (timeleft == 0)
12300Sstevel@tonic-gate 				timeleft = thresh - idletime;
12310Sstevel@tonic-gate 			else
12320Sstevel@tonic-gate 				timeleft = min(timeleft, (thresh - idletime));
12330Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, timeleft="
12340Sstevel@tonic-gate 			    "%lx\n", pmf, PM_DEVICE(dip), i, timeleft))
12350Sstevel@tonic-gate 		}
12360Sstevel@tonic-gate 	}
12370Sstevel@tonic-gate 	ndi_devi_exit(pdip, circ);
12380Sstevel@tonic-gate 	kmem_free(timestamp, size);
12390Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] timeleft(%lx)\n", pmf,
12400Sstevel@tonic-gate 	    PM_DEVICE(dip), timeleft))
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	/*
12430Sstevel@tonic-gate 	 * if components are already at lowest level, timeleft is left 0
12440Sstevel@tonic-gate 	 */
12450Sstevel@tonic-gate 	return ((timeleft == 0) ? LONG_MAX : timeleft);
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate /*
12490Sstevel@tonic-gate  * pm_scan_stop - cancel scheduled pm_rescan,
12500Sstevel@tonic-gate  *                wait for termination of dispatched pm_scan thread
12510Sstevel@tonic-gate  *                     and active pm_scan_dev thread.
12520Sstevel@tonic-gate  */
12530Sstevel@tonic-gate void
12540Sstevel@tonic-gate pm_scan_stop(dev_info_t *dip)
12550Sstevel@tonic-gate {
12560Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_stop")
12570Sstevel@tonic-gate 	pm_scan_t	*scanp;
12580Sstevel@tonic-gate 	timeout_id_t	scanid;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
12610Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
12620Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
12630Sstevel@tonic-gate 	if (!scanp) {
12640Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] scan not initialized\n",
12650Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
12660Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
12670Sstevel@tonic-gate 		return;
12680Sstevel@tonic-gate 	}
12690Sstevel@tonic-gate 	scanp->ps_scan_flags |= PM_SCAN_STOP;
12700Sstevel@tonic-gate 
12710Sstevel@tonic-gate 	/* cancel scheduled scan taskq */
12720Sstevel@tonic-gate 	while (scanp->ps_scan_id) {
12730Sstevel@tonic-gate 		scanid = scanp->ps_scan_id;
12740Sstevel@tonic-gate 		scanp->ps_scan_id = 0;
12750Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
12760Sstevel@tonic-gate 		(void) untimeout(scanid);
12770Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
12780Sstevel@tonic-gate 	}
12790Sstevel@tonic-gate 
12800Sstevel@tonic-gate 	while (scanp->ps_scan_flags & (PM_SCANNING | PM_SCAN_DISPATCHED)) {
12810Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
12820Sstevel@tonic-gate 		delay(1);
12830Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
12840Sstevel@tonic-gate 	}
12850Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
12860Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
12870Sstevel@tonic-gate }
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate int
12900Sstevel@tonic-gate pm_scan_stop_walk(dev_info_t *dip, void *arg)
12910Sstevel@tonic-gate {
12920Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate 	if (!PM_GET_PM_SCAN(dip))
12950Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
12960Sstevel@tonic-gate 	ASSERT(!PM_ISBC(dip));
12970Sstevel@tonic-gate 	pm_scan_stop(dip);
12980Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
12990Sstevel@tonic-gate }
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate /*
13020Sstevel@tonic-gate  * Converts a power level value to its index
13030Sstevel@tonic-gate  */
13040Sstevel@tonic-gate static int
13050Sstevel@tonic-gate power_val_to_index(pm_component_t *cp, int val)
13060Sstevel@tonic-gate {
13070Sstevel@tonic-gate 	int limit, i, *ip;
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 	ASSERT(val != PM_LEVEL_UPONLY && val != PM_LEVEL_DOWNONLY &&
13100Sstevel@tonic-gate 	    val != PM_LEVEL_EXACT);
13110Sstevel@tonic-gate 	/*  convert power value into index (i) */
13120Sstevel@tonic-gate 	limit = cp->pmc_comp.pmc_numlevels;
13130Sstevel@tonic-gate 	ip = cp->pmc_comp.pmc_lvals;
13140Sstevel@tonic-gate 	for (i = 0; i < limit; i++)
13150Sstevel@tonic-gate 		if (val == *ip++)
13160Sstevel@tonic-gate 			return (i);
13170Sstevel@tonic-gate 	return (-1);
13180Sstevel@tonic-gate }
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate /*
13210Sstevel@tonic-gate  * Converts a numeric power level to a printable string
13220Sstevel@tonic-gate  */
13230Sstevel@tonic-gate static char *
13240Sstevel@tonic-gate power_val_to_string(pm_component_t *cp, int val)
13250Sstevel@tonic-gate {
13260Sstevel@tonic-gate 	int index;
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate 	if (val == PM_LEVEL_UPONLY)
13290Sstevel@tonic-gate 		return ("<UPONLY>");
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 	if (val == PM_LEVEL_UNKNOWN ||
13320Sstevel@tonic-gate 	    (index = power_val_to_index(cp, val)) == -1)
13330Sstevel@tonic-gate 		return ("<LEVEL_UNKNOWN>");
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 	return (cp->pmc_comp.pmc_lnames[index]);
13360Sstevel@tonic-gate }
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate /*
13390Sstevel@tonic-gate  * Return true if this node has been claimed by a ppm.
13400Sstevel@tonic-gate  */
13410Sstevel@tonic-gate static int
13420Sstevel@tonic-gate pm_ppm_claimed(dev_info_t *dip)
13430Sstevel@tonic-gate {
13440Sstevel@tonic-gate 	return (PPM(dip) != NULL);
13450Sstevel@tonic-gate }
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate /*
13480Sstevel@tonic-gate  * A node which was voluntarily power managed has just used up its "free cycle"
13490Sstevel@tonic-gate  * and need is volpmd field cleared, and the same done to all its descendents
13500Sstevel@tonic-gate  */
13510Sstevel@tonic-gate static void
13520Sstevel@tonic-gate pm_clear_volpm_dip(dev_info_t *dip)
13530Sstevel@tonic-gate {
13540Sstevel@tonic-gate 	PMD_FUNC(pmf, "clear_volpm_dip")
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 	if (dip == NULL)
13570Sstevel@tonic-gate 		return;
13580Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: clear volpm from %s@%s(%s#%d)\n", pmf,
13590Sstevel@tonic-gate 	    PM_DEVICE(dip)))
13600Sstevel@tonic-gate 	DEVI(dip)->devi_pm_volpmd = 0;
13610Sstevel@tonic-gate 	for (dip = ddi_get_child(dip); dip; dip = ddi_get_next_sibling(dip)) {
13620Sstevel@tonic-gate 		pm_clear_volpm_dip(dip);
13630Sstevel@tonic-gate 	}
13640Sstevel@tonic-gate }
13650Sstevel@tonic-gate 
13660Sstevel@tonic-gate /*
13670Sstevel@tonic-gate  * A node which was voluntarily power managed has used up the "free cycles"
13680Sstevel@tonic-gate  * for the subtree that it is the root of.  Scan through the list of detached
13690Sstevel@tonic-gate  * nodes and adjust the counts of any that are descendents of the node.
13700Sstevel@tonic-gate  */
13710Sstevel@tonic-gate static void
13720Sstevel@tonic-gate pm_clear_volpm_list(dev_info_t *dip)
13730Sstevel@tonic-gate {
13740Sstevel@tonic-gate 	PMD_FUNC(pmf, "clear_volpm_list")
13750Sstevel@tonic-gate 	char	*pathbuf;
13760Sstevel@tonic-gate 	size_t	len;
13770Sstevel@tonic-gate 	pm_noinvol_t *ip;
13780Sstevel@tonic-gate 
13790Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
13800Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
13810Sstevel@tonic-gate 	len = strlen(pathbuf);
13820Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: clear volpm list %s\n", pmf, pathbuf))
13830Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
13840Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
13850Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: clear volpm: ni_path %s\n", pmf,
13860Sstevel@tonic-gate 		    ip->ni_path))
13870Sstevel@tonic-gate 		if (strncmp(pathbuf, ip->ni_path, len) == 0 &&
13880Sstevel@tonic-gate 		    ip->ni_path[len] == '/') {
13890Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: clear volpm: %s\n", pmf,
13900Sstevel@tonic-gate 			    ip->ni_path))
13910Sstevel@tonic-gate 			ip->ni_volpmd = 0;
13920Sstevel@tonic-gate 			ip->ni_wasvolpmd = 0;
13930Sstevel@tonic-gate 		}
13940Sstevel@tonic-gate 	}
13950Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
13960Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
13970Sstevel@tonic-gate }
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate /*
14000Sstevel@tonic-gate  * Powers a device, suspending or resuming the driver if it is a backward
14010Sstevel@tonic-gate  * compatible device, calling into ppm to change power level.
14020Sstevel@tonic-gate  * Called with the component's power lock held.
14030Sstevel@tonic-gate  */
14040Sstevel@tonic-gate static int
14050Sstevel@tonic-gate power_dev(dev_info_t *dip, int comp, int level, int old_level,
14060Sstevel@tonic-gate     pm_canblock_t canblock, pm_ppm_devlist_t **devlist)
14070Sstevel@tonic-gate {
14080Sstevel@tonic-gate 	PMD_FUNC(pmf, "power_dev")
14090Sstevel@tonic-gate 	power_req_t power_req;
14100Sstevel@tonic-gate 	int		power_op_ret;	/* DDI_SUCCESS or DDI_FAILURE */
14110Sstevel@tonic-gate 	int		resume_needed = 0;
14120Sstevel@tonic-gate 	int		suspended = 0;
14130Sstevel@tonic-gate 	int		result;
14140Sstevel@tonic-gate 	struct pm_component *cp = PM_CP(dip, comp);
14150Sstevel@tonic-gate 	int		bc = PM_ISBC(dip);
14160Sstevel@tonic-gate 	int pm_all_components_off(dev_info_t *);
14170Sstevel@tonic-gate 	int		clearvolpmd = 0;
14180Sstevel@tonic-gate 	char		pathbuf[MAXNAMELEN];
14190Sstevel@tonic-gate #ifdef DEBUG
14200Sstevel@tonic-gate 	char *ppmname, *ppmaddr;
14210Sstevel@tonic-gate #endif
14220Sstevel@tonic-gate 	/*
14230Sstevel@tonic-gate 	 * If this is comp 0 of a backwards compat device and we are
14240Sstevel@tonic-gate 	 * going to take the power away, we need to detach it with
14250Sstevel@tonic-gate 	 * DDI_PM_SUSPEND command.
14260Sstevel@tonic-gate 	 */
14270Sstevel@tonic-gate 	if (bc && comp == 0 && POWERING_OFF(old_level, level)) {
14280Sstevel@tonic-gate 		if (devi_detach(dip, DDI_PM_SUSPEND) != DDI_SUCCESS) {
14290Sstevel@tonic-gate 			/* We could not suspend before turning cmpt zero off */
14300Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: could not suspend %s@%s(%s#%d)\n",
14310Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
14320Sstevel@tonic-gate 			return (DDI_FAILURE);
14330Sstevel@tonic-gate 		} else {
14340Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= PMC_SUSPENDED;
14350Sstevel@tonic-gate 			suspended++;
14360Sstevel@tonic-gate 		}
14370Sstevel@tonic-gate 	}
14380Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_SET_POWER;
14390Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.who = dip;
14400Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.cmpt = comp;
14410Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.old_level = old_level;
14420Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.new_level = level;
14430Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.canblock = canblock;
14440Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.cookie = NULL;
14450Sstevel@tonic-gate #ifdef DEBUG
14460Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {
14470Sstevel@tonic-gate 		ppmname = PM_NAME(PPM(dip));
14480Sstevel@tonic-gate 		ppmaddr = PM_ADDR(PPM(dip));
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 	} else {
14510Sstevel@tonic-gate 		ppmname = "noppm";
14520Sstevel@tonic-gate 		ppmaddr = "0";
14530Sstevel@tonic-gate 	}
14540Sstevel@tonic-gate 	PMD(PMD_PPM, ("%s: %s@%s(%s#%d):%s[%d] %s (%d) -> %s (%d) via %s@%s\n",
14550Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), cp->pmc_comp.pmc_name, comp,
14560Sstevel@tonic-gate 	    power_val_to_string(cp, old_level), old_level,
14570Sstevel@tonic-gate 	    power_val_to_string(cp, level), level, ppmname, ppmaddr))
14580Sstevel@tonic-gate #endif
14590Sstevel@tonic-gate 	/*
14600Sstevel@tonic-gate 	 * If non-bc noinvolpm device is turning first comp on, or noinvolpm
14610Sstevel@tonic-gate 	 * bc device comp 0 is powering on, then we count it as a power cycle
14620Sstevel@tonic-gate 	 * against its voluntary count.
14630Sstevel@tonic-gate 	 */
14640Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_volpmd &&
14650Sstevel@tonic-gate 	    (!bc && pm_all_components_off(dip) && level != 0) ||
14660Sstevel@tonic-gate 	    (bc && comp == 0 && POWERING_ON(old_level, level)))
14670Sstevel@tonic-gate 		clearvolpmd = 1;
14680Sstevel@tonic-gate 	if ((power_op_ret = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
14690Sstevel@tonic-gate 	    &power_req, &result)) == DDI_SUCCESS) {
14700Sstevel@tonic-gate 		/*
14710Sstevel@tonic-gate 		 * Now do involuntary pm accounting;  If we've just cycled power
14720Sstevel@tonic-gate 		 * on a voluntarily pm'd node, and by inference on its entire
14730Sstevel@tonic-gate 		 * subtree, we need to set the subtree (including those nodes
14740Sstevel@tonic-gate 		 * already detached) volpmd counts to 0, and subtract out the
14750Sstevel@tonic-gate 		 * value of the current node's volpmd count from the ancestors
14760Sstevel@tonic-gate 		 */
14770Sstevel@tonic-gate 		if (clearvolpmd) {
14780Sstevel@tonic-gate 			int volpmd = DEVI(dip)->devi_pm_volpmd;
14790Sstevel@tonic-gate 			pm_clear_volpm_dip(dip);
14800Sstevel@tonic-gate 			pm_clear_volpm_list(dip);
14810Sstevel@tonic-gate 			if (volpmd) {
14820Sstevel@tonic-gate 				(void) ddi_pathname(dip, pathbuf);
14830Sstevel@tonic-gate 				(void) pm_noinvol_update(PM_BP_NOINVOL_POWER,
14840Sstevel@tonic-gate 				    volpmd, 0, pathbuf, dip);
14850Sstevel@tonic-gate 			}
14860Sstevel@tonic-gate 		}
14870Sstevel@tonic-gate 	} else {
14880Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: can't set comp %d (%s) of %s@%s(%s#%d) "
14890Sstevel@tonic-gate 		    "to level %d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name,
14900Sstevel@tonic-gate 		    PM_DEVICE(dip), level, power_val_to_string(cp, level)))
14910Sstevel@tonic-gate 	}
14920Sstevel@tonic-gate 	/*
14930Sstevel@tonic-gate 	 * If some other devices were also powered up (e.g. other cpus in
14940Sstevel@tonic-gate 	 * the same domain) return a pointer to that list
14950Sstevel@tonic-gate 	 */
14960Sstevel@tonic-gate 	if (devlist) {
14970Sstevel@tonic-gate 		*devlist = (pm_ppm_devlist_t *)
14980Sstevel@tonic-gate 		    power_req.req.ppm_set_power_req.cookie;
14990Sstevel@tonic-gate 	}
15000Sstevel@tonic-gate 	/*
15010Sstevel@tonic-gate 	 * We will have to resume the device if the device is backwards compat
15020Sstevel@tonic-gate 	 * device and either of the following is true:
15030Sstevel@tonic-gate 	 * -This is comp 0 and we have successfully powered it up
15040Sstevel@tonic-gate 	 * -This is comp 0 and we have failed to power it down. Resume is
15050Sstevel@tonic-gate 	 *  needed because we have suspended it above
15060Sstevel@tonic-gate 	 */
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 	if (bc && comp == 0) {
15090Sstevel@tonic-gate 		ASSERT(PM_ISDIRECT(dip) || DEVI_IS_DETACHING(dip));
15100Sstevel@tonic-gate 		if (power_op_ret == DDI_SUCCESS) {
15110Sstevel@tonic-gate 			if (POWERING_ON(old_level, level)) {
15120Sstevel@tonic-gate 				/*
15130Sstevel@tonic-gate 				 * It must be either suspended or resumed
15140Sstevel@tonic-gate 				 * via pm_power_has_changed path
15150Sstevel@tonic-gate 				 */
15160Sstevel@tonic-gate 				ASSERT((DEVI(dip)->devi_pm_flags &
15170Sstevel@tonic-gate 				    PMC_SUSPENDED) ||
15180Sstevel@tonic-gate 				    (PM_CP(dip, comp)->pmc_flags &
15190Sstevel@tonic-gate 				    PM_PHC_WHILE_SET_POWER));
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate 					resume_needed = suspended;
15220Sstevel@tonic-gate 			}
15230Sstevel@tonic-gate 		} else {
15240Sstevel@tonic-gate 			if (POWERING_OFF(old_level, level)) {
15250Sstevel@tonic-gate 				/*
15260Sstevel@tonic-gate 				 * It must be either suspended or resumed
15270Sstevel@tonic-gate 				 * via pm_power_has_changed path
15280Sstevel@tonic-gate 				 */
15290Sstevel@tonic-gate 				ASSERT((DEVI(dip)->devi_pm_flags &
15300Sstevel@tonic-gate 				    PMC_SUSPENDED) ||
15310Sstevel@tonic-gate 				    (PM_CP(dip, comp)->pmc_flags &
15320Sstevel@tonic-gate 				    PM_PHC_WHILE_SET_POWER));
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate 					resume_needed = suspended;
15350Sstevel@tonic-gate 			}
15360Sstevel@tonic-gate 		}
15370Sstevel@tonic-gate 	}
15380Sstevel@tonic-gate 	if (resume_needed) {
15390Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED);
15400Sstevel@tonic-gate 		/* ppm is not interested in DDI_PM_RESUME */
15410Sstevel@tonic-gate 		if ((power_op_ret = devi_attach(dip, DDI_PM_RESUME)) ==
15420Sstevel@tonic-gate 		    DDI_SUCCESS) {
15430Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED;
15440Sstevel@tonic-gate 		} else
15450Sstevel@tonic-gate 			cmn_err(CE_WARN, "!pm: Can't resume %s@%s(%s#%d)",
15460Sstevel@tonic-gate 			    PM_DEVICE(dip));
15470Sstevel@tonic-gate 	}
15480Sstevel@tonic-gate 	return (power_op_ret);
15490Sstevel@tonic-gate }
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate /*
15520Sstevel@tonic-gate  * Return true if we are the owner or a borrower of the devi lock.  See
15530Sstevel@tonic-gate  * pm_lock_power_single() about borrowing the lock.
15540Sstevel@tonic-gate  */
15550Sstevel@tonic-gate static int
15560Sstevel@tonic-gate pm_devi_lock_held(dev_info_t *dip)
15570Sstevel@tonic-gate {
15580Sstevel@tonic-gate 	lock_loan_t *cur;
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	if (DEVI_BUSY_OWNED(dip))
15610Sstevel@tonic-gate 	    return (1);
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 	/* return false if no locks borrowed */
15640Sstevel@tonic-gate 	if (lock_loan_head.pmlk_next == NULL)
15650Sstevel@tonic-gate 		return (0);
15660Sstevel@tonic-gate 
15670Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
15680Sstevel@tonic-gate 	/* see if our thread is registered as a lock borrower. */
15690Sstevel@tonic-gate 	for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
15700Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
15710Sstevel@tonic-gate 			break;
15720Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
15730Sstevel@tonic-gate 
15740Sstevel@tonic-gate 	return (cur != NULL && cur->pmlk_lender == DEVI(dip)->devi_busy_thread);
15750Sstevel@tonic-gate }
15760Sstevel@tonic-gate 
15770Sstevel@tonic-gate /*
15780Sstevel@tonic-gate  * pm_set_power: adjusts power level of device.	 Assumes device is power
15790Sstevel@tonic-gate  * manageable & component exists.
15800Sstevel@tonic-gate  *
15810Sstevel@tonic-gate  * Cases which require us to bring up devices we keep up ("wekeepups") for
15820Sstevel@tonic-gate  * backwards compatible devices:
15830Sstevel@tonic-gate  *	component 0 is off and we're bringing it up from 0
15840Sstevel@tonic-gate  *		bring up wekeepup first
15850Sstevel@tonic-gate  *	and recursively when component 0 is off and we bring some other
15860Sstevel@tonic-gate  *	component up from 0
15870Sstevel@tonic-gate  * For devices which are not backward compatible, our dependency notion is much
15880Sstevel@tonic-gate  * simpler.  Unless all components are off, then wekeeps must be on.
15890Sstevel@tonic-gate  * We don't treat component 0 differently.
15900Sstevel@tonic-gate  * Canblock tells how to deal with a direct pm'd device.
15910Sstevel@tonic-gate  * Scan arg tells us if we were called from scan, in which case we don't need
15920Sstevel@tonic-gate  * to go back to the root node and walk down to change power.
15930Sstevel@tonic-gate  */
15940Sstevel@tonic-gate int
15950Sstevel@tonic-gate pm_set_power(dev_info_t *dip, int comp, int level, int direction,
15960Sstevel@tonic-gate     pm_canblock_t canblock, int scan, int *retp)
15970Sstevel@tonic-gate {
15980Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_power")
15990Sstevel@tonic-gate 	char		*pathbuf;
16000Sstevel@tonic-gate 	pm_bp_child_pwrchg_t bpc;
16010Sstevel@tonic-gate 	pm_sp_misc_t	pspm;
16020Sstevel@tonic-gate 	int		ret = DDI_SUCCESS;
16030Sstevel@tonic-gate 	int		unused = DDI_SUCCESS;
16040Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
16050Sstevel@tonic-gate 
16060Sstevel@tonic-gate #ifdef DEBUG
16070Sstevel@tonic-gate 	int		diverted = 0;
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	/*
16100Sstevel@tonic-gate 	 * This prevents operations on the console from calling prom_printf and
16110Sstevel@tonic-gate 	 * either deadlocking or bringing up the console because of debug
16120Sstevel@tonic-gate 	 * output
16130Sstevel@tonic-gate 	 */
16140Sstevel@tonic-gate 	if (dip == cfb_dip) {
16150Sstevel@tonic-gate 		diverted++;
16160Sstevel@tonic-gate 		mutex_enter(&pm_debug_lock);
16170Sstevel@tonic-gate 		pm_divertdebug++;
16180Sstevel@tonic-gate 		mutex_exit(&pm_debug_lock);
16190Sstevel@tonic-gate 	}
16200Sstevel@tonic-gate #endif
16210Sstevel@tonic-gate 	ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY ||
16220Sstevel@tonic-gate 	    direction == PM_LEVEL_EXACT);
16230Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d, dir=%s, new=%d\n",
16240Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), comp, pm_decode_direction(direction), level))
16250Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
16260Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
16270Sstevel@tonic-gate 	bpc.bpc_dip = dip;
16280Sstevel@tonic-gate 	bpc.bpc_path = pathbuf;
16290Sstevel@tonic-gate 	bpc.bpc_comp = comp;
16300Sstevel@tonic-gate 	bpc.bpc_olevel = PM_CURPOWER(dip, comp);
16310Sstevel@tonic-gate 	bpc.bpc_nlevel = level;
16320Sstevel@tonic-gate 	pspm.pspm_direction = direction;
16330Sstevel@tonic-gate 	pspm.pspm_errnop = retp;
16340Sstevel@tonic-gate 	pspm.pspm_canblock = canblock;
16350Sstevel@tonic-gate 	pspm.pspm_scan = scan;
16360Sstevel@tonic-gate 	bpc.bpc_private = &pspm;
16370Sstevel@tonic-gate 
16380Sstevel@tonic-gate 	/*
16390Sstevel@tonic-gate 	 * If a config operation is being done (we've locked the parent) or
16400Sstevel@tonic-gate 	 * we already hold the power lock (we've locked the node)
16410Sstevel@tonic-gate 	 * then we can operate directly on the node because we have already
16420Sstevel@tonic-gate 	 * brought up all the ancestors, otherwise, we have to go back to the
16430Sstevel@tonic-gate 	 * top of the tree.
16440Sstevel@tonic-gate 	 */
16450Sstevel@tonic-gate 	if (pm_devi_lock_held(pdip) || pm_devi_lock_held(dip))
16460Sstevel@tonic-gate 		ret = pm_busop_set_power(dip, NULL, BUS_POWER_CHILD_PWRCHG,
16470Sstevel@tonic-gate 		    (void *)&bpc, (void *)&unused);
16480Sstevel@tonic-gate 	else
16490Sstevel@tonic-gate 		ret = pm_busop_bus_power(ddi_root_node(), NULL,
16500Sstevel@tonic-gate 		    BUS_POWER_CHILD_PWRCHG, (void *)&bpc, (void *)&unused);
16510Sstevel@tonic-gate #ifdef DEBUG
16520Sstevel@tonic-gate 	if (ret != DDI_SUCCESS || *retp != DDI_SUCCESS) {
16530Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) can't change power, ret=%d, "
16540Sstevel@tonic-gate 		    "errno=%d\n", pmf, PM_DEVICE(dip), ret, *retp))
16550Sstevel@tonic-gate 	}
16560Sstevel@tonic-gate 	if (diverted) {
16570Sstevel@tonic-gate 		mutex_enter(&pm_debug_lock);
16580Sstevel@tonic-gate 		pm_divertdebug--;
16590Sstevel@tonic-gate 		mutex_exit(&pm_debug_lock);
16600Sstevel@tonic-gate 	}
16610Sstevel@tonic-gate #endif
16620Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
16630Sstevel@tonic-gate 	return (ret);
16640Sstevel@tonic-gate }
16650Sstevel@tonic-gate 
16660Sstevel@tonic-gate 
16670Sstevel@tonic-gate static dev_info_t *
16680Sstevel@tonic-gate find_dip(dev_info_t *dip, char *dev_name, int holddip)
16690Sstevel@tonic-gate {
16700Sstevel@tonic-gate 	PMD_FUNC(pmf, "find_dip")
16710Sstevel@tonic-gate 	dev_info_t	*cdip;
16720Sstevel@tonic-gate 	char		*child_dev, *addr;
16730Sstevel@tonic-gate 	char		*device;	/* writeable copy of path */
16740Sstevel@tonic-gate 	int		dev_len = strlen(dev_name)+1;
16750Sstevel@tonic-gate 	int		circ;
16760Sstevel@tonic-gate 
16770Sstevel@tonic-gate 	device = kmem_zalloc(dev_len, KM_SLEEP);
16780Sstevel@tonic-gate 	(void) strcpy(device, dev_name);
16790Sstevel@tonic-gate 	addr = strchr(device, '@');
16800Sstevel@tonic-gate 	child_dev = strchr(device, '/');
16810Sstevel@tonic-gate 	if ((addr != NULL) && (child_dev == NULL || addr < child_dev)) {
16820Sstevel@tonic-gate 		/*
16830Sstevel@tonic-gate 		 * We have device = "name@addr..." form
16840Sstevel@tonic-gate 		 */
16850Sstevel@tonic-gate 		*addr++ = '\0';			/* for strcmp (and skip '@') */
16860Sstevel@tonic-gate 		if (child_dev != NULL)
16870Sstevel@tonic-gate 			*child_dev++ = '\0';	/* for strcmp (and skip '/') */
16880Sstevel@tonic-gate 	} else {
16890Sstevel@tonic-gate 		/*
16900Sstevel@tonic-gate 		 * We have device = "name/..." or "name"
16910Sstevel@tonic-gate 		 */
16920Sstevel@tonic-gate 		addr = "";
16930Sstevel@tonic-gate 		if (child_dev != NULL)
16940Sstevel@tonic-gate 			*child_dev++ = '\0';	/* for strcmp (and skip '/') */
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
16970Sstevel@tonic-gate 		if (strcmp(ddi_node_name(dip), device) == 0) {
16980Sstevel@tonic-gate 			/* If the driver isn't loaded, we prune the search */
16991333Scth 			if (!i_ddi_devi_attached(dip)) {
17000Sstevel@tonic-gate 				continue;
17010Sstevel@tonic-gate 			}
17020Sstevel@tonic-gate 			if (strcmp(ddi_get_name_addr(dip), addr) == 0) {
17030Sstevel@tonic-gate 				PMD(PMD_NAMETODIP, ("%s: matched %s@%s"
17040Sstevel@tonic-gate 				    "(%s#%d)\n", pmf, PM_DEVICE(dip)))
17050Sstevel@tonic-gate 				if (child_dev != NULL) {
17060Sstevel@tonic-gate 					PMD(PMD_NAMETODIP, ("%s: %s@%s(%s#%d): "
17070Sstevel@tonic-gate 					    "held, call find_dip %s\n", pmf,
17080Sstevel@tonic-gate 					    PM_DEVICE(dip), child_dev))
17090Sstevel@tonic-gate 					ndi_devi_enter(dip, &circ);
17100Sstevel@tonic-gate 					cdip = dip;
17110Sstevel@tonic-gate 					dip = find_dip(ddi_get_child(dip),
17120Sstevel@tonic-gate 					    child_dev, holddip);
17130Sstevel@tonic-gate 					ndi_devi_exit(cdip, circ);
17140Sstevel@tonic-gate 					PMD(PMD_NAMETODIP, ("%s: %s@%s(%s#%d): "
17150Sstevel@tonic-gate 					    "release, find_dip rets %s\n", pmf,
17160Sstevel@tonic-gate 					    PM_DEVICE(cdip), child_dev))
17170Sstevel@tonic-gate 				} else {
17180Sstevel@tonic-gate 					if (holddip) {
17190Sstevel@tonic-gate 						e_ddi_hold_devi(dip);
17200Sstevel@tonic-gate 						PMD(PMD_DHR | PMD_NAMETODIP,
17210Sstevel@tonic-gate 						    ("%s: held %s@%s(%s#%d), "
17220Sstevel@tonic-gate 						    "refcnt=%d\n", pmf,
17230Sstevel@tonic-gate 						    PM_DEVICE(dip),
17240Sstevel@tonic-gate 						    e_ddi_devi_holdcnt(dip)))
17250Sstevel@tonic-gate 					}
17260Sstevel@tonic-gate 				}
17270Sstevel@tonic-gate 				kmem_free(device, dev_len);
17280Sstevel@tonic-gate 				return (dip);
17290Sstevel@tonic-gate 			}
17300Sstevel@tonic-gate 		}
17310Sstevel@tonic-gate 	}
17320Sstevel@tonic-gate 	kmem_free(device, dev_len);
17330Sstevel@tonic-gate 	return (dip);
17340Sstevel@tonic-gate }
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate /*
17370Sstevel@tonic-gate  * If holddip is set, then if a dip is found we return with the node held
17380Sstevel@tonic-gate  */
17390Sstevel@tonic-gate dev_info_t *
17400Sstevel@tonic-gate pm_name_to_dip(char *pathname, int holddip)
17410Sstevel@tonic-gate {
17420Sstevel@tonic-gate 	PMD_FUNC(pmf, "name_to_dip")
17430Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
17440Sstevel@tonic-gate 	char		dev_name[MAXNAMELEN];
17450Sstevel@tonic-gate 	dev_info_t	*first_child;
17460Sstevel@tonic-gate 	int		circular;
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate 	if (!pathname)
17490Sstevel@tonic-gate 		return (NULL);
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 	(void) strncpy(dev_name, pathname, MAXNAMELEN);
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 	PMD(PMD_NAMETODIP, ("%s: devname: %s\n", pmf, dev_name))
17540Sstevel@tonic-gate 
17550Sstevel@tonic-gate 	/*
17560Sstevel@tonic-gate 	 * First we attempt to match the node in the tree.  If we succeed
17570Sstevel@tonic-gate 	 * we hold the driver and look up the dip again.
17580Sstevel@tonic-gate 	 * No need to hold the root as that node is always held.
17590Sstevel@tonic-gate 	 */
17600Sstevel@tonic-gate 	if (dev_name[0] == '/') {
17610Sstevel@tonic-gate 		ndi_devi_enter(ddi_root_node(), &circular);
17620Sstevel@tonic-gate 		first_child = ddi_get_child(ddi_root_node());
17630Sstevel@tonic-gate 		dip = find_dip(first_child, dev_name + 1, holddip);
17640Sstevel@tonic-gate 		ndi_devi_exit(ddi_root_node(), circular);
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 	} else {
17670Sstevel@tonic-gate 		PMD(PMD_NAMETODIP, ("%s: physpath with unrooted "
17680Sstevel@tonic-gate 		    "search\n", pmf))
17690Sstevel@tonic-gate 		return (NULL);
17700Sstevel@tonic-gate 	}
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate 	ASSERT(!dip ||
17730Sstevel@tonic-gate 	    (ddi_name_to_major(ddi_binding_name(dip)) != (major_t)-1));
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	return (dip);
17760Sstevel@tonic-gate }
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate /*
17790Sstevel@tonic-gate  * Search for a dependency and mark it unsatisfied
17800Sstevel@tonic-gate  */
17810Sstevel@tonic-gate static void
17820Sstevel@tonic-gate pm_unsatisfy(char *keeper, char *kept)
17830Sstevel@tonic-gate {
17840Sstevel@tonic-gate 	PMD_FUNC(pmf, "unsatisfy")
17850Sstevel@tonic-gate 	pm_pdr_t *dp;
17860Sstevel@tonic-gate 
17870Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, keeper, kept))
17880Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
17890Sstevel@tonic-gate 		if (!dp->pdr_isprop) {
17900Sstevel@tonic-gate 			if (strcmp(dp->pdr_keeper, keeper) == 0 &&
17910Sstevel@tonic-gate 			    (dp->pdr_kept_count > 0) &&
17920Sstevel@tonic-gate 			    strcmp(dp->pdr_kept_paths[0], kept) == 0) {
17930Sstevel@tonic-gate 				if (dp->pdr_satisfied) {
17940Sstevel@tonic-gate 					dp->pdr_satisfied = 0;
17950Sstevel@tonic-gate 					pm_unresolved_deps++;
17960Sstevel@tonic-gate 					PMD(PMD_KEEPS, ("%s: clear satisfied, "
17970Sstevel@tonic-gate 					    "pm_unresolved_deps now %d\n", pmf,
17980Sstevel@tonic-gate 					    pm_unresolved_deps))
17990Sstevel@tonic-gate 				}
18000Sstevel@tonic-gate 			}
18010Sstevel@tonic-gate 		}
18020Sstevel@tonic-gate 	}
18030Sstevel@tonic-gate }
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate /*
18060Sstevel@tonic-gate  * Device dip is being un power managed, it keeps up count other devices.
18070Sstevel@tonic-gate  * We need to release any hold we have on the kept devices, and also
18080Sstevel@tonic-gate  * mark the dependency no longer satisfied.
18090Sstevel@tonic-gate  */
18100Sstevel@tonic-gate static void
18110Sstevel@tonic-gate pm_unkeeps(int count, char *keeper, char **keptpaths, int pwr)
18120Sstevel@tonic-gate {
18130Sstevel@tonic-gate 	PMD_FUNC(pmf, "unkeeps")
18140Sstevel@tonic-gate 	int i, j;
18150Sstevel@tonic-gate 	dev_info_t *kept;
18160Sstevel@tonic-gate 	dev_info_t *dip;
18170Sstevel@tonic-gate 	struct pm_component *cp;
18180Sstevel@tonic-gate 	int keeper_on = 0, circ;
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: count=%d, keeper=%s, keptpaths=%p\n", pmf, count,
18210Sstevel@tonic-gate 	    keeper, (void *)keptpaths))
18220Sstevel@tonic-gate 	/*
18230Sstevel@tonic-gate 	 * Try to grab keeper. Keeper may have gone away by now,
18240Sstevel@tonic-gate 	 * in this case, used the passed in value pwr
18250Sstevel@tonic-gate 	 */
18260Sstevel@tonic-gate 	dip = pm_name_to_dip(keeper, 1);
18270Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
18280Sstevel@tonic-gate 		/* Release power hold */
18290Sstevel@tonic-gate 		kept = pm_name_to_dip(keptpaths[i], 1);
18300Sstevel@tonic-gate 		if (kept) {
18310Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)[%d]\n", pmf,
18320Sstevel@tonic-gate 			    PM_DEVICE(kept), i))
18330Sstevel@tonic-gate 			/*
18340Sstevel@tonic-gate 			 * We need to check if we skipped a bringup here
18350Sstevel@tonic-gate 			 * because we could have failed the bringup
18360Sstevel@tonic-gate 			 * (ie DIRECT PM device) and have
18370Sstevel@tonic-gate 			 * not increment the count.
18380Sstevel@tonic-gate 			 */
18390Sstevel@tonic-gate 			if ((dip != NULL) && (PM_GET_PM_INFO(dip) != NULL)) {
18400Sstevel@tonic-gate 				keeper_on = 0;
18410Sstevel@tonic-gate 				PM_LOCK_POWER(dip, &circ);
18420Sstevel@tonic-gate 				for (j = 0; j < PM_NUMCMPTS(dip); j++) {
18430Sstevel@tonic-gate 				    cp = &DEVI(dip)->devi_pm_components[j];
18440Sstevel@tonic-gate 					if (cur_power(cp)) {
18450Sstevel@tonic-gate 						keeper_on++;
18460Sstevel@tonic-gate 						break;
18470Sstevel@tonic-gate 					}
18480Sstevel@tonic-gate 				}
18490Sstevel@tonic-gate 				if (keeper_on && (PM_SKBU(kept) == 0)) {
18500Sstevel@tonic-gate 					pm_rele_power(kept);
18510Sstevel@tonic-gate 					DEVI(kept)->devi_pm_flags
18520Sstevel@tonic-gate 						&= ~PMC_SKIP_BRINGUP;
18530Sstevel@tonic-gate 				}
18540Sstevel@tonic-gate 				PM_UNLOCK_POWER(dip, circ);
18550Sstevel@tonic-gate 			} else if (pwr) {
18560Sstevel@tonic-gate 				if (PM_SKBU(kept) == 0) {
18570Sstevel@tonic-gate 					pm_rele_power(kept);
18580Sstevel@tonic-gate 					DEVI(kept)->devi_pm_flags
18590Sstevel@tonic-gate 					    &= ~PMC_SKIP_BRINGUP;
18600Sstevel@tonic-gate 				}
18610Sstevel@tonic-gate 			}
18620Sstevel@tonic-gate 			ddi_release_devi(kept);
18630Sstevel@tonic-gate 		}
18640Sstevel@tonic-gate 		/*
18650Sstevel@tonic-gate 		 * mark this dependency not satisfied
18660Sstevel@tonic-gate 		 */
18670Sstevel@tonic-gate 		pm_unsatisfy(keeper, keptpaths[i]);
18680Sstevel@tonic-gate 	}
18690Sstevel@tonic-gate 	if (dip)
18700Sstevel@tonic-gate 		ddi_release_devi(dip);
18710Sstevel@tonic-gate }
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate /*
18740Sstevel@tonic-gate  * Device kept is being un power managed, it is kept up by keeper.
18750Sstevel@tonic-gate  * We need to mark the dependency no longer satisfied.
18760Sstevel@tonic-gate  */
18770Sstevel@tonic-gate static void
18780Sstevel@tonic-gate pm_unkepts(char *kept, char *keeper)
18790Sstevel@tonic-gate {
18800Sstevel@tonic-gate 	PMD_FUNC(pmf, "unkepts")
18810Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: kept=%s, keeper=%s\n", pmf, kept, keeper))
18820Sstevel@tonic-gate 	ASSERT(keeper != NULL);
18830Sstevel@tonic-gate 	/*
18840Sstevel@tonic-gate 	 * mark this dependency not satisfied
18850Sstevel@tonic-gate 	 */
18860Sstevel@tonic-gate 	pm_unsatisfy(keeper, kept);
18870Sstevel@tonic-gate }
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate /*
18900Sstevel@tonic-gate  * Removes dependency information and hold on the kepts, if the path is a
18910Sstevel@tonic-gate  * path of a keeper.
18920Sstevel@tonic-gate  */
18930Sstevel@tonic-gate static void
18940Sstevel@tonic-gate pm_free_keeper(char *path, int pwr)
18950Sstevel@tonic-gate {
18960Sstevel@tonic-gate 	pm_pdr_t *dp;
18970Sstevel@tonic-gate 	int i;
18980Sstevel@tonic-gate 	size_t length;
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
19010Sstevel@tonic-gate 		if (strcmp(dp->pdr_keeper, path) != 0)
19020Sstevel@tonic-gate 			continue;
19030Sstevel@tonic-gate 		/*
19040Sstevel@tonic-gate 		 * Remove all our kept holds and the dependency records,
19050Sstevel@tonic-gate 		 * then free up the kept lists.
19060Sstevel@tonic-gate 		 */
19070Sstevel@tonic-gate 		pm_unkeeps(dp->pdr_kept_count, path, dp->pdr_kept_paths, pwr);
19080Sstevel@tonic-gate 		if (dp->pdr_kept_count)  {
19090Sstevel@tonic-gate 			for (i = 0; i < dp->pdr_kept_count; i++) {
19100Sstevel@tonic-gate 				length = strlen(dp->pdr_kept_paths[i]);
19110Sstevel@tonic-gate 				kmem_free(dp->pdr_kept_paths[i], length + 1);
19120Sstevel@tonic-gate 			}
19130Sstevel@tonic-gate 			kmem_free(dp->pdr_kept_paths,
19140Sstevel@tonic-gate 			    dp->pdr_kept_count * sizeof (char **));
19150Sstevel@tonic-gate 			dp->pdr_kept_paths = NULL;
19160Sstevel@tonic-gate 			dp->pdr_kept_count = 0;
19170Sstevel@tonic-gate 		}
19180Sstevel@tonic-gate 	}
19190Sstevel@tonic-gate }
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate /*
19220Sstevel@tonic-gate  * Removes the device represented by path from the list of kepts, if the
19230Sstevel@tonic-gate  * path is a path of a kept
19240Sstevel@tonic-gate  */
19250Sstevel@tonic-gate static void
19260Sstevel@tonic-gate pm_free_kept(char *path)
19270Sstevel@tonic-gate {
19280Sstevel@tonic-gate 	pm_pdr_t *dp;
19290Sstevel@tonic-gate 	int i;
19300Sstevel@tonic-gate 	int j, count;
19310Sstevel@tonic-gate 	size_t length;
19320Sstevel@tonic-gate 	char **paths;
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
19350Sstevel@tonic-gate 		if (dp->pdr_kept_count == 0)
19360Sstevel@tonic-gate 			continue;
19370Sstevel@tonic-gate 		count = dp->pdr_kept_count;
19380Sstevel@tonic-gate 		/* Remove this device from the kept path lists */
19390Sstevel@tonic-gate 		for (i = 0; i < count; i++) {
19400Sstevel@tonic-gate 			if (strcmp(dp->pdr_kept_paths[i], path) == 0) {
19410Sstevel@tonic-gate 				pm_unkepts(path, dp->pdr_keeper);
19420Sstevel@tonic-gate 				length = strlen(dp->pdr_kept_paths[i]) + 1;
19430Sstevel@tonic-gate 				kmem_free(dp->pdr_kept_paths[i], length);
19440Sstevel@tonic-gate 				dp->pdr_kept_paths[i] = NULL;
19450Sstevel@tonic-gate 				dp->pdr_kept_count--;
19460Sstevel@tonic-gate 			}
19470Sstevel@tonic-gate 		}
19480Sstevel@tonic-gate 		/* Compact the kept paths array */
19490Sstevel@tonic-gate 		if (dp->pdr_kept_count) {
19500Sstevel@tonic-gate 			length = dp->pdr_kept_count * sizeof (char **);
19510Sstevel@tonic-gate 			paths = kmem_zalloc(length, KM_SLEEP);
19520Sstevel@tonic-gate 			j = 0;
19530Sstevel@tonic-gate 			for (i = 0; i < count; i++) {
19540Sstevel@tonic-gate 				if (dp->pdr_kept_paths[i] != NULL) {
19550Sstevel@tonic-gate 					paths[j] = dp->pdr_kept_paths[i];
19560Sstevel@tonic-gate 					j++;
19570Sstevel@tonic-gate 				}
19580Sstevel@tonic-gate 			}
19590Sstevel@tonic-gate 			ASSERT(j == dp->pdr_kept_count);
19600Sstevel@tonic-gate 		}
19610Sstevel@tonic-gate 		/* Now free the old array and point to the new one */
19620Sstevel@tonic-gate 		kmem_free(dp->pdr_kept_paths, count * sizeof (char **));
19630Sstevel@tonic-gate 		if (dp->pdr_kept_count)
19640Sstevel@tonic-gate 			dp->pdr_kept_paths = paths;
19650Sstevel@tonic-gate 		else
19660Sstevel@tonic-gate 			dp->pdr_kept_paths = NULL;
19670Sstevel@tonic-gate 	}
19680Sstevel@tonic-gate }
19690Sstevel@tonic-gate 
19700Sstevel@tonic-gate /*
19710Sstevel@tonic-gate  * Free the dependency information for a device.
19720Sstevel@tonic-gate  */
19730Sstevel@tonic-gate void
19740Sstevel@tonic-gate pm_free_keeps(char *path, int pwr)
19750Sstevel@tonic-gate {
19760Sstevel@tonic-gate 	PMD_FUNC(pmf, "free_keeps")
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate #ifdef DEBUG
19790Sstevel@tonic-gate 	int doprdeps = 0;
19800Sstevel@tonic-gate 	void prdeps(char *);
19810Sstevel@tonic-gate 
19820Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s\n", pmf, path))
19830Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS) {
19840Sstevel@tonic-gate 		doprdeps = 1;
19850Sstevel@tonic-gate 		prdeps("pm_free_keeps before");
19860Sstevel@tonic-gate 	}
19870Sstevel@tonic-gate #endif
19880Sstevel@tonic-gate 	/*
19890Sstevel@tonic-gate 	 * First assume we are a keeper and remove all our kepts.
19900Sstevel@tonic-gate 	 */
19910Sstevel@tonic-gate 	pm_free_keeper(path, pwr);
19920Sstevel@tonic-gate 	/*
19930Sstevel@tonic-gate 	 * Now assume we a kept device, and remove all our records.
19940Sstevel@tonic-gate 	 */
19950Sstevel@tonic-gate 	pm_free_kept(path);
19960Sstevel@tonic-gate #ifdef	DEBUG
19970Sstevel@tonic-gate 	if (doprdeps) {
19980Sstevel@tonic-gate 		prdeps("pm_free_keeps after");
19990Sstevel@tonic-gate 	}
20000Sstevel@tonic-gate #endif
20010Sstevel@tonic-gate }
20020Sstevel@tonic-gate 
20030Sstevel@tonic-gate static int
20040Sstevel@tonic-gate pm_is_kept(char *path)
20050Sstevel@tonic-gate {
20060Sstevel@tonic-gate 	pm_pdr_t *dp;
20070Sstevel@tonic-gate 	int i;
20080Sstevel@tonic-gate 
20090Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
20100Sstevel@tonic-gate 		if (dp->pdr_kept_count == 0)
20110Sstevel@tonic-gate 			continue;
20120Sstevel@tonic-gate 		for (i = 0; i < dp->pdr_kept_count; i++) {
20130Sstevel@tonic-gate 			if (strcmp(dp->pdr_kept_paths[i], path) == 0)
20140Sstevel@tonic-gate 				return (1);
20150Sstevel@tonic-gate 		}
20160Sstevel@tonic-gate 	}
20170Sstevel@tonic-gate 	return (0);
20180Sstevel@tonic-gate }
20190Sstevel@tonic-gate 
20200Sstevel@tonic-gate static void
20210Sstevel@tonic-gate e_pm_hold_rele_power(dev_info_t *dip, int cnt)
20220Sstevel@tonic-gate {
20230Sstevel@tonic-gate 	PMD_FUNC(pmf, "hold_rele_power")
20240Sstevel@tonic-gate 	int circ;
20250Sstevel@tonic-gate 
2026*2009Sdm120769 	if ((dip == NULL) ||
2027*2009Sdm120769 	    (PM_GET_PM_INFO(dip) == NULL) || PM_ISBC(dip))
20280Sstevel@tonic-gate 		return;
20290Sstevel@tonic-gate 	PM_LOCK_POWER(dip, &circ);
20300Sstevel@tonic-gate 	ASSERT(cnt >= 0 && PM_KUC(dip) >= 0 || cnt < 0 && PM_KUC(dip) > 0);
20310Sstevel@tonic-gate 	PMD(PMD_KIDSUP, ("%s: kidsupcnt for %s@%s(%s#%d) %d->%d\n", pmf,
20320Sstevel@tonic-gate 	    PM_DEVICE(dip), PM_KUC(dip), (PM_KUC(dip) + cnt)))
20330Sstevel@tonic-gate 	PM_KUC(dip) += cnt;
20340Sstevel@tonic-gate 	ASSERT(PM_KUC(dip) >= 0);
20350Sstevel@tonic-gate 	PM_UNLOCK_POWER(dip, circ);
20360Sstevel@tonic-gate 	if (cnt < 0 && PM_KUC(dip) == 0)
20370Sstevel@tonic-gate 		pm_rescan(dip);
20380Sstevel@tonic-gate }
20390Sstevel@tonic-gate 
20400Sstevel@tonic-gate #define	MAX_PPM_HANDLERS	4
20410Sstevel@tonic-gate 
20420Sstevel@tonic-gate kmutex_t ppm_lock;	/* in case we ever do multi-threaded startup */
20430Sstevel@tonic-gate 
20440Sstevel@tonic-gate struct	ppm_callbacks {
20450Sstevel@tonic-gate 	int (*ppmc_func)(dev_info_t *);
20460Sstevel@tonic-gate 	dev_info_t	*ppmc_dip;
20470Sstevel@tonic-gate } ppm_callbacks[MAX_PPM_HANDLERS + 1];
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate /*
20510Sstevel@tonic-gate  * This routine calls into all the registered ppms to notify them
20520Sstevel@tonic-gate  * that either all components of power-managed devices are at their
20530Sstevel@tonic-gate  * lowest levels or no longer all are at their lowest levels.
20540Sstevel@tonic-gate  */
20550Sstevel@tonic-gate static void
20560Sstevel@tonic-gate pm_ppm_notify_all_lowest(dev_info_t *dip, int mode)
20570Sstevel@tonic-gate {
20580Sstevel@tonic-gate 	struct ppm_callbacks *ppmcp;
20590Sstevel@tonic-gate 	power_req_t power_req;
20600Sstevel@tonic-gate 	int result = 0;
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_ALL_LOWEST;
20630Sstevel@tonic-gate 	power_req.req.ppm_all_lowest_req.mode = mode;
20640Sstevel@tonic-gate 	mutex_enter(&ppm_lock);
20650Sstevel@tonic-gate 	for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++)
20660Sstevel@tonic-gate 		(void) pm_ctlops((dev_info_t *)ppmcp->ppmc_dip, dip,
20670Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
20680Sstevel@tonic-gate 	mutex_exit(&ppm_lock);
20690Sstevel@tonic-gate }
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate static void
20720Sstevel@tonic-gate pm_set_pm_info(dev_info_t *dip, void *value)
20730Sstevel@tonic-gate {
20740Sstevel@tonic-gate 	DEVI(dip)->devi_pm_info = value;
20750Sstevel@tonic-gate }
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate pm_rsvp_t *pm_blocked_list;
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate /*
20800Sstevel@tonic-gate  * Look up an entry in the blocked list by dip and component
20810Sstevel@tonic-gate  */
20820Sstevel@tonic-gate static pm_rsvp_t *
20830Sstevel@tonic-gate pm_rsvp_lookup(dev_info_t *dip, int comp)
20840Sstevel@tonic-gate {
20850Sstevel@tonic-gate 	pm_rsvp_t *p;
20860Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
20870Sstevel@tonic-gate 	for (p = pm_blocked_list; p; p = p->pr_next)
20880Sstevel@tonic-gate 		if (p->pr_dip == dip && p->pr_comp == comp) {
20890Sstevel@tonic-gate 			return (p);
20900Sstevel@tonic-gate 		}
20910Sstevel@tonic-gate 	return (NULL);
20920Sstevel@tonic-gate }
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate /*
20950Sstevel@tonic-gate  * Called when a device which is direct power managed (or the parent or
20960Sstevel@tonic-gate  * dependent of such a device) changes power, or when a pm clone is closed
20970Sstevel@tonic-gate  * that was direct power managing a device.  This call results in pm_blocked()
20980Sstevel@tonic-gate  * (below) returning.
20990Sstevel@tonic-gate  */
21000Sstevel@tonic-gate void
21010Sstevel@tonic-gate pm_proceed(dev_info_t *dip, int cmd, int comp, int newlevel)
21020Sstevel@tonic-gate {
21030Sstevel@tonic-gate 	PMD_FUNC(pmf, "proceed")
21040Sstevel@tonic-gate 	pm_rsvp_t *found = NULL;
21050Sstevel@tonic-gate 	pm_rsvp_t *p;
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate 	mutex_enter(&pm_rsvp_lock);
21080Sstevel@tonic-gate 	switch (cmd) {
21090Sstevel@tonic-gate 	/*
21100Sstevel@tonic-gate 	 * we're giving up control, let any pending op continue
21110Sstevel@tonic-gate 	 */
21120Sstevel@tonic-gate 	case PMP_RELEASE:
21130Sstevel@tonic-gate 		for (p = pm_blocked_list; p; p = p->pr_next) {
21140Sstevel@tonic-gate 			if (dip == p->pr_dip) {
21150Sstevel@tonic-gate 				p->pr_retval = PMP_RELEASE;
21160Sstevel@tonic-gate 				PMD(PMD_DPM, ("%s: RELEASE %s@%s(%s#%d)\n",
21170Sstevel@tonic-gate 				    pmf, PM_DEVICE(dip)))
21180Sstevel@tonic-gate 				cv_signal(&p->pr_cv);
21190Sstevel@tonic-gate 			}
21200Sstevel@tonic-gate 		}
21210Sstevel@tonic-gate 		break;
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate 	/*
21240Sstevel@tonic-gate 	 * process has done PM_SET_CURRENT_POWER; let a matching request
21250Sstevel@tonic-gate 	 * succeed and a non-matching request for the same device fail
21260Sstevel@tonic-gate 	 */
21270Sstevel@tonic-gate 	case PMP_SETPOWER:
21280Sstevel@tonic-gate 		found = pm_rsvp_lookup(dip, comp);
21290Sstevel@tonic-gate 		if (!found)	/* if driver not waiting */
21300Sstevel@tonic-gate 			break;
21310Sstevel@tonic-gate 		/*
21320Sstevel@tonic-gate 		 * This cannot be pm_lower_power, since that can only happen
21330Sstevel@tonic-gate 		 * during detach or probe
21340Sstevel@tonic-gate 		 */
21350Sstevel@tonic-gate 		if (found->pr_newlevel <= newlevel) {
21360Sstevel@tonic-gate 			found->pr_retval = PMP_SUCCEED;
21370Sstevel@tonic-gate 			PMD(PMD_DPM, ("%s: SUCCEED %s@%s(%s#%d)\n", pmf,
21380Sstevel@tonic-gate 			    PM_DEVICE(dip)))
21390Sstevel@tonic-gate 		} else {
21400Sstevel@tonic-gate 			found->pr_retval = PMP_FAIL;
21410Sstevel@tonic-gate 			PMD(PMD_DPM, ("%s: FAIL %s@%s(%s#%d)\n", pmf,
21420Sstevel@tonic-gate 			    PM_DEVICE(dip)))
21430Sstevel@tonic-gate 		}
21440Sstevel@tonic-gate 		cv_signal(&found->pr_cv);
21450Sstevel@tonic-gate 		break;
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 	default:
21480Sstevel@tonic-gate 		panic("pm_proceed unknown cmd %d", cmd);
21490Sstevel@tonic-gate 	}
21500Sstevel@tonic-gate 	mutex_exit(&pm_rsvp_lock);
21510Sstevel@tonic-gate }
21520Sstevel@tonic-gate 
21530Sstevel@tonic-gate /*
21540Sstevel@tonic-gate  * This routine dispatches new work to the dependency thread. Caller must
21550Sstevel@tonic-gate  * be prepared to block for memory if necessary.
21560Sstevel@tonic-gate  */
21570Sstevel@tonic-gate void
21580Sstevel@tonic-gate pm_dispatch_to_dep_thread(int cmd, char *keeper, char *kept, int wait,
21590Sstevel@tonic-gate     int *res, int cached_pwr)
21600Sstevel@tonic-gate {
21610Sstevel@tonic-gate 	pm_dep_wk_t	*new_work;
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate 	new_work = kmem_zalloc(sizeof (pm_dep_wk_t), KM_SLEEP);
21640Sstevel@tonic-gate 	new_work->pdw_type = cmd;
21650Sstevel@tonic-gate 	new_work->pdw_wait = wait;
21660Sstevel@tonic-gate 	new_work->pdw_done = 0;
21670Sstevel@tonic-gate 	new_work->pdw_ret = 0;
21680Sstevel@tonic-gate 	new_work->pdw_pwr = cached_pwr;
21690Sstevel@tonic-gate 	cv_init(&new_work->pdw_cv, NULL, CV_DEFAULT, NULL);
21700Sstevel@tonic-gate 	if (keeper != NULL) {
21710Sstevel@tonic-gate 		new_work->pdw_keeper = kmem_zalloc(strlen(keeper) + 1,
21720Sstevel@tonic-gate 		    KM_SLEEP);
21730Sstevel@tonic-gate 		(void) strcpy(new_work->pdw_keeper, keeper);
21740Sstevel@tonic-gate 	}
21750Sstevel@tonic-gate 	if (kept != NULL) {
21760Sstevel@tonic-gate 		new_work->pdw_kept = kmem_zalloc(strlen(kept) + 1, KM_SLEEP);
21770Sstevel@tonic-gate 		(void) strcpy(new_work->pdw_kept, kept);
21780Sstevel@tonic-gate 	}
21790Sstevel@tonic-gate 	mutex_enter(&pm_dep_thread_lock);
21800Sstevel@tonic-gate 	if (pm_dep_thread_workq == NULL) {
21810Sstevel@tonic-gate 		pm_dep_thread_workq = new_work;
21820Sstevel@tonic-gate 		pm_dep_thread_tail = new_work;
21830Sstevel@tonic-gate 		new_work->pdw_next = NULL;
21840Sstevel@tonic-gate 	} else {
21850Sstevel@tonic-gate 		pm_dep_thread_tail->pdw_next = new_work;
21860Sstevel@tonic-gate 		pm_dep_thread_tail = new_work;
21870Sstevel@tonic-gate 		new_work->pdw_next = NULL;
21880Sstevel@tonic-gate 	}
21890Sstevel@tonic-gate 	cv_signal(&pm_dep_thread_cv);
21900Sstevel@tonic-gate 	/* If caller asked for it, wait till it is done. */
21910Sstevel@tonic-gate 	if (wait)  {
21920Sstevel@tonic-gate 		while (!new_work->pdw_done)
21930Sstevel@tonic-gate 			cv_wait(&new_work->pdw_cv, &pm_dep_thread_lock);
21940Sstevel@tonic-gate 		/*
21950Sstevel@tonic-gate 		 * Pass return status, if any, back.
21960Sstevel@tonic-gate 		 */
21970Sstevel@tonic-gate 		if (res != NULL)
21980Sstevel@tonic-gate 			*res = new_work->pdw_ret;
21990Sstevel@tonic-gate 		/*
22000Sstevel@tonic-gate 		 * If we asked to wait, it is our job to free the request
22010Sstevel@tonic-gate 		 * structure.
22020Sstevel@tonic-gate 		 */
22030Sstevel@tonic-gate 		if (new_work->pdw_keeper)
22040Sstevel@tonic-gate 			kmem_free(new_work->pdw_keeper,
22050Sstevel@tonic-gate 			    strlen(new_work->pdw_keeper) + 1);
22060Sstevel@tonic-gate 		if (new_work->pdw_kept)
22070Sstevel@tonic-gate 			kmem_free(new_work->pdw_kept,
22080Sstevel@tonic-gate 			    strlen(new_work->pdw_kept) + 1);
22090Sstevel@tonic-gate 		kmem_free(new_work, sizeof (pm_dep_wk_t));
22100Sstevel@tonic-gate 	}
22110Sstevel@tonic-gate 	mutex_exit(&pm_dep_thread_lock);
22120Sstevel@tonic-gate }
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate /*
22150Sstevel@tonic-gate  * Release the pm resource for this device.
22160Sstevel@tonic-gate  */
22170Sstevel@tonic-gate void
22180Sstevel@tonic-gate pm_rem_info(dev_info_t *dip)
22190Sstevel@tonic-gate {
22200Sstevel@tonic-gate 	PMD_FUNC(pmf, "rem_info")
22210Sstevel@tonic-gate 	int		i, count = 0;
22220Sstevel@tonic-gate 	pm_info_t	*info = PM_GET_PM_INFO(dip);
22230Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
22240Sstevel@tonic-gate 	char		*pathbuf;
22250Sstevel@tonic-gate 	int		work_type = PM_DEP_WK_DETACH;
22260Sstevel@tonic-gate 
22270Sstevel@tonic-gate 	ASSERT(info);
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
22300Sstevel@tonic-gate 	if (PM_ISDIRECT(dip)) {
22310Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DIRECT;
22320Sstevel@tonic-gate 		ASSERT(info->pmi_clone);
22330Sstevel@tonic-gate 		info->pmi_clone = 0;
22340Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
22350Sstevel@tonic-gate 	}
22360Sstevel@tonic-gate 	ASSERT(!PM_GET_PM_SCAN(dip));
22370Sstevel@tonic-gate 
22380Sstevel@tonic-gate 	/*
22390Sstevel@tonic-gate 	 * Now adjust parent's kidsupcnt.  BC nodes we check only comp 0,
22400Sstevel@tonic-gate 	 * Others we check all components.  BC node that has already
22410Sstevel@tonic-gate 	 * called pm_destroy_components() has zero component count.
22420Sstevel@tonic-gate 	 * Parents that get notification are not adjusted because their
22430Sstevel@tonic-gate 	 * kidsupcnt is always 0 (or 1 during configuration).
22440Sstevel@tonic-gate 	 */
22450Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d) has %d components\n", pmf,
22460Sstevel@tonic-gate 	    PM_DEVICE(dip), PM_NUMCMPTS(dip)))
22470Sstevel@tonic-gate 
22480Sstevel@tonic-gate 	/* node is detached, so we can examine power without locking */
22490Sstevel@tonic-gate 	if (PM_ISBC(dip)) {
22500Sstevel@tonic-gate 		count = (PM_CURPOWER(dip, 0) != 0);
22510Sstevel@tonic-gate 	} else {
22520Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
22530Sstevel@tonic-gate 			count += (PM_CURPOWER(dip, i) != 0);
22540Sstevel@tonic-gate 	}
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) && pdip && !PM_WANTS_NOTIFICATION(pdip))
22570Sstevel@tonic-gate 		e_pm_hold_rele_power(pdip, -count);
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate 	/* Schedule a request to clean up dependency records */
22600Sstevel@tonic-gate 	pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
22610Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
22620Sstevel@tonic-gate 	pm_dispatch_to_dep_thread(work_type, pathbuf, pathbuf,
22630Sstevel@tonic-gate 	    PM_DEP_NOWAIT, NULL, (count > 0));
22640Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate 	/*
22670Sstevel@tonic-gate 	 * Adjust the pm_comps_notlowest count since this device is
22680Sstevel@tonic-gate 	 * not being power-managed anymore.
22690Sstevel@tonic-gate 	 */
22700Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
22710Sstevel@tonic-gate 		if (PM_CURPOWER(dip, i) != 0)
22720Sstevel@tonic-gate 			PM_DECR_NOTLOWEST(dip);
22730Sstevel@tonic-gate 	}
22740Sstevel@tonic-gate 	/*
22750Sstevel@tonic-gate 	 * Once we clear the info pointer, it looks like it is not power
22760Sstevel@tonic-gate 	 * managed to everybody else.
22770Sstevel@tonic-gate 	 */
22780Sstevel@tonic-gate 	pm_set_pm_info(dip, NULL);
22790Sstevel@tonic-gate 	kmem_free(info, sizeof (pm_info_t));
22800Sstevel@tonic-gate }
22810Sstevel@tonic-gate 
22820Sstevel@tonic-gate int
22830Sstevel@tonic-gate pm_get_norm_pwrs(dev_info_t *dip, int **valuep, size_t *length)
22840Sstevel@tonic-gate {
22850Sstevel@tonic-gate 	int components = PM_NUMCMPTS(dip);
22860Sstevel@tonic-gate 	int *bufp;
22870Sstevel@tonic-gate 	size_t size;
22880Sstevel@tonic-gate 	int i;
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate 	if (components <= 0) {
22910Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pm: %s@%s(%s#%d) has no components, "
22920Sstevel@tonic-gate 		    "can't get normal power values\n", PM_DEVICE(dip));
22930Sstevel@tonic-gate 		return (DDI_FAILURE);
22940Sstevel@tonic-gate 	} else {
22950Sstevel@tonic-gate 		size = components * sizeof (int);
22960Sstevel@tonic-gate 		bufp = kmem_alloc(size, KM_SLEEP);
22970Sstevel@tonic-gate 		for (i = 0; i < components; i++) {
22980Sstevel@tonic-gate 			bufp[i] = pm_get_normal_power(dip, i);
22990Sstevel@tonic-gate 		}
23000Sstevel@tonic-gate 	}
23010Sstevel@tonic-gate 	*length = size;
23020Sstevel@tonic-gate 	*valuep = bufp;
23030Sstevel@tonic-gate 	return (DDI_SUCCESS);
23040Sstevel@tonic-gate }
23050Sstevel@tonic-gate 
23060Sstevel@tonic-gate static int
23070Sstevel@tonic-gate pm_reset_timestamps(dev_info_t *dip, void *arg)
23080Sstevel@tonic-gate {
23090Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
23100Sstevel@tonic-gate 
23110Sstevel@tonic-gate 	int components;
23120Sstevel@tonic-gate 	int	i;
23130Sstevel@tonic-gate 
23140Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip))
23150Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
23160Sstevel@tonic-gate 	components = PM_NUMCMPTS(dip);
23170Sstevel@tonic-gate 	ASSERT(components > 0);
23180Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
23190Sstevel@tonic-gate 	for (i = 0; i < components; i++) {
23200Sstevel@tonic-gate 		struct pm_component *cp;
23210Sstevel@tonic-gate 		/*
23220Sstevel@tonic-gate 		 * If the component was not marked as busy,
23230Sstevel@tonic-gate 		 * reset its timestamp to now.
23240Sstevel@tonic-gate 		 */
23250Sstevel@tonic-gate 		cp = PM_CP(dip, i);
23260Sstevel@tonic-gate 		if (cp->pmc_timestamp)
23270Sstevel@tonic-gate 			cp->pmc_timestamp = gethrestime_sec();
23280Sstevel@tonic-gate 	}
23290Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
23300Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
23310Sstevel@tonic-gate }
23320Sstevel@tonic-gate 
23330Sstevel@tonic-gate /*
23340Sstevel@tonic-gate  * Convert a power level to an index into the levels array (or
23350Sstevel@tonic-gate  * just PM_LEVEL_UNKNOWN in that special case).
23360Sstevel@tonic-gate  */
23370Sstevel@tonic-gate static int
23380Sstevel@tonic-gate pm_level_to_index(dev_info_t *dip, pm_component_t *cp, int level)
23390Sstevel@tonic-gate {
23400Sstevel@tonic-gate 	PMD_FUNC(pmf, "level_to_index")
23410Sstevel@tonic-gate 	int i;
23420Sstevel@tonic-gate 	int limit = cp->pmc_comp.pmc_numlevels;
23430Sstevel@tonic-gate 	int *ip = cp->pmc_comp.pmc_lvals;
23440Sstevel@tonic-gate 
23450Sstevel@tonic-gate 	if (level == PM_LEVEL_UNKNOWN)
23460Sstevel@tonic-gate 		return (level);
23470Sstevel@tonic-gate 
23480Sstevel@tonic-gate 	for (i = 0; i < limit; i++) {
23490Sstevel@tonic-gate 		if (level == *ip++) {
23500Sstevel@tonic-gate 			PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d)[%d] to %x\n",
23510Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip),
23520Sstevel@tonic-gate 			    (int)(cp - DEVI(dip)->devi_pm_components), level))
23530Sstevel@tonic-gate 			return (i);
23540Sstevel@tonic-gate 		}
23550Sstevel@tonic-gate 	}
23560Sstevel@tonic-gate 	panic("pm_level_to_index: level %d not found for device "
23570Sstevel@tonic-gate 	    "%s@%s(%s#%d)", level, PM_DEVICE(dip));
23580Sstevel@tonic-gate 	/*NOTREACHED*/
23590Sstevel@tonic-gate }
23600Sstevel@tonic-gate 
23610Sstevel@tonic-gate /*
23620Sstevel@tonic-gate  * Internal function to set current power level
23630Sstevel@tonic-gate  */
23640Sstevel@tonic-gate static void
23650Sstevel@tonic-gate e_pm_set_cur_pwr(dev_info_t *dip, pm_component_t *cp, int level)
23660Sstevel@tonic-gate {
23670Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_cur_pwr")
23680Sstevel@tonic-gate 	int curpwr = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
23690Sstevel@tonic-gate 	    cp->pmc_phc_pwr : cp->pmc_cur_pwr);
23700Sstevel@tonic-gate 
23710Sstevel@tonic-gate 	/*
23720Sstevel@tonic-gate 	 * Nothing to adjust if current & new levels are the same.
23730Sstevel@tonic-gate 	 */
23740Sstevel@tonic-gate 	if (curpwr != PM_LEVEL_UNKNOWN &&
23750Sstevel@tonic-gate 	    level == cp->pmc_comp.pmc_lvals[curpwr])
23760Sstevel@tonic-gate 		return;
23770Sstevel@tonic-gate 
23780Sstevel@tonic-gate 	/*
23790Sstevel@tonic-gate 	 * Keep the count for comps doing transition to/from lowest
23800Sstevel@tonic-gate 	 * level.
23810Sstevel@tonic-gate 	 */
23820Sstevel@tonic-gate 	if (curpwr == 0) {
23830Sstevel@tonic-gate 		PM_INCR_NOTLOWEST(dip);
23840Sstevel@tonic-gate 	} else if (level == cp->pmc_comp.pmc_lvals[0]) {
23850Sstevel@tonic-gate 		PM_DECR_NOTLOWEST(dip);
23860Sstevel@tonic-gate 	}
23870Sstevel@tonic-gate 	cp->pmc_phc_pwr = PM_LEVEL_UNKNOWN;
23880Sstevel@tonic-gate 	cp->pmc_cur_pwr = pm_level_to_index(dip, cp, level);
23890Sstevel@tonic-gate }
23900Sstevel@tonic-gate 
23910Sstevel@tonic-gate /*
23920Sstevel@tonic-gate  * This is the default method of setting the power of a device if no ppm
23930Sstevel@tonic-gate  * driver has claimed it.
23940Sstevel@tonic-gate  */
23950Sstevel@tonic-gate int
23960Sstevel@tonic-gate pm_power(dev_info_t *dip, int comp, int level)
23970Sstevel@tonic-gate {
23980Sstevel@tonic-gate 	PMD_FUNC(pmf, "power")
23990Sstevel@tonic-gate 	struct dev_ops	*ops;
24000Sstevel@tonic-gate 	int		(*fn)(dev_info_t *, int, int);
24010Sstevel@tonic-gate 	struct pm_component *cp = PM_CP(dip, comp);
24020Sstevel@tonic-gate 	int retval;
24030Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
24040Sstevel@tonic-gate 	static int pm_phc_impl(dev_info_t *, int, int, int);
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 	PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf,
24070Sstevel@tonic-gate 	    PM_DEVICE(dip), comp, level))
24080Sstevel@tonic-gate 	if (!(ops = ddi_get_driver(dip))) {
24090Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) has no ops\n", pmf,
24100Sstevel@tonic-gate 		    PM_DEVICE(dip)))
24110Sstevel@tonic-gate 		return (DDI_FAILURE);
24120Sstevel@tonic-gate 	}
24130Sstevel@tonic-gate 	if ((ops->devo_rev < 2) || !(fn = ops->devo_power)) {
24140Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s%s\n", pmf,
24150Sstevel@tonic-gate 		    (ops->devo_rev < 2 ? " wrong devo_rev" : ""),
24160Sstevel@tonic-gate 		    (!fn ? " devo_power NULL" : "")))
24170Sstevel@tonic-gate 		return (DDI_FAILURE);
24180Sstevel@tonic-gate 	}
24190Sstevel@tonic-gate 	cp->pmc_flags |= PM_POWER_OP;
24200Sstevel@tonic-gate 	retval = (*fn)(dip, comp, level);
24210Sstevel@tonic-gate 	cp->pmc_flags &= ~PM_POWER_OP;
24220Sstevel@tonic-gate 	if (retval == DDI_SUCCESS) {
24230Sstevel@tonic-gate 		e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level);
24240Sstevel@tonic-gate 		return (DDI_SUCCESS);
24250Sstevel@tonic-gate 	}
24260Sstevel@tonic-gate 
24270Sstevel@tonic-gate 	/*
24280Sstevel@tonic-gate 	 * If pm_power_has_changed() detected a deadlock with pm_power() it
24290Sstevel@tonic-gate 	 * updated only the power level of the component.  If our attempt to
24300Sstevel@tonic-gate 	 * set the device new to a power level above has failed we sync the
24310Sstevel@tonic-gate 	 * total power state via phc code now.
24320Sstevel@tonic-gate 	 */
24330Sstevel@tonic-gate 	if (cp->pmc_flags & PM_PHC_WHILE_SET_POWER) {
24340Sstevel@tonic-gate 		int phc_lvl =
24350Sstevel@tonic-gate 		    cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr];
24360Sstevel@tonic-gate 
24370Sstevel@tonic-gate 		ASSERT(info);
24380Sstevel@tonic-gate 		(void) pm_phc_impl(dip, comp, phc_lvl, 0);
24390Sstevel@tonic-gate 		PMD(PMD_PHC, ("%s: phc %s@%s(%s#%d) comp=%d level=%d\n",
24400Sstevel@tonic-gate 			pmf, PM_DEVICE(dip), comp, phc_lvl))
24410Sstevel@tonic-gate 	}
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate 	PMD(PMD_FAIL, ("%s: can't set comp=%d (%s) of %s@%s(%s#%d) to "
24440Sstevel@tonic-gate 	    "level=%d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, PM_DEVICE(dip),
24450Sstevel@tonic-gate 	    level, power_val_to_string(cp, level)));
24460Sstevel@tonic-gate 	return (DDI_FAILURE);
24470Sstevel@tonic-gate }
24480Sstevel@tonic-gate 
24490Sstevel@tonic-gate int
24500Sstevel@tonic-gate pm_unmanage(dev_info_t *dip)
24510Sstevel@tonic-gate {
24520Sstevel@tonic-gate 	PMD_FUNC(pmf, "unmanage")
24530Sstevel@tonic-gate 	power_req_t power_req;
24540Sstevel@tonic-gate 	int result, retval = 0;
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
24570Sstevel@tonic-gate 	PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf,
24580Sstevel@tonic-gate 	    PM_DEVICE(dip)))
24590Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_UNMANAGE;
24600Sstevel@tonic-gate 	power_req.req.ppm_config_req.who = dip;
24610Sstevel@tonic-gate 	if (pm_ppm_claimed(dip))
24620Sstevel@tonic-gate 		retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
24630Sstevel@tonic-gate 		    &power_req, &result);
24640Sstevel@tonic-gate #ifdef DEBUG
24650Sstevel@tonic-gate 	else
24660Sstevel@tonic-gate 		retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
24670Sstevel@tonic-gate 		    &power_req, &result);
24680Sstevel@tonic-gate #endif
24690Sstevel@tonic-gate 	ASSERT(retval == DDI_SUCCESS);
24700Sstevel@tonic-gate 	pm_rem_info(dip);
24710Sstevel@tonic-gate 	return (retval);
24720Sstevel@tonic-gate }
24730Sstevel@tonic-gate 
24740Sstevel@tonic-gate int
24750Sstevel@tonic-gate pm_raise_power(dev_info_t *dip, int comp, int level)
24760Sstevel@tonic-gate {
24770Sstevel@tonic-gate 	if (level < 0)
24780Sstevel@tonic-gate 		return (DDI_FAILURE);
24790Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
24800Sstevel@tonic-gate 	    !e_pm_valid_power(dip, comp, level))
24810Sstevel@tonic-gate 		return (DDI_FAILURE);
24820Sstevel@tonic-gate 
24830Sstevel@tonic-gate 	return (dev_is_needed(dip, comp, level, PM_LEVEL_UPONLY));
24840Sstevel@tonic-gate }
24850Sstevel@tonic-gate 
24860Sstevel@tonic-gate int
24870Sstevel@tonic-gate pm_lower_power(dev_info_t *dip, int comp, int level)
24880Sstevel@tonic-gate {
24890Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_lower_power")
24900Sstevel@tonic-gate 
24910Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
24920Sstevel@tonic-gate 	    !e_pm_valid_power(dip, comp, level)) {
24930Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) "
24940Sstevel@tonic-gate 		    "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level))
24950Sstevel@tonic-gate 		return (DDI_FAILURE);
24960Sstevel@tonic-gate 	}
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 	if (!DEVI_IS_DETACHING(dip)) {
24990Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) not detaching\n",
25000Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
25010Sstevel@tonic-gate 		return (DDI_FAILURE);
25020Sstevel@tonic-gate 	}
25030Sstevel@tonic-gate 
25040Sstevel@tonic-gate 	/*
25050Sstevel@tonic-gate 	 * If we don't care about saving power, or we're treating this node
25060Sstevel@tonic-gate 	 * specially, then this is a no-op
25070Sstevel@tonic-gate 	 */
25080Sstevel@tonic-gate 	if (!autopm_enabled || pm_noinvol(dip)) {
25090Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) %s%s\n", pmf, PM_DEVICE(dip),
25100Sstevel@tonic-gate 		    !autopm_enabled ? "!autopm_enabled " : "",
25110Sstevel@tonic-gate 		    pm_noinvol(dip) ? "pm_noinvol()" : ""))
25120Sstevel@tonic-gate 		return (DDI_SUCCESS);
25130Sstevel@tonic-gate 	}
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate 	if (dev_is_needed(dip, comp, level, PM_LEVEL_DOWNONLY) != DDI_SUCCESS) {
25160Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) dev_is_needed failed\n", pmf,
25170Sstevel@tonic-gate 		    PM_DEVICE(dip)))
25180Sstevel@tonic-gate 		return (DDI_FAILURE);
25190Sstevel@tonic-gate 	}
25200Sstevel@tonic-gate 	return (DDI_SUCCESS);
25210Sstevel@tonic-gate }
25220Sstevel@tonic-gate 
25230Sstevel@tonic-gate /*
25240Sstevel@tonic-gate  * Find the entries struct for a given dip in the blocked list, return it locked
25250Sstevel@tonic-gate  */
25260Sstevel@tonic-gate static psce_t *
25270Sstevel@tonic-gate pm_psc_dip_to_direct(dev_info_t *dip, pscc_t **psccp)
25280Sstevel@tonic-gate {
25290Sstevel@tonic-gate 	pscc_t *p;
25300Sstevel@tonic-gate 	psce_t *psce;
25310Sstevel@tonic-gate 
25320Sstevel@tonic-gate 	rw_enter(&pm_pscc_direct_rwlock, RW_READER);
25330Sstevel@tonic-gate 	for (p = pm_pscc_direct; p; p = p->pscc_next) {
25340Sstevel@tonic-gate 		if (p->pscc_dip == dip) {
25350Sstevel@tonic-gate 			*psccp = p;
25360Sstevel@tonic-gate 			psce = p->pscc_entries;
25370Sstevel@tonic-gate 			mutex_enter(&psce->psce_lock);
25380Sstevel@tonic-gate 			ASSERT(psce);
25390Sstevel@tonic-gate 			rw_exit(&pm_pscc_direct_rwlock);
25400Sstevel@tonic-gate 			return (psce);
25410Sstevel@tonic-gate 		}
25420Sstevel@tonic-gate 	}
25430Sstevel@tonic-gate 	rw_exit(&pm_pscc_direct_rwlock);
25440Sstevel@tonic-gate 	panic("sunpm: no entry for dip %p in direct list", (void *)dip);
25450Sstevel@tonic-gate 	/*NOTREACHED*/
25460Sstevel@tonic-gate }
25470Sstevel@tonic-gate 
25480Sstevel@tonic-gate /*
25490Sstevel@tonic-gate  * Write an entry indicating a power level change (to be passed to a process
25500Sstevel@tonic-gate  * later) in the given psce.
25510Sstevel@tonic-gate  * If we were called in the path that brings up the console fb in the
25520Sstevel@tonic-gate  * case of entering the prom, we don't want to sleep.  If the alloc fails, then
25530Sstevel@tonic-gate  * we create a record that has a size of -1, a physaddr of NULL, and that
25540Sstevel@tonic-gate  * has the overflow flag set.
25550Sstevel@tonic-gate  */
25560Sstevel@tonic-gate static int
25570Sstevel@tonic-gate psc_entry(ushort_t event, psce_t *psce, dev_info_t *dip, int comp, int new,
25580Sstevel@tonic-gate     int old, int which, pm_canblock_t canblock)
25590Sstevel@tonic-gate {
25600Sstevel@tonic-gate 	char	buf[MAXNAMELEN];
25610Sstevel@tonic-gate 	pm_state_change_t *p;
25620Sstevel@tonic-gate 	size_t	size;
25630Sstevel@tonic-gate 	caddr_t physpath = NULL;
25640Sstevel@tonic-gate 	int	overrun = 0;
25650Sstevel@tonic-gate 
25660Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&psce->psce_lock));
25670Sstevel@tonic-gate 	(void) ddi_pathname(dip, buf);
25680Sstevel@tonic-gate 	size = strlen(buf) + 1;
25690Sstevel@tonic-gate 	p = psce->psce_in;
25700Sstevel@tonic-gate 	if (canblock == PM_CANBLOCK_BYPASS) {
25710Sstevel@tonic-gate 		physpath = kmem_alloc(size, KM_NOSLEEP);
25720Sstevel@tonic-gate 		if (physpath == NULL) {
25730Sstevel@tonic-gate 			/*
25740Sstevel@tonic-gate 			 * mark current entry as overrun
25750Sstevel@tonic-gate 			 */
25760Sstevel@tonic-gate 			p->flags |= PSC_EVENT_LOST;
25770Sstevel@tonic-gate 			size = (size_t)-1;
25780Sstevel@tonic-gate 		}
25790Sstevel@tonic-gate 	} else
25800Sstevel@tonic-gate 		physpath = kmem_alloc(size, KM_SLEEP);
25810Sstevel@tonic-gate 	if (p->size) {	/* overflow; mark the next entry */
25820Sstevel@tonic-gate 		if (p->size != (size_t)-1)
25830Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
25840Sstevel@tonic-gate 		ASSERT(psce->psce_out == p);
25850Sstevel@tonic-gate 		if (p == psce->psce_last) {
25860Sstevel@tonic-gate 			psce->psce_first->flags |= PSC_EVENT_LOST;
25870Sstevel@tonic-gate 			psce->psce_out = psce->psce_first;
25880Sstevel@tonic-gate 		} else {
25890Sstevel@tonic-gate 			(p + 1)->flags |= PSC_EVENT_LOST;
25900Sstevel@tonic-gate 			psce->psce_out = (p + 1);
25910Sstevel@tonic-gate 		}
25920Sstevel@tonic-gate 		overrun++;
25930Sstevel@tonic-gate 	} else if (physpath == NULL) {	/* alloc failed, mark this entry */
25940Sstevel@tonic-gate 		p->flags |= PSC_EVENT_LOST;
25950Sstevel@tonic-gate 		p->size = 0;
25960Sstevel@tonic-gate 		p->physpath = NULL;
25970Sstevel@tonic-gate 	}
25980Sstevel@tonic-gate 	if (which == PSC_INTEREST) {
25990Sstevel@tonic-gate 		mutex_enter(&pm_compcnt_lock);
26000Sstevel@tonic-gate 		if (pm_comps_notlowest == 0)
26010Sstevel@tonic-gate 			p->flags |= PSC_ALL_LOWEST;
26020Sstevel@tonic-gate 		else
26030Sstevel@tonic-gate 			p->flags &= ~PSC_ALL_LOWEST;
26040Sstevel@tonic-gate 		mutex_exit(&pm_compcnt_lock);
26050Sstevel@tonic-gate 	}
26060Sstevel@tonic-gate 	p->event = event;
26070Sstevel@tonic-gate 	p->timestamp = gethrestime_sec();
26080Sstevel@tonic-gate 	p->component = comp;
26090Sstevel@tonic-gate 	p->old_level = old;
26100Sstevel@tonic-gate 	p->new_level = new;
26110Sstevel@tonic-gate 	p->physpath = physpath;
26120Sstevel@tonic-gate 	p->size = size;
26130Sstevel@tonic-gate 	if (physpath != NULL)
26140Sstevel@tonic-gate 		(void) strcpy(p->physpath, buf);
26150Sstevel@tonic-gate 	if (p == psce->psce_last)
26160Sstevel@tonic-gate 		psce->psce_in = psce->psce_first;
26170Sstevel@tonic-gate 	else
26180Sstevel@tonic-gate 		psce->psce_in = ++p;
26190Sstevel@tonic-gate 	mutex_exit(&psce->psce_lock);
26200Sstevel@tonic-gate 	return (overrun);
26210Sstevel@tonic-gate }
26220Sstevel@tonic-gate 
26230Sstevel@tonic-gate /*
26240Sstevel@tonic-gate  * Find the next entry on the interest list.  We keep a pointer to the item we
26250Sstevel@tonic-gate  * last returned in the user's cooke.  Returns a locked entries struct.
26260Sstevel@tonic-gate  */
26270Sstevel@tonic-gate static psce_t *
26280Sstevel@tonic-gate psc_interest(void **cookie, pscc_t **psccp)
26290Sstevel@tonic-gate {
26300Sstevel@tonic-gate 	pscc_t *pscc;
26310Sstevel@tonic-gate 	pscc_t **cookiep = (pscc_t **)cookie;
26320Sstevel@tonic-gate 
26330Sstevel@tonic-gate 	if (*cookiep == NULL)
26340Sstevel@tonic-gate 		pscc = pm_pscc_interest;
26350Sstevel@tonic-gate 	else
26360Sstevel@tonic-gate 		pscc = (*cookiep)->pscc_next;
26370Sstevel@tonic-gate 	if (pscc) {
26380Sstevel@tonic-gate 		*cookiep = pscc;
26390Sstevel@tonic-gate 		*psccp = pscc;
26400Sstevel@tonic-gate 		mutex_enter(&pscc->pscc_entries->psce_lock);
26410Sstevel@tonic-gate 		return (pscc->pscc_entries);
26420Sstevel@tonic-gate 	} else {
26430Sstevel@tonic-gate 		return (NULL);
26440Sstevel@tonic-gate 	}
26450Sstevel@tonic-gate }
26460Sstevel@tonic-gate 
26470Sstevel@tonic-gate /*
26480Sstevel@tonic-gate  * Create an entry for a process to pick up indicating a power level change.
26490Sstevel@tonic-gate  */
26500Sstevel@tonic-gate static void
26510Sstevel@tonic-gate pm_enqueue_notify(ushort_t cmd, dev_info_t *dip, int comp,
26520Sstevel@tonic-gate     int newlevel, int oldlevel, pm_canblock_t canblock)
26530Sstevel@tonic-gate {
26540Sstevel@tonic-gate 	PMD_FUNC(pmf, "enqueue_notify")
26550Sstevel@tonic-gate 	pscc_t	*pscc;
26560Sstevel@tonic-gate 	psce_t	*psce;
26570Sstevel@tonic-gate 	void		*cookie = NULL;
26580Sstevel@tonic-gate 	int	overrun;
26590Sstevel@tonic-gate 
26600Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
26610Sstevel@tonic-gate 	switch (cmd) {
26620Sstevel@tonic-gate 	case PSC_PENDING_CHANGE:	/* only for controlling process */
26630Sstevel@tonic-gate 		PMD(PMD_DPM, ("%s: PENDING %s@%s(%s#%d), comp %d, %d -> %d\n",
26640Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), comp, oldlevel, newlevel))
26650Sstevel@tonic-gate 		psce = pm_psc_dip_to_direct(dip, &pscc);
26660Sstevel@tonic-gate 		ASSERT(psce);
26670Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("%s: PENDING: %s@%s(%s#%d) pm_poll_cnt[%d] "
26680Sstevel@tonic-gate 		    "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone,
26690Sstevel@tonic-gate 		    pm_poll_cnt[pscc->pscc_clone]))
26700Sstevel@tonic-gate 		overrun = psc_entry(cmd, psce, dip, comp, newlevel, oldlevel,
26710Sstevel@tonic-gate 		    PSC_DIRECT, canblock);
26720Sstevel@tonic-gate 		PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone))
26730Sstevel@tonic-gate 		mutex_enter(&pm_clone_lock);
26740Sstevel@tonic-gate 		if (!overrun)
26750Sstevel@tonic-gate 			pm_poll_cnt[pscc->pscc_clone]++;
26760Sstevel@tonic-gate 		cv_signal(&pm_clones_cv[pscc->pscc_clone]);
26770Sstevel@tonic-gate 		pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN));
26780Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
26790Sstevel@tonic-gate 		break;
26800Sstevel@tonic-gate 	case PSC_HAS_CHANGED:
26810Sstevel@tonic-gate 		PMD(PMD_DPM, ("%s: HAS %s@%s(%s#%d), comp %d, %d -> %d\n",
26820Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), comp, oldlevel, newlevel))
26830Sstevel@tonic-gate 		if (PM_ISDIRECT(dip) && canblock != PM_CANBLOCK_BYPASS) {
26840Sstevel@tonic-gate 			psce = pm_psc_dip_to_direct(dip, &pscc);
26850Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("%s: HAS: %s@%s(%s#%d) pm_poll_cnt[%d] "
26860Sstevel@tonic-gate 			    "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone,
26870Sstevel@tonic-gate 			    pm_poll_cnt[pscc->pscc_clone]))
26880Sstevel@tonic-gate 			overrun = psc_entry(cmd, psce, dip, comp, newlevel,
26890Sstevel@tonic-gate 			    oldlevel, PSC_DIRECT, canblock);
26900Sstevel@tonic-gate 			PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone))
26910Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
26920Sstevel@tonic-gate 			if (!overrun)
26930Sstevel@tonic-gate 				pm_poll_cnt[pscc->pscc_clone]++;
26940Sstevel@tonic-gate 			cv_signal(&pm_clones_cv[pscc->pscc_clone]);
26950Sstevel@tonic-gate 			pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN));
26960Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
26970Sstevel@tonic-gate 		}
26980Sstevel@tonic-gate 		mutex_enter(&pm_clone_lock);
26990Sstevel@tonic-gate 		rw_enter(&pm_pscc_interest_rwlock, RW_READER);
27000Sstevel@tonic-gate 		while ((psce = psc_interest(&cookie, &pscc)) != NULL) {
27010Sstevel@tonic-gate 			(void) psc_entry(cmd, psce, dip, comp, newlevel,
27020Sstevel@tonic-gate 			    oldlevel, PSC_INTEREST, canblock);
27030Sstevel@tonic-gate 			cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27040Sstevel@tonic-gate 		}
27050Sstevel@tonic-gate 		rw_exit(&pm_pscc_interest_rwlock);
27060Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
27070Sstevel@tonic-gate 		break;
27080Sstevel@tonic-gate #ifdef DEBUG
27090Sstevel@tonic-gate 	default:
27100Sstevel@tonic-gate 		ASSERT(0);
27110Sstevel@tonic-gate #endif
27120Sstevel@tonic-gate 	}
27130Sstevel@tonic-gate }
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate static void
27160Sstevel@tonic-gate pm_enqueue_notify_others(pm_ppm_devlist_t **listp, pm_canblock_t canblock)
27170Sstevel@tonic-gate {
27180Sstevel@tonic-gate 	if (listp) {
27190Sstevel@tonic-gate 		pm_ppm_devlist_t *p, *next = NULL;
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate 		for (p = *listp; p; p = next) {
27220Sstevel@tonic-gate 			next = p->ppd_next;
27230Sstevel@tonic-gate 			pm_enqueue_notify(PSC_HAS_CHANGED, p->ppd_who,
27240Sstevel@tonic-gate 			    p->ppd_cmpt, p->ppd_new_level, p->ppd_old_level,
27250Sstevel@tonic-gate 			    canblock);
27260Sstevel@tonic-gate 			kmem_free(p, sizeof (pm_ppm_devlist_t));
27270Sstevel@tonic-gate 		}
27280Sstevel@tonic-gate 		*listp = NULL;
27290Sstevel@tonic-gate 	}
27300Sstevel@tonic-gate }
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate /*
27330Sstevel@tonic-gate  * Try to get the power locks of the parent node and target (child)
27340Sstevel@tonic-gate  * node.  Return true if successful (with both locks held) or false
27350Sstevel@tonic-gate  * (with no locks held).
27360Sstevel@tonic-gate  */
27370Sstevel@tonic-gate static int
27380Sstevel@tonic-gate pm_try_parent_child_locks(dev_info_t *pdip,
27390Sstevel@tonic-gate     dev_info_t *dip, int *pcircp, int *circp)
27400Sstevel@tonic-gate {
27410Sstevel@tonic-gate 	if (ndi_devi_tryenter(pdip, pcircp))
27420Sstevel@tonic-gate 		if (PM_TRY_LOCK_POWER(dip, circp)) {
27430Sstevel@tonic-gate 			return (1);
27440Sstevel@tonic-gate 		} else {
27450Sstevel@tonic-gate 			ndi_devi_exit(pdip, *pcircp);
27460Sstevel@tonic-gate 		}
27470Sstevel@tonic-gate 	return (0);
27480Sstevel@tonic-gate }
27490Sstevel@tonic-gate 
27500Sstevel@tonic-gate /*
27510Sstevel@tonic-gate  * Determine if the power lock owner is blocked by current thread.
27520Sstevel@tonic-gate  * returns :
27530Sstevel@tonic-gate  * 	1 - If the thread owning the effective power lock (the first lock on
27540Sstevel@tonic-gate  *          which a thread blocks when it does PM_LOCK_POWER) is blocked by
27550Sstevel@tonic-gate  *          a mutex held by the current thread.
27560Sstevel@tonic-gate  *
27570Sstevel@tonic-gate  *	0 - otherwise
27580Sstevel@tonic-gate  *
27590Sstevel@tonic-gate  * Note : This function is called by pm_power_has_changed to determine whether
27600Sstevel@tonic-gate  * it is executing in parallel with pm_set_power.
27610Sstevel@tonic-gate  */
27620Sstevel@tonic-gate static int
27630Sstevel@tonic-gate pm_blocked_by_us(dev_info_t *dip)
27640Sstevel@tonic-gate {
27650Sstevel@tonic-gate 	power_req_t power_req;
27660Sstevel@tonic-gate 	kthread_t *owner;
27670Sstevel@tonic-gate 	int result;
27680Sstevel@tonic-gate 	kmutex_t *mp;
27690Sstevel@tonic-gate 	dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm;
27700Sstevel@tonic-gate 
27710Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_POWER_LOCK_OWNER;
27720Sstevel@tonic-gate 	power_req.req.ppm_power_lock_owner_req.who = dip;
27730Sstevel@tonic-gate 	if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, &result) !=
27740Sstevel@tonic-gate 	    DDI_SUCCESS) {
27750Sstevel@tonic-gate 		/*
27760Sstevel@tonic-gate 		 * It is assumed that if the device is claimed by ppm, ppm
27770Sstevel@tonic-gate 		 * will always implement this request type and it'll always
27780Sstevel@tonic-gate 		 * return success. We panic here, if it fails.
27790Sstevel@tonic-gate 		 */
27800Sstevel@tonic-gate 		panic("pm: Can't determine power lock owner of %s@%s(%s#%d)\n",
27810Sstevel@tonic-gate 		    PM_DEVICE(dip));
27820Sstevel@tonic-gate 		/*NOTREACHED*/
27830Sstevel@tonic-gate 	}
27840Sstevel@tonic-gate 
27850Sstevel@tonic-gate 	if ((owner = power_req.req.ppm_power_lock_owner_req.owner) != NULL &&
27860Sstevel@tonic-gate 	    owner->t_state == TS_SLEEP &&
27870Sstevel@tonic-gate 	    owner->t_sobj_ops &&
27880Sstevel@tonic-gate 	    SOBJ_TYPE(owner->t_sobj_ops) == SOBJ_MUTEX &&
27890Sstevel@tonic-gate 	    (mp = (kmutex_t *)owner->t_wchan) &&
27900Sstevel@tonic-gate 	    mutex_owner(mp) == curthread)
27910Sstevel@tonic-gate 		return (1);
27920Sstevel@tonic-gate 
27930Sstevel@tonic-gate 	return (0);
27940Sstevel@tonic-gate }
27950Sstevel@tonic-gate 
27960Sstevel@tonic-gate /*
27970Sstevel@tonic-gate  * Notify parent which wants to hear about a child's power changes.
27980Sstevel@tonic-gate  */
27990Sstevel@tonic-gate static void
28000Sstevel@tonic-gate pm_notify_parent(dev_info_t *dip,
28010Sstevel@tonic-gate     dev_info_t *pdip, int comp, int old_level, int level)
28020Sstevel@tonic-gate {
28030Sstevel@tonic-gate 	pm_bp_has_changed_t bphc;
28040Sstevel@tonic-gate 	pm_sp_misc_t pspm;
28050Sstevel@tonic-gate 	char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
28060Sstevel@tonic-gate 	int result = DDI_SUCCESS;
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 	bphc.bphc_dip = dip;
28090Sstevel@tonic-gate 	bphc.bphc_path = ddi_pathname(dip, pathbuf);
28100Sstevel@tonic-gate 	bphc.bphc_comp = comp;
28110Sstevel@tonic-gate 	bphc.bphc_olevel = old_level;
28120Sstevel@tonic-gate 	bphc.bphc_nlevel = level;
28130Sstevel@tonic-gate 	pspm.pspm_canblock = PM_CANBLOCK_BLOCK;
28140Sstevel@tonic-gate 	pspm.pspm_scan = 0;
28150Sstevel@tonic-gate 	bphc.bphc_private = &pspm;
28160Sstevel@tonic-gate 	(void) (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
28170Sstevel@tonic-gate 	    BUS_POWER_HAS_CHANGED, (void *)&bphc, (void *)&result);
28180Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
28190Sstevel@tonic-gate }
28200Sstevel@tonic-gate 
28210Sstevel@tonic-gate /*
28220Sstevel@tonic-gate  * Check if we need to resume a BC device, and make the attach call as required.
28230Sstevel@tonic-gate  */
28240Sstevel@tonic-gate static int
28250Sstevel@tonic-gate pm_check_and_resume(dev_info_t *dip, int comp, int old_level, int level)
28260Sstevel@tonic-gate {
28270Sstevel@tonic-gate 	int ret = DDI_SUCCESS;
28280Sstevel@tonic-gate 
28290Sstevel@tonic-gate 	if (PM_ISBC(dip) && comp == 0 && old_level == 0 && level != 0) {
28300Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED);
28310Sstevel@tonic-gate 		/* ppm is not interested in DDI_PM_RESUME */
28320Sstevel@tonic-gate 		if ((ret = devi_attach(dip, DDI_PM_RESUME)) != DDI_SUCCESS)
28330Sstevel@tonic-gate 			/* XXX Should we mark it resumed, */
28340Sstevel@tonic-gate 			/* even though it failed? */
28350Sstevel@tonic-gate 			cmn_err(CE_WARN, "!pm: Can't resume %s@%s",
28360Sstevel@tonic-gate 			    PM_NAME(dip), PM_ADDR(dip));
28370Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED;
28380Sstevel@tonic-gate 	}
28390Sstevel@tonic-gate 
28400Sstevel@tonic-gate 	return (ret);
28410Sstevel@tonic-gate }
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate /*
28440Sstevel@tonic-gate  * Tests outside the lock to see if we should bother to enqueue an entry
28450Sstevel@tonic-gate  * for any watching process.  If yes, then caller will take the lock and
28460Sstevel@tonic-gate  * do the full protocol
28470Sstevel@tonic-gate  */
28480Sstevel@tonic-gate static int
28490Sstevel@tonic-gate pm_watchers()
28500Sstevel@tonic-gate {
28510Sstevel@tonic-gate 	if (pm_processes_stopped)
28520Sstevel@tonic-gate 		return (0);
28530Sstevel@tonic-gate 	return (pm_pscc_direct || pm_pscc_interest);
28540Sstevel@tonic-gate }
28550Sstevel@tonic-gate 
28560Sstevel@tonic-gate /*
28570Sstevel@tonic-gate  * A driver is reporting that the power of one of its device's components
28580Sstevel@tonic-gate  * has changed.  Update the power state accordingly.
28590Sstevel@tonic-gate  */
28600Sstevel@tonic-gate int
28610Sstevel@tonic-gate pm_power_has_changed(dev_info_t *dip, int comp, int level)
28620Sstevel@tonic-gate {
28630Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_power_has_changed")
28640Sstevel@tonic-gate 	int ret;
28650Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
28660Sstevel@tonic-gate 	struct pm_component *cp;
28670Sstevel@tonic-gate 	int blocked, circ, pcirc, old_level;
28680Sstevel@tonic-gate 	static int pm_phc_impl(dev_info_t *, int, int, int);
28690Sstevel@tonic-gate 
28700Sstevel@tonic-gate 	if (level < 0) {
28710Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d): bad level=%d\n", pmf,
28720Sstevel@tonic-gate 		    PM_DEVICE(dip), level))
28730Sstevel@tonic-gate 		return (DDI_FAILURE);
28740Sstevel@tonic-gate 	}
28750Sstevel@tonic-gate 
28760Sstevel@tonic-gate 	PMD(PMD_KIDSUP | PMD_DEP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf,
28770Sstevel@tonic-gate 	    PM_DEVICE(dip), comp, level))
28780Sstevel@tonic-gate 
28790Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, &cp) ||
28800Sstevel@tonic-gate 	    !e_pm_valid_power(dip, comp, level))
28810Sstevel@tonic-gate 		return (DDI_FAILURE);
28820Sstevel@tonic-gate 
28830Sstevel@tonic-gate 	/*
28840Sstevel@tonic-gate 	 * A driver thread calling pm_power_has_changed and another thread
28850Sstevel@tonic-gate 	 * calling pm_set_power can deadlock.  The problem is not resolvable
28860Sstevel@tonic-gate 	 * by changing lock order, so we use pm_blocked_by_us() to detect
28870Sstevel@tonic-gate 	 * this specific deadlock.  If we can't get the lock immediately
28880Sstevel@tonic-gate 	 * and we are deadlocked, just update the component's level, do
28890Sstevel@tonic-gate 	 * notifications, and return.  We intend to update the total power
28900Sstevel@tonic-gate 	 * state later (if the other thread fails to set power to the
28910Sstevel@tonic-gate 	 * desired level).  If we were called because of a power change on a
28920Sstevel@tonic-gate 	 * component that isn't involved in a set_power op, update all state
28930Sstevel@tonic-gate 	 * immediately.
28940Sstevel@tonic-gate 	 */
28950Sstevel@tonic-gate 	cp = PM_CP(dip, comp);
28960Sstevel@tonic-gate 	while (!pm_try_parent_child_locks(pdip, dip, &pcirc, &circ)) {
28970Sstevel@tonic-gate 		if (((blocked = pm_blocked_by_us(dip)) != 0) &&
28980Sstevel@tonic-gate 		    (cp->pmc_flags & PM_POWER_OP)) {
28990Sstevel@tonic-gate 			if (pm_watchers()) {
29000Sstevel@tonic-gate 				mutex_enter(&pm_rsvp_lock);
29010Sstevel@tonic-gate 				pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp,
29020Sstevel@tonic-gate 				    level, cur_power(cp), PM_CANBLOCK_BLOCK);
29030Sstevel@tonic-gate 				mutex_exit(&pm_rsvp_lock);
29040Sstevel@tonic-gate 			}
29050Sstevel@tonic-gate 			if (pdip && PM_WANTS_NOTIFICATION(pdip))
29060Sstevel@tonic-gate 				pm_notify_parent(dip,
29070Sstevel@tonic-gate 				    pdip, comp, cur_power(cp), level);
29080Sstevel@tonic-gate 			(void) pm_check_and_resume(dip,
29090Sstevel@tonic-gate 			    comp, cur_power(cp), level);
29100Sstevel@tonic-gate 
29110Sstevel@tonic-gate 			/*
29120Sstevel@tonic-gate 			 * Stash the old power index, update curpwr, and flag
29130Sstevel@tonic-gate 			 * that the total power state needs to be synched.
29140Sstevel@tonic-gate 			 */
29150Sstevel@tonic-gate 			cp->pmc_flags |= PM_PHC_WHILE_SET_POWER;
29160Sstevel@tonic-gate 			/*
29170Sstevel@tonic-gate 			 * Several pm_power_has_changed calls could arrive
29180Sstevel@tonic-gate 			 * while the set power path remains blocked.  Keep the
29190Sstevel@tonic-gate 			 * oldest old power and the newest new power of any
29200Sstevel@tonic-gate 			 * sequence of phc calls which arrive during deadlock.
29210Sstevel@tonic-gate 			 */
29220Sstevel@tonic-gate 			if (cp->pmc_phc_pwr == PM_LEVEL_UNKNOWN)
29230Sstevel@tonic-gate 				cp->pmc_phc_pwr = cp->pmc_cur_pwr;
29240Sstevel@tonic-gate 			cp->pmc_cur_pwr =
29250Sstevel@tonic-gate 			    pm_level_to_index(dip, cp, level);
29260Sstevel@tonic-gate 			PMD(PMD_PHC, ("%s: deadlock for %s@%s(%s#%d), comp=%d, "
29270Sstevel@tonic-gate 			    "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
29280Sstevel@tonic-gate 			return (DDI_SUCCESS);
29290Sstevel@tonic-gate 		} else
29300Sstevel@tonic-gate 			if (blocked) {	/* blocked, but different cmpt? */
29310Sstevel@tonic-gate 				if (!ndi_devi_tryenter(pdip, &pcirc)) {
29320Sstevel@tonic-gate 					cmn_err(CE_NOTE,
29330Sstevel@tonic-gate 					    "!pm: parent kuc not updated due "
29340Sstevel@tonic-gate 					    "to possible deadlock.\n");
29350Sstevel@tonic-gate 					return (pm_phc_impl(dip,
29360Sstevel@tonic-gate 						    comp, level, 1));
29370Sstevel@tonic-gate 				}
29380Sstevel@tonic-gate 				old_level = cur_power(cp);
29390Sstevel@tonic-gate 				if (pdip && !PM_WANTS_NOTIFICATION(pdip) &&
29400Sstevel@tonic-gate 				    (!PM_ISBC(dip) || comp == 0) &&
29410Sstevel@tonic-gate 				    POWERING_ON(old_level, level))
29420Sstevel@tonic-gate 					pm_hold_power(pdip);
29430Sstevel@tonic-gate 				ret = pm_phc_impl(dip, comp, level, 1);
29440Sstevel@tonic-gate 				if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
29450Sstevel@tonic-gate 					if ((!PM_ISBC(dip) ||
29460Sstevel@tonic-gate 					    comp == 0) && level == 0 &&
29470Sstevel@tonic-gate 					    old_level != PM_LEVEL_UNKNOWN)
29480Sstevel@tonic-gate 						pm_rele_power(pdip);
29490Sstevel@tonic-gate 				}
29500Sstevel@tonic-gate 				ndi_devi_exit(pdip, pcirc);
29510Sstevel@tonic-gate 				/* child lock not held: deadlock */
29520Sstevel@tonic-gate 				return (ret);
29530Sstevel@tonic-gate 			}
29540Sstevel@tonic-gate 		delay(1);
29550Sstevel@tonic-gate 		PMD(PMD_PHC, ("%s: try lock again\n", pmf))
29560Sstevel@tonic-gate 	}
29570Sstevel@tonic-gate 
29580Sstevel@tonic-gate 	/* non-deadlock case */
29590Sstevel@tonic-gate 	old_level = cur_power(cp);
29600Sstevel@tonic-gate 	if (pdip && !PM_WANTS_NOTIFICATION(pdip) &&
29610Sstevel@tonic-gate 	    (!PM_ISBC(dip) || comp == 0) && POWERING_ON(old_level, level))
29620Sstevel@tonic-gate 		pm_hold_power(pdip);
29630Sstevel@tonic-gate 	ret = pm_phc_impl(dip, comp, level, 1);
29640Sstevel@tonic-gate 	if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
29650Sstevel@tonic-gate 		if ((!PM_ISBC(dip) || comp == 0) && level == 0 &&
29660Sstevel@tonic-gate 		    old_level != PM_LEVEL_UNKNOWN)
29670Sstevel@tonic-gate 			pm_rele_power(pdip);
29680Sstevel@tonic-gate 	}
29690Sstevel@tonic-gate 	PM_UNLOCK_POWER(dip, circ);
29700Sstevel@tonic-gate 	ndi_devi_exit(pdip, pcirc);
29710Sstevel@tonic-gate 	return (ret);
29720Sstevel@tonic-gate }
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate /*
29750Sstevel@tonic-gate  * Account for power changes to a component of the the console frame buffer.
29760Sstevel@tonic-gate  * If lowering power from full (or "unkown", which is treatd as full)
29770Sstevel@tonic-gate  * we will increment the "components off" count of the fb device.
29780Sstevel@tonic-gate  * Subsequent lowering of the same component doesn't affect the count.  If
29790Sstevel@tonic-gate  * raising a component back to full power, we will decrement the count.
29800Sstevel@tonic-gate  *
29810Sstevel@tonic-gate  * Return: the increment value for pm_cfb_comps_off (-1, 0, or 1)
29820Sstevel@tonic-gate  */
29830Sstevel@tonic-gate static int
29840Sstevel@tonic-gate calc_cfb_comps_incr(dev_info_t *dip, int cmpt, int old, int new)
29850Sstevel@tonic-gate {
29860Sstevel@tonic-gate 	struct pm_component *cp = PM_CP(dip, cmpt);
29870Sstevel@tonic-gate 	int on = (old == PM_LEVEL_UNKNOWN || old == cp->pmc_norm_pwr);
29880Sstevel@tonic-gate 	int want_normal = (new == cp->pmc_norm_pwr);
29890Sstevel@tonic-gate 	int incr = 0;
29900Sstevel@tonic-gate 
29910Sstevel@tonic-gate 	if (on && !want_normal)
29920Sstevel@tonic-gate 		incr = 1;
29930Sstevel@tonic-gate 	else if (!on && want_normal)
29940Sstevel@tonic-gate 		incr = -1;
29950Sstevel@tonic-gate 	return (incr);
29960Sstevel@tonic-gate }
29970Sstevel@tonic-gate 
29980Sstevel@tonic-gate /*
29990Sstevel@tonic-gate  * Adjust the count of console frame buffer components < full power.
30000Sstevel@tonic-gate  */
30010Sstevel@tonic-gate static void
30020Sstevel@tonic-gate update_comps_off(int incr, dev_info_t *dip)
30030Sstevel@tonic-gate {
30040Sstevel@tonic-gate 		mutex_enter(&pm_cfb_lock);
30050Sstevel@tonic-gate 		pm_cfb_comps_off += incr;
30060Sstevel@tonic-gate 		ASSERT(pm_cfb_comps_off <= PM_NUMCMPTS(dip));
30070Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
30080Sstevel@tonic-gate }
30090Sstevel@tonic-gate 
30100Sstevel@tonic-gate /*
30110Sstevel@tonic-gate  * Update the power state in the framework (via the ppm).  The 'notify'
30120Sstevel@tonic-gate  * argument tells whether to notify watchers.  Power lock is already held.
30130Sstevel@tonic-gate  */
30140Sstevel@tonic-gate static int
30150Sstevel@tonic-gate pm_phc_impl(dev_info_t *dip, int comp, int level, int notify)
30160Sstevel@tonic-gate {
30170Sstevel@tonic-gate 	PMD_FUNC(pmf, "phc_impl")
30180Sstevel@tonic-gate 	power_req_t power_req;
30190Sstevel@tonic-gate 	int i, dodeps = 0;
30200Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
30210Sstevel@tonic-gate 	int result;
30220Sstevel@tonic-gate 	int old_level;
30230Sstevel@tonic-gate 	struct pm_component *cp;
30240Sstevel@tonic-gate 	int incr = 0;
30250Sstevel@tonic-gate 	dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm;
30260Sstevel@tonic-gate 	int work_type = 0;
30270Sstevel@tonic-gate 	char *pathbuf;
30280Sstevel@tonic-gate 
30290Sstevel@tonic-gate 	/* Must use "official" power level for this test. */
30300Sstevel@tonic-gate 	cp = PM_CP(dip, comp);
30310Sstevel@tonic-gate 	old_level = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
30320Sstevel@tonic-gate 	    cp->pmc_phc_pwr : cp->pmc_cur_pwr);
30330Sstevel@tonic-gate 	if (old_level != PM_LEVEL_UNKNOWN)
30340Sstevel@tonic-gate 		old_level = cp->pmc_comp.pmc_lvals[old_level];
30350Sstevel@tonic-gate 
30360Sstevel@tonic-gate 	if (level == old_level) {
30370Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d is already at "
30380Sstevel@tonic-gate 		    "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
30390Sstevel@tonic-gate 		return (DDI_SUCCESS);
30400Sstevel@tonic-gate 	}
30410Sstevel@tonic-gate 
30420Sstevel@tonic-gate 	/*
30430Sstevel@tonic-gate 	 * Tell ppm about this.
30440Sstevel@tonic-gate 	 */
30450Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY;
30460Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.who = dip;
30470Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.cmpt = comp;
30480Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.new_level = level;
30490Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.old_level = old_level;
30500Sstevel@tonic-gate 	if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req,
30510Sstevel@tonic-gate 	    &result) == DDI_FAILURE) {
30520Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: pm_ctlops %s@%s(%s#%d) to %d failed\n",
30530Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), level))
30540Sstevel@tonic-gate 		return (DDI_FAILURE);
30550Sstevel@tonic-gate 	}
30560Sstevel@tonic-gate 
30570Sstevel@tonic-gate 	if (PM_IS_CFB(dip)) {
30580Sstevel@tonic-gate 		incr = calc_cfb_comps_incr(dip, comp, old_level, level);
30590Sstevel@tonic-gate 
30600Sstevel@tonic-gate 		if (incr) {
30610Sstevel@tonic-gate 			update_comps_off(incr, dip);
30620Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d) comp=%d %d->%d "
30630Sstevel@tonic-gate 			    "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
30640Sstevel@tonic-gate 			    comp, old_level, level, pm_cfb_comps_off))
30650Sstevel@tonic-gate 		}
30660Sstevel@tonic-gate 	}
30670Sstevel@tonic-gate 	e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level);
30680Sstevel@tonic-gate 	result = DDI_SUCCESS;
30690Sstevel@tonic-gate 
30700Sstevel@tonic-gate 	if (notify) {
30710Sstevel@tonic-gate 		if (pdip && PM_WANTS_NOTIFICATION(pdip))
30720Sstevel@tonic-gate 			pm_notify_parent(dip, pdip, comp, old_level, level);
30730Sstevel@tonic-gate 		(void) pm_check_and_resume(dip, comp, old_level, level);
30740Sstevel@tonic-gate 	}
30750Sstevel@tonic-gate 
30760Sstevel@tonic-gate 	/*
30770Sstevel@tonic-gate 	 * Decrement the dependency kidsup count if we turn a device
30780Sstevel@tonic-gate 	 * off.
30790Sstevel@tonic-gate 	 */
30800Sstevel@tonic-gate 	if (POWERING_OFF(old_level, level)) {
30810Sstevel@tonic-gate 		dodeps = 1;
30820Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++) {
30830Sstevel@tonic-gate 			cp = PM_CP(dip, i);
30840Sstevel@tonic-gate 			if (cur_power(cp)) {
30850Sstevel@tonic-gate 				dodeps = 0;
30860Sstevel@tonic-gate 				break;
30870Sstevel@tonic-gate 			}
30880Sstevel@tonic-gate 		}
30890Sstevel@tonic-gate 		if (dodeps)
30900Sstevel@tonic-gate 			work_type = PM_DEP_WK_POWER_OFF;
30910Sstevel@tonic-gate 	}
30920Sstevel@tonic-gate 
30930Sstevel@tonic-gate 	/*
30940Sstevel@tonic-gate 	 * Increment if we turn it on. Check to see
30950Sstevel@tonic-gate 	 * if other comps are already on, if so,
30960Sstevel@tonic-gate 	 * dont increment.
30970Sstevel@tonic-gate 	 */
30980Sstevel@tonic-gate 	if (POWERING_ON(old_level, level)) {
30990Sstevel@tonic-gate 		dodeps = 1;
31000Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++) {
31010Sstevel@tonic-gate 			cp = PM_CP(dip, i);
31020Sstevel@tonic-gate 			if (comp == i)
31030Sstevel@tonic-gate 				continue;
31040Sstevel@tonic-gate 			/* -1 also treated as 0 in this case */
31050Sstevel@tonic-gate 			if (cur_power(cp) > 0) {
31060Sstevel@tonic-gate 				dodeps = 0;
31070Sstevel@tonic-gate 				break;
31080Sstevel@tonic-gate 			}
31090Sstevel@tonic-gate 		}
31100Sstevel@tonic-gate 		if (dodeps)
31110Sstevel@tonic-gate 			work_type = PM_DEP_WK_POWER_ON;
31120Sstevel@tonic-gate 	}
31130Sstevel@tonic-gate 
31140Sstevel@tonic-gate 	if (dodeps) {
31150Sstevel@tonic-gate 		pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
31160Sstevel@tonic-gate 		(void) ddi_pathname(dip, pathbuf);
31170Sstevel@tonic-gate 		pm_dispatch_to_dep_thread(work_type, pathbuf, NULL,
31180Sstevel@tonic-gate 		    PM_DEP_NOWAIT, NULL, 0);
31190Sstevel@tonic-gate 		kmem_free(pathbuf, MAXPATHLEN);
31200Sstevel@tonic-gate 	}
31210Sstevel@tonic-gate 
31220Sstevel@tonic-gate 	if (notify && (level != old_level) && pm_watchers()) {
31230Sstevel@tonic-gate 		mutex_enter(&pm_rsvp_lock);
31240Sstevel@tonic-gate 		pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, level, old_level,
31250Sstevel@tonic-gate 		    PM_CANBLOCK_BLOCK);
31260Sstevel@tonic-gate 		mutex_exit(&pm_rsvp_lock);
31270Sstevel@tonic-gate 	}
31280Sstevel@tonic-gate 
31290Sstevel@tonic-gate 	PMD(PMD_RESCAN, ("%s: %s@%s(%s#%d): pm_rescan\n", pmf, PM_DEVICE(dip)))
31300Sstevel@tonic-gate 	pm_rescan(dip);
31310Sstevel@tonic-gate 	return (DDI_SUCCESS);
31320Sstevel@tonic-gate }
31330Sstevel@tonic-gate 
31340Sstevel@tonic-gate /*
31350Sstevel@tonic-gate  * This function is called at startup time to notify pm of the existence
31360Sstevel@tonic-gate  * of any platform power managers for this platform.  As a result of
31370Sstevel@tonic-gate  * this registration, each function provided will be called each time
31380Sstevel@tonic-gate  * a device node is attached, until one returns true, and it must claim the
31390Sstevel@tonic-gate  * device node (by returning non-zero) if it wants to be involved in the
31400Sstevel@tonic-gate  * node's power management.  If it does claim the node, then it will
31410Sstevel@tonic-gate  * subsequently be notified of attach and detach events.
31420Sstevel@tonic-gate  *
31430Sstevel@tonic-gate  */
31440Sstevel@tonic-gate 
31450Sstevel@tonic-gate int
31460Sstevel@tonic-gate pm_register_ppm(int (*func)(dev_info_t *), dev_info_t *dip)
31470Sstevel@tonic-gate {
31480Sstevel@tonic-gate 	PMD_FUNC(pmf, "register_ppm")
31490Sstevel@tonic-gate 	struct ppm_callbacks *ppmcp;
31500Sstevel@tonic-gate 	pm_component_t *cp;
31510Sstevel@tonic-gate 	int i, pwr, result, circ;
31520Sstevel@tonic-gate 	power_req_t power_req;
31530Sstevel@tonic-gate 	struct ppm_notify_level_req *p = &power_req.req.ppm_notify_level_req;
31540Sstevel@tonic-gate 	void pm_ppm_claim(dev_info_t *);
31550Sstevel@tonic-gate 
31560Sstevel@tonic-gate 	mutex_enter(&ppm_lock);
31570Sstevel@tonic-gate 	ppmcp = ppm_callbacks;
31580Sstevel@tonic-gate 	for (i = 0; i < MAX_PPM_HANDLERS; i++, ppmcp++) {
31590Sstevel@tonic-gate 		if (ppmcp->ppmc_func == NULL) {
31600Sstevel@tonic-gate 			ppmcp->ppmc_func = func;
31610Sstevel@tonic-gate 			ppmcp->ppmc_dip = dip;
31620Sstevel@tonic-gate 			break;
31630Sstevel@tonic-gate 		}
31640Sstevel@tonic-gate 	}
31650Sstevel@tonic-gate 	mutex_exit(&ppm_lock);
31660Sstevel@tonic-gate 
31670Sstevel@tonic-gate 	if (i >= MAX_PPM_HANDLERS)
31680Sstevel@tonic-gate 		return (DDI_FAILURE);
31690Sstevel@tonic-gate 	while ((dip = ddi_get_parent(dip)) != NULL) {
31700Sstevel@tonic-gate 		if (PM_GET_PM_INFO(dip) == NULL)
31710Sstevel@tonic-gate 			continue;
31720Sstevel@tonic-gate 		pm_ppm_claim(dip);
31730Sstevel@tonic-gate 		if (pm_ppm_claimed(dip)) {
31740Sstevel@tonic-gate 			/*
31750Sstevel@tonic-gate 			 * Tell ppm about this.
31760Sstevel@tonic-gate 			 */
31770Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY;
31780Sstevel@tonic-gate 			p->old_level = PM_LEVEL_UNKNOWN;
31790Sstevel@tonic-gate 			p->who = dip;
31800Sstevel@tonic-gate 			PM_LOCK_POWER(dip, &circ);
31810Sstevel@tonic-gate 			for (i = 0; i < PM_NUMCMPTS(dip); i++) {
31820Sstevel@tonic-gate 				cp = PM_CP(dip, i);
31830Sstevel@tonic-gate 				pwr = cp->pmc_cur_pwr;
31840Sstevel@tonic-gate 				if (pwr != PM_LEVEL_UNKNOWN) {
31850Sstevel@tonic-gate 					p->cmpt = i;
31860Sstevel@tonic-gate 					p->new_level = cur_power(cp);
31870Sstevel@tonic-gate 					p->old_level = PM_LEVEL_UNKNOWN;
31880Sstevel@tonic-gate 					if (pm_ctlops(PPM(dip), dip,
31890Sstevel@tonic-gate 					    DDI_CTLOPS_POWER, &power_req,
31900Sstevel@tonic-gate 					    &result) == DDI_FAILURE) {
31910Sstevel@tonic-gate 						PMD(PMD_FAIL, ("%s: pc "
31920Sstevel@tonic-gate 						    "%s@%s(%s#%d) to %d "
31930Sstevel@tonic-gate 						    "fails\n", pmf,
31940Sstevel@tonic-gate 						    PM_DEVICE(dip), pwr))
31950Sstevel@tonic-gate 					}
31960Sstevel@tonic-gate 				}
31970Sstevel@tonic-gate 			}
31980Sstevel@tonic-gate 			PM_UNLOCK_POWER(dip, circ);
31990Sstevel@tonic-gate 		}
32000Sstevel@tonic-gate 	}
32010Sstevel@tonic-gate 	return (DDI_SUCCESS);
32020Sstevel@tonic-gate }
32030Sstevel@tonic-gate 
32040Sstevel@tonic-gate /*
32050Sstevel@tonic-gate  * Call the ppm's that have registered and adjust the devinfo struct as
32060Sstevel@tonic-gate  * appropriate.  First one to claim it gets it.  The sets of devices claimed
32070Sstevel@tonic-gate  * by each ppm are assumed to be disjoint.
32080Sstevel@tonic-gate  */
32090Sstevel@tonic-gate void
32100Sstevel@tonic-gate pm_ppm_claim(dev_info_t *dip)
32110Sstevel@tonic-gate {
32120Sstevel@tonic-gate 	struct ppm_callbacks *ppmcp;
32130Sstevel@tonic-gate 
32140Sstevel@tonic-gate 	if (PPM(dip)) {
32150Sstevel@tonic-gate 		return;
32160Sstevel@tonic-gate 	}
32170Sstevel@tonic-gate 	mutex_enter(&ppm_lock);
32180Sstevel@tonic-gate 	for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) {
32190Sstevel@tonic-gate 		if ((*ppmcp->ppmc_func)(dip)) {
32200Sstevel@tonic-gate 			DEVI(dip)->devi_pm_ppm =
32210Sstevel@tonic-gate 			    (struct dev_info *)ppmcp->ppmc_dip;
32220Sstevel@tonic-gate 			mutex_exit(&ppm_lock);
32230Sstevel@tonic-gate 			return;
32240Sstevel@tonic-gate 		}
32250Sstevel@tonic-gate 	}
32260Sstevel@tonic-gate 	mutex_exit(&ppm_lock);
32270Sstevel@tonic-gate }
32280Sstevel@tonic-gate 
32290Sstevel@tonic-gate /*
32300Sstevel@tonic-gate  * Node is being detached so stop autopm until we see if it succeeds, in which
32310Sstevel@tonic-gate  * case pm_stop will be called.  For backwards compatible devices we bring the
32320Sstevel@tonic-gate  * device up to full power on the assumption the detach will succeed.
32330Sstevel@tonic-gate  */
32340Sstevel@tonic-gate void
32350Sstevel@tonic-gate pm_detaching(dev_info_t *dip)
32360Sstevel@tonic-gate {
32370Sstevel@tonic-gate 	PMD_FUNC(pmf, "detaching")
32380Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
32390Sstevel@tonic-gate 	int iscons;
32400Sstevel@tonic-gate 
32410Sstevel@tonic-gate 	PMD(PMD_REMDEV, ("%s: %s@%s(%s#%d), %d comps\n", pmf, PM_DEVICE(dip),
32420Sstevel@tonic-gate 	    PM_NUMCMPTS(dip)))
32430Sstevel@tonic-gate 	if (info == NULL)
32440Sstevel@tonic-gate 		return;
32450Sstevel@tonic-gate 	ASSERT(DEVI_IS_DETACHING(dip));
32460Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
32470Sstevel@tonic-gate 	info->pmi_dev_pm_state |= PM_DETACHING;
32480Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
32490Sstevel@tonic-gate 	if (!PM_ISBC(dip))
32500Sstevel@tonic-gate 		pm_scan_stop(dip);
32510Sstevel@tonic-gate 
32520Sstevel@tonic-gate 	/*
32530Sstevel@tonic-gate 	 * console and old-style devices get brought up when detaching.
32540Sstevel@tonic-gate 	 */
32550Sstevel@tonic-gate 	iscons = PM_IS_CFB(dip);
32560Sstevel@tonic-gate 	if (iscons || PM_ISBC(dip)) {
32570Sstevel@tonic-gate 		(void) pm_all_to_normal(dip, PM_CANBLOCK_BYPASS);
32580Sstevel@tonic-gate 		if (iscons) {
32590Sstevel@tonic-gate 			mutex_enter(&pm_cfb_lock);
32600Sstevel@tonic-gate 			while (cfb_inuse) {
32610Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
32620Sstevel@tonic-gate 				PMD(PMD_CFB, ("%s: delay; cfb_inuse\n", pmf))
32630Sstevel@tonic-gate 				delay(1);
32640Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
32650Sstevel@tonic-gate 			}
32660Sstevel@tonic-gate 			ASSERT(cfb_dip_detaching == NULL);
32670Sstevel@tonic-gate 			ASSERT(cfb_dip);
32680Sstevel@tonic-gate 			cfb_dip_detaching = cfb_dip;	/* case detach fails */
32690Sstevel@tonic-gate 			cfb_dip = NULL;
32700Sstevel@tonic-gate 			mutex_exit(&pm_cfb_lock);
32710Sstevel@tonic-gate 		}
32720Sstevel@tonic-gate 	}
32730Sstevel@tonic-gate }
32740Sstevel@tonic-gate 
32750Sstevel@tonic-gate /*
32760Sstevel@tonic-gate  * Node failed to detach.  If it used to be autopm'd, make it so again.
32770Sstevel@tonic-gate  */
32780Sstevel@tonic-gate void
32790Sstevel@tonic-gate pm_detach_failed(dev_info_t *dip)
32800Sstevel@tonic-gate {
32810Sstevel@tonic-gate 	PMD_FUNC(pmf, "detach_failed")
32820Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
32830Sstevel@tonic-gate 	int pm_all_at_normal(dev_info_t *);
32840Sstevel@tonic-gate 
32850Sstevel@tonic-gate 	if (info == NULL)
32860Sstevel@tonic-gate 		return;
32870Sstevel@tonic-gate 	ASSERT(DEVI_IS_DETACHING(dip));
32880Sstevel@tonic-gate 	if (info->pmi_dev_pm_state & PM_DETACHING) {
32890Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DETACHING;
32900Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_ALLNORM_DEFERRED) {
32910Sstevel@tonic-gate 			/* Make sure the operation is still needed */
32920Sstevel@tonic-gate 			if (!pm_all_at_normal(dip)) {
32930Sstevel@tonic-gate 				if (pm_all_to_normal(dip,
32940Sstevel@tonic-gate 				    PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
32950Sstevel@tonic-gate 					PMD(PMD_ERROR, ("%s: could not bring "
32960Sstevel@tonic-gate 					    "%s@%s(%s#%d) to normal\n", pmf,
32970Sstevel@tonic-gate 					    PM_DEVICE(dip)))
32980Sstevel@tonic-gate 				}
32990Sstevel@tonic-gate 			}
33000Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_ALLNORM_DEFERRED;
33010Sstevel@tonic-gate 		}
33020Sstevel@tonic-gate 	}
33030Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
33040Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
33050Sstevel@tonic-gate 		if (autopm_enabled)
33060Sstevel@tonic-gate 			pm_scan_init(dip);
33070Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
33080Sstevel@tonic-gate 		pm_rescan(dip);
33090Sstevel@tonic-gate 	}
33100Sstevel@tonic-gate }
33110Sstevel@tonic-gate 
33120Sstevel@tonic-gate /* generic Backwards Compatible component */
33130Sstevel@tonic-gate static char *bc_names[] = {"off", "on"};
33140Sstevel@tonic-gate 
33150Sstevel@tonic-gate static pm_comp_t bc_comp = {"unknown", 2, NULL, NULL, &bc_names[0]};
33160Sstevel@tonic-gate 
33170Sstevel@tonic-gate static void
33180Sstevel@tonic-gate e_pm_default_levels(dev_info_t *dip, pm_component_t *cp, int norm)
33190Sstevel@tonic-gate {
33200Sstevel@tonic-gate 	pm_comp_t *pmc;
33210Sstevel@tonic-gate 	pmc = &cp->pmc_comp;
33220Sstevel@tonic-gate 	pmc->pmc_numlevels = 2;
33230Sstevel@tonic-gate 	pmc->pmc_lvals[0] = 0;
33240Sstevel@tonic-gate 	pmc->pmc_lvals[1] = norm;
33250Sstevel@tonic-gate 	e_pm_set_cur_pwr(dip, cp, norm);
33260Sstevel@tonic-gate }
33270Sstevel@tonic-gate 
33280Sstevel@tonic-gate static void
33290Sstevel@tonic-gate e_pm_default_components(dev_info_t *dip, int cmpts)
33300Sstevel@tonic-gate {
33310Sstevel@tonic-gate 	int i;
33320Sstevel@tonic-gate 	pm_component_t *p = DEVI(dip)->devi_pm_components;
33330Sstevel@tonic-gate 
33340Sstevel@tonic-gate 	p = DEVI(dip)->devi_pm_components;
33350Sstevel@tonic-gate 	for (i = 0; i < cmpts; i++, p++) {
33360Sstevel@tonic-gate 		p->pmc_comp = bc_comp;	/* struct assignment */
33370Sstevel@tonic-gate 		p->pmc_comp.pmc_lvals = kmem_zalloc(2 * sizeof (int),
33380Sstevel@tonic-gate 		    KM_SLEEP);
33390Sstevel@tonic-gate 		p->pmc_comp.pmc_thresh = kmem_alloc(2 * sizeof (int),
33400Sstevel@tonic-gate 		    KM_SLEEP);
33410Sstevel@tonic-gate 		p->pmc_comp.pmc_numlevels = 2;
33420Sstevel@tonic-gate 		p->pmc_comp.pmc_thresh[0] = INT_MAX;
33430Sstevel@tonic-gate 		p->pmc_comp.pmc_thresh[1] = INT_MAX;
33440Sstevel@tonic-gate 	}
33450Sstevel@tonic-gate }
33460Sstevel@tonic-gate 
33470Sstevel@tonic-gate /*
33480Sstevel@tonic-gate  * Called from functions that require components to exist already to allow
33490Sstevel@tonic-gate  * for their creation by parsing the pm-components property.
33500Sstevel@tonic-gate  * Device will not be power managed as a result of this call
33510Sstevel@tonic-gate  * No locking needed because we're single threaded by the ndi_devi_enter
33520Sstevel@tonic-gate  * done while attaching, and the device isn't visible until after it has
33530Sstevel@tonic-gate  * attached
33540Sstevel@tonic-gate  */
33550Sstevel@tonic-gate int
33560Sstevel@tonic-gate pm_premanage(dev_info_t *dip, int style)
33570Sstevel@tonic-gate {
33580Sstevel@tonic-gate 	PMD_FUNC(pmf, "premanage")
33590Sstevel@tonic-gate 	pm_comp_t	*pcp, *compp;
33600Sstevel@tonic-gate 	int		cmpts, i, norm, error;
33610Sstevel@tonic-gate 	pm_component_t *p = DEVI(dip)->devi_pm_components;
33620Sstevel@tonic-gate 	pm_comp_t *pm_autoconfig(dev_info_t *, int *);
33630Sstevel@tonic-gate 
33640Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
33650Sstevel@tonic-gate 	/*
33660Sstevel@tonic-gate 	 * If this dip has already been processed, don't mess with it
33670Sstevel@tonic-gate 	 */
33680Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE)
33690Sstevel@tonic-gate 		return (DDI_SUCCESS);
33700Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_FAILED) {
33710Sstevel@tonic-gate 		return (DDI_FAILURE);
33720Sstevel@tonic-gate 	}
33730Sstevel@tonic-gate 	/*
33740Sstevel@tonic-gate 	 * Look up pm-components property and create components accordingly
33750Sstevel@tonic-gate 	 * If that fails, fall back to backwards compatibility
33760Sstevel@tonic-gate 	 */
33770Sstevel@tonic-gate 	if ((compp = pm_autoconfig(dip, &error)) == NULL) {
33780Sstevel@tonic-gate 		/*
33790Sstevel@tonic-gate 		 * If error is set, the property existed but was not well formed
33800Sstevel@tonic-gate 		 */
33810Sstevel@tonic-gate 		if (error || (style == PM_STYLE_NEW)) {
33820Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_FAILED;
33830Sstevel@tonic-gate 			return (DDI_FAILURE);
33840Sstevel@tonic-gate 		}
33850Sstevel@tonic-gate 		/*
33860Sstevel@tonic-gate 		 * If they don't have the pm-components property, then we
33870Sstevel@tonic-gate 		 * want the old "no pm until PM_SET_DEVICE_THRESHOLDS ioctl"
33880Sstevel@tonic-gate 		 * behavior driver must have called pm_create_components, and
33890Sstevel@tonic-gate 		 * we need to flesh out dummy components
33900Sstevel@tonic-gate 		 */
33910Sstevel@tonic-gate 		if ((cmpts = PM_NUMCMPTS(dip)) == 0) {
33920Sstevel@tonic-gate 			/*
33930Sstevel@tonic-gate 			 * Not really failure, but we don't want the
33940Sstevel@tonic-gate 			 * caller to treat it as success
33950Sstevel@tonic-gate 			 */
33960Sstevel@tonic-gate 			return (DDI_FAILURE);
33970Sstevel@tonic-gate 		}
33980Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags |= PMC_BC;
33990Sstevel@tonic-gate 		e_pm_default_components(dip, cmpts);
34000Sstevel@tonic-gate 		for (i = 0; i < cmpts; i++) {
34010Sstevel@tonic-gate 			/*
34020Sstevel@tonic-gate 			 * if normal power not set yet, we don't really know
34030Sstevel@tonic-gate 			 * what *ANY* of the power values are.  If normal
34040Sstevel@tonic-gate 			 * power is set, then we assume for this backwards
34050Sstevel@tonic-gate 			 * compatible case that the values are 0, normal power.
34060Sstevel@tonic-gate 			 */
34070Sstevel@tonic-gate 			norm = pm_get_normal_power(dip, i);
34080Sstevel@tonic-gate 			if (norm == (uint_t)-1) {
34090Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: %s@%s(%s#%d)[%d]\n", pmf,
34100Sstevel@tonic-gate 				    PM_DEVICE(dip), i))
34110Sstevel@tonic-gate 				return (DDI_FAILURE);
34120Sstevel@tonic-gate 			}
34130Sstevel@tonic-gate 			/*
34140Sstevel@tonic-gate 			 * Components of BC devices start at their normal power,
34150Sstevel@tonic-gate 			 * so count them to be not at their lowest power.
34160Sstevel@tonic-gate 			 */
34170Sstevel@tonic-gate 			PM_INCR_NOTLOWEST(dip);
34180Sstevel@tonic-gate 			e_pm_default_levels(dip, PM_CP(dip, i), norm);
34190Sstevel@tonic-gate 		}
34200Sstevel@tonic-gate 	} else {
34210Sstevel@tonic-gate 		/*
34220Sstevel@tonic-gate 		 * e_pm_create_components was called from pm_autoconfig(), it
34230Sstevel@tonic-gate 		 * creates components with no descriptions (or known levels)
34240Sstevel@tonic-gate 		 */
34250Sstevel@tonic-gate 		cmpts = PM_NUMCMPTS(dip);
34260Sstevel@tonic-gate 		ASSERT(cmpts != 0);
34270Sstevel@tonic-gate 		pcp = compp;
34280Sstevel@tonic-gate 		p = DEVI(dip)->devi_pm_components;
34290Sstevel@tonic-gate 		for (i = 0; i < cmpts; i++, p++) {
34300Sstevel@tonic-gate 			p->pmc_comp = *pcp++;   /* struct assignment */
34310Sstevel@tonic-gate 			ASSERT(PM_CP(dip, i)->pmc_cur_pwr == 0);
34320Sstevel@tonic-gate 			e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN);
34330Sstevel@tonic-gate 		}
34340Sstevel@tonic-gate 		pm_set_device_threshold(dip, pm_system_idle_threshold,
34350Sstevel@tonic-gate 		    PMC_DEF_THRESH);
34360Sstevel@tonic-gate 		kmem_free(compp, cmpts * sizeof (pm_comp_t));
34370Sstevel@tonic-gate 	}
34380Sstevel@tonic-gate 	return (DDI_SUCCESS);
34390Sstevel@tonic-gate }
34400Sstevel@tonic-gate 
34410Sstevel@tonic-gate /*
34420Sstevel@tonic-gate  * Called from during or after the device's attach to let us know it is ready
34430Sstevel@tonic-gate  * to play autopm.   Look up the pm model and manage the device accordingly.
34440Sstevel@tonic-gate  * Returns system call errno value.
34450Sstevel@tonic-gate  * If DDI_ATTACH and DDI_DETACH were in same namespace, this would be
34460Sstevel@tonic-gate  * a little cleaner
34470Sstevel@tonic-gate  *
34480Sstevel@tonic-gate  * Called with dip lock held, return with dip lock unheld.
34490Sstevel@tonic-gate  */
34500Sstevel@tonic-gate 
34510Sstevel@tonic-gate int
34520Sstevel@tonic-gate e_pm_manage(dev_info_t *dip, int style)
34530Sstevel@tonic-gate {
34540Sstevel@tonic-gate 	PMD_FUNC(pmf, "e_manage")
34550Sstevel@tonic-gate 	pm_info_t	*info;
34560Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
34570Sstevel@tonic-gate 	int	pm_thresh_specd(dev_info_t *);
34580Sstevel@tonic-gate 	int	count;
34590Sstevel@tonic-gate 	char	*pathbuf;
34600Sstevel@tonic-gate 
34610Sstevel@tonic-gate 	if (pm_premanage(dip, style) != DDI_SUCCESS) {
34620Sstevel@tonic-gate 		return (DDI_FAILURE);
34630Sstevel@tonic-gate 	}
34640Sstevel@tonic-gate 	PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
34650Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip) == NULL);
34660Sstevel@tonic-gate 	info = kmem_zalloc(sizeof (pm_info_t), KM_SLEEP);
34670Sstevel@tonic-gate 
34680Sstevel@tonic-gate 	/*
34690Sstevel@tonic-gate 	 * Now set up parent's kidsupcnt.  BC nodes are assumed to start
34700Sstevel@tonic-gate 	 * out at their normal power, so they are "up", others start out
34710Sstevel@tonic-gate 	 * unknown, which is effectively "up".  Parent which want notification
34720Sstevel@tonic-gate 	 * get kidsupcnt of 0 always.
34730Sstevel@tonic-gate 	 */
34740Sstevel@tonic-gate 	count = (PM_ISBC(dip)) ? 1 : PM_NUMCMPTS(dip);
34750Sstevel@tonic-gate 	if (count && pdip && !PM_WANTS_NOTIFICATION(pdip))
34760Sstevel@tonic-gate 		e_pm_hold_rele_power(pdip, count);
34770Sstevel@tonic-gate 
34780Sstevel@tonic-gate 	pm_set_pm_info(dip, info);
34790Sstevel@tonic-gate 	/*
34800Sstevel@tonic-gate 	 * Apply any recorded thresholds
34810Sstevel@tonic-gate 	 */
34820Sstevel@tonic-gate 	(void) pm_thresh_specd(dip);
34830Sstevel@tonic-gate 
34840Sstevel@tonic-gate 	/*
34850Sstevel@tonic-gate 	 * Do dependency processing.
34860Sstevel@tonic-gate 	 */
34870Sstevel@tonic-gate 	pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
34880Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
34890Sstevel@tonic-gate 	pm_dispatch_to_dep_thread(PM_DEP_WK_ATTACH, pathbuf, pathbuf,
34900Sstevel@tonic-gate 	    PM_DEP_NOWAIT, NULL, 0);
34910Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
34920Sstevel@tonic-gate 
34930Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
34940Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
34950Sstevel@tonic-gate 		if (autopm_enabled) {
34960Sstevel@tonic-gate 			pm_scan_init(dip);
34970Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
34980Sstevel@tonic-gate 			pm_rescan(dip);
34990Sstevel@tonic-gate 		} else {
35000Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
35010Sstevel@tonic-gate 		}
35020Sstevel@tonic-gate 	}
35030Sstevel@tonic-gate 	return (0);
35040Sstevel@tonic-gate }
35050Sstevel@tonic-gate 
35060Sstevel@tonic-gate /*
35070Sstevel@tonic-gate  * This is the obsolete exported interface for a driver to find out its
35080Sstevel@tonic-gate  * "normal" (max) power.
35090Sstevel@tonic-gate  * We only get components destroyed while no power management is
35100Sstevel@tonic-gate  * going on (and the device is detached), so we don't need a mutex here
35110Sstevel@tonic-gate  */
35120Sstevel@tonic-gate int
35130Sstevel@tonic-gate pm_get_normal_power(dev_info_t *dip, int comp)
35140Sstevel@tonic-gate {
35150Sstevel@tonic-gate 
35160Sstevel@tonic-gate 	if (comp >= 0 && comp < PM_NUMCMPTS(dip)) {
35170Sstevel@tonic-gate 		return (PM_CP(dip, comp)->pmc_norm_pwr);
35180Sstevel@tonic-gate 	}
35190Sstevel@tonic-gate 	return (DDI_FAILURE);
35200Sstevel@tonic-gate }
35210Sstevel@tonic-gate 
35220Sstevel@tonic-gate /*
35230Sstevel@tonic-gate  * Fetches the current power level.  Return DDI_SUCCESS or DDI_FAILURE.
35240Sstevel@tonic-gate  */
35250Sstevel@tonic-gate int
35260Sstevel@tonic-gate pm_get_current_power(dev_info_t *dip, int comp, int *levelp)
35270Sstevel@tonic-gate {
35280Sstevel@tonic-gate 	if (comp >= 0 && comp < PM_NUMCMPTS(dip)) {
35290Sstevel@tonic-gate 		*levelp = PM_CURPOWER(dip, comp);
35300Sstevel@tonic-gate 		return (DDI_SUCCESS);
35310Sstevel@tonic-gate 	}
35320Sstevel@tonic-gate 	return (DDI_FAILURE);
35330Sstevel@tonic-gate }
35340Sstevel@tonic-gate 
35350Sstevel@tonic-gate /*
35360Sstevel@tonic-gate  * Returns current threshold of indicated component
35370Sstevel@tonic-gate  */
35380Sstevel@tonic-gate static int
35390Sstevel@tonic-gate cur_threshold(dev_info_t *dip, int comp)
35400Sstevel@tonic-gate {
35410Sstevel@tonic-gate 	pm_component_t *cp = PM_CP(dip, comp);
35420Sstevel@tonic-gate 	int pwr;
35430Sstevel@tonic-gate 
35440Sstevel@tonic-gate 	if (PM_ISBC(dip)) {
35450Sstevel@tonic-gate 		/*
35460Sstevel@tonic-gate 		 * backwards compatible nodes only have one threshold
35470Sstevel@tonic-gate 		 */
35480Sstevel@tonic-gate 		return (cp->pmc_comp.pmc_thresh[1]);
35490Sstevel@tonic-gate 	}
35500Sstevel@tonic-gate 	pwr = cp->pmc_cur_pwr;
35510Sstevel@tonic-gate 	if (pwr == PM_LEVEL_UNKNOWN) {
35520Sstevel@tonic-gate 		int thresh;
35530Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH)
35540Sstevel@tonic-gate 			thresh = pm_default_nexus_threshold;
35550Sstevel@tonic-gate 		else
35560Sstevel@tonic-gate 			thresh = pm_system_idle_threshold;
35570Sstevel@tonic-gate 		return (thresh);
35580Sstevel@tonic-gate 	}
35590Sstevel@tonic-gate 	ASSERT(cp->pmc_comp.pmc_thresh);
35600Sstevel@tonic-gate 	return (cp->pmc_comp.pmc_thresh[pwr]);
35610Sstevel@tonic-gate }
35620Sstevel@tonic-gate 
35630Sstevel@tonic-gate /*
35640Sstevel@tonic-gate  * Compute next lower component power level given power index.
35650Sstevel@tonic-gate  */
35660Sstevel@tonic-gate static int
35670Sstevel@tonic-gate pm_next_lower_power(pm_component_t *cp, int pwrndx)
35680Sstevel@tonic-gate {
35690Sstevel@tonic-gate 	int nxt_pwr;
35700Sstevel@tonic-gate 
35710Sstevel@tonic-gate 	if (pwrndx == PM_LEVEL_UNKNOWN) {
35720Sstevel@tonic-gate 		nxt_pwr = cp->pmc_comp.pmc_lvals[0];
35730Sstevel@tonic-gate 	} else {
35740Sstevel@tonic-gate 		pwrndx--;
35750Sstevel@tonic-gate 		ASSERT(pwrndx >= 0);
35760Sstevel@tonic-gate 		nxt_pwr = cp->pmc_comp.pmc_lvals[pwrndx];
35770Sstevel@tonic-gate 	}
35780Sstevel@tonic-gate 	return (nxt_pwr);
35790Sstevel@tonic-gate }
35800Sstevel@tonic-gate 
35810Sstevel@tonic-gate /*
35820Sstevel@tonic-gate  * Bring all components of device to normal power
35830Sstevel@tonic-gate  */
35840Sstevel@tonic-gate int
35850Sstevel@tonic-gate pm_all_to_normal(dev_info_t *dip, pm_canblock_t canblock)
35860Sstevel@tonic-gate {
35870Sstevel@tonic-gate 	PMD_FUNC(pmf, "all_to_normal")
35880Sstevel@tonic-gate 	int		*normal;
35890Sstevel@tonic-gate 	int		i, ncomps, result;
35900Sstevel@tonic-gate 	size_t		size;
35910Sstevel@tonic-gate 	int		changefailed = 0;
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
35940Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip));
35950Sstevel@tonic-gate 	if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
35960Sstevel@tonic-gate 		PMD(PMD_ALLNORM, ("%s: can't get norm pwrs for "
35970Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
35980Sstevel@tonic-gate 		return (DDI_FAILURE);
35990Sstevel@tonic-gate 	}
36000Sstevel@tonic-gate 	ncomps = PM_NUMCMPTS(dip);
36010Sstevel@tonic-gate 	for (i = 0; i < ncomps; i++) {
36020Sstevel@tonic-gate 		if (pm_set_power(dip, i, normal[i],
36030Sstevel@tonic-gate 		    PM_LEVEL_UPONLY, canblock, 0, &result) != DDI_SUCCESS) {
36040Sstevel@tonic-gate 			changefailed++;
36050Sstevel@tonic-gate 			PMD(PMD_ALLNORM | PMD_FAIL, ("%s: failed to set "
36060Sstevel@tonic-gate 			    "%s@%s(%s#%d)[%d] to %d, errno %d\n", pmf,
36070Sstevel@tonic-gate 			    PM_DEVICE(dip), i, normal[i], result))
36080Sstevel@tonic-gate 		}
36090Sstevel@tonic-gate 	}
36100Sstevel@tonic-gate 	kmem_free(normal, size);
36110Sstevel@tonic-gate 	if (changefailed) {
36120Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) "
36130Sstevel@tonic-gate 		    "to full power\n", pmf, changefailed, PM_DEVICE(dip)))
36140Sstevel@tonic-gate 		return (DDI_FAILURE);
36150Sstevel@tonic-gate 	}
36160Sstevel@tonic-gate 	return (DDI_SUCCESS);
36170Sstevel@tonic-gate }
36180Sstevel@tonic-gate 
36190Sstevel@tonic-gate /*
36200Sstevel@tonic-gate  * Returns true if all components of device are at normal power
36210Sstevel@tonic-gate  */
36220Sstevel@tonic-gate int
36230Sstevel@tonic-gate pm_all_at_normal(dev_info_t *dip)
36240Sstevel@tonic-gate {
36250Sstevel@tonic-gate 	PMD_FUNC(pmf, "all_at_normal")
36260Sstevel@tonic-gate 	int		*normal;
36270Sstevel@tonic-gate 	int		i;
36280Sstevel@tonic-gate 	size_t		size;
36290Sstevel@tonic-gate 
36300Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
36310Sstevel@tonic-gate 	if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
36320Sstevel@tonic-gate 		PMD(PMD_ALLNORM, ("%s: can't get normal power\n", pmf))
36330Sstevel@tonic-gate 		return (DDI_FAILURE);
36340Sstevel@tonic-gate 	}
36350Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
36360Sstevel@tonic-gate 		int current = PM_CURPOWER(dip, i);
36370Sstevel@tonic-gate 		if (normal[i] > current) {
36380Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d) comp=%d, "
36390Sstevel@tonic-gate 			    "norm=%d, cur=%d\n", pmf, PM_DEVICE(dip), i,
36400Sstevel@tonic-gate 			    normal[i], current))
36410Sstevel@tonic-gate 			break;
36420Sstevel@tonic-gate 		}
36430Sstevel@tonic-gate 	}
36440Sstevel@tonic-gate 	kmem_free(normal, size);
36450Sstevel@tonic-gate 	if (i != PM_NUMCMPTS(dip)) {
36460Sstevel@tonic-gate 		return (0);
36470Sstevel@tonic-gate 	}
36480Sstevel@tonic-gate 	return (1);
36490Sstevel@tonic-gate }
36500Sstevel@tonic-gate 
36510Sstevel@tonic-gate static void
36520Sstevel@tonic-gate bring_wekeeps_up(char *keeper)
36530Sstevel@tonic-gate {
36540Sstevel@tonic-gate 	PMD_FUNC(pmf, "bring_wekeeps_up")
36550Sstevel@tonic-gate 	int i;
36560Sstevel@tonic-gate 	pm_pdr_t *dp;
36570Sstevel@tonic-gate 	pm_info_t *wku_info;
36580Sstevel@tonic-gate 	char *kept_path;
36590Sstevel@tonic-gate 	dev_info_t *kept;
36600Sstevel@tonic-gate 	static void bring_pmdep_up(dev_info_t *, int);
36610Sstevel@tonic-gate 
36620Sstevel@tonic-gate 	if (panicstr) {
36630Sstevel@tonic-gate 		return;
36640Sstevel@tonic-gate 	}
36650Sstevel@tonic-gate 	/*
36660Sstevel@tonic-gate 	 * We process the request even if the keeper detaches because
36670Sstevel@tonic-gate 	 * detach processing expects this to increment kidsupcnt of kept.
36680Sstevel@tonic-gate 	 */
36690Sstevel@tonic-gate 	PMD(PMD_BRING, ("%s: keeper= %s\n", pmf, keeper))
36700Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
36710Sstevel@tonic-gate 		if (strcmp(dp->pdr_keeper, keeper) != 0)
36720Sstevel@tonic-gate 			continue;
36730Sstevel@tonic-gate 		for (i = 0; i < dp->pdr_kept_count; i++) {
36740Sstevel@tonic-gate 			kept_path = dp->pdr_kept_paths[i];
36750Sstevel@tonic-gate 			if (kept_path == NULL)
36760Sstevel@tonic-gate 				continue;
36770Sstevel@tonic-gate 			ASSERT(kept_path[0] != '\0');
36780Sstevel@tonic-gate 			if ((kept = pm_name_to_dip(kept_path, 1)) == NULL)
36790Sstevel@tonic-gate 				continue;
36800Sstevel@tonic-gate 			wku_info = PM_GET_PM_INFO(kept);
36810Sstevel@tonic-gate 			if (wku_info == NULL) {
36820Sstevel@tonic-gate 				if (kept)
36830Sstevel@tonic-gate 					ddi_release_devi(kept);
36840Sstevel@tonic-gate 				continue;
36850Sstevel@tonic-gate 			}
36860Sstevel@tonic-gate 			/*
36870Sstevel@tonic-gate 			 * Don't mess with it if it is being detached, it isn't
36880Sstevel@tonic-gate 			 * safe to call its power entry point
36890Sstevel@tonic-gate 			 */
36900Sstevel@tonic-gate 			if (wku_info->pmi_dev_pm_state & PM_DETACHING) {
36910Sstevel@tonic-gate 				if (kept)
36920Sstevel@tonic-gate 					ddi_release_devi(kept);
36930Sstevel@tonic-gate 				continue;
36940Sstevel@tonic-gate 			}
36950Sstevel@tonic-gate 			bring_pmdep_up(kept, 1);
36960Sstevel@tonic-gate 			ddi_release_devi(kept);
36970Sstevel@tonic-gate 		}
36980Sstevel@tonic-gate 	}
36990Sstevel@tonic-gate }
37000Sstevel@tonic-gate 
37010Sstevel@tonic-gate /*
37020Sstevel@tonic-gate  * Bring up the 'kept' device passed as argument
37030Sstevel@tonic-gate  */
37040Sstevel@tonic-gate static void
37050Sstevel@tonic-gate bring_pmdep_up(dev_info_t *kept_dip, int hold)
37060Sstevel@tonic-gate {
37070Sstevel@tonic-gate 	PMD_FUNC(pmf, "bring_pmdep_up")
37080Sstevel@tonic-gate 	int is_all_at_normal = 0;
37090Sstevel@tonic-gate 
37100Sstevel@tonic-gate 	/*
37110Sstevel@tonic-gate 	 * If the kept device has been unmanaged, do nothing.
37120Sstevel@tonic-gate 	 */
37130Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(kept_dip))
37140Sstevel@tonic-gate 		return;
37150Sstevel@tonic-gate 
37160Sstevel@tonic-gate 	/* Just ignore DIRECT PM device till they are released. */
37170Sstevel@tonic-gate 	if (!pm_processes_stopped && PM_ISDIRECT(kept_dip) &&
37180Sstevel@tonic-gate 	    !(is_all_at_normal = pm_all_at_normal(kept_dip))) {
37190Sstevel@tonic-gate 		PMD(PMD_BRING, ("%s: can't bring up PM_DIRECT %s@%s(%s#%d) "
37200Sstevel@tonic-gate 		    "controlling process did something else\n", pmf,
37210Sstevel@tonic-gate 		    PM_DEVICE(kept_dip)))
37220Sstevel@tonic-gate 		DEVI(kept_dip)->devi_pm_flags |= PMC_SKIP_BRINGUP;
37230Sstevel@tonic-gate 		return;
37240Sstevel@tonic-gate 	}
37250Sstevel@tonic-gate 	/* if we got here the keeper had a transition from OFF->ON */
37260Sstevel@tonic-gate 	if (hold)
37270Sstevel@tonic-gate 		pm_hold_power(kept_dip);
37280Sstevel@tonic-gate 
37290Sstevel@tonic-gate 	if (!is_all_at_normal)
37300Sstevel@tonic-gate 		(void) pm_all_to_normal(kept_dip, PM_CANBLOCK_FAIL);
37310Sstevel@tonic-gate }
37320Sstevel@tonic-gate 
37330Sstevel@tonic-gate /*
37340Sstevel@tonic-gate  * A bunch of stuff that belongs only to the next routine (or two)
37350Sstevel@tonic-gate  */
37360Sstevel@tonic-gate 
37370Sstevel@tonic-gate static const char namestr[] = "NAME=";
37380Sstevel@tonic-gate static const int nameln = sizeof (namestr) - 1;
37390Sstevel@tonic-gate static const char pmcompstr[] = "pm-components";
37400Sstevel@tonic-gate 
37410Sstevel@tonic-gate struct pm_comp_pkg {
37420Sstevel@tonic-gate 	pm_comp_t		*comp;
37430Sstevel@tonic-gate 	struct pm_comp_pkg	*next;
37440Sstevel@tonic-gate };
37450Sstevel@tonic-gate 
37460Sstevel@tonic-gate #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
37470Sstevel@tonic-gate 
37480Sstevel@tonic-gate #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
37490Sstevel@tonic-gate 			((ch) >= 'A' && (ch) <= 'F'))
37500Sstevel@tonic-gate 
37510Sstevel@tonic-gate /*
37520Sstevel@tonic-gate  * Rather than duplicate this code ...
37530Sstevel@tonic-gate  * (this code excerpted from the function that follows it)
37540Sstevel@tonic-gate  */
37550Sstevel@tonic-gate #define	FINISH_COMP { \
37560Sstevel@tonic-gate 	ASSERT(compp); \
37570Sstevel@tonic-gate 	compp->pmc_lnames_sz = size; \
37580Sstevel@tonic-gate 	tp = compp->pmc_lname_buf = kmem_alloc(size, KM_SLEEP); \
37590Sstevel@tonic-gate 	compp->pmc_numlevels = level; \
37600Sstevel@tonic-gate 	compp->pmc_lnames = kmem_alloc(level * sizeof (char *), KM_SLEEP); \
37610Sstevel@tonic-gate 	compp->pmc_lvals = kmem_alloc(level * sizeof (int), KM_SLEEP); \
37620Sstevel@tonic-gate 	compp->pmc_thresh = kmem_alloc(level * sizeof (int), KM_SLEEP); \
37630Sstevel@tonic-gate 	/* copy string out of prop array into buffer */ \
37640Sstevel@tonic-gate 	for (j = 0; j < level; j++) { \
37650Sstevel@tonic-gate 		compp->pmc_thresh[j] = INT_MAX;		/* only [0] sticks */ \
37660Sstevel@tonic-gate 		compp->pmc_lvals[j] = lvals[j]; \
37670Sstevel@tonic-gate 		(void) strcpy(tp, lnames[j]); \
37680Sstevel@tonic-gate 		compp->pmc_lnames[j] = tp; \
37690Sstevel@tonic-gate 		tp += lszs[j]; \
37700Sstevel@tonic-gate 	} \
37710Sstevel@tonic-gate 	ASSERT(tp > compp->pmc_lname_buf && tp <= \
37720Sstevel@tonic-gate 	    compp->pmc_lname_buf + compp->pmc_lnames_sz); \
37730Sstevel@tonic-gate 	}
37740Sstevel@tonic-gate 
37750Sstevel@tonic-gate /*
37760Sstevel@tonic-gate  * Create (empty) component data structures.
37770Sstevel@tonic-gate  */
37780Sstevel@tonic-gate static void
37790Sstevel@tonic-gate e_pm_create_components(dev_info_t *dip, int num_components)
37800Sstevel@tonic-gate {
37810Sstevel@tonic-gate 	struct pm_component *compp, *ocompp;
37820Sstevel@tonic-gate 	int i, size = 0;
37830Sstevel@tonic-gate 
37840Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
37850Sstevel@tonic-gate 	ASSERT(!DEVI(dip)->devi_pm_components);
37860Sstevel@tonic-gate 	ASSERT(!(DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE));
37870Sstevel@tonic-gate 	size = sizeof (struct pm_component) * num_components;
37880Sstevel@tonic-gate 
37890Sstevel@tonic-gate 	compp = kmem_zalloc(size, KM_SLEEP);
37900Sstevel@tonic-gate 	ocompp = compp;
37910Sstevel@tonic-gate 	DEVI(dip)->devi_pm_comp_size = size;
37920Sstevel@tonic-gate 	DEVI(dip)->devi_pm_num_components = num_components;
37930Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
37940Sstevel@tonic-gate 	for (i = 0; i < num_components;  i++) {
37950Sstevel@tonic-gate 		compp->pmc_timestamp = gethrestime_sec();
37960Sstevel@tonic-gate 		compp->pmc_norm_pwr = (uint_t)-1;
37970Sstevel@tonic-gate 		compp++;
37980Sstevel@tonic-gate 	}
37990Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
38000Sstevel@tonic-gate 	DEVI(dip)->devi_pm_components = ocompp;
38010Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_DONE;
38020Sstevel@tonic-gate }
38030Sstevel@tonic-gate 
38040Sstevel@tonic-gate /*
38050Sstevel@tonic-gate  * Parse hex or decimal value from char string
38060Sstevel@tonic-gate  */
38070Sstevel@tonic-gate static char *
38080Sstevel@tonic-gate pm_parsenum(char *cp, int *valp)
38090Sstevel@tonic-gate {
38100Sstevel@tonic-gate 	int ch, offset;
38110Sstevel@tonic-gate 	char numbuf[256];
38120Sstevel@tonic-gate 	char *np = numbuf;
38130Sstevel@tonic-gate 	int value = 0;
38140Sstevel@tonic-gate 
38150Sstevel@tonic-gate 	ch = *cp++;
38160Sstevel@tonic-gate 	if (isdigit(ch)) {
38170Sstevel@tonic-gate 		if (ch == '0') {
38180Sstevel@tonic-gate 			if ((ch = *cp++) == 'x' || ch == 'X') {
38190Sstevel@tonic-gate 				ch = *cp++;
38200Sstevel@tonic-gate 				while (isxdigit(ch)) {
38210Sstevel@tonic-gate 					*np++ = (char)ch;
38220Sstevel@tonic-gate 					ch = *cp++;
38230Sstevel@tonic-gate 				}
38240Sstevel@tonic-gate 				*np = 0;
38250Sstevel@tonic-gate 				cp--;
38260Sstevel@tonic-gate 				goto hexval;
38270Sstevel@tonic-gate 			} else {
38280Sstevel@tonic-gate 				goto digit;
38290Sstevel@tonic-gate 			}
38300Sstevel@tonic-gate 		} else {
38310Sstevel@tonic-gate digit:
38320Sstevel@tonic-gate 			while (isdigit(ch)) {
38330Sstevel@tonic-gate 				*np++ = (char)ch;
38340Sstevel@tonic-gate 				ch = *cp++;
38350Sstevel@tonic-gate 			}
38360Sstevel@tonic-gate 			*np = 0;
38370Sstevel@tonic-gate 			cp--;
38380Sstevel@tonic-gate 			goto decval;
38390Sstevel@tonic-gate 		}
38400Sstevel@tonic-gate 	} else
38410Sstevel@tonic-gate 		return (NULL);
38420Sstevel@tonic-gate 
38430Sstevel@tonic-gate hexval:
38440Sstevel@tonic-gate 	for (np = numbuf; *np; np++) {
38450Sstevel@tonic-gate 		if (*np >= 'a' && *np <= 'f')
38460Sstevel@tonic-gate 			offset = 'a' - 10;
38470Sstevel@tonic-gate 		else if (*np >= 'A' && *np <= 'F')
38480Sstevel@tonic-gate 			offset = 'A' - 10;
38490Sstevel@tonic-gate 		else if (*np >= '0' && *np <= '9')
38500Sstevel@tonic-gate 			offset = '0';
38510Sstevel@tonic-gate 		value *= 16;
38520Sstevel@tonic-gate 		value += *np - offset;
38530Sstevel@tonic-gate 	}
38540Sstevel@tonic-gate 	*valp = value;
38550Sstevel@tonic-gate 	return (cp);
38560Sstevel@tonic-gate 
38570Sstevel@tonic-gate decval:
38580Sstevel@tonic-gate 	offset = '0';
38590Sstevel@tonic-gate 	for (np = numbuf; *np; np++) {
38600Sstevel@tonic-gate 		value *= 10;
38610Sstevel@tonic-gate 		value += *np - offset;
38620Sstevel@tonic-gate 	}
38630Sstevel@tonic-gate 	*valp = value;
38640Sstevel@tonic-gate 	return (cp);
38650Sstevel@tonic-gate }
38660Sstevel@tonic-gate 
38670Sstevel@tonic-gate /*
38680Sstevel@tonic-gate  * Set max (previously documented as "normal") power.
38690Sstevel@tonic-gate  */
38700Sstevel@tonic-gate static void
38710Sstevel@tonic-gate e_pm_set_max_power(dev_info_t *dip, int component_number, int level)
38720Sstevel@tonic-gate {
38730Sstevel@tonic-gate 	PM_CP(dip, component_number)->pmc_norm_pwr = level;
38740Sstevel@tonic-gate }
38750Sstevel@tonic-gate 
38760Sstevel@tonic-gate /*
38770Sstevel@tonic-gate  * Internal routine for destroying components
38780Sstevel@tonic-gate  * It is called even when there might not be any, so it must be forgiving.
38790Sstevel@tonic-gate  */
38800Sstevel@tonic-gate static void
38810Sstevel@tonic-gate e_pm_destroy_components(dev_info_t *dip)
38820Sstevel@tonic-gate {
38830Sstevel@tonic-gate 	int i;
38840Sstevel@tonic-gate 	struct pm_component *cp;
38850Sstevel@tonic-gate 
38860Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
38870Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) == 0)
38880Sstevel@tonic-gate 		return;
38890Sstevel@tonic-gate 	cp = DEVI(dip)->devi_pm_components;
38900Sstevel@tonic-gate 	ASSERT(cp);
38910Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++, cp++) {
38920Sstevel@tonic-gate 		int nlevels = cp->pmc_comp.pmc_numlevels;
38930Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_lvals, nlevels * sizeof (int));
38940Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_thresh, nlevels * sizeof (int));
38950Sstevel@tonic-gate 		/*
38960Sstevel@tonic-gate 		 * For BC nodes, the rest is static in bc_comp, so skip it
38970Sstevel@tonic-gate 		 */
38980Sstevel@tonic-gate 		if (PM_ISBC(dip))
38990Sstevel@tonic-gate 			continue;
39000Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_name, cp->pmc_comp.pmc_name_sz);
39010Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_lnames, nlevels * sizeof (char *));
39020Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_lname_buf,
39030Sstevel@tonic-gate 				cp->pmc_comp.pmc_lnames_sz);
39040Sstevel@tonic-gate 	}
39050Sstevel@tonic-gate 	kmem_free(DEVI(dip)->devi_pm_components, DEVI(dip)->devi_pm_comp_size);
39060Sstevel@tonic-gate 	DEVI(dip)->devi_pm_components = NULL;
39070Sstevel@tonic-gate 	DEVI(dip)->devi_pm_num_components = 0;
39080Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &=
39090Sstevel@tonic-gate 	    ~(PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED);
39100Sstevel@tonic-gate }
39110Sstevel@tonic-gate 
39120Sstevel@tonic-gate /*
39130Sstevel@tonic-gate  * Read the pm-components property (if there is one) and use it to set up
39140Sstevel@tonic-gate  * components.  Returns a pointer to an array of component structures if
39150Sstevel@tonic-gate  * pm-components found and successfully parsed, else returns NULL.
39160Sstevel@tonic-gate  * Sets error return *errp to true to indicate a failure (as opposed to no
39170Sstevel@tonic-gate  * property being present).
39180Sstevel@tonic-gate  */
39190Sstevel@tonic-gate pm_comp_t *
39200Sstevel@tonic-gate pm_autoconfig(dev_info_t *dip, int *errp)
39210Sstevel@tonic-gate {
39220Sstevel@tonic-gate 	PMD_FUNC(pmf, "autoconfig")
39230Sstevel@tonic-gate 	uint_t nelems;
39240Sstevel@tonic-gate 	char **pp;
39250Sstevel@tonic-gate 	pm_comp_t *compp = NULL;
39260Sstevel@tonic-gate 	int i, j, level, components = 0;
39270Sstevel@tonic-gate 	size_t size = 0;
39280Sstevel@tonic-gate 	struct pm_comp_pkg *p, *ptail;
39290Sstevel@tonic-gate 	struct pm_comp_pkg *phead = NULL;
39300Sstevel@tonic-gate 	int *lvals = NULL;
39310Sstevel@tonic-gate 	int *lszs = NULL;
39320Sstevel@tonic-gate 	int *np = NULL;
39330Sstevel@tonic-gate 	int npi = 0;
39340Sstevel@tonic-gate 	char **lnames = NULL;
39350Sstevel@tonic-gate 	char *cp, *tp;
39360Sstevel@tonic-gate 	pm_comp_t *ret = NULL;
39370Sstevel@tonic-gate 
39380Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
39390Sstevel@tonic-gate 	*errp = 0;	/* assume success */
39400Sstevel@tonic-gate 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
39410Sstevel@tonic-gate 	    (char *)pmcompstr, &pp, &nelems) != DDI_PROP_SUCCESS) {
39420Sstevel@tonic-gate 		return (NULL);
39430Sstevel@tonic-gate 	}
39440Sstevel@tonic-gate 
39450Sstevel@tonic-gate 	if (nelems < 3) {	/* need at least one name and two levels */
39460Sstevel@tonic-gate 		goto errout;
39470Sstevel@tonic-gate 	}
39480Sstevel@tonic-gate 
39490Sstevel@tonic-gate 	/*
39500Sstevel@tonic-gate 	 * pm_create_components is no longer allowed
39510Sstevel@tonic-gate 	 */
39520Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) != 0) {
39530Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) has %d comps\n",
39540Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), PM_NUMCMPTS(dip)))
39550Sstevel@tonic-gate 		goto errout;
39560Sstevel@tonic-gate 	}
39570Sstevel@tonic-gate 
39580Sstevel@tonic-gate 	lvals = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
39590Sstevel@tonic-gate 	lszs = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
39600Sstevel@tonic-gate 	lnames = kmem_alloc(nelems * sizeof (char *), KM_SLEEP);
39610Sstevel@tonic-gate 	np = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
39620Sstevel@tonic-gate 
39630Sstevel@tonic-gate 	level = 0;
39640Sstevel@tonic-gate 	phead = NULL;
39650Sstevel@tonic-gate 	for (i = 0; i < nelems; i++) {
39660Sstevel@tonic-gate 		cp = pp[i];
39670Sstevel@tonic-gate 		if (!isdigit(*cp)) {	/*  must be name */
39680Sstevel@tonic-gate 			if (strncmp(cp, namestr, nameln) != 0) {
39690Sstevel@tonic-gate 				goto errout;
39700Sstevel@tonic-gate 			}
39710Sstevel@tonic-gate 			if (i != 0) {
39720Sstevel@tonic-gate 				if (level == 0) {	/* no level spec'd */
39730Sstevel@tonic-gate 					PMD(PMD_ERROR, ("%s: no level spec'd\n",
39740Sstevel@tonic-gate 					    pmf))
39750Sstevel@tonic-gate 					goto errout;
39760Sstevel@tonic-gate 				}
39770Sstevel@tonic-gate 				np[npi++] = lvals[level - 1];
39780Sstevel@tonic-gate 				/* finish up previous component levels */
39790Sstevel@tonic-gate 				FINISH_COMP;
39800Sstevel@tonic-gate 			}
39810Sstevel@tonic-gate 			cp += nameln;
39820Sstevel@tonic-gate 			if (!*cp) {
39830Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: nsa\n", pmf))
39840Sstevel@tonic-gate 				goto errout;
39850Sstevel@tonic-gate 			}
39860Sstevel@tonic-gate 			p = kmem_zalloc(sizeof (*phead), KM_SLEEP);
39870Sstevel@tonic-gate 			if (phead == NULL) {
39880Sstevel@tonic-gate 				phead = ptail = p;
39890Sstevel@tonic-gate 			} else {
39900Sstevel@tonic-gate 				ptail->next = p;
39910Sstevel@tonic-gate 				ptail = p;
39920Sstevel@tonic-gate 			}
39930Sstevel@tonic-gate 			compp = p->comp = kmem_zalloc(sizeof (pm_comp_t),
39940Sstevel@tonic-gate 			    KM_SLEEP);
39950Sstevel@tonic-gate 			compp->pmc_name_sz = strlen(cp) + 1;
39960Sstevel@tonic-gate 			compp->pmc_name = kmem_zalloc(compp->pmc_name_sz,
39970Sstevel@tonic-gate 			    KM_SLEEP);
39980Sstevel@tonic-gate 			(void) strncpy(compp->pmc_name, cp, compp->pmc_name_sz);
39990Sstevel@tonic-gate 			components++;
40000Sstevel@tonic-gate 			level = 0;
40010Sstevel@tonic-gate 		} else {	/* better be power level <num>=<name> */
40020Sstevel@tonic-gate #ifdef DEBUG
40030Sstevel@tonic-gate 			tp = cp;
40040Sstevel@tonic-gate #endif
40050Sstevel@tonic-gate 			if (i == 0 ||
40060Sstevel@tonic-gate 			    (cp = pm_parsenum(cp, &lvals[level])) == NULL) {
40070Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: parsenum(%s)\n", pmf, tp))
40080Sstevel@tonic-gate 				goto errout;
40090Sstevel@tonic-gate 			}
40100Sstevel@tonic-gate #ifdef DEBUG
40110Sstevel@tonic-gate 			tp = cp;
40120Sstevel@tonic-gate #endif
40130Sstevel@tonic-gate 			if (*cp++ != '=' || !*cp) {
40140Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: ex =, got %s\n", pmf, tp))
40150Sstevel@tonic-gate 				goto errout;
40160Sstevel@tonic-gate 			}
40170Sstevel@tonic-gate 
40180Sstevel@tonic-gate 			lszs[level] = strlen(cp) + 1;
40190Sstevel@tonic-gate 			size += lszs[level];
40200Sstevel@tonic-gate 			lnames[level] = cp;	/* points into prop string */
40210Sstevel@tonic-gate 			level++;
40220Sstevel@tonic-gate 		}
40230Sstevel@tonic-gate 	}
40240Sstevel@tonic-gate 	np[npi++] = lvals[level - 1];
40250Sstevel@tonic-gate 	if (level == 0) {	/* ended with a name */
40260Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: ewn\n", pmf))
40270Sstevel@tonic-gate 		goto errout;
40280Sstevel@tonic-gate 	}
40290Sstevel@tonic-gate 	FINISH_COMP;
40300Sstevel@tonic-gate 
40310Sstevel@tonic-gate 
40320Sstevel@tonic-gate 	/*
40330Sstevel@tonic-gate 	 * Now we have a list of components--we have to return instead an
40340Sstevel@tonic-gate 	 * array of them, but we can just copy the top level and leave
40350Sstevel@tonic-gate 	 * the rest as is
40360Sstevel@tonic-gate 	 */
40370Sstevel@tonic-gate 	(void) e_pm_create_components(dip, components);
40380Sstevel@tonic-gate 	for (i = 0; i < components; i++)
40390Sstevel@tonic-gate 		e_pm_set_max_power(dip, i, np[i]);
40400Sstevel@tonic-gate 
40410Sstevel@tonic-gate 	ret = kmem_zalloc(components * sizeof (pm_comp_t), KM_SLEEP);
40420Sstevel@tonic-gate 	for (i = 0, p = phead; i < components; i++) {
40430Sstevel@tonic-gate 		ASSERT(p);
40440Sstevel@tonic-gate 		/*
40450Sstevel@tonic-gate 		 * Now sanity-check values:  levels must be monotonically
40460Sstevel@tonic-gate 		 * increasing
40470Sstevel@tonic-gate 		 */
40480Sstevel@tonic-gate 		if (p->comp->pmc_numlevels < 2) {
40490Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) only %d "
40500Sstevel@tonic-gate 			    "levels\n", pmf,
40510Sstevel@tonic-gate 			    p->comp->pmc_name, PM_DEVICE(dip),
40520Sstevel@tonic-gate 			    p->comp->pmc_numlevels))
40530Sstevel@tonic-gate 			goto errout;
40540Sstevel@tonic-gate 		}
40550Sstevel@tonic-gate 		for (j = 0; j < p->comp->pmc_numlevels; j++) {
40560Sstevel@tonic-gate 			if ((p->comp->pmc_lvals[j] < 0) || ((j > 0) &&
40570Sstevel@tonic-gate 			    (p->comp->pmc_lvals[j] <=
40580Sstevel@tonic-gate 			    p->comp->pmc_lvals[j - 1]))) {
40590Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) "
40600Sstevel@tonic-gate 				    "not mono. incr, %d follows %d\n", pmf,
40610Sstevel@tonic-gate 				    p->comp->pmc_name, PM_DEVICE(dip),
40620Sstevel@tonic-gate 				    p->comp->pmc_lvals[j],
40630Sstevel@tonic-gate 				    p->comp->pmc_lvals[j - 1]))
40640Sstevel@tonic-gate 				goto errout;
40650Sstevel@tonic-gate 			}
40660Sstevel@tonic-gate 		}
40670Sstevel@tonic-gate 		ret[i] = *p->comp;	/* struct assignment */
40680Sstevel@tonic-gate 		for (j = 0; j < i; j++) {
40690Sstevel@tonic-gate 			/*
40700Sstevel@tonic-gate 			 * Test for unique component names
40710Sstevel@tonic-gate 			 */
40720Sstevel@tonic-gate 			if (strcmp(ret[j].pmc_name, ret[i].pmc_name) == 0) {
40730Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: %s of %s@%s(%s#%d) not "
40740Sstevel@tonic-gate 				    "unique\n", pmf, ret[j].pmc_name,
40750Sstevel@tonic-gate 				    PM_DEVICE(dip)))
40760Sstevel@tonic-gate 				goto errout;
40770Sstevel@tonic-gate 			}
40780Sstevel@tonic-gate 		}
40790Sstevel@tonic-gate 		ptail = p;
40800Sstevel@tonic-gate 		p = p->next;
40810Sstevel@tonic-gate 		phead = p;	/* errout depends on phead making sense */
40820Sstevel@tonic-gate 		kmem_free(ptail->comp, sizeof (*ptail->comp));
40830Sstevel@tonic-gate 		kmem_free(ptail, sizeof (*ptail));
40840Sstevel@tonic-gate 	}
40850Sstevel@tonic-gate out:
40860Sstevel@tonic-gate 	ddi_prop_free(pp);
40870Sstevel@tonic-gate 	if (lvals)
40880Sstevel@tonic-gate 		kmem_free(lvals, nelems * sizeof (int));
40890Sstevel@tonic-gate 	if (lszs)
40900Sstevel@tonic-gate 		kmem_free(lszs, nelems * sizeof (int));
40910Sstevel@tonic-gate 	if (lnames)
40920Sstevel@tonic-gate 		kmem_free(lnames, nelems * sizeof (char *));
40930Sstevel@tonic-gate 	if (np)
40940Sstevel@tonic-gate 		kmem_free(np, nelems * sizeof (int));
40950Sstevel@tonic-gate 	return (ret);
40960Sstevel@tonic-gate 
40970Sstevel@tonic-gate errout:
40980Sstevel@tonic-gate 	e_pm_destroy_components(dip);
40990Sstevel@tonic-gate 	*errp = 1;	/* signal failure */
41000Sstevel@tonic-gate 	cmn_err(CE_CONT, "!pm: %s property ", pmcompstr);
41010Sstevel@tonic-gate 	for (i = 0; i < nelems - 1; i++)
41020Sstevel@tonic-gate 		cmn_err(CE_CONT, "!'%s', ", pp[i]);
41030Sstevel@tonic-gate 	if (nelems != 0)
41040Sstevel@tonic-gate 		cmn_err(CE_CONT, "!'%s'", pp[nelems - 1]);
41050Sstevel@tonic-gate 	cmn_err(CE_CONT, "! for %s@%s(%s#%d) is ill-formed.\n", PM_DEVICE(dip));
41060Sstevel@tonic-gate 	for (p = phead; p; ) {
41070Sstevel@tonic-gate 		pm_comp_t *pp;
41080Sstevel@tonic-gate 		int n;
41090Sstevel@tonic-gate 
41100Sstevel@tonic-gate 		ptail = p;
41110Sstevel@tonic-gate 		/*
41120Sstevel@tonic-gate 		 * Free component data structures
41130Sstevel@tonic-gate 		 */
41140Sstevel@tonic-gate 		pp = p->comp;
41150Sstevel@tonic-gate 		n = pp->pmc_numlevels;
41160Sstevel@tonic-gate 		if (pp->pmc_name_sz) {
41170Sstevel@tonic-gate 			kmem_free(pp->pmc_name, pp->pmc_name_sz);
41180Sstevel@tonic-gate 		}
41190Sstevel@tonic-gate 		if (pp->pmc_lnames_sz) {
41200Sstevel@tonic-gate 			kmem_free(pp->pmc_lname_buf, pp->pmc_lnames_sz);
41210Sstevel@tonic-gate 		}
41220Sstevel@tonic-gate 		if (pp->pmc_lnames) {
41230Sstevel@tonic-gate 			kmem_free(pp->pmc_lnames, n * (sizeof (char *)));
41240Sstevel@tonic-gate 		}
41250Sstevel@tonic-gate 		if (pp->pmc_thresh) {
41260Sstevel@tonic-gate 			kmem_free(pp->pmc_thresh, n * (sizeof (int)));
41270Sstevel@tonic-gate 		}
41280Sstevel@tonic-gate 		if (pp->pmc_lvals) {
41290Sstevel@tonic-gate 			kmem_free(pp->pmc_lvals, n * (sizeof (int)));
41300Sstevel@tonic-gate 		}
41310Sstevel@tonic-gate 		p = ptail->next;
41320Sstevel@tonic-gate 		kmem_free(ptail, sizeof (*ptail));
41330Sstevel@tonic-gate 	}
41340Sstevel@tonic-gate 	if (ret != NULL)
41350Sstevel@tonic-gate 		kmem_free(ret, components * sizeof (pm_comp_t));
41360Sstevel@tonic-gate 	ret = NULL;
41370Sstevel@tonic-gate 	goto out;
41380Sstevel@tonic-gate }
41390Sstevel@tonic-gate 
41400Sstevel@tonic-gate /*
41410Sstevel@tonic-gate  * Set threshold values for a devices components by dividing the target
41420Sstevel@tonic-gate  * threshold (base) by the number of transitions and assign each transition
41430Sstevel@tonic-gate  * that threshold.  This will get the entire device down in the target time if
41440Sstevel@tonic-gate  * all components are idle and even if there are dependencies among components.
41450Sstevel@tonic-gate  *
41460Sstevel@tonic-gate  * Devices may well get powered all the way down before the target time, but
41470Sstevel@tonic-gate  * at least the EPA will be happy.
41480Sstevel@tonic-gate  */
41490Sstevel@tonic-gate void
41500Sstevel@tonic-gate pm_set_device_threshold(dev_info_t *dip, int base, int flag)
41510Sstevel@tonic-gate {
41520Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_device_threshold")
41530Sstevel@tonic-gate 	int target_threshold = (base * 95) / 100;
41540Sstevel@tonic-gate 	int level, comp;		/* loop counters */
41550Sstevel@tonic-gate 	int transitions = 0;
41560Sstevel@tonic-gate 	int ncomp = PM_NUMCMPTS(dip);
41570Sstevel@tonic-gate 	int thresh;
41580Sstevel@tonic-gate 	int remainder;
41590Sstevel@tonic-gate 	pm_comp_t *pmc;
41600Sstevel@tonic-gate 	int i, circ;
41610Sstevel@tonic-gate 
41620Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
41630Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
41640Sstevel@tonic-gate 	/*
41650Sstevel@tonic-gate 	 * First we handle the easy one.  If we're setting the default
41660Sstevel@tonic-gate 	 * threshold for a node with children, then we set it to the
41670Sstevel@tonic-gate 	 * default nexus threshold (currently 0) and mark it as default
41680Sstevel@tonic-gate 	 * nexus threshold instead
41690Sstevel@tonic-gate 	 */
41700Sstevel@tonic-gate 	if (PM_IS_NEXUS(dip)) {
41710Sstevel@tonic-gate 		if (flag == PMC_DEF_THRESH) {
41720Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: [%s@%s(%s#%d) NEXDEF]\n", pmf,
41730Sstevel@tonic-gate 			    PM_DEVICE(dip)))
41740Sstevel@tonic-gate 			thresh = pm_default_nexus_threshold;
41750Sstevel@tonic-gate 			for (comp = 0; comp < ncomp; comp++) {
41760Sstevel@tonic-gate 				pmc = &PM_CP(dip, comp)->pmc_comp;
41770Sstevel@tonic-gate 				for (level = 1; level < pmc->pmc_numlevels;
41780Sstevel@tonic-gate 				    level++) {
41790Sstevel@tonic-gate 					pmc->pmc_thresh[level] = thresh;
41800Sstevel@tonic-gate 				}
41810Sstevel@tonic-gate 			}
41820Sstevel@tonic-gate 			DEVI(dip)->devi_pm_dev_thresh =
41830Sstevel@tonic-gate 			    pm_default_nexus_threshold;
41840Sstevel@tonic-gate 			/*
41850Sstevel@tonic-gate 			 * If the nexus node is being reconfigured back to
41860Sstevel@tonic-gate 			 * the default threshold, adjust the notlowest count.
41870Sstevel@tonic-gate 			 */
41880Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_flags &
41890Sstevel@tonic-gate 			    (PMC_DEV_THRESH|PMC_COMP_THRESH)) {
41900Sstevel@tonic-gate 				PM_LOCK_POWER(dip, &circ);
41910Sstevel@tonic-gate 				for (i = 0; i < PM_NUMCMPTS(dip); i++) {
41920Sstevel@tonic-gate 					if (PM_CURPOWER(dip, i) == 0)
41930Sstevel@tonic-gate 						continue;
41940Sstevel@tonic-gate 					mutex_enter(&pm_compcnt_lock);
41950Sstevel@tonic-gate 					ASSERT(pm_comps_notlowest);
41960Sstevel@tonic-gate 					pm_comps_notlowest--;
41970Sstevel@tonic-gate 					PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr "
41980Sstevel@tonic-gate 					    "notlowest to %d\n", pmf,
41990Sstevel@tonic-gate 					    PM_DEVICE(dip), pm_comps_notlowest))
42000Sstevel@tonic-gate 					if (pm_comps_notlowest == 0)
42010Sstevel@tonic-gate 						pm_ppm_notify_all_lowest(dip,
42020Sstevel@tonic-gate 						    PM_ALL_LOWEST);
42030Sstevel@tonic-gate 					mutex_exit(&pm_compcnt_lock);
42040Sstevel@tonic-gate 				}
42050Sstevel@tonic-gate 				PM_UNLOCK_POWER(dip, circ);
42060Sstevel@tonic-gate 			}
42070Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
42080Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= PMC_NEXDEF_THRESH;
42090Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
42100Sstevel@tonic-gate 			return;
42110Sstevel@tonic-gate 		} else if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) {
42120Sstevel@tonic-gate 			/*
42130Sstevel@tonic-gate 			 * If the nexus node is being configured for a
42140Sstevel@tonic-gate 			 * non-default threshold, include that node in
42150Sstevel@tonic-gate 			 * the notlowest accounting.
42160Sstevel@tonic-gate 			 */
42170Sstevel@tonic-gate 			PM_LOCK_POWER(dip, &circ);
42180Sstevel@tonic-gate 			for (i = 0; i < PM_NUMCMPTS(dip); i++) {
42190Sstevel@tonic-gate 				if (PM_CURPOWER(dip, i) == 0)
42200Sstevel@tonic-gate 					continue;
42210Sstevel@tonic-gate 				mutex_enter(&pm_compcnt_lock);
42220Sstevel@tonic-gate 				if (pm_comps_notlowest == 0)
42230Sstevel@tonic-gate 					pm_ppm_notify_all_lowest(dip,
42240Sstevel@tonic-gate 					    PM_NOT_ALL_LOWEST);
42250Sstevel@tonic-gate 				pm_comps_notlowest++;
42260Sstevel@tonic-gate 				PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr "
42270Sstevel@tonic-gate 				    "notlowest to %d\n", pmf,
42280Sstevel@tonic-gate 				    PM_DEVICE(dip), pm_comps_notlowest))
42290Sstevel@tonic-gate 				mutex_exit(&pm_compcnt_lock);
42300Sstevel@tonic-gate 			}
42310Sstevel@tonic-gate 			PM_UNLOCK_POWER(dip, circ);
42320Sstevel@tonic-gate 		}
42330Sstevel@tonic-gate 	}
42340Sstevel@tonic-gate 	/*
42350Sstevel@tonic-gate 	 * Compute the total number of transitions for all components
42360Sstevel@tonic-gate 	 * of the device.  Distribute the threshold evenly over them
42370Sstevel@tonic-gate 	 */
42380Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
42390Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
42400Sstevel@tonic-gate 		ASSERT(pmc->pmc_numlevels > 1);
42410Sstevel@tonic-gate 		transitions += pmc->pmc_numlevels - 1;
42420Sstevel@tonic-gate 	}
42430Sstevel@tonic-gate 	ASSERT(transitions);
42440Sstevel@tonic-gate 	thresh = target_threshold / transitions;
42450Sstevel@tonic-gate 
42460Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
42470Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
42480Sstevel@tonic-gate 		for (level = 1; level < pmc->pmc_numlevels; level++) {
42490Sstevel@tonic-gate 			pmc->pmc_thresh[level] = thresh;
42500Sstevel@tonic-gate 		}
42510Sstevel@tonic-gate 	}
42520Sstevel@tonic-gate 
42530Sstevel@tonic-gate #ifdef DEBUG
42540Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
42550Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
42560Sstevel@tonic-gate 		for (level = 1; level < pmc->pmc_numlevels; level++) {
42570Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: thresh before %s@%s(%s#%d) "
42580Sstevel@tonic-gate 			    "comp=%d, level=%d, %d\n", pmf, PM_DEVICE(dip),
42590Sstevel@tonic-gate 			    comp, level, pmc->pmc_thresh[level]))
42600Sstevel@tonic-gate 		}
42610Sstevel@tonic-gate 	}
42620Sstevel@tonic-gate #endif
42630Sstevel@tonic-gate 	/*
42640Sstevel@tonic-gate 	 * Distribute any remainder till they are all gone
42650Sstevel@tonic-gate 	 */
42660Sstevel@tonic-gate 	remainder = target_threshold - thresh * transitions;
42670Sstevel@tonic-gate 	level = 1;
42680Sstevel@tonic-gate #ifdef DEBUG
42690Sstevel@tonic-gate 	PMD(PMD_THRESH, ("%s: remainder=%d target_threshold=%d thresh=%d "
42700Sstevel@tonic-gate 	    "trans=%d\n", pmf, remainder, target_threshold, thresh,
42710Sstevel@tonic-gate 	    transitions))
42720Sstevel@tonic-gate #endif
42730Sstevel@tonic-gate 	while (remainder > 0) {
42740Sstevel@tonic-gate 		comp = 0;
42750Sstevel@tonic-gate 		while (remainder && (comp < ncomp)) {
42760Sstevel@tonic-gate 			pmc = &PM_CP(dip, comp)->pmc_comp;
42770Sstevel@tonic-gate 			if (level < pmc->pmc_numlevels) {
42780Sstevel@tonic-gate 				pmc->pmc_thresh[level] += 1;
42790Sstevel@tonic-gate 				remainder--;
42800Sstevel@tonic-gate 			}
42810Sstevel@tonic-gate 			comp++;
42820Sstevel@tonic-gate 		}
42830Sstevel@tonic-gate 		level++;
42840Sstevel@tonic-gate 	}
42850Sstevel@tonic-gate #ifdef DEBUG
42860Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
42870Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
42880Sstevel@tonic-gate 		for (level = 1; level < pmc->pmc_numlevels; level++) {
42890Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: thresh after %s@%s(%s#%d) "
42900Sstevel@tonic-gate 			    "comp=%d level=%d, %d\n", pmf, PM_DEVICE(dip),
42910Sstevel@tonic-gate 			    comp, level, pmc->pmc_thresh[level]))
42920Sstevel@tonic-gate 		}
42930Sstevel@tonic-gate 	}
42940Sstevel@tonic-gate #endif
42950Sstevel@tonic-gate 	ASSERT(PM_IAM_LOCKING_DIP(dip));
42960Sstevel@tonic-gate 	DEVI(dip)->devi_pm_dev_thresh = base;
42970Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
42980Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= flag;
42990Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
43000Sstevel@tonic-gate }
43010Sstevel@tonic-gate 
43020Sstevel@tonic-gate /*
43030Sstevel@tonic-gate  * Called when there is no old-style platform power management driver
43040Sstevel@tonic-gate  */
43050Sstevel@tonic-gate static int
43060Sstevel@tonic-gate ddi_no_platform_power(power_req_t *req)
43070Sstevel@tonic-gate {
43080Sstevel@tonic-gate 	_NOTE(ARGUNUSED(req))
43090Sstevel@tonic-gate 	return (DDI_FAILURE);
43100Sstevel@tonic-gate }
43110Sstevel@tonic-gate 
43120Sstevel@tonic-gate /*
43130Sstevel@tonic-gate  * This function calls the entry point supplied by the platform-specific
43140Sstevel@tonic-gate  * pm driver to bring the device component 'pm_cmpt' to power level 'pm_level'.
43150Sstevel@tonic-gate  * The use of global for getting the  function name from platform-specific
43160Sstevel@tonic-gate  * pm driver is not ideal, but it is simple and efficient.
43170Sstevel@tonic-gate  * The previous property lookup was being done in the idle loop on swift
43180Sstevel@tonic-gate  * systems without pmc chips and hurt deskbench performance as well as
43190Sstevel@tonic-gate  * violating scheduler locking rules
43200Sstevel@tonic-gate  */
43210Sstevel@tonic-gate int	(*pm_platform_power)(power_req_t *) = ddi_no_platform_power;
43220Sstevel@tonic-gate 
43230Sstevel@tonic-gate /*
43240Sstevel@tonic-gate  * Old obsolete interface for a device to request a power change (but only
43250Sstevel@tonic-gate  * an increase in power)
43260Sstevel@tonic-gate  */
43270Sstevel@tonic-gate int
43280Sstevel@tonic-gate ddi_dev_is_needed(dev_info_t *dip, int cmpt, int level)
43290Sstevel@tonic-gate {
43300Sstevel@tonic-gate 	return (pm_raise_power(dip, cmpt, level));
43310Sstevel@tonic-gate }
43320Sstevel@tonic-gate 
43330Sstevel@tonic-gate /*
43340Sstevel@tonic-gate  * The old obsolete interface to platform power management.  Only used by
43350Sstevel@tonic-gate  * Gypsy platform and APM on X86.
43360Sstevel@tonic-gate  */
43370Sstevel@tonic-gate int
43380Sstevel@tonic-gate ddi_power(dev_info_t *dip, int pm_cmpt, int pm_level)
43390Sstevel@tonic-gate {
43400Sstevel@tonic-gate 	power_req_t	request;
43410Sstevel@tonic-gate 
43420Sstevel@tonic-gate 	request.request_type = PMR_SET_POWER;
43430Sstevel@tonic-gate 	request.req.set_power_req.who = dip;
43440Sstevel@tonic-gate 	request.req.set_power_req.cmpt = pm_cmpt;
43450Sstevel@tonic-gate 	request.req.set_power_req.level = pm_level;
43460Sstevel@tonic-gate 	return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
43470Sstevel@tonic-gate }
43480Sstevel@tonic-gate 
43490Sstevel@tonic-gate /*
43500Sstevel@tonic-gate  * A driver can invoke this from its detach routine when DDI_SUSPEND is
43510Sstevel@tonic-gate  * passed.  Returns true if subsequent processing could result in power being
43520Sstevel@tonic-gate  * removed from the device.  The arg is not currently used because it is
43530Sstevel@tonic-gate  * implicit in the operation of cpr/DR.
43540Sstevel@tonic-gate  */
43550Sstevel@tonic-gate int
43560Sstevel@tonic-gate ddi_removing_power(dev_info_t *dip)
43570Sstevel@tonic-gate {
43580Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
43590Sstevel@tonic-gate 	return (pm_powering_down);
43600Sstevel@tonic-gate }
43610Sstevel@tonic-gate 
43620Sstevel@tonic-gate /*
43630Sstevel@tonic-gate  * Returns true if a device indicates that its parent handles suspend/resume
43640Sstevel@tonic-gate  * processing for it.
43650Sstevel@tonic-gate  */
43660Sstevel@tonic-gate int
43670Sstevel@tonic-gate e_ddi_parental_suspend_resume(dev_info_t *dip)
43680Sstevel@tonic-gate {
43690Sstevel@tonic-gate 	return (DEVI(dip)->devi_pm_flags & PMC_PARENTAL_SR);
43700Sstevel@tonic-gate }
43710Sstevel@tonic-gate 
43720Sstevel@tonic-gate /*
43730Sstevel@tonic-gate  * Called for devices which indicate that their parent does suspend/resume
43740Sstevel@tonic-gate  * handling for them
43750Sstevel@tonic-gate  */
43760Sstevel@tonic-gate int
43770Sstevel@tonic-gate e_ddi_suspend(dev_info_t *dip, ddi_detach_cmd_t cmd)
43780Sstevel@tonic-gate {
43790Sstevel@tonic-gate 	power_req_t	request;
43800Sstevel@tonic-gate 	request.request_type = PMR_SUSPEND;
43810Sstevel@tonic-gate 	request.req.suspend_req.who = dip;
43820Sstevel@tonic-gate 	request.req.suspend_req.cmd = cmd;
43830Sstevel@tonic-gate 	return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
43840Sstevel@tonic-gate }
43850Sstevel@tonic-gate 
43860Sstevel@tonic-gate /*
43870Sstevel@tonic-gate  * Called for devices which indicate that their parent does suspend/resume
43880Sstevel@tonic-gate  * handling for them
43890Sstevel@tonic-gate  */
43900Sstevel@tonic-gate int
43910Sstevel@tonic-gate e_ddi_resume(dev_info_t *dip, ddi_attach_cmd_t cmd)
43920Sstevel@tonic-gate {
43930Sstevel@tonic-gate 	power_req_t	request;
43940Sstevel@tonic-gate 	request.request_type = PMR_RESUME;
43950Sstevel@tonic-gate 	request.req.resume_req.who = dip;
43960Sstevel@tonic-gate 	request.req.resume_req.cmd = cmd;
43970Sstevel@tonic-gate 	return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
43980Sstevel@tonic-gate }
43990Sstevel@tonic-gate 
44000Sstevel@tonic-gate /*
44010Sstevel@tonic-gate  * Old obsolete exported interface for drivers to create components.
44020Sstevel@tonic-gate  * This is now handled by exporting the pm-components property.
44030Sstevel@tonic-gate  */
44040Sstevel@tonic-gate int
44050Sstevel@tonic-gate pm_create_components(dev_info_t *dip, int num_components)
44060Sstevel@tonic-gate {
44070Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_create_components")
44080Sstevel@tonic-gate 
44090Sstevel@tonic-gate 	if (num_components < 1)
44100Sstevel@tonic-gate 		return (DDI_FAILURE);
44110Sstevel@tonic-gate 
44120Sstevel@tonic-gate 	if (!DEVI_IS_ATTACHING(dip)) {
44130Sstevel@tonic-gate 		return (DDI_FAILURE);
44140Sstevel@tonic-gate 	}
44150Sstevel@tonic-gate 
44160Sstevel@tonic-gate 	/* don't need to lock dip because attach is single threaded */
44170Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_components) {
44180Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) already has %d\n", pmf,
44190Sstevel@tonic-gate 		    PM_DEVICE(dip), PM_NUMCMPTS(dip)))
44200Sstevel@tonic-gate 		return (DDI_FAILURE);
44210Sstevel@tonic-gate 	}
44220Sstevel@tonic-gate 	e_pm_create_components(dip, num_components);
44230Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= PMC_BC;
44240Sstevel@tonic-gate 	e_pm_default_components(dip, num_components);
44250Sstevel@tonic-gate 	return (DDI_SUCCESS);
44260Sstevel@tonic-gate }
44270Sstevel@tonic-gate 
44280Sstevel@tonic-gate /*
44290Sstevel@tonic-gate  * Obsolete interface previously called by drivers to destroy their components
44300Sstevel@tonic-gate  * at detach time.  This is now done automatically.  However, we need to keep
44310Sstevel@tonic-gate  * this for the old drivers.
44320Sstevel@tonic-gate  */
44330Sstevel@tonic-gate void
44340Sstevel@tonic-gate pm_destroy_components(dev_info_t *dip)
44350Sstevel@tonic-gate {
44360Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_destroy_components")
44370Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
44380Sstevel@tonic-gate 
44390Sstevel@tonic-gate 	PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf,
44400Sstevel@tonic-gate 	    PM_DEVICE(dip)))
44410Sstevel@tonic-gate 	ASSERT(DEVI_IS_DETACHING(dip));
44420Sstevel@tonic-gate #ifdef DEBUG
44430Sstevel@tonic-gate 	if (!PM_ISBC(dip))
44440Sstevel@tonic-gate 		cmn_err(CE_WARN, "!driver exporting pm-components property "
44450Sstevel@tonic-gate 		    "(%s@%s) calls pm_destroy_components", PM_NAME(dip),
44460Sstevel@tonic-gate 		    PM_ADDR(dip));
44470Sstevel@tonic-gate #endif
44480Sstevel@tonic-gate 	/*
44490Sstevel@tonic-gate 	 * We ignore this unless this is an old-style driver, except for
44500Sstevel@tonic-gate 	 * printing the message above
44510Sstevel@tonic-gate 	 */
44520Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) == 0 || !PM_ISBC(dip)) {
44530Sstevel@tonic-gate 		PMD(PMD_REMDEV, ("%s: ignore %s@%s(%s#%d)\n", pmf,
44540Sstevel@tonic-gate 		    PM_DEVICE(dip)))
44550Sstevel@tonic-gate 		return;
44560Sstevel@tonic-gate 	}
44570Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip));
44580Sstevel@tonic-gate 
44590Sstevel@tonic-gate 	/*
44600Sstevel@tonic-gate 	 * pm_unmanage will clear info pointer later, after dealing with
44610Sstevel@tonic-gate 	 * dependencies
44620Sstevel@tonic-gate 	 */
44630Sstevel@tonic-gate 	ASSERT(!PM_GET_PM_SCAN(dip));	/* better be gone already */
44640Sstevel@tonic-gate 	/*
44650Sstevel@tonic-gate 	 * Now adjust parent's kidsupcnt.  We check only comp 0.
44660Sstevel@tonic-gate 	 * Parents that get notification are not adjusted because their
44670Sstevel@tonic-gate 	 * kidsupcnt is always 0 (or 1 during probe and attach).
44680Sstevel@tonic-gate 	 */
44690Sstevel@tonic-gate 	if ((PM_CURPOWER(dip, 0) != 0) && pdip && !PM_WANTS_NOTIFICATION(pdip))
44700Sstevel@tonic-gate 		pm_rele_power(pdip);
44710Sstevel@tonic-gate #ifdef DEBUG
44720Sstevel@tonic-gate 	else {
44730Sstevel@tonic-gate 		PMD(PMD_KIDSUP, ("%s: kuc stays %s@%s(%s#%d) comps gone\n",
44740Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
44750Sstevel@tonic-gate 	}
44760Sstevel@tonic-gate #endif
44770Sstevel@tonic-gate 	e_pm_destroy_components(dip);
44780Sstevel@tonic-gate 	/*
44790Sstevel@tonic-gate 	 * Forget we ever knew anything about the components of this  device
44800Sstevel@tonic-gate 	 */
44810Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &=
44820Sstevel@tonic-gate 	    ~(PMC_BC | PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED);
44830Sstevel@tonic-gate }
44840Sstevel@tonic-gate 
44850Sstevel@tonic-gate /*
44860Sstevel@tonic-gate  * Exported interface for a driver to set a component busy.
44870Sstevel@tonic-gate  */
44880Sstevel@tonic-gate int
44890Sstevel@tonic-gate pm_busy_component(dev_info_t *dip, int cmpt)
44900Sstevel@tonic-gate {
44910Sstevel@tonic-gate 	struct pm_component *cp;
44920Sstevel@tonic-gate 
44930Sstevel@tonic-gate 	ASSERT(dip != NULL);
44940Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp))
44950Sstevel@tonic-gate 		return (DDI_FAILURE);
44960Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
44970Sstevel@tonic-gate 	cp->pmc_busycount++;
44980Sstevel@tonic-gate 	cp->pmc_timestamp = 0;
44990Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
45000Sstevel@tonic-gate 	return (DDI_SUCCESS);
45010Sstevel@tonic-gate }
45020Sstevel@tonic-gate 
45030Sstevel@tonic-gate /*
45040Sstevel@tonic-gate  * Exported interface for a driver to set a component idle.
45050Sstevel@tonic-gate  */
45060Sstevel@tonic-gate int
45070Sstevel@tonic-gate pm_idle_component(dev_info_t *dip, int cmpt)
45080Sstevel@tonic-gate {
45090Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_idle_component")
45100Sstevel@tonic-gate 	struct pm_component *cp;
45110Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
45120Sstevel@tonic-gate 
45130Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp))
45140Sstevel@tonic-gate 		return (DDI_FAILURE);
45150Sstevel@tonic-gate 
45160Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
45170Sstevel@tonic-gate 	if (cp->pmc_busycount) {
45180Sstevel@tonic-gate 		if (--(cp->pmc_busycount) == 0)
45190Sstevel@tonic-gate 			cp->pmc_timestamp = gethrestime_sec();
45200Sstevel@tonic-gate 	} else {
45210Sstevel@tonic-gate 		cp->pmc_timestamp = gethrestime_sec();
45220Sstevel@tonic-gate 	}
45230Sstevel@tonic-gate 
45240Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
45250Sstevel@tonic-gate 
45260Sstevel@tonic-gate 	/*
45270Sstevel@tonic-gate 	 * if device becomes idle during idle down period, try scan it down
45280Sstevel@tonic-gate 	 */
45290Sstevel@tonic-gate 	if (scanp && PM_IS_PID(dip)) {
45300Sstevel@tonic-gate 		PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d) idle.\n", pmf,
45310Sstevel@tonic-gate 		    PM_DEVICE(dip)))
45320Sstevel@tonic-gate 		pm_rescan(dip);
45330Sstevel@tonic-gate 		return (DDI_SUCCESS);
45340Sstevel@tonic-gate 	}
45350Sstevel@tonic-gate 
45360Sstevel@tonic-gate 	/*
45370Sstevel@tonic-gate 	 * handle scan not running with nexus threshold == 0
45380Sstevel@tonic-gate 	 */
45390Sstevel@tonic-gate 
45400Sstevel@tonic-gate 	if (PM_IS_NEXUS(dip) && (cp->pmc_busycount == 0)) {
45410Sstevel@tonic-gate 		pm_rescan(dip);
45420Sstevel@tonic-gate 	}
45430Sstevel@tonic-gate 
45440Sstevel@tonic-gate 	return (DDI_SUCCESS);
45450Sstevel@tonic-gate }
45460Sstevel@tonic-gate 
45470Sstevel@tonic-gate /*
45480Sstevel@tonic-gate  * This is the old  obsolete interface called by drivers to set their normal
45490Sstevel@tonic-gate  * power.  Thus we can't fix its behavior or return a value.
45500Sstevel@tonic-gate  * This functionality is replaced by the pm-component property.
45510Sstevel@tonic-gate  * We'll only get components destroyed while no power management is
45520Sstevel@tonic-gate  * going on (and the device is detached), so we don't need a mutex here
45530Sstevel@tonic-gate  */
45540Sstevel@tonic-gate void
45550Sstevel@tonic-gate pm_set_normal_power(dev_info_t *dip, int comp, int level)
45560Sstevel@tonic-gate {
45570Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_normal_power")
45580Sstevel@tonic-gate #ifdef DEBUG
45590Sstevel@tonic-gate 	if (!PM_ISBC(dip))
45600Sstevel@tonic-gate 		cmn_err(CE_WARN, "!call to pm_set_normal_power() by %s@%s "
45610Sstevel@tonic-gate 		    "(driver exporting pm-components property) ignored",
45620Sstevel@tonic-gate 		    PM_NAME(dip), PM_ADDR(dip));
45630Sstevel@tonic-gate #endif
45640Sstevel@tonic-gate 	if (PM_ISBC(dip)) {
45650Sstevel@tonic-gate 		PMD(PMD_NORM, ("%s: %s@%s(%s#%d) set normal power comp=%d, "
45660Sstevel@tonic-gate 		    "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
45670Sstevel@tonic-gate 		e_pm_set_max_power(dip, comp, level);
45680Sstevel@tonic-gate 		e_pm_default_levels(dip, PM_CP(dip, comp), level);
45690Sstevel@tonic-gate 	}
45700Sstevel@tonic-gate }
45710Sstevel@tonic-gate 
45720Sstevel@tonic-gate /*
45730Sstevel@tonic-gate  * Called on a successfully detached driver to free pm resources
45740Sstevel@tonic-gate  */
45750Sstevel@tonic-gate static void
45760Sstevel@tonic-gate pm_stop(dev_info_t *dip)
45770Sstevel@tonic-gate {
45780Sstevel@tonic-gate 	PMD_FUNC(pmf, "stop")
45790Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
45800Sstevel@tonic-gate 
45810Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
45820Sstevel@tonic-gate 	/* stopping scan, destroy scan data structure */
45830Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
45840Sstevel@tonic-gate 		pm_scan_stop(dip);
45850Sstevel@tonic-gate 		pm_scan_fini(dip);
45860Sstevel@tonic-gate 	}
45870Sstevel@tonic-gate 
45880Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) != NULL) {
45890Sstevel@tonic-gate 		if (pm_unmanage(dip) == DDI_SUCCESS) {
45900Sstevel@tonic-gate 			/*
45910Sstevel@tonic-gate 			 * Old style driver may have called
45920Sstevel@tonic-gate 			 * pm_destroy_components already, but just in case ...
45930Sstevel@tonic-gate 			 */
45940Sstevel@tonic-gate 			e_pm_destroy_components(dip);
45950Sstevel@tonic-gate 		} else {
45960Sstevel@tonic-gate 			PMD(PMD_FAIL, ("%s: can't pm_unmanage %s@%s(%s#%d)\n",
45970Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
45980Sstevel@tonic-gate 		}
45990Sstevel@tonic-gate 	} else {
46000Sstevel@tonic-gate 		if (PM_NUMCMPTS(dip))
46010Sstevel@tonic-gate 			e_pm_destroy_components(dip);
46020Sstevel@tonic-gate 		else {
46030Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_flags & PMC_NOPMKID) {
46040Sstevel@tonic-gate 				DEVI(dip)->devi_pm_flags &= ~PMC_NOPMKID;
46050Sstevel@tonic-gate 				if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
46060Sstevel@tonic-gate 					pm_rele_power(pdip);
4607*2009Sdm120769 				} else if (pdip && MDI_VHCI(pdip)) {
46080Sstevel@tonic-gate 					(void) mdi_power(pdip,
46090Sstevel@tonic-gate 					    MDI_PM_RELE_POWER,
46100Sstevel@tonic-gate 					    (void *)dip, NULL, 0);
46110Sstevel@tonic-gate 				}
46120Sstevel@tonic-gate 			}
46130Sstevel@tonic-gate 		}
46140Sstevel@tonic-gate 	}
46150Sstevel@tonic-gate }
46160Sstevel@tonic-gate 
46170Sstevel@tonic-gate /*
46180Sstevel@tonic-gate  * The node is the subject of a reparse pm props ioctl. Throw away the old
46190Sstevel@tonic-gate  * info and start over.
46200Sstevel@tonic-gate  */
46210Sstevel@tonic-gate int
46220Sstevel@tonic-gate e_new_pm_props(dev_info_t *dip)
46230Sstevel@tonic-gate {
46240Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) != NULL) {
46250Sstevel@tonic-gate 		pm_stop(dip);
46260Sstevel@tonic-gate 
46270Sstevel@tonic-gate 		if (e_pm_manage(dip, PM_STYLE_NEW) != DDI_SUCCESS) {
46280Sstevel@tonic-gate 			return (DDI_FAILURE);
46290Sstevel@tonic-gate 		}
46300Sstevel@tonic-gate 	}
46310Sstevel@tonic-gate 	e_pm_props(dip);
46320Sstevel@tonic-gate 	return (DDI_SUCCESS);
46330Sstevel@tonic-gate }
46340Sstevel@tonic-gate 
46350Sstevel@tonic-gate /*
46360Sstevel@tonic-gate  * Device has been attached, so process its pm properties
46370Sstevel@tonic-gate  */
46380Sstevel@tonic-gate void
46390Sstevel@tonic-gate e_pm_props(dev_info_t *dip)
46400Sstevel@tonic-gate {
46410Sstevel@tonic-gate 	char *pp;
46420Sstevel@tonic-gate 	int len;
46430Sstevel@tonic-gate 	int flags = 0;
46440Sstevel@tonic-gate 	int propflag = DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP;
46450Sstevel@tonic-gate 
46460Sstevel@tonic-gate 	/*
46470Sstevel@tonic-gate 	 * It doesn't matter if we do this more than once, we should always
46480Sstevel@tonic-gate 	 * get the same answers, and if not, then the last one in is the
46490Sstevel@tonic-gate 	 * best one.
46500Sstevel@tonic-gate 	 */
46510Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-hardware-state",
46520Sstevel@tonic-gate 	    (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) {
46530Sstevel@tonic-gate 		if (strcmp(pp, "needs-suspend-resume") == 0) {
46540Sstevel@tonic-gate 			flags = PMC_NEEDS_SR;
46550Sstevel@tonic-gate 		} else if (strcmp(pp, "no-suspend-resume") == 0) {
46560Sstevel@tonic-gate 			flags = PMC_NO_SR;
46570Sstevel@tonic-gate 		} else if (strcmp(pp, "parental-suspend-resume") == 0) {
46580Sstevel@tonic-gate 			flags = PMC_PARENTAL_SR;
46590Sstevel@tonic-gate 		} else {
46600Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!device %s@%s has unrecognized "
46610Sstevel@tonic-gate 			    "%s property value '%s'", PM_NAME(dip),
46620Sstevel@tonic-gate 			    PM_ADDR(dip), "pm-hardware-state", pp);
46630Sstevel@tonic-gate 		}
46640Sstevel@tonic-gate 		kmem_free(pp, len);
46650Sstevel@tonic-gate 	}
46660Sstevel@tonic-gate 	/*
46670Sstevel@tonic-gate 	 * This next segment (PMC_WANTS_NOTIFY) is in
46680Sstevel@tonic-gate 	 * support of nexus drivers which will want to be involved in
46690Sstevel@tonic-gate 	 * (or at least notified of) their child node's power level transitions.
46700Sstevel@tonic-gate 	 * "pm-want-child-notification?" is defined by the parent.
46710Sstevel@tonic-gate 	 */
46720Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag,
46730Sstevel@tonic-gate 	    "pm-want-child-notification?") && PM_HAS_BUS_POWER(dip))
46740Sstevel@tonic-gate 		flags |= PMC_WANTS_NOTIFY;
46750Sstevel@tonic-gate 	ASSERT(PM_HAS_BUS_POWER(dip) || !ddi_prop_exists(DDI_DEV_T_ANY,
46760Sstevel@tonic-gate 	    dip, propflag, "pm-want-child-notification?"));
46770Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag,
46780Sstevel@tonic-gate 	    "no-involuntary-power-cycles"))
46790Sstevel@tonic-gate 		flags |= PMC_NO_INVOL;
46800Sstevel@tonic-gate 	/* devfs single threads us */
46810Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= flags;
46820Sstevel@tonic-gate }
46830Sstevel@tonic-gate 
46840Sstevel@tonic-gate /*
46850Sstevel@tonic-gate  * This is the DDI_CTLOPS_POWER handler that is used when there is no ppm
46860Sstevel@tonic-gate  * driver which has claimed a node.
46870Sstevel@tonic-gate  * Sets old_power in arg struct.
46880Sstevel@tonic-gate  */
46890Sstevel@tonic-gate static int
46900Sstevel@tonic-gate pm_default_ctlops(dev_info_t *dip, dev_info_t *rdip,
46910Sstevel@tonic-gate     ddi_ctl_enum_t ctlop, void *arg, void *result)
46920Sstevel@tonic-gate {
46930Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
46940Sstevel@tonic-gate 	PMD_FUNC(pmf, "ctlops")
46950Sstevel@tonic-gate 	power_req_t *reqp = (power_req_t *)arg;
46960Sstevel@tonic-gate 	int retval;
46970Sstevel@tonic-gate 	dev_info_t *target_dip;
46980Sstevel@tonic-gate 	int new_level, old_level, cmpt;
46990Sstevel@tonic-gate #ifdef DEBUG
47000Sstevel@tonic-gate 	char *format;
47010Sstevel@tonic-gate #endif
47020Sstevel@tonic-gate 
47030Sstevel@tonic-gate 	/*
47040Sstevel@tonic-gate 	 * The interface for doing the actual power level changes is now
47050Sstevel@tonic-gate 	 * through the DDI_CTLOPS_POWER bus_ctl, so that we can plug in
47060Sstevel@tonic-gate 	 * different platform-specific power control drivers.
47070Sstevel@tonic-gate 	 *
47080Sstevel@tonic-gate 	 * This driver implements the "default" version of this interface.
47090Sstevel@tonic-gate 	 * If no ppm driver has been installed then this interface is called
47100Sstevel@tonic-gate 	 * instead.
47110Sstevel@tonic-gate 	 */
47120Sstevel@tonic-gate 	ASSERT(dip == NULL);
47130Sstevel@tonic-gate 	switch (ctlop) {
47140Sstevel@tonic-gate 	case DDI_CTLOPS_POWER:
47150Sstevel@tonic-gate 		switch (reqp->request_type) {
47160Sstevel@tonic-gate 		case PMR_PPM_SET_POWER:
47170Sstevel@tonic-gate 		{
47180Sstevel@tonic-gate 			target_dip = reqp->req.ppm_set_power_req.who;
47190Sstevel@tonic-gate 			ASSERT(target_dip == rdip);
47200Sstevel@tonic-gate 			new_level = reqp->req.ppm_set_power_req.new_level;
47210Sstevel@tonic-gate 			cmpt = reqp->req.ppm_set_power_req.cmpt;
47220Sstevel@tonic-gate 			/* pass back old power for the PM_LEVEL_UNKNOWN case */
47230Sstevel@tonic-gate 			old_level = PM_CURPOWER(target_dip, cmpt);
47240Sstevel@tonic-gate 			reqp->req.ppm_set_power_req.old_level = old_level;
47250Sstevel@tonic-gate 			retval = pm_power(target_dip, cmpt, new_level);
47260Sstevel@tonic-gate 			PMD(PMD_PPM, ("%s: PPM_SET_POWER %s@%s(%s#%d)[%d] %d->"
47270Sstevel@tonic-gate 			    "%d %s\n", pmf, PM_DEVICE(target_dip), cmpt,
47280Sstevel@tonic-gate 			    old_level, new_level, (retval == DDI_SUCCESS ?
47290Sstevel@tonic-gate 			    "chd" : "no chg")))
47300Sstevel@tonic-gate 			return (retval);
47310Sstevel@tonic-gate 		}
47320Sstevel@tonic-gate 
47330Sstevel@tonic-gate 		case PMR_PPM_PRE_DETACH:
47340Sstevel@tonic-gate 		case PMR_PPM_POST_DETACH:
47350Sstevel@tonic-gate 		case PMR_PPM_PRE_ATTACH:
47360Sstevel@tonic-gate 		case PMR_PPM_POST_ATTACH:
47370Sstevel@tonic-gate 		case PMR_PPM_PRE_PROBE:
47380Sstevel@tonic-gate 		case PMR_PPM_POST_PROBE:
47390Sstevel@tonic-gate 		case PMR_PPM_PRE_RESUME:
47400Sstevel@tonic-gate 		case PMR_PPM_INIT_CHILD:
47410Sstevel@tonic-gate 		case PMR_PPM_UNINIT_CHILD:
47420Sstevel@tonic-gate #ifdef DEBUG
47430Sstevel@tonic-gate 			switch (reqp->request_type) {
47440Sstevel@tonic-gate 				case PMR_PPM_PRE_DETACH:
47450Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_DETACH "
47460Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
47470Sstevel@tonic-gate 					break;
47480Sstevel@tonic-gate 				case PMR_PPM_POST_DETACH:
47490Sstevel@tonic-gate 					format = "%s: PMR_PPM_POST_DETACH "
47500Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
47510Sstevel@tonic-gate 					break;
47520Sstevel@tonic-gate 				case PMR_PPM_PRE_ATTACH:
47530Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_ATTACH "
47540Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
47550Sstevel@tonic-gate 					break;
47560Sstevel@tonic-gate 				case PMR_PPM_POST_ATTACH:
47570Sstevel@tonic-gate 					format = "%s: PMR_PPM_POST_ATTACH "
47580Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
47590Sstevel@tonic-gate 					break;
47600Sstevel@tonic-gate 				case PMR_PPM_PRE_PROBE:
47610Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_PROBE "
47620Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
47630Sstevel@tonic-gate 					break;
47640Sstevel@tonic-gate 				case PMR_PPM_POST_PROBE:
47650Sstevel@tonic-gate 					format = "%s: PMR_PPM_POST_PROBE "
47660Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
47670Sstevel@tonic-gate 					break;
47680Sstevel@tonic-gate 				case PMR_PPM_PRE_RESUME:
47690Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_RESUME "
47700Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
47710Sstevel@tonic-gate 					break;
47720Sstevel@tonic-gate 				case PMR_PPM_INIT_CHILD:
47730Sstevel@tonic-gate 					format = "%s: PMR_PPM_INIT_CHILD "
47740Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
47750Sstevel@tonic-gate 					break;
47760Sstevel@tonic-gate 				case PMR_PPM_UNINIT_CHILD:
47770Sstevel@tonic-gate 					format = "%s: PMR_PPM_UNINIT_CHILD "
47780Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
47790Sstevel@tonic-gate 					break;
47800Sstevel@tonic-gate 				default:
47810Sstevel@tonic-gate 					break;
47820Sstevel@tonic-gate 			}
47830Sstevel@tonic-gate 			PMD(PMD_PPM, (format, pmf, PM_DEVICE(rdip),
47840Sstevel@tonic-gate 			    reqp->req.ppm_config_req.result))
47850Sstevel@tonic-gate #endif
47860Sstevel@tonic-gate 			return (DDI_SUCCESS);
47870Sstevel@tonic-gate 
47880Sstevel@tonic-gate 		case PMR_PPM_POWER_CHANGE_NOTIFY:
47890Sstevel@tonic-gate 			/*
47900Sstevel@tonic-gate 			 * Nothing for us to do
47910Sstevel@tonic-gate 			 */
47920Sstevel@tonic-gate 			ASSERT(reqp->req.ppm_notify_level_req.who == rdip);
47930Sstevel@tonic-gate 			PMD(PMD_PPM, ("%s: PMR_PPM_POWER_CHANGE_NOTIFY "
47940Sstevel@tonic-gate 			    "%s@%s(%s#%d)[%d] %d->%d\n", pmf,
47950Sstevel@tonic-gate 			    PM_DEVICE(reqp->req.ppm_notify_level_req.who),
47960Sstevel@tonic-gate 			    reqp->req.ppm_notify_level_req.cmpt,
47970Sstevel@tonic-gate 			    PM_CURPOWER(reqp->req.ppm_notify_level_req.who,
47980Sstevel@tonic-gate 			    reqp->req.ppm_notify_level_req.cmpt),
47990Sstevel@tonic-gate 			    reqp->req.ppm_notify_level_req.new_level))
48000Sstevel@tonic-gate 			return (DDI_SUCCESS);
48010Sstevel@tonic-gate 
48020Sstevel@tonic-gate 		case PMR_PPM_UNMANAGE:
48030Sstevel@tonic-gate 			PMD(PMD_PPM, ("%s: PMR_PPM_UNMANAGE %s@%s(%s#%d)\n",
48040Sstevel@tonic-gate 			    pmf, PM_DEVICE(rdip)))
48050Sstevel@tonic-gate 			return (DDI_SUCCESS);
48060Sstevel@tonic-gate 
48070Sstevel@tonic-gate 		case PMR_PPM_LOCK_POWER:
48080Sstevel@tonic-gate 			pm_lock_power_single(reqp->req.ppm_lock_power_req.who,
48090Sstevel@tonic-gate 			    reqp->req.ppm_lock_power_req.circp);
48100Sstevel@tonic-gate 			return (DDI_SUCCESS);
48110Sstevel@tonic-gate 
48120Sstevel@tonic-gate 		case PMR_PPM_UNLOCK_POWER:
48130Sstevel@tonic-gate 			pm_unlock_power_single(
48140Sstevel@tonic-gate 			    reqp->req.ppm_unlock_power_req.who,
48150Sstevel@tonic-gate 			    reqp->req.ppm_unlock_power_req.circ);
48160Sstevel@tonic-gate 			return (DDI_SUCCESS);
48170Sstevel@tonic-gate 
48180Sstevel@tonic-gate 		case PMR_PPM_TRY_LOCK_POWER:
48190Sstevel@tonic-gate 			*(int *)result = pm_try_locking_power_single(
48200Sstevel@tonic-gate 			    reqp->req.ppm_lock_power_req.who,
48210Sstevel@tonic-gate 			    reqp->req.ppm_lock_power_req.circp);
48220Sstevel@tonic-gate 			return (DDI_SUCCESS);
48230Sstevel@tonic-gate 
48240Sstevel@tonic-gate 		case PMR_PPM_POWER_LOCK_OWNER:
48250Sstevel@tonic-gate 			target_dip = reqp->req.ppm_power_lock_owner_req.who;
48260Sstevel@tonic-gate 			ASSERT(target_dip == rdip);
48270Sstevel@tonic-gate 			reqp->req.ppm_power_lock_owner_req.owner =
48280Sstevel@tonic-gate 			    DEVI(rdip)->devi_busy_thread;
48290Sstevel@tonic-gate 			return (DDI_SUCCESS);
48300Sstevel@tonic-gate 		default:
48310Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: default!\n", pmf))
48320Sstevel@tonic-gate 			return (DDI_FAILURE);
48330Sstevel@tonic-gate 		}
48340Sstevel@tonic-gate 
48350Sstevel@tonic-gate 	default:
48360Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: unknown\n", pmf))
48370Sstevel@tonic-gate 		return (DDI_FAILURE);
48380Sstevel@tonic-gate 	}
48390Sstevel@tonic-gate }
48400Sstevel@tonic-gate 
48410Sstevel@tonic-gate /*
48420Sstevel@tonic-gate  * We overload the bus_ctl ops here--perhaps we ought to have a distinct
48430Sstevel@tonic-gate  * power_ops struct for this functionality instead?
48440Sstevel@tonic-gate  * However, we only ever do this on a ppm driver.
48450Sstevel@tonic-gate  */
48460Sstevel@tonic-gate int
48470Sstevel@tonic-gate pm_ctlops(dev_info_t *d, dev_info_t *r, ddi_ctl_enum_t op, void *a, void *v)
48480Sstevel@tonic-gate {
48490Sstevel@tonic-gate 	int (*fp)();
48500Sstevel@tonic-gate 
48510Sstevel@tonic-gate 	/* if no ppm handler, call the default routine */
48520Sstevel@tonic-gate 	if (d == NULL) {
48530Sstevel@tonic-gate 		return (pm_default_ctlops(d, r, op, a, v));
48540Sstevel@tonic-gate 	}
48550Sstevel@tonic-gate 	if (!d || !r)
48560Sstevel@tonic-gate 		return (DDI_FAILURE);
48570Sstevel@tonic-gate 	ASSERT(DEVI(d)->devi_ops && DEVI(d)->devi_ops->devo_bus_ops &&
48580Sstevel@tonic-gate 		DEVI(d)->devi_ops->devo_bus_ops->bus_ctl);
48590Sstevel@tonic-gate 
48600Sstevel@tonic-gate 	fp = DEVI(d)->devi_ops->devo_bus_ops->bus_ctl;
48610Sstevel@tonic-gate 	return ((*fp)(d, r, op, a, v));
48620Sstevel@tonic-gate }
48630Sstevel@tonic-gate 
48640Sstevel@tonic-gate /*
48650Sstevel@tonic-gate  * Called on a node when attach completes or the driver makes its first pm
48660Sstevel@tonic-gate  * call (whichever comes first).
48670Sstevel@tonic-gate  * In the attach case, device may not be power manageable at all.
48680Sstevel@tonic-gate  * Don't need to lock the dip because we're single threaded by the devfs code
48690Sstevel@tonic-gate  */
48700Sstevel@tonic-gate static int
48710Sstevel@tonic-gate pm_start(dev_info_t *dip)
48720Sstevel@tonic-gate {
48730Sstevel@tonic-gate 	PMD_FUNC(pmf, "start")
48740Sstevel@tonic-gate 	int ret;
48750Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
48760Sstevel@tonic-gate 	int e_pm_manage(dev_info_t *, int);
48770Sstevel@tonic-gate 	void pm_noinvol_specd(dev_info_t *dip);
48780Sstevel@tonic-gate 
48790Sstevel@tonic-gate 	e_pm_props(dip);
48800Sstevel@tonic-gate 	pm_noinvol_specd(dip);
48810Sstevel@tonic-gate 	/*
48820Sstevel@tonic-gate 	 * If this dip has already been processed, don't mess with it
48830Sstevel@tonic-gate 	 * (but decrement the speculative count we did above, as whatever
48840Sstevel@tonic-gate 	 * code put it under pm already will have dealt with it)
48850Sstevel@tonic-gate 	 */
48860Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip)) {
48870Sstevel@tonic-gate 		PMD(PMD_KIDSUP, ("%s: pm already done for %s@%s(%s#%d)\n",
48880Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
48890Sstevel@tonic-gate 		return (0);
48900Sstevel@tonic-gate 	}
48910Sstevel@tonic-gate 	ret = e_pm_manage(dip, PM_STYLE_UNKNOWN);
48920Sstevel@tonic-gate 
48930Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) == NULL) {
48940Sstevel@tonic-gate 		/*
48950Sstevel@tonic-gate 		 * keep the kidsupcount increment as is
48960Sstevel@tonic-gate 		 */
48970Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags |= PMC_NOPMKID;
48980Sstevel@tonic-gate 		if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
48990Sstevel@tonic-gate 			pm_hold_power(pdip);
4900*2009Sdm120769 		} else if (pdip && MDI_VHCI(pdip)) {
49010Sstevel@tonic-gate 			(void) mdi_power(pdip, MDI_PM_HOLD_POWER,
49020Sstevel@tonic-gate 			    (void *)dip, NULL, 0);
49030Sstevel@tonic-gate 		}
49040Sstevel@tonic-gate 
49050Sstevel@tonic-gate 		PMD(PMD_KIDSUP, ("%s: pm of %s@%s(%s#%d) failed, parent "
49060Sstevel@tonic-gate 		    "left up\n", pmf, PM_DEVICE(dip)))
49070Sstevel@tonic-gate 	}
49080Sstevel@tonic-gate 
49090Sstevel@tonic-gate 	return (ret);
49100Sstevel@tonic-gate }
49110Sstevel@tonic-gate 
49120Sstevel@tonic-gate /*
49130Sstevel@tonic-gate  * Keep a list of recorded thresholds.  For now we just keep a list and
49140Sstevel@tonic-gate  * search it linearly.  We don't expect too many entries.  Can always hash it
49150Sstevel@tonic-gate  * later if we need to.
49160Sstevel@tonic-gate  */
49170Sstevel@tonic-gate void
49180Sstevel@tonic-gate pm_record_thresh(pm_thresh_rec_t *rp)
49190Sstevel@tonic-gate {
49200Sstevel@tonic-gate 	pm_thresh_rec_t *pptr, *ptr;
49210Sstevel@tonic-gate 
49220Sstevel@tonic-gate 	ASSERT(*rp->ptr_physpath);
49230Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_WRITER);
49240Sstevel@tonic-gate 	for (pptr = NULL, ptr = pm_thresh_head;
49250Sstevel@tonic-gate 	    ptr; pptr = ptr,  ptr = ptr->ptr_next) {
49260Sstevel@tonic-gate 		if (strcmp(rp->ptr_physpath, ptr->ptr_physpath) == 0) {
49270Sstevel@tonic-gate 			/* replace this one */
49280Sstevel@tonic-gate 			rp->ptr_next = ptr->ptr_next;
49290Sstevel@tonic-gate 			if (pptr) {
49300Sstevel@tonic-gate 				pptr->ptr_next = rp;
49310Sstevel@tonic-gate 			} else {
49320Sstevel@tonic-gate 				pm_thresh_head = rp;
49330Sstevel@tonic-gate 			}
49340Sstevel@tonic-gate 			rw_exit(&pm_thresh_rwlock);
49350Sstevel@tonic-gate 			kmem_free(ptr, ptr->ptr_size);
49360Sstevel@tonic-gate 			return;
49370Sstevel@tonic-gate 		}
49380Sstevel@tonic-gate 		continue;
49390Sstevel@tonic-gate 	}
49400Sstevel@tonic-gate 	/*
49410Sstevel@tonic-gate 	 * There was not a match in the list, insert this one in front
49420Sstevel@tonic-gate 	 */
49430Sstevel@tonic-gate 	if (pm_thresh_head) {
49440Sstevel@tonic-gate 		rp->ptr_next = pm_thresh_head;
49450Sstevel@tonic-gate 		pm_thresh_head = rp;
49460Sstevel@tonic-gate 	} else {
49470Sstevel@tonic-gate 		rp->ptr_next = NULL;
49480Sstevel@tonic-gate 		pm_thresh_head = rp;
49490Sstevel@tonic-gate 	}
49500Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
49510Sstevel@tonic-gate }
49520Sstevel@tonic-gate 
49530Sstevel@tonic-gate /*
49540Sstevel@tonic-gate  * Create a new dependency record and hang a new dependency entry off of it
49550Sstevel@tonic-gate  */
49560Sstevel@tonic-gate pm_pdr_t *
49570Sstevel@tonic-gate newpdr(char *kept, char *keeps, int isprop)
49580Sstevel@tonic-gate {
49590Sstevel@tonic-gate 	size_t size = strlen(kept) + strlen(keeps) + 2 + sizeof (pm_pdr_t);
49600Sstevel@tonic-gate 	pm_pdr_t *p = kmem_zalloc(size, KM_SLEEP);
49610Sstevel@tonic-gate 	p->pdr_size = size;
49620Sstevel@tonic-gate 	p->pdr_isprop = isprop;
49630Sstevel@tonic-gate 	p->pdr_kept_paths = NULL;
49640Sstevel@tonic-gate 	p->pdr_kept_count = 0;
49650Sstevel@tonic-gate 	p->pdr_kept = (char *)((intptr_t)p + sizeof (pm_pdr_t));
49660Sstevel@tonic-gate 	(void) strcpy(p->pdr_kept, kept);
49670Sstevel@tonic-gate 	p->pdr_keeper = (char *)((intptr_t)p->pdr_kept + strlen(kept) + 1);
49680Sstevel@tonic-gate 	(void) strcpy(p->pdr_keeper, keeps);
49690Sstevel@tonic-gate 	ASSERT((intptr_t)p->pdr_keeper + strlen(p->pdr_keeper) + 1 <=
49700Sstevel@tonic-gate 	    (intptr_t)p + size);
49710Sstevel@tonic-gate 	ASSERT((intptr_t)p->pdr_kept + strlen(p->pdr_kept) + 1 <=
49720Sstevel@tonic-gate 	    (intptr_t)p + size);
49730Sstevel@tonic-gate 	return (p);
49740Sstevel@tonic-gate }
49750Sstevel@tonic-gate 
49760Sstevel@tonic-gate /*
49770Sstevel@tonic-gate  * Keep a list of recorded dependencies.  We only keep the
49780Sstevel@tonic-gate  * keeper -> kept list for simplification. At this point We do not
49790Sstevel@tonic-gate  * care about whether the devices are attached or not yet,
49800Sstevel@tonic-gate  * this would be done in pm_keeper() and pm_kept().
49810Sstevel@tonic-gate  * If a PM_RESET_PM happens, then we tear down and forget the dependencies,
49820Sstevel@tonic-gate  * and it is up to the user to issue the ioctl again if they want it
49830Sstevel@tonic-gate  * (e.g. pmconfig)
49840Sstevel@tonic-gate  * Returns true if dependency already exists in the list.
49850Sstevel@tonic-gate  */
49860Sstevel@tonic-gate int
49870Sstevel@tonic-gate pm_record_keeper(char *kept, char *keeper, int isprop)
49880Sstevel@tonic-gate {
49890Sstevel@tonic-gate 	PMD_FUNC(pmf, "record_keeper")
49900Sstevel@tonic-gate 	pm_pdr_t *npdr, *ppdr, *pdr;
49910Sstevel@tonic-gate 
49920Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s, %s\n", pmf, kept, keeper))
49930Sstevel@tonic-gate 	ASSERT(kept && keeper);
49940Sstevel@tonic-gate #ifdef DEBUG
49950Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
49960Sstevel@tonic-gate 		prdeps("pm_record_keeper entry");
49970Sstevel@tonic-gate #endif
49980Sstevel@tonic-gate 	for (ppdr = NULL, pdr = pm_dep_head; pdr;
49990Sstevel@tonic-gate 	    ppdr = pdr, pdr = pdr->pdr_next) {
50000Sstevel@tonic-gate 		PMD(PMD_KEEPS, ("%s: check %s, %s\n", pmf, pdr->pdr_kept,
50010Sstevel@tonic-gate 		    pdr->pdr_keeper))
50020Sstevel@tonic-gate 		if (strcmp(kept, pdr->pdr_kept) == 0 &&
50030Sstevel@tonic-gate 		    strcmp(keeper, pdr->pdr_keeper) == 0) {
50040Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: match\n", pmf))
50050Sstevel@tonic-gate 			return (1);
50060Sstevel@tonic-gate 		}
50070Sstevel@tonic-gate 	}
50080Sstevel@tonic-gate 	/*
50090Sstevel@tonic-gate 	 * We did not find any match, so we have to make an entry
50100Sstevel@tonic-gate 	 */
50110Sstevel@tonic-gate 	npdr = newpdr(kept, keeper, isprop);
50120Sstevel@tonic-gate 	if (ppdr) {
50130Sstevel@tonic-gate 		ASSERT(ppdr->pdr_next == NULL);
50140Sstevel@tonic-gate 		ppdr->pdr_next = npdr;
50150Sstevel@tonic-gate 	} else {
50160Sstevel@tonic-gate 		ASSERT(pm_dep_head == NULL);
50170Sstevel@tonic-gate 		pm_dep_head = npdr;
50180Sstevel@tonic-gate 	}
50190Sstevel@tonic-gate #ifdef DEBUG
50200Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
50210Sstevel@tonic-gate 		prdeps("pm_record_keeper after new record");
50220Sstevel@tonic-gate #endif
50230Sstevel@tonic-gate 	if (!isprop)
50240Sstevel@tonic-gate 		pm_unresolved_deps++;
50250Sstevel@tonic-gate 	else
50260Sstevel@tonic-gate 		pm_prop_deps++;
50270Sstevel@tonic-gate 	return (0);
50280Sstevel@tonic-gate }
50290Sstevel@tonic-gate 
50300Sstevel@tonic-gate /*
50310Sstevel@tonic-gate  * Look up this device in the set of devices we've seen ioctls for
50320Sstevel@tonic-gate  * to see if we are holding a threshold spec for it.  If so, make it so.
50330Sstevel@tonic-gate  * At ioctl time, we were given the physical path of the device.
50340Sstevel@tonic-gate  */
50350Sstevel@tonic-gate int
50360Sstevel@tonic-gate pm_thresh_specd(dev_info_t *dip)
50370Sstevel@tonic-gate {
50380Sstevel@tonic-gate 	void pm_apply_recorded_thresh(dev_info_t *, pm_thresh_rec_t *);
50390Sstevel@tonic-gate 	char *path = 0;
50400Sstevel@tonic-gate 	char pathbuf[MAXNAMELEN];
50410Sstevel@tonic-gate 	pm_thresh_rec_t *rp;
50420Sstevel@tonic-gate 
50430Sstevel@tonic-gate 	path = ddi_pathname(dip, pathbuf);
50440Sstevel@tonic-gate 
50450Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_READER);
50460Sstevel@tonic-gate 	for (rp = pm_thresh_head; rp; rp = rp->ptr_next) {
50470Sstevel@tonic-gate 		if (strcmp(rp->ptr_physpath, path) != 0)
50480Sstevel@tonic-gate 			continue;
50490Sstevel@tonic-gate 		pm_apply_recorded_thresh(dip, rp);
50500Sstevel@tonic-gate 		rw_exit(&pm_thresh_rwlock);
50510Sstevel@tonic-gate 		return (1);
50520Sstevel@tonic-gate 	}
50530Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
50540Sstevel@tonic-gate 	return (0);
50550Sstevel@tonic-gate }
50560Sstevel@tonic-gate 
50570Sstevel@tonic-gate static int
50580Sstevel@tonic-gate pm_set_keeping(dev_info_t *keeper, dev_info_t *kept)
50590Sstevel@tonic-gate {
50600Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_keeping")
50610Sstevel@tonic-gate 	pm_info_t *kept_info;
50620Sstevel@tonic-gate 	int j, up = 0, circ;
50630Sstevel@tonic-gate 	void prdeps(char *);
50640Sstevel@tonic-gate 
50650Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), kept=%s@%s(%s#%d)\n", pmf,
50660Sstevel@tonic-gate 	    PM_DEVICE(keeper), PM_DEVICE(kept)))
50670Sstevel@tonic-gate #ifdef DEBUG
50680Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
50690Sstevel@tonic-gate 		prdeps("Before PAD\n");
50700Sstevel@tonic-gate #endif
50710Sstevel@tonic-gate 	ASSERT(keeper != kept);
50720Sstevel@tonic-gate 	if (PM_GET_PM_INFO(keeper) == NULL) {
50730Sstevel@tonic-gate 		cmn_err(CE_CONT, "!device %s@%s(%s#%d) keeps up device "
50740Sstevel@tonic-gate 		    "%s@%s(%s#%d), but the latter is not power managed",
50750Sstevel@tonic-gate 		    PM_DEVICE(keeper), PM_DEVICE(kept));
50760Sstevel@tonic-gate 		PMD((PMD_FAIL | PMD_KEEPS), ("%s: keeper %s@%s(%s#%d) is not"
50770Sstevel@tonic-gate 		    "power managed\n", pmf, PM_DEVICE(keeper)))
50780Sstevel@tonic-gate 		return (0);
50790Sstevel@tonic-gate 	}
50800Sstevel@tonic-gate 	kept_info = PM_GET_PM_INFO(kept);
50810Sstevel@tonic-gate 	ASSERT(kept_info);
50820Sstevel@tonic-gate 	PM_LOCK_POWER(keeper, &circ);
50830Sstevel@tonic-gate 	for (j = 0; j < PM_NUMCMPTS(keeper); j++) {
50840Sstevel@tonic-gate 		if (PM_CURPOWER(keeper, j)) {
50850Sstevel@tonic-gate 			up++;
50860Sstevel@tonic-gate 			break;
50870Sstevel@tonic-gate 		}
50880Sstevel@tonic-gate 	}
50890Sstevel@tonic-gate 	if (up) {
50900Sstevel@tonic-gate 		/* Bringup and maintain a hold on the kept */
50910Sstevel@tonic-gate 		PMD(PMD_KEEPS, ("%s: place a hold on kept %s@%s(%s#%d)\n", pmf,
50920Sstevel@tonic-gate 		    PM_DEVICE(kept)))
50930Sstevel@tonic-gate 		bring_pmdep_up(kept, 1);
50940Sstevel@tonic-gate 	}
50950Sstevel@tonic-gate 	PM_UNLOCK_POWER(keeper, circ);
50960Sstevel@tonic-gate #ifdef DEBUG
50970Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
50980Sstevel@tonic-gate 		prdeps("After PAD\n");
50990Sstevel@tonic-gate #endif
51000Sstevel@tonic-gate 	return (1);
51010Sstevel@tonic-gate }
51020Sstevel@tonic-gate 
51030Sstevel@tonic-gate /*
51040Sstevel@tonic-gate  * Should this device keep up another device?
51050Sstevel@tonic-gate  * Look up this device in the set of devices we've seen ioctls for
51060Sstevel@tonic-gate  * to see if we are holding a dependency spec for it.  If so, make it so.
51070Sstevel@tonic-gate  * Because we require the kept device to be attached already in order to
51080Sstevel@tonic-gate  * make the list entry (and hold it), we only need to look for keepers.
51090Sstevel@tonic-gate  * At ioctl time, we were given the physical path of the device.
51100Sstevel@tonic-gate  */
51110Sstevel@tonic-gate int
51120Sstevel@tonic-gate pm_keeper(char *keeper)
51130Sstevel@tonic-gate {
51140Sstevel@tonic-gate 	PMD_FUNC(pmf, "keeper")
51150Sstevel@tonic-gate 	int pm_apply_recorded_dep(dev_info_t *, pm_pdr_t *);
51160Sstevel@tonic-gate 	dev_info_t *dip;
51170Sstevel@tonic-gate 	pm_pdr_t *dp;
51180Sstevel@tonic-gate 	dev_info_t *kept = NULL;
51190Sstevel@tonic-gate 	int ret = 0;
51200Sstevel@tonic-gate 	int i;
51210Sstevel@tonic-gate 
51220Sstevel@tonic-gate 	if (!pm_unresolved_deps && !pm_prop_deps)
51230Sstevel@tonic-gate 		return (0);
51240Sstevel@tonic-gate 	ASSERT(keeper != NULL);
51250Sstevel@tonic-gate 	dip = pm_name_to_dip(keeper, 1);
51260Sstevel@tonic-gate 	if (dip == NULL)
51270Sstevel@tonic-gate 		return (0);
51280Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: keeper=%s\n", pmf, keeper))
51290Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
51300Sstevel@tonic-gate 		if (!dp->pdr_isprop) {
51310Sstevel@tonic-gate 			if (!pm_unresolved_deps)
51320Sstevel@tonic-gate 				continue;
51330Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: keeper %s\n", pmf, dp->pdr_keeper))
51340Sstevel@tonic-gate 			if (dp->pdr_satisfied) {
51350Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: satisfied\n", pmf))
51360Sstevel@tonic-gate 				continue;
51370Sstevel@tonic-gate 			}
51380Sstevel@tonic-gate 			if (strcmp(dp->pdr_keeper, keeper) == 0) {
51390Sstevel@tonic-gate 				ret += pm_apply_recorded_dep(dip, dp);
51400Sstevel@tonic-gate 			}
51410Sstevel@tonic-gate 		} else {
51420Sstevel@tonic-gate 			if (strcmp(dp->pdr_keeper, keeper) != 0)
51430Sstevel@tonic-gate 				continue;
51440Sstevel@tonic-gate 			for (i = 0; i < dp->pdr_kept_count; i++) {
51450Sstevel@tonic-gate 				if (dp->pdr_kept_paths[i] == NULL)
51460Sstevel@tonic-gate 					continue;
51470Sstevel@tonic-gate 				kept = pm_name_to_dip(dp->pdr_kept_paths[i], 1);
51480Sstevel@tonic-gate 				if (kept == NULL)
51490Sstevel@tonic-gate 					continue;
51500Sstevel@tonic-gate 				ASSERT(ddi_prop_exists(DDI_DEV_T_ANY, kept,
51510Sstevel@tonic-gate 				    DDI_PROP_DONTPASS, dp->pdr_kept));
51520Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), "
51530Sstevel@tonic-gate 				    "kept=%s@%s(%s#%d) keptcnt=%d\n",
51540Sstevel@tonic-gate 				    pmf, PM_DEVICE(dip), PM_DEVICE(kept),
51550Sstevel@tonic-gate 				    dp->pdr_kept_count))
51560Sstevel@tonic-gate 				if (kept != dip) {
51570Sstevel@tonic-gate 					ret += pm_set_keeping(dip, kept);
51580Sstevel@tonic-gate 				}
51590Sstevel@tonic-gate 				ddi_release_devi(kept);
51600Sstevel@tonic-gate 			}
51610Sstevel@tonic-gate 
51620Sstevel@tonic-gate 		}
51630Sstevel@tonic-gate 	}
51640Sstevel@tonic-gate 	ddi_release_devi(dip);
51650Sstevel@tonic-gate 	return (ret);
51660Sstevel@tonic-gate }
51670Sstevel@tonic-gate 
51680Sstevel@tonic-gate /*
51690Sstevel@tonic-gate  * Should this device be kept up by another device?
51700Sstevel@tonic-gate  * Look up all dependency recorded from PM_ADD_DEPENDENT and
51710Sstevel@tonic-gate  * PM_ADD_DEPENDENT_PROPERTY ioctls. Record down on the keeper's
51720Sstevel@tonic-gate  * kept device lists.
51730Sstevel@tonic-gate  */
51740Sstevel@tonic-gate static int
51750Sstevel@tonic-gate pm_kept(char *keptp)
51760Sstevel@tonic-gate {
51770Sstevel@tonic-gate 	PMD_FUNC(pmf, "kept")
51780Sstevel@tonic-gate 	pm_pdr_t *dp;
51790Sstevel@tonic-gate 	int found = 0;
51800Sstevel@tonic-gate 	int ret = 0;
51810Sstevel@tonic-gate 	dev_info_t *keeper;
51820Sstevel@tonic-gate 	dev_info_t *kept;
51830Sstevel@tonic-gate 	size_t length;
51840Sstevel@tonic-gate 	int i;
51850Sstevel@tonic-gate 	char **paths;
51860Sstevel@tonic-gate 	char *path;
51870Sstevel@tonic-gate 
51880Sstevel@tonic-gate 	ASSERT(keptp != NULL);
51890Sstevel@tonic-gate 	kept = pm_name_to_dip(keptp, 1);
51900Sstevel@tonic-gate 	if (kept == NULL)
51910Sstevel@tonic-gate 		return (0);
51920Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(kept)))
51930Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
51940Sstevel@tonic-gate 		if (dp->pdr_isprop) {
51950Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: property %s\n", pmf, dp->pdr_kept))
51960Sstevel@tonic-gate 			if (ddi_prop_exists(DDI_DEV_T_ANY, kept,
51970Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, dp->pdr_kept)) {
51980Sstevel@tonic-gate 				/*
51990Sstevel@tonic-gate 				 * Dont allow self dependency.
52000Sstevel@tonic-gate 				 */
52010Sstevel@tonic-gate 				if (strcmp(dp->pdr_keeper, keptp) == 0)
52020Sstevel@tonic-gate 					continue;
52030Sstevel@tonic-gate 				keeper = pm_name_to_dip(dp->pdr_keeper, 1);
52040Sstevel@tonic-gate 				if (keeper == NULL)
52050Sstevel@tonic-gate 					continue;
52060Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: adding to kepts path list "
52070Sstevel@tonic-gate 				    "%p\n", pmf, (void *)kept))
52080Sstevel@tonic-gate #ifdef DEBUG
52090Sstevel@tonic-gate 				if (pm_debug & PMD_DEP)
52100Sstevel@tonic-gate 					prdeps("Before Adding from pm_kept\n");
52110Sstevel@tonic-gate #endif
52120Sstevel@tonic-gate 				/*
52130Sstevel@tonic-gate 				 * Add ourselves to the dip list.
52140Sstevel@tonic-gate 				 */
52150Sstevel@tonic-gate 				if (dp->pdr_kept_count == 0) {
52160Sstevel@tonic-gate 					length = strlen(keptp) + 1;
52170Sstevel@tonic-gate 					path =
52180Sstevel@tonic-gate 					    kmem_alloc(length, KM_SLEEP);
52190Sstevel@tonic-gate 					paths = kmem_alloc(sizeof (char **),
52200Sstevel@tonic-gate 						    KM_SLEEP);
52210Sstevel@tonic-gate 					(void) strcpy(path, keptp);
52220Sstevel@tonic-gate 					paths[0] = path;
52230Sstevel@tonic-gate 					dp->pdr_kept_paths = paths;
52240Sstevel@tonic-gate 					dp->pdr_kept_count++;
52250Sstevel@tonic-gate 				} else {
52260Sstevel@tonic-gate 					/* Check to see if already on list */
52270Sstevel@tonic-gate 					for (i = 0; i < dp->pdr_kept_count;
52280Sstevel@tonic-gate 					    i++) {
52290Sstevel@tonic-gate 						if (strcmp(keptp,
52300Sstevel@tonic-gate 						    dp->pdr_kept_paths[i])
52310Sstevel@tonic-gate 						    == 0) {
52320Sstevel@tonic-gate 							found++;
52330Sstevel@tonic-gate 							break;
52340Sstevel@tonic-gate 						}
52350Sstevel@tonic-gate 					}
52360Sstevel@tonic-gate 					if (found) {
52370Sstevel@tonic-gate 						ddi_release_devi(keeper);
52380Sstevel@tonic-gate 						continue;
52390Sstevel@tonic-gate 					}
52400Sstevel@tonic-gate 					length = dp->pdr_kept_count *
52410Sstevel@tonic-gate 					    sizeof (char **);
52420Sstevel@tonic-gate 					paths = kmem_alloc(
52430Sstevel@tonic-gate 					    length + sizeof (char **),
52440Sstevel@tonic-gate 					    KM_SLEEP);
52450Sstevel@tonic-gate 					if (dp->pdr_kept_count) {
52460Sstevel@tonic-gate 						bcopy(dp->pdr_kept_paths,
52470Sstevel@tonic-gate 						    paths, length);
52480Sstevel@tonic-gate 						kmem_free(dp->pdr_kept_paths,
52490Sstevel@tonic-gate 							length);
52500Sstevel@tonic-gate 					}
52510Sstevel@tonic-gate 					dp->pdr_kept_paths = paths;
52520Sstevel@tonic-gate 					length = strlen(keptp) + 1;
52530Sstevel@tonic-gate 					path =
52540Sstevel@tonic-gate 					    kmem_alloc(length, KM_SLEEP);
52550Sstevel@tonic-gate 					(void) strcpy(path, keptp);
52560Sstevel@tonic-gate 					dp->pdr_kept_paths[i] = path;
52570Sstevel@tonic-gate 					dp->pdr_kept_count++;
52580Sstevel@tonic-gate 				}
52590Sstevel@tonic-gate #ifdef DEBUG
52600Sstevel@tonic-gate 				if (pm_debug & PMD_DEP)
52610Sstevel@tonic-gate 					prdeps("After from pm_kept\n");
52620Sstevel@tonic-gate #endif
52630Sstevel@tonic-gate 				if (keeper) {
52640Sstevel@tonic-gate 					ret += pm_set_keeping(keeper, kept);
52650Sstevel@tonic-gate 					ddi_release_devi(keeper);
52660Sstevel@tonic-gate 				}
52670Sstevel@tonic-gate 			}
52680Sstevel@tonic-gate 		} else {
52690Sstevel@tonic-gate 			/*
52700Sstevel@tonic-gate 			 * pm_keeper would be called later to do
52710Sstevel@tonic-gate 			 * the actual pm_set_keeping.
52720Sstevel@tonic-gate 			 */
52730Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: adding to kepts path list %p\n",
52740Sstevel@tonic-gate 			    pmf, (void *)kept))
52750Sstevel@tonic-gate #ifdef DEBUG
52760Sstevel@tonic-gate 			if (pm_debug & PMD_DEP)
52770Sstevel@tonic-gate 				prdeps("Before Adding from pm_kept\n");
52780Sstevel@tonic-gate #endif
52790Sstevel@tonic-gate 			if (strcmp(keptp, dp->pdr_kept) == 0) {
52800Sstevel@tonic-gate 				if (dp->pdr_kept_paths == NULL) {
52810Sstevel@tonic-gate 					length = strlen(keptp) + 1;
52820Sstevel@tonic-gate 					path =
52830Sstevel@tonic-gate 					    kmem_alloc(length, KM_SLEEP);
52840Sstevel@tonic-gate 					paths = kmem_alloc(sizeof (char **),
52850Sstevel@tonic-gate 						KM_SLEEP);
52860Sstevel@tonic-gate 					(void) strcpy(path, keptp);
52870Sstevel@tonic-gate 					paths[0] = path;
52880Sstevel@tonic-gate 					dp->pdr_kept_paths = paths;
52890Sstevel@tonic-gate 					dp->pdr_kept_count++;
52900Sstevel@tonic-gate 				}
52910Sstevel@tonic-gate 			}
52920Sstevel@tonic-gate #ifdef DEBUG
52930Sstevel@tonic-gate 			if (pm_debug & PMD_DEP)
52940Sstevel@tonic-gate 			    prdeps("After from pm_kept\n");
52950Sstevel@tonic-gate #endif
52960Sstevel@tonic-gate 		}
52970Sstevel@tonic-gate 	}
52980Sstevel@tonic-gate 	ddi_release_devi(kept);
52990Sstevel@tonic-gate 	return (ret);
53000Sstevel@tonic-gate }
53010Sstevel@tonic-gate 
53020Sstevel@tonic-gate /*
53030Sstevel@tonic-gate  * Apply a recorded dependency.  dp specifies the dependency, and
53040Sstevel@tonic-gate  * keeper is already known to be the device that keeps up the other (kept) one.
53050Sstevel@tonic-gate  * We have to the whole tree for the "kept" device, then apply
53060Sstevel@tonic-gate  * the dependency (which may already be applied).
53070Sstevel@tonic-gate  */
53080Sstevel@tonic-gate int
53090Sstevel@tonic-gate pm_apply_recorded_dep(dev_info_t *keeper, pm_pdr_t *dp)
53100Sstevel@tonic-gate {
53110Sstevel@tonic-gate 	PMD_FUNC(pmf, "apply_recorded_dep")
53120Sstevel@tonic-gate 	dev_info_t *kept = NULL;
53130Sstevel@tonic-gate 	int ret = 0;
53140Sstevel@tonic-gate 	char *keptp = NULL;
53150Sstevel@tonic-gate 
53160Sstevel@tonic-gate 	/*
53170Sstevel@tonic-gate 	 * Device to Device dependency can only be 1 to 1.
53180Sstevel@tonic-gate 	 */
53190Sstevel@tonic-gate 	if (dp->pdr_kept_paths == NULL)
53200Sstevel@tonic-gate 		return (0);
53210Sstevel@tonic-gate 	keptp = dp->pdr_kept_paths[0];
53220Sstevel@tonic-gate 	if (keptp == NULL)
53230Sstevel@tonic-gate 		return (0);
53240Sstevel@tonic-gate 	ASSERT(*keptp != '\0');
53250Sstevel@tonic-gate 	kept = pm_name_to_dip(keptp, 1);
53260Sstevel@tonic-gate 	if (kept == NULL)
53270Sstevel@tonic-gate 		return (0);
53280Sstevel@tonic-gate 	if (kept) {
53290Sstevel@tonic-gate 		PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf,
53300Sstevel@tonic-gate 		    dp->pdr_keeper, keptp))
53310Sstevel@tonic-gate 		if (pm_set_keeping(keeper, kept)) {
53320Sstevel@tonic-gate 			ASSERT(dp->pdr_satisfied == 0);
53330Sstevel@tonic-gate 			dp->pdr_satisfied = 1;
53340Sstevel@tonic-gate 			ASSERT(pm_unresolved_deps);
53350Sstevel@tonic-gate 			pm_unresolved_deps--;
53360Sstevel@tonic-gate 			ret++;
53370Sstevel@tonic-gate 		}
53380Sstevel@tonic-gate 	}
53390Sstevel@tonic-gate 	ddi_release_devi(kept);
53400Sstevel@tonic-gate 
53410Sstevel@tonic-gate 	return (ret);
53420Sstevel@tonic-gate }
53430Sstevel@tonic-gate 
53440Sstevel@tonic-gate /*
53450Sstevel@tonic-gate  * Called from common/io/pm.c
53460Sstevel@tonic-gate  */
53470Sstevel@tonic-gate int
53480Sstevel@tonic-gate pm_cur_power(pm_component_t *cp)
53490Sstevel@tonic-gate {
53500Sstevel@tonic-gate 	return (cur_power(cp));
53510Sstevel@tonic-gate }
53520Sstevel@tonic-gate 
53530Sstevel@tonic-gate /*
53540Sstevel@tonic-gate  * External interface to sanity-check a power level.
53550Sstevel@tonic-gate  */
53560Sstevel@tonic-gate int
53570Sstevel@tonic-gate pm_valid_power(dev_info_t *dip, int comp, int level)
53580Sstevel@tonic-gate {
53590Sstevel@tonic-gate 	PMD_FUNC(pmf, "valid_power")
53600Sstevel@tonic-gate 
53610Sstevel@tonic-gate 	if (comp >= 0 && comp < PM_NUMCMPTS(dip) && level >= 0)
53620Sstevel@tonic-gate 		return (e_pm_valid_power(dip, comp, level));
53630Sstevel@tonic-gate 	else {
53640Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: comp=%d, ncomp=%d, level=%d\n",
53650Sstevel@tonic-gate 		    pmf, comp, PM_NUMCMPTS(dip), level))
53660Sstevel@tonic-gate 		return (0);
53670Sstevel@tonic-gate 	}
53680Sstevel@tonic-gate }
53690Sstevel@tonic-gate 
53700Sstevel@tonic-gate /*
53710Sstevel@tonic-gate  * Called when a device that is direct power managed needs to change state.
53720Sstevel@tonic-gate  * This routine arranges to block the request until the process managing
53730Sstevel@tonic-gate  * the device makes the change (or some other incompatible change) or
53740Sstevel@tonic-gate  * the process closes /dev/pm.
53750Sstevel@tonic-gate  */
53760Sstevel@tonic-gate static int
53770Sstevel@tonic-gate pm_block(dev_info_t *dip, int comp, int newpower, int oldpower)
53780Sstevel@tonic-gate {
53790Sstevel@tonic-gate 	pm_rsvp_t *new = kmem_zalloc(sizeof (*new), KM_SLEEP);
53800Sstevel@tonic-gate 	int ret = 0;
53810Sstevel@tonic-gate 	void pm_dequeue_blocked(pm_rsvp_t *);
53820Sstevel@tonic-gate 	void pm_enqueue_blocked(pm_rsvp_t *);
53830Sstevel@tonic-gate 
53840Sstevel@tonic-gate 	ASSERT(!pm_processes_stopped);
53850Sstevel@tonic-gate 	ASSERT(PM_IAM_LOCKING_DIP(dip));
53860Sstevel@tonic-gate 	new->pr_dip = dip;
53870Sstevel@tonic-gate 	new->pr_comp = comp;
53880Sstevel@tonic-gate 	new->pr_newlevel = newpower;
53890Sstevel@tonic-gate 	new->pr_oldlevel = oldpower;
53900Sstevel@tonic-gate 	cv_init(&new->pr_cv, NULL, CV_DEFAULT, NULL);
53910Sstevel@tonic-gate 	mutex_enter(&pm_rsvp_lock);
53920Sstevel@tonic-gate 	pm_enqueue_blocked(new);
53930Sstevel@tonic-gate 	pm_enqueue_notify(PSC_PENDING_CHANGE, dip, comp, newpower, oldpower,
53940Sstevel@tonic-gate 	    PM_CANBLOCK_BLOCK);
53950Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
53960Sstevel@tonic-gate 	/*
53970Sstevel@tonic-gate 	 * truss may make the cv_wait_sig return prematurely
53980Sstevel@tonic-gate 	 */
53990Sstevel@tonic-gate 	while (ret == 0) {
54000Sstevel@tonic-gate 		/*
54010Sstevel@tonic-gate 		 * Normally there will be no user context involved, but if
54020Sstevel@tonic-gate 		 * there is (e.g. we are here via an ioctl call to a driver)
54030Sstevel@tonic-gate 		 * then we should allow the process to abort the request,
54040Sstevel@tonic-gate 		 * or we get an unkillable process if the same thread does
54050Sstevel@tonic-gate 		 * PM_DIRECT_PM and pm_raise_power
54060Sstevel@tonic-gate 		 */
54070Sstevel@tonic-gate 		if (cv_wait_sig(&new->pr_cv, &pm_rsvp_lock) == 0) {
54080Sstevel@tonic-gate 			ret = PMP_FAIL;
54090Sstevel@tonic-gate 		} else {
54100Sstevel@tonic-gate 			ret = new->pr_retval;
54110Sstevel@tonic-gate 		}
54120Sstevel@tonic-gate 	}
54130Sstevel@tonic-gate 	pm_dequeue_blocked(new);
54140Sstevel@tonic-gate 	mutex_exit(&pm_rsvp_lock);
54150Sstevel@tonic-gate 	cv_destroy(&new->pr_cv);
54160Sstevel@tonic-gate 	kmem_free(new, sizeof (*new));
54170Sstevel@tonic-gate 	return (ret);
54180Sstevel@tonic-gate }
54190Sstevel@tonic-gate 
54200Sstevel@tonic-gate /*
54210Sstevel@tonic-gate  * Returns true if the process is interested in power level changes (has issued
54220Sstevel@tonic-gate  * PM_GET_STATE_CHANGE ioctl).
54230Sstevel@tonic-gate  */
54240Sstevel@tonic-gate int
54250Sstevel@tonic-gate pm_interest_registered(int clone)
54260Sstevel@tonic-gate {
54270Sstevel@tonic-gate 	ASSERT(clone >= 0 && clone < PM_MAX_CLONE - 1);
54280Sstevel@tonic-gate 	return (pm_interest[clone]);
54290Sstevel@tonic-gate }
54300Sstevel@tonic-gate 
54310Sstevel@tonic-gate /*
54320Sstevel@tonic-gate  * Process with clone has just done PM_DIRECT_PM on dip, or has asked to
54330Sstevel@tonic-gate  * watch all state transitions (dip == NULL).  Set up data
54340Sstevel@tonic-gate  * structs to communicate with process about state changes.
54350Sstevel@tonic-gate  */
54360Sstevel@tonic-gate void
54370Sstevel@tonic-gate pm_register_watcher(int clone, dev_info_t *dip)
54380Sstevel@tonic-gate {
54390Sstevel@tonic-gate 	pscc_t	*p;
54400Sstevel@tonic-gate 	psce_t	*psce;
54410Sstevel@tonic-gate 	static void pm_enqueue_pscc(pscc_t *, pscc_t **);
54420Sstevel@tonic-gate 
54430Sstevel@tonic-gate 	/*
54440Sstevel@tonic-gate 	 * We definitely need a control struct, then we have to search to see
54450Sstevel@tonic-gate 	 * there is already an entries struct (in the dip != NULL case).
54460Sstevel@tonic-gate 	 */
54470Sstevel@tonic-gate 	pscc_t	*pscc = kmem_zalloc(sizeof (*pscc), KM_SLEEP);
54480Sstevel@tonic-gate 	pscc->pscc_clone = clone;
54490Sstevel@tonic-gate 	pscc->pscc_dip = dip;
54500Sstevel@tonic-gate 
54510Sstevel@tonic-gate 	if (dip) {
54520Sstevel@tonic-gate 		int found = 0;
54530Sstevel@tonic-gate 		rw_enter(&pm_pscc_direct_rwlock, RW_WRITER);
54540Sstevel@tonic-gate 		for (p = pm_pscc_direct; p; p = p->pscc_next) {
54550Sstevel@tonic-gate 			/*
54560Sstevel@tonic-gate 			 * Already an entry for this clone, so just use it
54570Sstevel@tonic-gate 			 * for the new one (for the case where a single
54580Sstevel@tonic-gate 			 * process is watching multiple devices)
54590Sstevel@tonic-gate 			 */
54600Sstevel@tonic-gate 			if (p->pscc_clone == clone) {
54610Sstevel@tonic-gate 				ASSERT(p->pscc_dip != dip);
54620Sstevel@tonic-gate 				pscc->pscc_entries = p->pscc_entries;
54630Sstevel@tonic-gate 				pscc->pscc_entries->psce_references++;
54640Sstevel@tonic-gate 				found++;
54650Sstevel@tonic-gate 			}
54660Sstevel@tonic-gate 		}
54670Sstevel@tonic-gate 		if (!found) {		/* create a new one */
54680Sstevel@tonic-gate 			psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP);
54690Sstevel@tonic-gate 			mutex_init(&psce->psce_lock, NULL, MUTEX_DEFAULT, NULL);
54700Sstevel@tonic-gate 			psce->psce_first =
54710Sstevel@tonic-gate 			    kmem_zalloc(sizeof (pm_state_change_t) * PSCCOUNT,
54720Sstevel@tonic-gate 			    KM_SLEEP);
54730Sstevel@tonic-gate 			psce->psce_in = psce->psce_out = psce->psce_first;
54740Sstevel@tonic-gate 			psce->psce_last = &psce->psce_first[PSCCOUNT - 1];
54750Sstevel@tonic-gate 			psce->psce_references = 1;
54760Sstevel@tonic-gate 			pscc->pscc_entries = psce;
54770Sstevel@tonic-gate 		}
54780Sstevel@tonic-gate 		pm_enqueue_pscc(pscc, &pm_pscc_direct);
54790Sstevel@tonic-gate 		rw_exit(&pm_pscc_direct_rwlock);
54800Sstevel@tonic-gate 	} else {
54810Sstevel@tonic-gate 		ASSERT(!pm_interest_registered(clone));
54820Sstevel@tonic-gate 		rw_enter(&pm_pscc_interest_rwlock, RW_WRITER);
54830Sstevel@tonic-gate #ifdef DEBUG
54840Sstevel@tonic-gate 		for (p = pm_pscc_interest; p; p = p->pscc_next) {
54850Sstevel@tonic-gate 			/*
54860Sstevel@tonic-gate 			 * Should not be an entry for this clone!
54870Sstevel@tonic-gate 			 */
54880Sstevel@tonic-gate 			ASSERT(p->pscc_clone != clone);
54890Sstevel@tonic-gate 		}
54900Sstevel@tonic-gate #endif
54910Sstevel@tonic-gate 		psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP);
54920Sstevel@tonic-gate 		psce->psce_first = kmem_zalloc(sizeof (pm_state_change_t) *
54930Sstevel@tonic-gate 		    PSCCOUNT, KM_SLEEP);
54940Sstevel@tonic-gate 		psce->psce_in = psce->psce_out = psce->psce_first;
54950Sstevel@tonic-gate 		psce->psce_last = &psce->psce_first[PSCCOUNT - 1];
54960Sstevel@tonic-gate 		psce->psce_references = 1;
54970Sstevel@tonic-gate 		pscc->pscc_entries = psce;
54980Sstevel@tonic-gate 		pm_enqueue_pscc(pscc, &pm_pscc_interest);
54990Sstevel@tonic-gate 		pm_interest[clone] = 1;
55000Sstevel@tonic-gate 		rw_exit(&pm_pscc_interest_rwlock);
55010Sstevel@tonic-gate 	}
55020Sstevel@tonic-gate }
55030Sstevel@tonic-gate 
55040Sstevel@tonic-gate /*
55050Sstevel@tonic-gate  * Remove the given entry from the blocked list
55060Sstevel@tonic-gate  */
55070Sstevel@tonic-gate void
55080Sstevel@tonic-gate pm_dequeue_blocked(pm_rsvp_t *p)
55090Sstevel@tonic-gate {
55100Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
55110Sstevel@tonic-gate 	if (pm_blocked_list == p) {
55120Sstevel@tonic-gate 		ASSERT(p->pr_prev == NULL);
55130Sstevel@tonic-gate 		if (p->pr_next != NULL)
55140Sstevel@tonic-gate 			p->pr_next->pr_prev = NULL;
55150Sstevel@tonic-gate 		pm_blocked_list = p->pr_next;
55160Sstevel@tonic-gate 	} else {
55170Sstevel@tonic-gate 		ASSERT(p->pr_prev != NULL);
55180Sstevel@tonic-gate 		p->pr_prev->pr_next = p->pr_next;
55190Sstevel@tonic-gate 		if (p->pr_next != NULL)
55200Sstevel@tonic-gate 			p->pr_next->pr_prev = p->pr_prev;
55210Sstevel@tonic-gate 	}
55220Sstevel@tonic-gate }
55230Sstevel@tonic-gate 
55240Sstevel@tonic-gate /*
55250Sstevel@tonic-gate  * Remove the given control struct from the given list
55260Sstevel@tonic-gate  */
55270Sstevel@tonic-gate static void
55280Sstevel@tonic-gate pm_dequeue_pscc(pscc_t *p, pscc_t **list)
55290Sstevel@tonic-gate {
55300Sstevel@tonic-gate 	if (*list == p) {
55310Sstevel@tonic-gate 		ASSERT(p->pscc_prev == NULL);
55320Sstevel@tonic-gate 		if (p->pscc_next != NULL)
55330Sstevel@tonic-gate 			p->pscc_next->pscc_prev = NULL;
55340Sstevel@tonic-gate 		*list = p->pscc_next;
55350Sstevel@tonic-gate 	} else {
55360Sstevel@tonic-gate 		ASSERT(p->pscc_prev != NULL);
55370Sstevel@tonic-gate 		p->pscc_prev->pscc_next = p->pscc_next;
55380Sstevel@tonic-gate 		if (p->pscc_next != NULL)
55390Sstevel@tonic-gate 			p->pscc_next->pscc_prev = p->pscc_prev;
55400Sstevel@tonic-gate 	}
55410Sstevel@tonic-gate }
55420Sstevel@tonic-gate 
55430Sstevel@tonic-gate /*
55440Sstevel@tonic-gate  * Stick the control struct specified on the front of the list
55450Sstevel@tonic-gate  */
55460Sstevel@tonic-gate static void
55470Sstevel@tonic-gate pm_enqueue_pscc(pscc_t *p, pscc_t **list)
55480Sstevel@tonic-gate {
55490Sstevel@tonic-gate 	pscc_t *h;	/* entry at head of list */
55500Sstevel@tonic-gate 	if ((h = *list) == NULL) {
55510Sstevel@tonic-gate 		*list = p;
55520Sstevel@tonic-gate 		ASSERT(p->pscc_next == NULL);
55530Sstevel@tonic-gate 		ASSERT(p->pscc_prev == NULL);
55540Sstevel@tonic-gate 	} else {
55550Sstevel@tonic-gate 		p->pscc_next = h;
55560Sstevel@tonic-gate 		ASSERT(h->pscc_prev == NULL);
55570Sstevel@tonic-gate 		h->pscc_prev = p;
55580Sstevel@tonic-gate 		ASSERT(p->pscc_prev == NULL);
55590Sstevel@tonic-gate 		*list = p;
55600Sstevel@tonic-gate 	}
55610Sstevel@tonic-gate }
55620Sstevel@tonic-gate 
55630Sstevel@tonic-gate /*
55640Sstevel@tonic-gate  * If dip is NULL, process is closing "clone" clean up all its registrations.
55650Sstevel@tonic-gate  * Otherwise only clean up those for dip because process is just giving up
55660Sstevel@tonic-gate  * control of a direct device.
55670Sstevel@tonic-gate  */
55680Sstevel@tonic-gate void
55690Sstevel@tonic-gate pm_deregister_watcher(int clone, dev_info_t *dip)
55700Sstevel@tonic-gate {
55710Sstevel@tonic-gate 	pscc_t	*p, *pn;
55720Sstevel@tonic-gate 	psce_t	*psce;
55730Sstevel@tonic-gate 	int found = 0;
55740Sstevel@tonic-gate 
55750Sstevel@tonic-gate 	if (dip == NULL) {
55760Sstevel@tonic-gate 		rw_enter(&pm_pscc_interest_rwlock, RW_WRITER);
55770Sstevel@tonic-gate 		for (p = pm_pscc_interest; p; p = pn) {
55780Sstevel@tonic-gate 			pn = p->pscc_next;
55790Sstevel@tonic-gate 			if (p->pscc_clone == clone) {
55800Sstevel@tonic-gate 				pm_dequeue_pscc(p, &pm_pscc_interest);
55810Sstevel@tonic-gate 				psce = p->pscc_entries;
55820Sstevel@tonic-gate 				ASSERT(psce->psce_references == 1);
55830Sstevel@tonic-gate 				mutex_destroy(&psce->psce_lock);
55840Sstevel@tonic-gate 				kmem_free(psce->psce_first,
55850Sstevel@tonic-gate 				    sizeof (pm_state_change_t) * PSCCOUNT);
55860Sstevel@tonic-gate 				kmem_free(psce, sizeof (*psce));
55870Sstevel@tonic-gate 				kmem_free(p, sizeof (*p));
55880Sstevel@tonic-gate 			}
55890Sstevel@tonic-gate 		}
55900Sstevel@tonic-gate 		pm_interest[clone] = 0;
55910Sstevel@tonic-gate 		rw_exit(&pm_pscc_interest_rwlock);
55920Sstevel@tonic-gate 	}
55930Sstevel@tonic-gate 	found = 0;
55940Sstevel@tonic-gate 	rw_enter(&pm_pscc_direct_rwlock, RW_WRITER);
55950Sstevel@tonic-gate 	for (p = pm_pscc_direct; p; p = pn) {
55960Sstevel@tonic-gate 		pn = p->pscc_next;
55970Sstevel@tonic-gate 		if ((dip && p->pscc_dip == dip) ||
55980Sstevel@tonic-gate 		    (dip == NULL && clone == p->pscc_clone)) {
55990Sstevel@tonic-gate 			ASSERT(clone == p->pscc_clone);
56000Sstevel@tonic-gate 			found++;
56010Sstevel@tonic-gate 			/*
56020Sstevel@tonic-gate 			 * Remove from control list
56030Sstevel@tonic-gate 			 */
56040Sstevel@tonic-gate 			pm_dequeue_pscc(p, &pm_pscc_direct);
56050Sstevel@tonic-gate 			/*
56060Sstevel@tonic-gate 			 * If we're the last reference, free the
56070Sstevel@tonic-gate 			 * entries struct.
56080Sstevel@tonic-gate 			 */
56090Sstevel@tonic-gate 			psce = p->pscc_entries;
56100Sstevel@tonic-gate 			ASSERT(psce);
56110Sstevel@tonic-gate 			if (psce->psce_references == 1) {
56120Sstevel@tonic-gate 				kmem_free(psce->psce_first,
56130Sstevel@tonic-gate 				    PSCCOUNT * sizeof (pm_state_change_t));
56140Sstevel@tonic-gate 				kmem_free(psce, sizeof (*psce));
56150Sstevel@tonic-gate 			} else {
56160Sstevel@tonic-gate 				psce->psce_references--;
56170Sstevel@tonic-gate 			}
56180Sstevel@tonic-gate 			kmem_free(p, sizeof (*p));
56190Sstevel@tonic-gate 		}
56200Sstevel@tonic-gate 	}
56210Sstevel@tonic-gate 	ASSERT(dip == NULL || found);
56220Sstevel@tonic-gate 	rw_exit(&pm_pscc_direct_rwlock);
56230Sstevel@tonic-gate }
56240Sstevel@tonic-gate 
56250Sstevel@tonic-gate /*
56260Sstevel@tonic-gate  * Search the indicated list for an entry that matches clone, and return a
56270Sstevel@tonic-gate  * pointer to it.  To be interesting, the entry must have something ready to
56280Sstevel@tonic-gate  * be passed up to the controlling process.
56290Sstevel@tonic-gate  * The returned entry will be locked upon return from this call.
56300Sstevel@tonic-gate  */
56310Sstevel@tonic-gate static psce_t *
56320Sstevel@tonic-gate pm_psc_find_clone(int clone, pscc_t **list, krwlock_t *lock)
56330Sstevel@tonic-gate {
56340Sstevel@tonic-gate 	pscc_t	*p;
56350Sstevel@tonic-gate 	psce_t	*psce;
56360Sstevel@tonic-gate 	rw_enter(lock, RW_READER);
56370Sstevel@tonic-gate 	for (p = *list; p; p = p->pscc_next) {
56380Sstevel@tonic-gate 		if (clone == p->pscc_clone) {
56390Sstevel@tonic-gate 			psce = p->pscc_entries;
56400Sstevel@tonic-gate 			mutex_enter(&psce->psce_lock);
56410Sstevel@tonic-gate 			if (psce->psce_out->size) {
56420Sstevel@tonic-gate 				rw_exit(lock);
56430Sstevel@tonic-gate 				return (psce);
56440Sstevel@tonic-gate 			} else {
56450Sstevel@tonic-gate 				mutex_exit(&psce->psce_lock);
56460Sstevel@tonic-gate 			}
56470Sstevel@tonic-gate 		}
56480Sstevel@tonic-gate 	}
56490Sstevel@tonic-gate 	rw_exit(lock);
56500Sstevel@tonic-gate 	return (NULL);
56510Sstevel@tonic-gate }
56520Sstevel@tonic-gate 
56530Sstevel@tonic-gate /*
56540Sstevel@tonic-gate  * Find an entry for a particular clone in the direct list.
56550Sstevel@tonic-gate  */
56560Sstevel@tonic-gate psce_t *
56570Sstevel@tonic-gate pm_psc_clone_to_direct(int clone)
56580Sstevel@tonic-gate {
56590Sstevel@tonic-gate 	static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *);
56600Sstevel@tonic-gate 	return (pm_psc_find_clone(clone, &pm_pscc_direct,
56610Sstevel@tonic-gate 	    &pm_pscc_direct_rwlock));
56620Sstevel@tonic-gate }
56630Sstevel@tonic-gate 
56640Sstevel@tonic-gate /*
56650Sstevel@tonic-gate  * Find an entry for a particular clone in the interest list.
56660Sstevel@tonic-gate  */
56670Sstevel@tonic-gate psce_t *
56680Sstevel@tonic-gate pm_psc_clone_to_interest(int clone)
56690Sstevel@tonic-gate {
56700Sstevel@tonic-gate 	static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *);
56710Sstevel@tonic-gate 	return (pm_psc_find_clone(clone, &pm_pscc_interest,
56720Sstevel@tonic-gate 	    &pm_pscc_interest_rwlock));
56730Sstevel@tonic-gate }
56740Sstevel@tonic-gate 
56750Sstevel@tonic-gate /*
56760Sstevel@tonic-gate  * Put the given entry at the head of the blocked list
56770Sstevel@tonic-gate  */
56780Sstevel@tonic-gate void
56790Sstevel@tonic-gate pm_enqueue_blocked(pm_rsvp_t *p)
56800Sstevel@tonic-gate {
56810Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
56820Sstevel@tonic-gate 	ASSERT(p->pr_next == NULL);
56830Sstevel@tonic-gate 	ASSERT(p->pr_prev == NULL);
56840Sstevel@tonic-gate 	if (pm_blocked_list != NULL) {
56850Sstevel@tonic-gate 		p->pr_next = pm_blocked_list;
56860Sstevel@tonic-gate 		ASSERT(pm_blocked_list->pr_prev == NULL);
56870Sstevel@tonic-gate 		pm_blocked_list->pr_prev = p;
56880Sstevel@tonic-gate 		pm_blocked_list = p;
56890Sstevel@tonic-gate 	} else {
56900Sstevel@tonic-gate 		pm_blocked_list = p;
56910Sstevel@tonic-gate 	}
56920Sstevel@tonic-gate }
56930Sstevel@tonic-gate 
56940Sstevel@tonic-gate /*
56950Sstevel@tonic-gate  * Sets every power managed device back to its default threshold
56960Sstevel@tonic-gate  */
56970Sstevel@tonic-gate void
56980Sstevel@tonic-gate pm_all_to_default_thresholds(void)
56990Sstevel@tonic-gate {
57000Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_set_dev_thr_walk,
57010Sstevel@tonic-gate 	    (void *) &pm_system_idle_threshold);
57020Sstevel@tonic-gate }
57030Sstevel@tonic-gate 
57040Sstevel@tonic-gate static int
57050Sstevel@tonic-gate pm_set_dev_thr_walk(dev_info_t *dip, void *arg)
57060Sstevel@tonic-gate {
57070Sstevel@tonic-gate 	int thr = (int)(*(int *)arg);
57080Sstevel@tonic-gate 
57090Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip))
57100Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
57110Sstevel@tonic-gate 	pm_set_device_threshold(dip, thr, PMC_DEF_THRESH);
57120Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
57130Sstevel@tonic-gate }
57140Sstevel@tonic-gate 
57150Sstevel@tonic-gate /*
57160Sstevel@tonic-gate  * Returns the current threshold value (in seconds) for the indicated component
57170Sstevel@tonic-gate  */
57180Sstevel@tonic-gate int
57190Sstevel@tonic-gate pm_current_threshold(dev_info_t *dip, int comp, int *threshp)
57200Sstevel@tonic-gate {
57210Sstevel@tonic-gate 	if (comp < 0 || comp >= PM_NUMCMPTS(dip)) {
57220Sstevel@tonic-gate 		return (DDI_FAILURE);
57230Sstevel@tonic-gate 	} else {
57240Sstevel@tonic-gate 		*threshp = cur_threshold(dip, comp);
57250Sstevel@tonic-gate 		return (DDI_SUCCESS);
57260Sstevel@tonic-gate 	}
57270Sstevel@tonic-gate }
57280Sstevel@tonic-gate 
57290Sstevel@tonic-gate /*
57300Sstevel@tonic-gate  * To be called when changing the power level of a component of a device.
57310Sstevel@tonic-gate  * On some platforms, changing power on one device may require that power
57320Sstevel@tonic-gate  * be changed on other, related devices in the same transaction.  Thus, we
57330Sstevel@tonic-gate  * always pass this request to the platform power manager so that all the
57340Sstevel@tonic-gate  * affected devices will be locked.
57350Sstevel@tonic-gate  */
57360Sstevel@tonic-gate void
57370Sstevel@tonic-gate pm_lock_power(dev_info_t *dip, int *circp)
57380Sstevel@tonic-gate {
57390Sstevel@tonic-gate 	power_req_t power_req;
57400Sstevel@tonic-gate 	int result;
57410Sstevel@tonic-gate 
57420Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_LOCK_POWER;
57430Sstevel@tonic-gate 	power_req.req.ppm_lock_power_req.who = dip;
57440Sstevel@tonic-gate 	power_req.req.ppm_lock_power_req.circp = circp;
57450Sstevel@tonic-gate 	(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
57460Sstevel@tonic-gate }
57470Sstevel@tonic-gate 
57480Sstevel@tonic-gate /*
57490Sstevel@tonic-gate  * Release the lock (or locks) acquired to change the power of a device.
57500Sstevel@tonic-gate  * See comments for pm_lock_power.
57510Sstevel@tonic-gate  */
57520Sstevel@tonic-gate void
57530Sstevel@tonic-gate pm_unlock_power(dev_info_t *dip, int circ)
57540Sstevel@tonic-gate {
57550Sstevel@tonic-gate 	power_req_t power_req;
57560Sstevel@tonic-gate 	int result;
57570Sstevel@tonic-gate 
57580Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_UNLOCK_POWER;
57590Sstevel@tonic-gate 	power_req.req.ppm_unlock_power_req.who = dip;
57600Sstevel@tonic-gate 	power_req.req.ppm_unlock_power_req.circ = circ;
57610Sstevel@tonic-gate 	(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
57620Sstevel@tonic-gate }
57630Sstevel@tonic-gate 
57640Sstevel@tonic-gate 
57650Sstevel@tonic-gate /*
57660Sstevel@tonic-gate  * Attempt (without blocking) to acquire the lock(s) needed to change the
57670Sstevel@tonic-gate  * power of a component of a device.  See comments for pm_lock_power.
57680Sstevel@tonic-gate  *
57690Sstevel@tonic-gate  * Return: 1 if lock(s) acquired, 0 if not.
57700Sstevel@tonic-gate  */
57710Sstevel@tonic-gate int
57720Sstevel@tonic-gate pm_try_locking_power(dev_info_t *dip, int *circp)
57730Sstevel@tonic-gate {
57740Sstevel@tonic-gate 	power_req_t power_req;
57750Sstevel@tonic-gate 	int result;
57760Sstevel@tonic-gate 
57770Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_TRY_LOCK_POWER;
57780Sstevel@tonic-gate 	power_req.req.ppm_lock_power_req.who = dip;
57790Sstevel@tonic-gate 	power_req.req.ppm_lock_power_req.circp = circp;
57800Sstevel@tonic-gate 	(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
57810Sstevel@tonic-gate 	return (result);
57820Sstevel@tonic-gate }
57830Sstevel@tonic-gate 
57840Sstevel@tonic-gate 
57850Sstevel@tonic-gate /*
57860Sstevel@tonic-gate  * Lock power state of a device.
57870Sstevel@tonic-gate  *
57880Sstevel@tonic-gate  * The implementation handles a special case where another thread may have
57890Sstevel@tonic-gate  * acquired the lock and created/launched this thread to do the work.  If
57900Sstevel@tonic-gate  * the lock cannot be acquired immediately, we check to see if this thread
57910Sstevel@tonic-gate  * is registered as a borrower of the lock.  If so, we may proceed without
57920Sstevel@tonic-gate  * the lock.  This assumes that the lending thread blocks on the completion
57930Sstevel@tonic-gate  * of this thread.
57940Sstevel@tonic-gate  *
57950Sstevel@tonic-gate  * Note 1: for use by ppm only.
57960Sstevel@tonic-gate  *
57970Sstevel@tonic-gate  * Note 2: On failing to get the lock immediately, we search lock_loan list
57980Sstevel@tonic-gate  * for curthread (as borrower of the lock).  On a hit, we check that the
57990Sstevel@tonic-gate  * lending thread already owns the lock we want.  It is safe to compare
58000Sstevel@tonic-gate  * devi_busy_thread and thread id of the lender because in the == case (the
58010Sstevel@tonic-gate  * only one we care about) we know that the owner is blocked.  Similarly,
58020Sstevel@tonic-gate  * If we find that curthread isn't registered as a lock borrower, it is safe
58030Sstevel@tonic-gate  * to use the blocking call (ndi_devi_enter) because we know that if we
58040Sstevel@tonic-gate  * weren't already listed as a borrower (upstream on the call stack) we won't
58050Sstevel@tonic-gate  * become one.
58060Sstevel@tonic-gate  */
58070Sstevel@tonic-gate void
58080Sstevel@tonic-gate pm_lock_power_single(dev_info_t *dip, int *circp)
58090Sstevel@tonic-gate {
58100Sstevel@tonic-gate 	lock_loan_t *cur;
58110Sstevel@tonic-gate 
58120Sstevel@tonic-gate 	/* if the lock is available, we are done. */
58130Sstevel@tonic-gate 	if (ndi_devi_tryenter(dip, circp))
58140Sstevel@tonic-gate 		return;
58150Sstevel@tonic-gate 
58160Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
58170Sstevel@tonic-gate 	/* see if our thread is registered as a lock borrower. */
58180Sstevel@tonic-gate 	for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
58190Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
58200Sstevel@tonic-gate 			break;
58210Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
58220Sstevel@tonic-gate 
58230Sstevel@tonic-gate 	/* if this thread not already registered, it is safe to block */
58240Sstevel@tonic-gate 	if (cur == NULL)
58250Sstevel@tonic-gate 		ndi_devi_enter(dip, circp);
58260Sstevel@tonic-gate 	else {
58270Sstevel@tonic-gate 		/* registered: does lender own the lock we want? */
58280Sstevel@tonic-gate 		if (cur->pmlk_lender == DEVI(dip)->devi_busy_thread) {
58290Sstevel@tonic-gate 			ASSERT(cur->pmlk_dip == NULL || cur->pmlk_dip == dip);
58300Sstevel@tonic-gate 			cur->pmlk_dip = dip;
58310Sstevel@tonic-gate 		} else /* no: just block for it */
58320Sstevel@tonic-gate 			ndi_devi_enter(dip, circp);
58330Sstevel@tonic-gate 
58340Sstevel@tonic-gate 	}
58350Sstevel@tonic-gate }
58360Sstevel@tonic-gate 
58370Sstevel@tonic-gate /*
58380Sstevel@tonic-gate  * Drop the lock on the device's power state.  See comment for
58390Sstevel@tonic-gate  * pm_lock_power_single() for special implementation considerations.
58400Sstevel@tonic-gate  *
58410Sstevel@tonic-gate  * Note: for use by ppm only.
58420Sstevel@tonic-gate  */
58430Sstevel@tonic-gate void
58440Sstevel@tonic-gate pm_unlock_power_single(dev_info_t *dip, int circ)
58450Sstevel@tonic-gate {
58460Sstevel@tonic-gate 	lock_loan_t *cur;
58470Sstevel@tonic-gate 
58480Sstevel@tonic-gate 	/* optimization: mutex not needed to check empty list */
58490Sstevel@tonic-gate 	if (lock_loan_head.pmlk_next == NULL) {
58500Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
58510Sstevel@tonic-gate 		return;
58520Sstevel@tonic-gate 	}
58530Sstevel@tonic-gate 
58540Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
58550Sstevel@tonic-gate 	/* see if our thread is registered as a lock borrower. */
58560Sstevel@tonic-gate 	for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
58570Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
58580Sstevel@tonic-gate 			break;
58590Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
58600Sstevel@tonic-gate 
58610Sstevel@tonic-gate 	if (cur == NULL || cur->pmlk_dip != dip)
58620Sstevel@tonic-gate 		/* we acquired the lock directly, so return it */
58630Sstevel@tonic-gate 		ndi_devi_exit(dip, circ);
58640Sstevel@tonic-gate }
58650Sstevel@tonic-gate 
58660Sstevel@tonic-gate /*
58670Sstevel@tonic-gate  * Try to take the lock for changing the power level of a component.
58680Sstevel@tonic-gate  *
58690Sstevel@tonic-gate  * Note: for use by ppm only.
58700Sstevel@tonic-gate  */
58710Sstevel@tonic-gate int
58720Sstevel@tonic-gate pm_try_locking_power_single(dev_info_t *dip, int *circp)
58730Sstevel@tonic-gate {
58740Sstevel@tonic-gate 	return (ndi_devi_tryenter(dip, circp));
58750Sstevel@tonic-gate }
58760Sstevel@tonic-gate 
58770Sstevel@tonic-gate #ifdef	DEBUG
58780Sstevel@tonic-gate /*
58790Sstevel@tonic-gate  * The following are used only to print out data structures for debugging
58800Sstevel@tonic-gate  */
58810Sstevel@tonic-gate void
58820Sstevel@tonic-gate prdeps(char *msg)
58830Sstevel@tonic-gate {
58840Sstevel@tonic-gate 
58850Sstevel@tonic-gate 	pm_pdr_t *rp;
58860Sstevel@tonic-gate 	int i;
58870Sstevel@tonic-gate 
58880Sstevel@tonic-gate 	pm_log("pm_dep_head %s %p\n", msg, (void *)pm_dep_head);
58890Sstevel@tonic-gate 	for (rp = pm_dep_head; rp; rp = rp->pdr_next) {
58900Sstevel@tonic-gate 		pm_log("%p: %s keeper %s, kept %s, kept count %d, next %p\n",
58910Sstevel@tonic-gate 		    (void *)rp, (rp->pdr_isprop ? "property" : "device"),
58920Sstevel@tonic-gate 		    rp->pdr_keeper, rp->pdr_kept, rp->pdr_kept_count,
58930Sstevel@tonic-gate 		    (void *)rp->pdr_next);
58940Sstevel@tonic-gate 		if (rp->pdr_kept_count != 0) {
58950Sstevel@tonic-gate 			pm_log("kept list = ");
58960Sstevel@tonic-gate 			i = 0;
58970Sstevel@tonic-gate 			while (i < rp->pdr_kept_count) {
58980Sstevel@tonic-gate 				pm_log("%s ", rp->pdr_kept_paths[i]);
58990Sstevel@tonic-gate 				i++;
59000Sstevel@tonic-gate 			}
59010Sstevel@tonic-gate 			pm_log("\n");
59020Sstevel@tonic-gate 		}
59030Sstevel@tonic-gate 	}
59040Sstevel@tonic-gate }
59050Sstevel@tonic-gate 
59060Sstevel@tonic-gate void
59070Sstevel@tonic-gate pr_noinvol(char *hdr)
59080Sstevel@tonic-gate {
59090Sstevel@tonic-gate 	pm_noinvol_t *ip;
59100Sstevel@tonic-gate 
59110Sstevel@tonic-gate 	pm_log("%s\n", hdr);
59120Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
59130Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next)
59140Sstevel@tonic-gate 		pm_log("\tmaj %d, flags %x, noinvolpm %d %s\n",
59150Sstevel@tonic-gate 		    ip->ni_major, ip->ni_flags, ip->ni_noinvolpm, ip->ni_path);
59160Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
59170Sstevel@tonic-gate }
59180Sstevel@tonic-gate #endif
59190Sstevel@tonic-gate 
59200Sstevel@tonic-gate /*
59210Sstevel@tonic-gate  * Attempt to apply the thresholds indicated by rp to the node specified by
59220Sstevel@tonic-gate  * dip.
59230Sstevel@tonic-gate  */
59240Sstevel@tonic-gate void
59250Sstevel@tonic-gate pm_apply_recorded_thresh(dev_info_t *dip, pm_thresh_rec_t *rp)
59260Sstevel@tonic-gate {
59270Sstevel@tonic-gate 	PMD_FUNC(pmf, "apply_recorded_thresh")
59280Sstevel@tonic-gate 	int i, j;
59290Sstevel@tonic-gate 	int comps = PM_NUMCMPTS(dip);
59300Sstevel@tonic-gate 	struct pm_component *cp;
59310Sstevel@tonic-gate 	pm_pte_t *ep;
59320Sstevel@tonic-gate 	int pm_valid_thresh(dev_info_t *, pm_thresh_rec_t *);
59330Sstevel@tonic-gate 
59340Sstevel@tonic-gate 	PMD(PMD_THRESH, ("%s: part: %s@%s(%s#%d), rp %p, %s\n", pmf,
59350Sstevel@tonic-gate 	    PM_DEVICE(dip), (void *)rp, rp->ptr_physpath))
59360Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
59370Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip) || !pm_valid_thresh(dip, rp)) {
59380Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_GET_PM_INFO %p\n",
59390Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), (void*)PM_GET_PM_INFO(dip)))
59400Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_ISBC %d\n",
59410Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), PM_ISBC(dip)))
59420Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) pm_valid_thresh %d\n",
59430Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), pm_valid_thresh(dip, rp)))
59440Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
59450Sstevel@tonic-gate 		return;
59460Sstevel@tonic-gate 	}
59470Sstevel@tonic-gate 
59480Sstevel@tonic-gate 	ep = rp->ptr_entries;
59490Sstevel@tonic-gate 	/*
59500Sstevel@tonic-gate 	 * Here we do the special case of a device threshold
59510Sstevel@tonic-gate 	 */
59520Sstevel@tonic-gate 	if (rp->ptr_numcomps == 0) {	/* PM_SET_DEVICE_THRESHOLD product */
59530Sstevel@tonic-gate 		ASSERT(ep && ep->pte_numthresh == 1);
59540Sstevel@tonic-gate 		PMD(PMD_THRESH, ("%s: set dev thr %s@%s(%s#%d) to 0x%x\n",
59550Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), ep->pte_thresh[0]))
59560Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
59570Sstevel@tonic-gate 		pm_set_device_threshold(dip, ep->pte_thresh[0], PMC_DEV_THRESH);
59580Sstevel@tonic-gate 		if (autopm_enabled)
59590Sstevel@tonic-gate 			pm_rescan(dip);
59600Sstevel@tonic-gate 		return;
59610Sstevel@tonic-gate 	}
59620Sstevel@tonic-gate 	for (i = 0; i < comps; i++) {
59630Sstevel@tonic-gate 		cp = PM_CP(dip, i);
59640Sstevel@tonic-gate 		for (j = 0; j < ep->pte_numthresh; j++) {
59650Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: set thr %d for %s@%s(%s#%d)[%d] "
59660Sstevel@tonic-gate 			    "to %x\n", pmf, j, PM_DEVICE(dip),
59670Sstevel@tonic-gate 			    i, ep->pte_thresh[j]))
59680Sstevel@tonic-gate 			cp->pmc_comp.pmc_thresh[j + 1] = ep->pte_thresh[j];
59690Sstevel@tonic-gate 		}
59700Sstevel@tonic-gate 		ep++;
59710Sstevel@tonic-gate 	}
59720Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
59730Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= PMC_COMP_THRESH;
59740Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
59750Sstevel@tonic-gate 
59760Sstevel@tonic-gate 	if (autopm_enabled)
59770Sstevel@tonic-gate 		pm_rescan(dip);
59780Sstevel@tonic-gate }
59790Sstevel@tonic-gate 
59800Sstevel@tonic-gate /*
59810Sstevel@tonic-gate  * Returns true if the threshold specified by rp could be applied to dip
59820Sstevel@tonic-gate  * (that is, the number of components and transitions are the same)
59830Sstevel@tonic-gate  */
59840Sstevel@tonic-gate int
59850Sstevel@tonic-gate pm_valid_thresh(dev_info_t *dip, pm_thresh_rec_t *rp)
59860Sstevel@tonic-gate {
59870Sstevel@tonic-gate 	PMD_FUNC(pmf, "valid_thresh")
59880Sstevel@tonic-gate 	int comps, i;
59890Sstevel@tonic-gate 	pm_component_t *cp;
59900Sstevel@tonic-gate 	pm_pte_t *ep;
59910Sstevel@tonic-gate 
59920Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
59930Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s: no pm_info or BC\n", pmf,
59940Sstevel@tonic-gate 		    rp->ptr_physpath))
59950Sstevel@tonic-gate 		return (0);
59960Sstevel@tonic-gate 	}
59970Sstevel@tonic-gate 	/*
59980Sstevel@tonic-gate 	 * Special case: we represent the PM_SET_DEVICE_THRESHOLD case by
59990Sstevel@tonic-gate 	 * an entry with numcomps == 0, (since we don't know how many
60000Sstevel@tonic-gate 	 * components there are in advance).  This is always a valid
60010Sstevel@tonic-gate 	 * spec.
60020Sstevel@tonic-gate 	 */
60030Sstevel@tonic-gate 	if (rp->ptr_numcomps == 0) {
60040Sstevel@tonic-gate 		ASSERT(rp->ptr_entries && rp->ptr_entries->pte_numthresh == 1);
60050Sstevel@tonic-gate 		return (1);
60060Sstevel@tonic-gate 	}
60070Sstevel@tonic-gate 	if (rp->ptr_numcomps != (comps = PM_NUMCMPTS(dip))) {
60080Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: comp # mm (dip %d cmd %d) for %s\n",
60090Sstevel@tonic-gate 		    pmf, PM_NUMCMPTS(dip), rp->ptr_numcomps, rp->ptr_physpath))
60100Sstevel@tonic-gate 		return (0);
60110Sstevel@tonic-gate 	}
60120Sstevel@tonic-gate 	ep = rp->ptr_entries;
60130Sstevel@tonic-gate 	for (i = 0; i < comps; i++) {
60140Sstevel@tonic-gate 		cp = PM_CP(dip, i);
60150Sstevel@tonic-gate 		if ((ep + i)->pte_numthresh !=
60160Sstevel@tonic-gate 		    cp->pmc_comp.pmc_numlevels - 1) {
60170Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: %s[%d]: thresh=%d, record=%d\n",
60180Sstevel@tonic-gate 			    pmf, rp->ptr_physpath, i,
60190Sstevel@tonic-gate 			    cp->pmc_comp.pmc_numlevels - 1,
60200Sstevel@tonic-gate 			    (ep + i)->pte_numthresh))
60210Sstevel@tonic-gate 			return (0);
60220Sstevel@tonic-gate 		}
60230Sstevel@tonic-gate 	}
60240Sstevel@tonic-gate 	return (1);
60250Sstevel@tonic-gate }
60260Sstevel@tonic-gate 
60270Sstevel@tonic-gate /*
60280Sstevel@tonic-gate  * Remove any recorded threshold for device physpath
60290Sstevel@tonic-gate  * We know there will be at most one.
60300Sstevel@tonic-gate  */
60310Sstevel@tonic-gate void
60320Sstevel@tonic-gate pm_unrecord_threshold(char *physpath)
60330Sstevel@tonic-gate {
60340Sstevel@tonic-gate 	pm_thresh_rec_t *pptr, *ptr;
60350Sstevel@tonic-gate 
60360Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_WRITER);
60370Sstevel@tonic-gate 	for (pptr = NULL, ptr = pm_thresh_head; ptr; ptr = ptr->ptr_next) {
60380Sstevel@tonic-gate 		if (strcmp(physpath, ptr->ptr_physpath) == 0) {
60390Sstevel@tonic-gate 			if (pptr) {
60400Sstevel@tonic-gate 				pptr->ptr_next = ptr->ptr_next;
60410Sstevel@tonic-gate 			} else {
60420Sstevel@tonic-gate 				ASSERT(pm_thresh_head == ptr);
60430Sstevel@tonic-gate 				pm_thresh_head = ptr->ptr_next;
60440Sstevel@tonic-gate 			}
60450Sstevel@tonic-gate 			kmem_free(ptr, ptr->ptr_size);
60460Sstevel@tonic-gate 			break;
60470Sstevel@tonic-gate 		}
60480Sstevel@tonic-gate 		pptr = ptr;
60490Sstevel@tonic-gate 	}
60500Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
60510Sstevel@tonic-gate }
60520Sstevel@tonic-gate 
60530Sstevel@tonic-gate /*
60540Sstevel@tonic-gate  * Discard all recorded thresholds.  We are returning to the default pm state.
60550Sstevel@tonic-gate  */
60560Sstevel@tonic-gate void
60570Sstevel@tonic-gate pm_discard_thresholds(void)
60580Sstevel@tonic-gate {
60590Sstevel@tonic-gate 	pm_thresh_rec_t *rp;
60600Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_WRITER);
60610Sstevel@tonic-gate 	while (pm_thresh_head) {
60620Sstevel@tonic-gate 		rp = pm_thresh_head;
60630Sstevel@tonic-gate 		pm_thresh_head = rp->ptr_next;
60640Sstevel@tonic-gate 		kmem_free(rp, rp->ptr_size);
60650Sstevel@tonic-gate 	}
60660Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
60670Sstevel@tonic-gate }
60680Sstevel@tonic-gate 
60690Sstevel@tonic-gate /*
60700Sstevel@tonic-gate  * Discard all recorded dependencies.  We are returning to the default pm state.
60710Sstevel@tonic-gate  */
60720Sstevel@tonic-gate void
60730Sstevel@tonic-gate pm_discard_dependencies(void)
60740Sstevel@tonic-gate {
60750Sstevel@tonic-gate 	pm_pdr_t *rp;
60760Sstevel@tonic-gate 	int i;
60770Sstevel@tonic-gate 	size_t length;
60780Sstevel@tonic-gate 
60790Sstevel@tonic-gate #ifdef DEBUG
60800Sstevel@tonic-gate 	if (pm_debug & PMD_DEP)
60810Sstevel@tonic-gate 		prdeps("Before discard\n");
60820Sstevel@tonic-gate #endif
60830Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_discard_dep_walk, NULL);
60840Sstevel@tonic-gate 
60850Sstevel@tonic-gate #ifdef DEBUG
60860Sstevel@tonic-gate 	if (pm_debug & PMD_DEP)
60870Sstevel@tonic-gate 		prdeps("After discard\n");
60880Sstevel@tonic-gate #endif
60890Sstevel@tonic-gate 	while (pm_dep_head) {
60900Sstevel@tonic-gate 		rp = pm_dep_head;
60910Sstevel@tonic-gate 		if (!rp->pdr_isprop) {
60920Sstevel@tonic-gate 			ASSERT(rp->pdr_satisfied == 0);
60930Sstevel@tonic-gate 			ASSERT(pm_unresolved_deps);
60940Sstevel@tonic-gate 			pm_unresolved_deps--;
60950Sstevel@tonic-gate 		} else {
60960Sstevel@tonic-gate 			ASSERT(pm_prop_deps);
60970Sstevel@tonic-gate 			pm_prop_deps--;
60980Sstevel@tonic-gate 		}
60990Sstevel@tonic-gate 		pm_dep_head = rp->pdr_next;
61000Sstevel@tonic-gate 		if (rp->pdr_kept_count)  {
61010Sstevel@tonic-gate 			for (i = 0; i < rp->pdr_kept_count; i++) {
61020Sstevel@tonic-gate 				length = strlen(rp->pdr_kept_paths[i]) + 1;
61030Sstevel@tonic-gate 				kmem_free(rp->pdr_kept_paths[i], length);
61040Sstevel@tonic-gate 			}
61050Sstevel@tonic-gate 			kmem_free(rp->pdr_kept_paths,
61060Sstevel@tonic-gate 				rp->pdr_kept_count * sizeof (char **));
61070Sstevel@tonic-gate 		}
61080Sstevel@tonic-gate 		kmem_free(rp, rp->pdr_size);
61090Sstevel@tonic-gate 	}
61100Sstevel@tonic-gate }
61110Sstevel@tonic-gate 
61120Sstevel@tonic-gate 
61130Sstevel@tonic-gate static int
61140Sstevel@tonic-gate pm_discard_dep_walk(dev_info_t *dip, void *arg)
61150Sstevel@tonic-gate {
61160Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
61170Sstevel@tonic-gate 	char *pathbuf;
61180Sstevel@tonic-gate 
61190Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) == NULL)
61200Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
61210Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
61220Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
61230Sstevel@tonic-gate 	pm_free_keeper(pathbuf, 0);
61240Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
61250Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
61260Sstevel@tonic-gate }
61270Sstevel@tonic-gate 
61280Sstevel@tonic-gate static int
61290Sstevel@tonic-gate pm_kept_walk(dev_info_t *dip, void *arg)
61300Sstevel@tonic-gate {
61310Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
61320Sstevel@tonic-gate 	char *pathbuf;
61330Sstevel@tonic-gate 
61340Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
61350Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
61360Sstevel@tonic-gate 	(void) pm_kept(pathbuf);
61370Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
61380Sstevel@tonic-gate 
61390Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
61400Sstevel@tonic-gate }
61410Sstevel@tonic-gate 
61420Sstevel@tonic-gate static int
61430Sstevel@tonic-gate pm_keeper_walk(dev_info_t *dip, void *arg)
61440Sstevel@tonic-gate {
61450Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
61460Sstevel@tonic-gate 	char *pathbuf;
61470Sstevel@tonic-gate 
61480Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
61490Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
61500Sstevel@tonic-gate 	(void) pm_keeper(pathbuf);
61510Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
61520Sstevel@tonic-gate 
61530Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
61540Sstevel@tonic-gate }
61550Sstevel@tonic-gate 
61560Sstevel@tonic-gate static char *
61570Sstevel@tonic-gate pdw_type_decode(int type)
61580Sstevel@tonic-gate {
61590Sstevel@tonic-gate 	switch (type) {
61600Sstevel@tonic-gate 	case PM_DEP_WK_POWER_ON:
61610Sstevel@tonic-gate 		return ("power on");
61620Sstevel@tonic-gate 	case PM_DEP_WK_POWER_OFF:
61630Sstevel@tonic-gate 		return ("power off");
61640Sstevel@tonic-gate 	case PM_DEP_WK_DETACH:
61650Sstevel@tonic-gate 		return ("detach");
61660Sstevel@tonic-gate 	case PM_DEP_WK_REMOVE_DEP:
61670Sstevel@tonic-gate 		return ("remove dep");
61680Sstevel@tonic-gate 	case PM_DEP_WK_BRINGUP_SELF:
61690Sstevel@tonic-gate 		return ("bringup self");
61700Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER:
61710Sstevel@tonic-gate 		return ("add dependent");
61720Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER_PROP:
61730Sstevel@tonic-gate 		return ("add dependent property");
61740Sstevel@tonic-gate 	case PM_DEP_WK_KEPT:
61750Sstevel@tonic-gate 		return ("kept");
61760Sstevel@tonic-gate 	case PM_DEP_WK_KEEPER:
61770Sstevel@tonic-gate 		return ("keeper");
61780Sstevel@tonic-gate 	case PM_DEP_WK_ATTACH:
61790Sstevel@tonic-gate 		return ("attach");
61800Sstevel@tonic-gate 	case PM_DEP_WK_CHECK_KEPT:
61810Sstevel@tonic-gate 		return ("check kept");
61820Sstevel@tonic-gate 	case PM_DEP_WK_CPR_SUSPEND:
61830Sstevel@tonic-gate 		return ("suspend");
61840Sstevel@tonic-gate 	case PM_DEP_WK_CPR_RESUME:
61850Sstevel@tonic-gate 		return ("resume");
61860Sstevel@tonic-gate 	default:
61870Sstevel@tonic-gate 		return ("unknown");
61880Sstevel@tonic-gate 	}
61890Sstevel@tonic-gate 
61900Sstevel@tonic-gate }
61910Sstevel@tonic-gate 
61920Sstevel@tonic-gate static void
61930Sstevel@tonic-gate pm_rele_dep(char *keeper)
61940Sstevel@tonic-gate {
61950Sstevel@tonic-gate 	PMD_FUNC(pmf, "rele_dep")
61960Sstevel@tonic-gate 	pm_pdr_t *dp;
61970Sstevel@tonic-gate 	char *kept_path = NULL;
61980Sstevel@tonic-gate 	dev_info_t *kept = NULL;
61990Sstevel@tonic-gate 	int count = 0;
62000Sstevel@tonic-gate 
62010Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
62020Sstevel@tonic-gate 		if (strcmp(dp->pdr_keeper, keeper) != 0)
62030Sstevel@tonic-gate 			continue;
62040Sstevel@tonic-gate 		for (count = 0; count < dp->pdr_kept_count; count++) {
62050Sstevel@tonic-gate 			kept_path = dp->pdr_kept_paths[count];
62060Sstevel@tonic-gate 			if (kept_path == NULL)
62070Sstevel@tonic-gate 				continue;
62080Sstevel@tonic-gate 			kept = pm_name_to_dip(kept_path, 1);
62090Sstevel@tonic-gate 			if (kept) {
62100Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: release kept=%s@%s(%s#%d) "
62110Sstevel@tonic-gate 				    "of keeper=%s\n", pmf, PM_DEVICE(kept),
62120Sstevel@tonic-gate 				    keeper))
62130Sstevel@tonic-gate 				ASSERT(DEVI(kept)->devi_pm_kidsupcnt > 0);
62140Sstevel@tonic-gate 				pm_rele_power(kept);
62150Sstevel@tonic-gate 				ddi_release_devi(kept);
62160Sstevel@tonic-gate 			}
62170Sstevel@tonic-gate 		}
62180Sstevel@tonic-gate 	}
62190Sstevel@tonic-gate }
62200Sstevel@tonic-gate 
62210Sstevel@tonic-gate /*
62220Sstevel@tonic-gate  * Called when we are just released from direct PM.  Bring ourself up
62230Sstevel@tonic-gate  * if our keeper is up since dependency is not honored while a kept
62240Sstevel@tonic-gate  * device is under direct PM.
62250Sstevel@tonic-gate  */
62260Sstevel@tonic-gate static void
62270Sstevel@tonic-gate pm_bring_self_up(char *keptpath)
62280Sstevel@tonic-gate {
62290Sstevel@tonic-gate 	PMD_FUNC(pmf, "bring_self_up")
62300Sstevel@tonic-gate 	dev_info_t *kept;
62310Sstevel@tonic-gate 	dev_info_t *keeper;
62320Sstevel@tonic-gate 	pm_pdr_t *dp;
62330Sstevel@tonic-gate 	int i, j;
62340Sstevel@tonic-gate 	int up = 0, circ;
62350Sstevel@tonic-gate 
62360Sstevel@tonic-gate 	kept = pm_name_to_dip(keptpath, 1);
62370Sstevel@tonic-gate 	if (kept == NULL)
62380Sstevel@tonic-gate 		return;
62390Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: kept=%s@%s(%s#%d)\n", pmf, PM_DEVICE(kept)))
62400Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
62410Sstevel@tonic-gate 		if (dp->pdr_kept_count == 0)
62420Sstevel@tonic-gate 			continue;
62430Sstevel@tonic-gate 		for (i = 0; i < dp->pdr_kept_count; i++) {
62440Sstevel@tonic-gate 			if (strcmp(dp->pdr_kept_paths[i], keptpath) != 0)
62450Sstevel@tonic-gate 				continue;
62460Sstevel@tonic-gate 			keeper = pm_name_to_dip(dp->pdr_keeper, 1);
62470Sstevel@tonic-gate 			if (keeper) {
62480Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d)\n",
62490Sstevel@tonic-gate 				    pmf, PM_DEVICE(keeper)))
62500Sstevel@tonic-gate 				PM_LOCK_POWER(keeper, &circ);
62510Sstevel@tonic-gate 				for (j = 0; j < PM_NUMCMPTS(keeper);
62520Sstevel@tonic-gate 				    j++) {
62530Sstevel@tonic-gate 					if (PM_CURPOWER(keeper, j)) {
62540Sstevel@tonic-gate 						PMD(PMD_KEEPS, ("%s: comp="
62550Sstevel@tonic-gate 						    "%d is up\n", pmf, j))
62560Sstevel@tonic-gate 						up++;
62570Sstevel@tonic-gate 					}
62580Sstevel@tonic-gate 				}
62590Sstevel@tonic-gate 				if (up) {
62600Sstevel@tonic-gate 					if (PM_SKBU(kept))
62610Sstevel@tonic-gate 						DEVI(kept)->devi_pm_flags &=
62620Sstevel@tonic-gate 						    ~PMC_SKIP_BRINGUP;
62630Sstevel@tonic-gate 					bring_pmdep_up(kept, 1);
62640Sstevel@tonic-gate 				}
62650Sstevel@tonic-gate 				PM_UNLOCK_POWER(keeper, circ);
62660Sstevel@tonic-gate 				ddi_release_devi(keeper);
62670Sstevel@tonic-gate 			}
62680Sstevel@tonic-gate 		}
62690Sstevel@tonic-gate 	}
62700Sstevel@tonic-gate 	ddi_release_devi(kept);
62710Sstevel@tonic-gate }
62720Sstevel@tonic-gate 
62730Sstevel@tonic-gate static void
62740Sstevel@tonic-gate pm_process_dep_request(pm_dep_wk_t *work)
62750Sstevel@tonic-gate {
62760Sstevel@tonic-gate 	PMD_FUNC(pmf, "dep_req")
62770Sstevel@tonic-gate 	int ret;
62780Sstevel@tonic-gate 
62790Sstevel@tonic-gate 	PMD(PMD_DEP, ("%s: work=%s\n", pmf,
62800Sstevel@tonic-gate 	    pdw_type_decode(work->pdw_type)))
62810Sstevel@tonic-gate 	PMD(PMD_DEP, ("%s: keeper=%s, kept=%s\n", pmf,
62820Sstevel@tonic-gate 	    (work->pdw_keeper ? work->pdw_keeper : "NULL"),
62830Sstevel@tonic-gate 	    (work->pdw_kept ? work->pdw_kept : "NULL")))
62840Sstevel@tonic-gate 
62850Sstevel@tonic-gate 	switch (work->pdw_type) {
62860Sstevel@tonic-gate 	case PM_DEP_WK_POWER_ON:
62870Sstevel@tonic-gate 		/* Bring up the kept devices and put a hold on them */
62880Sstevel@tonic-gate 		bring_wekeeps_up(work->pdw_keeper);
62890Sstevel@tonic-gate 		break;
62900Sstevel@tonic-gate 	case PM_DEP_WK_POWER_OFF:
62910Sstevel@tonic-gate 		/* Release the kept devices */
62920Sstevel@tonic-gate 		pm_rele_dep(work->pdw_keeper);
62930Sstevel@tonic-gate 		break;
62940Sstevel@tonic-gate 	case PM_DEP_WK_DETACH:
62950Sstevel@tonic-gate 		pm_free_keeps(work->pdw_keeper, work->pdw_pwr);
62960Sstevel@tonic-gate 		break;
62970Sstevel@tonic-gate 	case PM_DEP_WK_REMOVE_DEP:
62980Sstevel@tonic-gate 		pm_discard_dependencies();
62990Sstevel@tonic-gate 		break;
63000Sstevel@tonic-gate 	case PM_DEP_WK_BRINGUP_SELF:
63010Sstevel@tonic-gate 		/*
63020Sstevel@tonic-gate 		 * We deferred satisfying our dependency till now, so satisfy
63030Sstevel@tonic-gate 		 * it again and bring ourselves up.
63040Sstevel@tonic-gate 		 */
63050Sstevel@tonic-gate 		pm_bring_self_up(work->pdw_kept);
63060Sstevel@tonic-gate 		break;
63070Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER:
63080Sstevel@tonic-gate 		(void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 0);
63090Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
63100Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
63110Sstevel@tonic-gate 		break;
63120Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER_PROP:
63130Sstevel@tonic-gate 		(void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 1);
63140Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
63150Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
63160Sstevel@tonic-gate 		break;
63170Sstevel@tonic-gate 	case PM_DEP_WK_KEPT:
63180Sstevel@tonic-gate 		ret = pm_kept(work->pdw_kept);
63190Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_KEPT: pm_kept returns %d\n", pmf,
63200Sstevel@tonic-gate 		    ret))
63210Sstevel@tonic-gate 		break;
63220Sstevel@tonic-gate 	case PM_DEP_WK_KEEPER:
63230Sstevel@tonic-gate 		ret = pm_keeper(work->pdw_keeper);
63240Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_KEEPER: pm_keeper returns %d\n",
63250Sstevel@tonic-gate 		    pmf, ret))
63260Sstevel@tonic-gate 		break;
63270Sstevel@tonic-gate 	case PM_DEP_WK_ATTACH:
63280Sstevel@tonic-gate 		ret = pm_keeper(work->pdw_keeper);
63290Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_keeper returns %d\n",
63300Sstevel@tonic-gate 		    pmf, ret))
63310Sstevel@tonic-gate 		ret = pm_kept(work->pdw_kept);
63320Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_kept returns %d\n",
63330Sstevel@tonic-gate 		    pmf, ret))
63340Sstevel@tonic-gate 		break;
63350Sstevel@tonic-gate 	case PM_DEP_WK_CHECK_KEPT:
63360Sstevel@tonic-gate 		ret = pm_is_kept(work->pdw_kept);
63370Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_CHECK_KEPT: kept=%s, ret=%d\n",
63380Sstevel@tonic-gate 		    pmf, work->pdw_kept, ret))
63390Sstevel@tonic-gate 		break;
63400Sstevel@tonic-gate 	case PM_DEP_WK_CPR_SUSPEND:
63410Sstevel@tonic-gate 		pm_discard_dependencies();
63420Sstevel@tonic-gate 		break;
63430Sstevel@tonic-gate 	case PM_DEP_WK_CPR_RESUME:
63440Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
63450Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
63460Sstevel@tonic-gate 		break;
63470Sstevel@tonic-gate 	default:
63480Sstevel@tonic-gate 		ASSERT(0);
63490Sstevel@tonic-gate 		break;
63500Sstevel@tonic-gate 	}
63510Sstevel@tonic-gate 	/*
63520Sstevel@tonic-gate 	 * Free the work structure if the requester is not waiting
63530Sstevel@tonic-gate 	 * Otherwise it is the requester's responsiblity to free it.
63540Sstevel@tonic-gate 	 */
63550Sstevel@tonic-gate 	if (!work->pdw_wait) {
63560Sstevel@tonic-gate 		if (work->pdw_keeper)
63570Sstevel@tonic-gate 			kmem_free(work->pdw_keeper,
63580Sstevel@tonic-gate 			    strlen(work->pdw_keeper) + 1);
63590Sstevel@tonic-gate 		if (work->pdw_kept)
63600Sstevel@tonic-gate 			kmem_free(work->pdw_kept, strlen(work->pdw_kept) + 1);
63610Sstevel@tonic-gate 		kmem_free(work, sizeof (pm_dep_wk_t));
63620Sstevel@tonic-gate 	} else {
63630Sstevel@tonic-gate 		/*
63640Sstevel@tonic-gate 		 * Notify requester if it is waiting for it.
63650Sstevel@tonic-gate 		 */
63660Sstevel@tonic-gate 		work->pdw_ret = ret;
63670Sstevel@tonic-gate 		work->pdw_done = 1;
63680Sstevel@tonic-gate 		cv_signal(&work->pdw_cv);
63690Sstevel@tonic-gate 	}
63700Sstevel@tonic-gate }
63710Sstevel@tonic-gate 
63720Sstevel@tonic-gate /*
63730Sstevel@tonic-gate  * Process PM dependency requests.
63740Sstevel@tonic-gate  */
63750Sstevel@tonic-gate static void
63760Sstevel@tonic-gate pm_dep_thread(void)
63770Sstevel@tonic-gate {
63780Sstevel@tonic-gate 	pm_dep_wk_t *work;
63790Sstevel@tonic-gate 	callb_cpr_t cprinfo;
63800Sstevel@tonic-gate 
63810Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &pm_dep_thread_lock, callb_generic_cpr,
63820Sstevel@tonic-gate 	    "pm_dep_thread");
63830Sstevel@tonic-gate 	for (;;) {
63840Sstevel@tonic-gate 		mutex_enter(&pm_dep_thread_lock);
63850Sstevel@tonic-gate 		if (pm_dep_thread_workq == NULL) {
63860Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
63870Sstevel@tonic-gate 			cv_wait(&pm_dep_thread_cv, &pm_dep_thread_lock);
63880Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo, &pm_dep_thread_lock);
63890Sstevel@tonic-gate 		}
63900Sstevel@tonic-gate 		work = pm_dep_thread_workq;
63910Sstevel@tonic-gate 		pm_dep_thread_workq = work->pdw_next;
63920Sstevel@tonic-gate 		if (pm_dep_thread_tail == work)
63930Sstevel@tonic-gate 			pm_dep_thread_tail = work->pdw_next;
63940Sstevel@tonic-gate 		mutex_exit(&pm_dep_thread_lock);
63950Sstevel@tonic-gate 		pm_process_dep_request(work);
63960Sstevel@tonic-gate 
63970Sstevel@tonic-gate 	}
63980Sstevel@tonic-gate 	/*NOTREACHED*/
63990Sstevel@tonic-gate }
64000Sstevel@tonic-gate 
64010Sstevel@tonic-gate /*
64020Sstevel@tonic-gate  * Set the power level of the indicated device to unknown (if it is not a
64030Sstevel@tonic-gate  * backwards compatible device), as it has just been resumed, and it won't
64040Sstevel@tonic-gate  * know if the power was removed or not. Adjust parent's kidsupcnt if necessary.
64050Sstevel@tonic-gate  */
64060Sstevel@tonic-gate void
64070Sstevel@tonic-gate pm_forget_power_level(dev_info_t *dip)
64080Sstevel@tonic-gate {
64090Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
64100Sstevel@tonic-gate 	int i, count = 0;
64110Sstevel@tonic-gate 
64120Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
64130Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
64140Sstevel@tonic-gate 			count += (PM_CURPOWER(dip, i) == 0);
64150Sstevel@tonic-gate 
64160Sstevel@tonic-gate 		if (count && pdip && !PM_WANTS_NOTIFICATION(pdip))
64170Sstevel@tonic-gate 			e_pm_hold_rele_power(pdip, count);
64180Sstevel@tonic-gate 
64190Sstevel@tonic-gate 		/*
64200Sstevel@tonic-gate 		 * Count this as a power cycle if we care
64210Sstevel@tonic-gate 		 */
64220Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_volpmd &&
64230Sstevel@tonic-gate 		    PM_CP(dip, 0)->pmc_cur_pwr == 0)
64240Sstevel@tonic-gate 			DEVI(dip)->devi_pm_volpmd = 0;
64250Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
64260Sstevel@tonic-gate 			e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN);
64270Sstevel@tonic-gate 	}
64280Sstevel@tonic-gate }
64290Sstevel@tonic-gate 
64300Sstevel@tonic-gate /*
64310Sstevel@tonic-gate  * This function advises the caller whether it should make a power-off
64320Sstevel@tonic-gate  * transition at this time or not.  If the transition is not advised
64330Sstevel@tonic-gate  * at this time, the time that the next power-off transition can
64340Sstevel@tonic-gate  * be made from now is returned through "intervalp" pointer.
64350Sstevel@tonic-gate  * This function returns:
64360Sstevel@tonic-gate  *
64370Sstevel@tonic-gate  *  1  power-off advised
64380Sstevel@tonic-gate  *  0  power-off not advised, intervalp will point to seconds from
64390Sstevel@tonic-gate  *	  now that a power-off is advised.  If it is passed the number
64400Sstevel@tonic-gate  *	  of years that policy specifies the device should last,
64410Sstevel@tonic-gate  *	  a large number is returned as the time interval.
64420Sstevel@tonic-gate  *  -1  error
64430Sstevel@tonic-gate  */
64440Sstevel@tonic-gate int
64450Sstevel@tonic-gate pm_trans_check(struct pm_trans_data *datap, time_t *intervalp)
64460Sstevel@tonic-gate {
64470Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_trans_check")
64480Sstevel@tonic-gate 	char dbuf[DC_SCSI_MFR_LEN];
64490Sstevel@tonic-gate 	struct pm_scsi_cycles *scp;
64500Sstevel@tonic-gate 	int service_years, service_weeks, full_years;
64510Sstevel@tonic-gate 	time_t now, service_seconds, tdiff;
64520Sstevel@tonic-gate 	time_t within_year, when_allowed;
64530Sstevel@tonic-gate 	char *ptr;
64540Sstevel@tonic-gate 	int lower_bound_cycles, upper_bound_cycles, cycles_allowed;
64550Sstevel@tonic-gate 	int cycles_diff, cycles_over;
64560Sstevel@tonic-gate 
64570Sstevel@tonic-gate 	if (datap == NULL) {
64580Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: NULL data pointer!\n", pmf))
64590Sstevel@tonic-gate 		return (-1);
64600Sstevel@tonic-gate 	}
64610Sstevel@tonic-gate 
64620Sstevel@tonic-gate 	if (datap->format == DC_SCSI_FORMAT) {
64630Sstevel@tonic-gate 		/*
64640Sstevel@tonic-gate 		 * Power cycles of the scsi drives are distributed
64650Sstevel@tonic-gate 		 * over 5 years with the following percentage ratio:
64660Sstevel@tonic-gate 		 *
64670Sstevel@tonic-gate 		 *	30%, 25%, 20%, 15%, and 10%
64680Sstevel@tonic-gate 		 *
64690Sstevel@tonic-gate 		 * The power cycle quota for each year is distributed
64700Sstevel@tonic-gate 		 * linearly through out the year.  The equation for
64710Sstevel@tonic-gate 		 * determining the expected cycles is:
64720Sstevel@tonic-gate 		 *
64730Sstevel@tonic-gate 		 *	e = a * (n / y)
64740Sstevel@tonic-gate 		 *
64750Sstevel@tonic-gate 		 * e = expected cycles
64760Sstevel@tonic-gate 		 * a = allocated cycles for this year
64770Sstevel@tonic-gate 		 * n = number of seconds since beginning of this year
64780Sstevel@tonic-gate 		 * y = number of seconds in a year
64790Sstevel@tonic-gate 		 *
64800Sstevel@tonic-gate 		 * Note that beginning of the year starts the day that
64810Sstevel@tonic-gate 		 * the drive has been put on service.
64820Sstevel@tonic-gate 		 *
64830Sstevel@tonic-gate 		 * If the drive has passed its expected cycles, we
64840Sstevel@tonic-gate 		 * can determine when it can start to power cycle
64850Sstevel@tonic-gate 		 * again to keep it on track to meet the 5-year
64860Sstevel@tonic-gate 		 * life expectancy.  The equation for determining
64870Sstevel@tonic-gate 		 * when to power cycle is:
64880Sstevel@tonic-gate 		 *
64890Sstevel@tonic-gate 		 *	w = y * (c / a)
64900Sstevel@tonic-gate 		 *
64910Sstevel@tonic-gate 		 * w = when it can power cycle again
64920Sstevel@tonic-gate 		 * y = number of seconds in a year
64930Sstevel@tonic-gate 		 * c = current number of cycles
64940Sstevel@tonic-gate 		 * a = allocated cycles for the year
64950Sstevel@tonic-gate 		 *
64960Sstevel@tonic-gate 		 */
64970Sstevel@tonic-gate 		char pcnt[DC_SCSI_NPY] = { 30, 55, 75, 90, 100 };
64980Sstevel@tonic-gate 
64990Sstevel@tonic-gate 		scp = &datap->un.scsi_cycles;
65000Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: format=%d, lifemax=%d, ncycles=%d, "
65010Sstevel@tonic-gate 		    "svc_date=%s, svc_flag=%d\n", pmf, datap->format,
65020Sstevel@tonic-gate 		    scp->lifemax, scp->ncycles, scp->svc_date, scp->flag))
65030Sstevel@tonic-gate 		if (scp->ncycles < 0 || scp->flag != 0) {
65040Sstevel@tonic-gate 			PMD(PMD_TCHECK, ("%s: ncycles < 0 || flag != 0\n", pmf))
65050Sstevel@tonic-gate 			return (-1);
65060Sstevel@tonic-gate 		}
65070Sstevel@tonic-gate 
65080Sstevel@tonic-gate 		if (scp->ncycles > scp->lifemax) {
65090Sstevel@tonic-gate 			*intervalp = (LONG_MAX / hz);
65100Sstevel@tonic-gate 			return (0);
65110Sstevel@tonic-gate 		}
65120Sstevel@tonic-gate 
65130Sstevel@tonic-gate 		/*
65140Sstevel@tonic-gate 		 * convert service date to time_t
65150Sstevel@tonic-gate 		 */
65160Sstevel@tonic-gate 		bcopy(scp->svc_date, dbuf, DC_SCSI_YEAR_LEN);
65170Sstevel@tonic-gate 		dbuf[DC_SCSI_YEAR_LEN] = '\0';
65180Sstevel@tonic-gate 		ptr = dbuf;
65190Sstevel@tonic-gate 		service_years = stoi(&ptr) - EPOCH_YEAR;
65200Sstevel@tonic-gate 		bcopy(&scp->svc_date[DC_SCSI_YEAR_LEN], dbuf,
65210Sstevel@tonic-gate 		    DC_SCSI_WEEK_LEN);
65220Sstevel@tonic-gate 		dbuf[DC_SCSI_WEEK_LEN] = '\0';
65230Sstevel@tonic-gate 
65240Sstevel@tonic-gate 		/*
65250Sstevel@tonic-gate 		 * scsi standard does not specify WW data,
65260Sstevel@tonic-gate 		 * could be (00-51) or (01-52)
65270Sstevel@tonic-gate 		 */
65280Sstevel@tonic-gate 		ptr = dbuf;
65290Sstevel@tonic-gate 		service_weeks = stoi(&ptr);
65300Sstevel@tonic-gate 		if (service_years < 0 ||
65310Sstevel@tonic-gate 		    service_weeks < 0 || service_weeks > 52) {
65320Sstevel@tonic-gate 			PMD(PMD_TCHECK, ("%s: service year %d and week %d\n",
65330Sstevel@tonic-gate 			    pmf, service_years, service_weeks))
65340Sstevel@tonic-gate 			return (-1);
65350Sstevel@tonic-gate 		}
65360Sstevel@tonic-gate 
65370Sstevel@tonic-gate 		/*
65380Sstevel@tonic-gate 		 * calculate service date in seconds-since-epoch,
65390Sstevel@tonic-gate 		 * adding one day for each leap-year.
65400Sstevel@tonic-gate 		 *
65410Sstevel@tonic-gate 		 * (years-since-epoch + 2) fixes integer truncation,
65420Sstevel@tonic-gate 		 * example: (8) leap-years during [1972, 2000]
65430Sstevel@tonic-gate 		 * (2000 - 1970) = 30;  and  (30 + 2) / 4 = 8;
65440Sstevel@tonic-gate 		 */
65450Sstevel@tonic-gate 		service_seconds = (service_years * DC_SPY) +
65460Sstevel@tonic-gate 		    (service_weeks * DC_SPW) +
65470Sstevel@tonic-gate 		    (((service_years + 2) / 4) * DC_SPD);
65480Sstevel@tonic-gate 
65490Sstevel@tonic-gate 		now = gethrestime_sec();
65500Sstevel@tonic-gate 		/*
65510Sstevel@tonic-gate 		 * since the granularity of 'svc_date' is day not second,
65520Sstevel@tonic-gate 		 * 'now' should be rounded up to full day.
65530Sstevel@tonic-gate 		 */
65540Sstevel@tonic-gate 		now = ((now + DC_SPD -1) / DC_SPD) * DC_SPD;
65550Sstevel@tonic-gate 		if (service_seconds > now) {
65560Sstevel@tonic-gate 			PMD(PMD_TCHECK, ("%s: service date (%ld) later "
65570Sstevel@tonic-gate 			    "than now (%ld)!\n", pmf, service_seconds, now))
65580Sstevel@tonic-gate 			return (-1);
65590Sstevel@tonic-gate 		}
65600Sstevel@tonic-gate 
65610Sstevel@tonic-gate 		tdiff = now - service_seconds;
65620Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: age is %ld sec\n", pmf, tdiff))
65630Sstevel@tonic-gate 
65640Sstevel@tonic-gate 		/*
65650Sstevel@tonic-gate 		 * NOTE - Leap years are not considered in the calculations
65660Sstevel@tonic-gate 		 * below.
65670Sstevel@tonic-gate 		 */
65680Sstevel@tonic-gate 		full_years = (tdiff / DC_SPY);
65690Sstevel@tonic-gate 		if ((full_years >= DC_SCSI_NPY) &&
65700Sstevel@tonic-gate 		    (scp->ncycles <= scp->lifemax))
65710Sstevel@tonic-gate 			return (1);
65720Sstevel@tonic-gate 
65730Sstevel@tonic-gate 		/*
65740Sstevel@tonic-gate 		 * Determine what is the normal cycle usage for the
65750Sstevel@tonic-gate 		 * device at the beginning and the end of this year.
65760Sstevel@tonic-gate 		 */
65770Sstevel@tonic-gate 		lower_bound_cycles = (!full_years) ? 0 :
65780Sstevel@tonic-gate 		    ((scp->lifemax * pcnt[full_years - 1]) / 100);
65790Sstevel@tonic-gate 		upper_bound_cycles = (scp->lifemax * pcnt[full_years]) / 100;
65800Sstevel@tonic-gate 
65810Sstevel@tonic-gate 		if (scp->ncycles <= lower_bound_cycles)
65820Sstevel@tonic-gate 			return (1);
65830Sstevel@tonic-gate 
65840Sstevel@tonic-gate 		/*
65850Sstevel@tonic-gate 		 * The linear slope that determines how many cycles
65860Sstevel@tonic-gate 		 * are allowed this year is number of seconds
65870Sstevel@tonic-gate 		 * passed this year over total number of seconds in a year.
65880Sstevel@tonic-gate 		 */
65890Sstevel@tonic-gate 		cycles_diff = (upper_bound_cycles - lower_bound_cycles);
65900Sstevel@tonic-gate 		within_year = (tdiff % DC_SPY);
65910Sstevel@tonic-gate 		cycles_allowed = lower_bound_cycles +
65920Sstevel@tonic-gate 		    (((uint64_t)cycles_diff * (uint64_t)within_year) / DC_SPY);
65930Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: lived %d yrs and %ld secs\n", pmf,
65940Sstevel@tonic-gate 		    full_years, within_year))
65950Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: # of cycles allowed %d\n", pmf,
65960Sstevel@tonic-gate 		    cycles_allowed))
65970Sstevel@tonic-gate 
65980Sstevel@tonic-gate 		if (scp->ncycles <= cycles_allowed)
65990Sstevel@tonic-gate 			return (1);
66000Sstevel@tonic-gate 
66010Sstevel@tonic-gate 		/*
66020Sstevel@tonic-gate 		 * The transition is not advised now but we can
66030Sstevel@tonic-gate 		 * determine when the next transition can be made.
66040Sstevel@tonic-gate 		 *
66050Sstevel@tonic-gate 		 * Depending on how many cycles the device has been
66060Sstevel@tonic-gate 		 * over-used, we may need to skip years with
66070Sstevel@tonic-gate 		 * different percentage quota in order to determine
66080Sstevel@tonic-gate 		 * when the next transition can be made.
66090Sstevel@tonic-gate 		 */
66100Sstevel@tonic-gate 		cycles_over = (scp->ncycles - lower_bound_cycles);
66110Sstevel@tonic-gate 		while (cycles_over > cycles_diff) {
66120Sstevel@tonic-gate 			full_years++;
66130Sstevel@tonic-gate 			if (full_years >= DC_SCSI_NPY) {
66140Sstevel@tonic-gate 				*intervalp = (LONG_MAX / hz);
66150Sstevel@tonic-gate 				return (0);
66160Sstevel@tonic-gate 			}
66170Sstevel@tonic-gate 			cycles_over -= cycles_diff;
66180Sstevel@tonic-gate 			lower_bound_cycles = upper_bound_cycles;
66190Sstevel@tonic-gate 			upper_bound_cycles =
66200Sstevel@tonic-gate 			    (scp->lifemax * pcnt[full_years]) / 100;
66210Sstevel@tonic-gate 			cycles_diff = (upper_bound_cycles - lower_bound_cycles);
66220Sstevel@tonic-gate 		}
66230Sstevel@tonic-gate 
66240Sstevel@tonic-gate 		/*
66250Sstevel@tonic-gate 		 * The linear slope that determines when the next transition
66260Sstevel@tonic-gate 		 * can be made is the relative position of used cycles within a
66270Sstevel@tonic-gate 		 * year over total number of cycles within that year.
66280Sstevel@tonic-gate 		 */
66290Sstevel@tonic-gate 		when_allowed = service_seconds + (full_years * DC_SPY) +
66300Sstevel@tonic-gate 		    (((uint64_t)DC_SPY * (uint64_t)cycles_over) / cycles_diff);
66310Sstevel@tonic-gate 		*intervalp = (when_allowed - now);
66320Sstevel@tonic-gate 		if (*intervalp > (LONG_MAX / hz))
66330Sstevel@tonic-gate 			*intervalp = (LONG_MAX / hz);
66340Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: no cycle is allowed in %ld secs\n", pmf,
66350Sstevel@tonic-gate 		    *intervalp))
66360Sstevel@tonic-gate 		return (0);
66370Sstevel@tonic-gate 	}
66380Sstevel@tonic-gate 
66390Sstevel@tonic-gate 	PMD(PMD_TCHECK, ("%s: unknown format!\n", pmf))
66400Sstevel@tonic-gate 	return (-1);
66410Sstevel@tonic-gate }
66420Sstevel@tonic-gate 
66430Sstevel@tonic-gate /*
66440Sstevel@tonic-gate  * Nexus drivers call into pm framework to indicate which child driver is about
66450Sstevel@tonic-gate  * to be installed.  In some platforms, ppm may need to configure the hardware
66460Sstevel@tonic-gate  * for successful installation of a driver.
66470Sstevel@tonic-gate  */
66480Sstevel@tonic-gate int
66490Sstevel@tonic-gate pm_init_child(dev_info_t *dip)
66500Sstevel@tonic-gate {
66510Sstevel@tonic-gate 	power_req_t power_req;
66520Sstevel@tonic-gate 
66530Sstevel@tonic-gate 	ASSERT(ddi_binding_name(dip));
66540Sstevel@tonic-gate 	ASSERT(ddi_get_name_addr(dip));
66550Sstevel@tonic-gate 	pm_ppm_claim(dip);
66560Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {	/* if ppm driver claims the node */
66570Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_INIT_CHILD;
66580Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
66590Sstevel@tonic-gate 		ASSERT(PPM(dip) != NULL);
66600Sstevel@tonic-gate 		return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req,
66610Sstevel@tonic-gate 		    NULL));
66620Sstevel@tonic-gate 	} else {
66630Sstevel@tonic-gate #ifdef DEBUG
66640Sstevel@tonic-gate 		/* pass it to the default handler so we can debug things */
66650Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_INIT_CHILD;
66660Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
66670Sstevel@tonic-gate 		(void) pm_ctlops(NULL, dip,
66680Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, NULL);
66690Sstevel@tonic-gate #endif
66700Sstevel@tonic-gate 	}
66710Sstevel@tonic-gate 	return (DDI_SUCCESS);
66720Sstevel@tonic-gate }
66730Sstevel@tonic-gate 
66740Sstevel@tonic-gate /*
66750Sstevel@tonic-gate  * Bring parent of a node that is about to be probed up to full power, and
66760Sstevel@tonic-gate  * arrange for it to stay up until pm_post_probe() or pm_post_attach() decide
66770Sstevel@tonic-gate  * it is time to let it go down again
66780Sstevel@tonic-gate  */
66790Sstevel@tonic-gate void
66800Sstevel@tonic-gate pm_pre_probe(dev_info_t *dip, pm_ppm_cookie_t *cp)
66810Sstevel@tonic-gate {
66820Sstevel@tonic-gate 	int result;
66830Sstevel@tonic-gate 	power_req_t power_req;
66840Sstevel@tonic-gate 
66850Sstevel@tonic-gate 	bzero(cp, sizeof (*cp));
66860Sstevel@tonic-gate 	cp->ppc_dip = dip;
66870Sstevel@tonic-gate 
66880Sstevel@tonic-gate 	pm_ppm_claim(dip);
66890Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {	/* if ppm driver claims the node */
66900Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_PRE_PROBE;
66910Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
66920Sstevel@tonic-gate 		ASSERT(PPM(dip) != NULL);
66930Sstevel@tonic-gate 		(void) pm_ctlops(PPM(dip), dip,
66940Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
66950Sstevel@tonic-gate 		cp->ppc_ppm = PPM(dip);
66960Sstevel@tonic-gate 	} else {
66970Sstevel@tonic-gate #ifdef DEBUG
66980Sstevel@tonic-gate 		/* pass it to the default handler so we can debug things */
66990Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_PRE_PROBE;
67000Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
67010Sstevel@tonic-gate 		(void) pm_ctlops(NULL, dip,
67020Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
67030Sstevel@tonic-gate #endif
67040Sstevel@tonic-gate 		cp->ppc_ppm = NULL;
67050Sstevel@tonic-gate 	}
67060Sstevel@tonic-gate }
67070Sstevel@tonic-gate 
67080Sstevel@tonic-gate int
67090Sstevel@tonic-gate pm_pre_config(dev_info_t *dip, char *devnm)
67100Sstevel@tonic-gate {
67110Sstevel@tonic-gate 	PMD_FUNC(pmf, "pre_config")
67120Sstevel@tonic-gate 	int ret;
67130Sstevel@tonic-gate 
67140Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
67150Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
67160Sstevel@tonic-gate 		ret = mdi_power(dip, MDI_PM_PRE_CONFIG, NULL, devnm, 0);
67170Sstevel@tonic-gate 		return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
67180Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
67190Sstevel@tonic-gate 		return (DDI_SUCCESS);
67200Sstevel@tonic-gate 
67210Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
67220Sstevel@tonic-gate 	pm_hold_power(dip);
67230Sstevel@tonic-gate 	ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK);
67240Sstevel@tonic-gate 	if (ret != DDI_SUCCESS)
67250Sstevel@tonic-gate 		pm_rele_power(dip);
67260Sstevel@tonic-gate 	return (ret);
67270Sstevel@tonic-gate }
67280Sstevel@tonic-gate 
67290Sstevel@tonic-gate /*
67300Sstevel@tonic-gate  * This routine is called by devfs during its walk to unconfigue a node.
67310Sstevel@tonic-gate  * If the call is due to auto mod_unloads and the dip is not at its
67320Sstevel@tonic-gate  * full power, we return DDI_FAILURE to terminate the walk, otherwise
67330Sstevel@tonic-gate  * return DDI_SUCCESS.
67340Sstevel@tonic-gate  */
67350Sstevel@tonic-gate int
67360Sstevel@tonic-gate pm_pre_unconfig(dev_info_t *dip, int flags, int *held, char *devnm)
67370Sstevel@tonic-gate {
67380Sstevel@tonic-gate 	PMD_FUNC(pmf, "pre_unconfig")
67390Sstevel@tonic-gate 	int ret;
67400Sstevel@tonic-gate 
67410Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
67420Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf,
67430Sstevel@tonic-gate 		    PM_DEVICE(dip), flags))
67440Sstevel@tonic-gate 		ret = mdi_power(dip, MDI_PM_PRE_UNCONFIG, held, devnm, flags);
67450Sstevel@tonic-gate 		return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
67460Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
67470Sstevel@tonic-gate 		return (DDI_SUCCESS);
67480Sstevel@tonic-gate 
67490Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, PM_DEVICE(dip),
67500Sstevel@tonic-gate 	    flags))
67510Sstevel@tonic-gate 	*held = 0;
67520Sstevel@tonic-gate 
67530Sstevel@tonic-gate 	/*
67540Sstevel@tonic-gate 	 * If the dip is a leaf node, don't power it up.
67550Sstevel@tonic-gate 	 */
67560Sstevel@tonic-gate 	if (!ddi_get_child(dip))
67570Sstevel@tonic-gate 		return (DDI_SUCCESS);
67580Sstevel@tonic-gate 
67590Sstevel@tonic-gate 	/*
67600Sstevel@tonic-gate 	 * Do not power up the node if it is called due to auto-modunload.
67610Sstevel@tonic-gate 	 */
67620Sstevel@tonic-gate 	if ((flags & NDI_AUTODETACH) && !pm_all_at_normal(dip))
67630Sstevel@tonic-gate 		return (DDI_FAILURE);
67640Sstevel@tonic-gate 
67650Sstevel@tonic-gate 	pm_hold_power(dip);
67660Sstevel@tonic-gate 	*held = 1;
67670Sstevel@tonic-gate 	ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK);
67680Sstevel@tonic-gate 	if (ret != DDI_SUCCESS) {
67690Sstevel@tonic-gate 		pm_rele_power(dip);
67700Sstevel@tonic-gate 		*held = 0;
67710Sstevel@tonic-gate 	}
67720Sstevel@tonic-gate 	return (ret);
67730Sstevel@tonic-gate }
67740Sstevel@tonic-gate 
67750Sstevel@tonic-gate /*
67760Sstevel@tonic-gate  * Notify ppm of attach action.  Parent is already held at full power by
67770Sstevel@tonic-gate  * probe action.
67780Sstevel@tonic-gate  */
67790Sstevel@tonic-gate void
67800Sstevel@tonic-gate pm_pre_attach(dev_info_t *dip, pm_ppm_cookie_t *cp, ddi_attach_cmd_t cmd)
67810Sstevel@tonic-gate {
67820Sstevel@tonic-gate 	static char *me = "pm_pre_attach";
67830Sstevel@tonic-gate 	power_req_t power_req;
67840Sstevel@tonic-gate 	int result;
67850Sstevel@tonic-gate 
67860Sstevel@tonic-gate 	/*
67870Sstevel@tonic-gate 	 * Initialize and fill in the PPM cookie
67880Sstevel@tonic-gate 	 */
67890Sstevel@tonic-gate 	bzero(cp, sizeof (*cp));
67900Sstevel@tonic-gate 	cp->ppc_cmd = (int)cmd;
67910Sstevel@tonic-gate 	cp->ppc_ppm = PPM(dip);
67920Sstevel@tonic-gate 	cp->ppc_dip = dip;
67930Sstevel@tonic-gate 
67940Sstevel@tonic-gate 	/*
67950Sstevel@tonic-gate 	 * DDI_ATTACH and DDI_RESUME cmds need to call platform specific
67960Sstevel@tonic-gate 	 * Power Management stuff. DDI_RESUME also has to purge it's
67970Sstevel@tonic-gate 	 * powerlevel information.
67980Sstevel@tonic-gate 	 */
67990Sstevel@tonic-gate 	switch (cmd) {
68000Sstevel@tonic-gate 	case DDI_ATTACH:
68010Sstevel@tonic-gate 		if (cp->ppc_ppm) {	/* if ppm driver claims the node */
68020Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_ATTACH;
68030Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
68040Sstevel@tonic-gate 			ASSERT(PPM(dip));
68050Sstevel@tonic-gate 			(void) pm_ctlops(cp->ppc_ppm, dip, DDI_CTLOPS_POWER,
68060Sstevel@tonic-gate 			    &power_req, &result);
68070Sstevel@tonic-gate 		}
68080Sstevel@tonic-gate #ifdef DEBUG
68090Sstevel@tonic-gate 		else {
68100Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_ATTACH;
68110Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
68120Sstevel@tonic-gate 			(void) pm_ctlops(NULL, dip,
68130Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
68140Sstevel@tonic-gate 		}
68150Sstevel@tonic-gate #endif
68160Sstevel@tonic-gate 		break;
68170Sstevel@tonic-gate 	case DDI_RESUME:
68180Sstevel@tonic-gate 		pm_forget_power_level(dip);
68190Sstevel@tonic-gate 
68200Sstevel@tonic-gate 		if (cp->ppc_ppm) {	/* if ppm driver claims the node */
68210Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_RESUME;
68220Sstevel@tonic-gate 			power_req.req.resume_req.who = cp->ppc_dip;
68230Sstevel@tonic-gate 			power_req.req.resume_req.cmd =
68240Sstevel@tonic-gate 			    (ddi_attach_cmd_t)cp->ppc_cmd;
68250Sstevel@tonic-gate 			ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
68260Sstevel@tonic-gate 			(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
68270Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
68280Sstevel@tonic-gate 		}
68290Sstevel@tonic-gate #ifdef DEBUG
68300Sstevel@tonic-gate 		else {
68310Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_RESUME;
68320Sstevel@tonic-gate 			power_req.req.resume_req.who = cp->ppc_dip;
68330Sstevel@tonic-gate 			power_req.req.resume_req.cmd =
68340Sstevel@tonic-gate 			    (ddi_attach_cmd_t)cp->ppc_cmd;
68350Sstevel@tonic-gate 			(void) pm_ctlops(NULL, cp->ppc_dip,
68360Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
68370Sstevel@tonic-gate 		}
68380Sstevel@tonic-gate #endif
68390Sstevel@tonic-gate 		break;
68400Sstevel@tonic-gate 
68410Sstevel@tonic-gate 	case DDI_PM_RESUME:
68420Sstevel@tonic-gate 		break;
68430Sstevel@tonic-gate 
68440Sstevel@tonic-gate 	default:
68450Sstevel@tonic-gate 		panic(me);
68460Sstevel@tonic-gate 	}
68470Sstevel@tonic-gate }
68480Sstevel@tonic-gate 
68490Sstevel@tonic-gate /*
68500Sstevel@tonic-gate  * Nexus drivers call into pm framework to indicate which child driver is
68510Sstevel@tonic-gate  * being uninstalled.  In some platforms, ppm may need to reconfigure the
68520Sstevel@tonic-gate  * hardware since the device driver is no longer installed.
68530Sstevel@tonic-gate  */
68540Sstevel@tonic-gate int
68550Sstevel@tonic-gate pm_uninit_child(dev_info_t *dip)
68560Sstevel@tonic-gate {
68570Sstevel@tonic-gate 	power_req_t power_req;
68580Sstevel@tonic-gate 
68590Sstevel@tonic-gate 	ASSERT(ddi_binding_name(dip));
68600Sstevel@tonic-gate 	ASSERT(ddi_get_name_addr(dip));
68610Sstevel@tonic-gate 	pm_ppm_claim(dip);
68620Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {	/* if ppm driver claims the node */
68630Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_UNINIT_CHILD;
68640Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
68650Sstevel@tonic-gate 		ASSERT(PPM(dip));
68660Sstevel@tonic-gate 		return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req,
68670Sstevel@tonic-gate 		    NULL));
68680Sstevel@tonic-gate 	} else {
68690Sstevel@tonic-gate #ifdef DEBUG
68700Sstevel@tonic-gate 		/* pass it to the default handler so we can debug things */
68710Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_UNINIT_CHILD;
68720Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
68730Sstevel@tonic-gate 		(void) pm_ctlops(NULL, dip, DDI_CTLOPS_POWER, &power_req, NULL);
68740Sstevel@tonic-gate #endif
68750Sstevel@tonic-gate 	}
68760Sstevel@tonic-gate 	return (DDI_SUCCESS);
68770Sstevel@tonic-gate }
68780Sstevel@tonic-gate /*
68790Sstevel@tonic-gate  * Decrement kidsupcnt so scan can turn the parent back off if it is idle
68800Sstevel@tonic-gate  * Also notify ppm of result of probe if there is a ppm that cares
68810Sstevel@tonic-gate  */
68820Sstevel@tonic-gate void
68830Sstevel@tonic-gate pm_post_probe(pm_ppm_cookie_t *cp, int ret, int probe_failed)
68840Sstevel@tonic-gate {
68850Sstevel@tonic-gate 	_NOTE(ARGUNUSED(probe_failed))
68860Sstevel@tonic-gate 	int result;
68870Sstevel@tonic-gate 	power_req_t power_req;
68880Sstevel@tonic-gate 
68890Sstevel@tonic-gate 	if (cp->ppc_ppm) {	/* if ppm driver claims the node */
68900Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_PROBE;
68910Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
68920Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
68930Sstevel@tonic-gate 		ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
68940Sstevel@tonic-gate 		(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, DDI_CTLOPS_POWER,
68950Sstevel@tonic-gate 		    &power_req, &result);
68960Sstevel@tonic-gate 	}
68970Sstevel@tonic-gate #ifdef DEBUG
68980Sstevel@tonic-gate 	else {
68990Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_PROBE;
69000Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
69010Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
69020Sstevel@tonic-gate 		(void) pm_ctlops(NULL, cp->ppc_dip, DDI_CTLOPS_POWER,
69030Sstevel@tonic-gate 		    &power_req, &result);
69040Sstevel@tonic-gate 	}
69050Sstevel@tonic-gate #endif
69060Sstevel@tonic-gate }
69070Sstevel@tonic-gate 
69080Sstevel@tonic-gate void
69090Sstevel@tonic-gate pm_post_config(dev_info_t *dip, char *devnm)
69100Sstevel@tonic-gate {
69110Sstevel@tonic-gate 	PMD_FUNC(pmf, "post_config")
69120Sstevel@tonic-gate 
69130Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
69140Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
69150Sstevel@tonic-gate 		(void) mdi_power(dip, MDI_PM_POST_CONFIG, NULL, devnm, 0);
69160Sstevel@tonic-gate 		return;
69170Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
69180Sstevel@tonic-gate 		return;
69190Sstevel@tonic-gate 
69200Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
69210Sstevel@tonic-gate 	pm_rele_power(dip);
69220Sstevel@tonic-gate }
69230Sstevel@tonic-gate 
69240Sstevel@tonic-gate void
69250Sstevel@tonic-gate pm_post_unconfig(dev_info_t *dip, int held, char *devnm)
69260Sstevel@tonic-gate {
69270Sstevel@tonic-gate 	PMD_FUNC(pmf, "post_unconfig")
69280Sstevel@tonic-gate 
69290Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
69300Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf,
69310Sstevel@tonic-gate 		    PM_DEVICE(dip), held))
69320Sstevel@tonic-gate 		(void) mdi_power(dip, MDI_PM_POST_UNCONFIG, &held, devnm, 0);
69330Sstevel@tonic-gate 		return;
69340Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
69350Sstevel@tonic-gate 		return;
69360Sstevel@tonic-gate 
69370Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, PM_DEVICE(dip),
69380Sstevel@tonic-gate 	    held))
69390Sstevel@tonic-gate 	if (!held)
69400Sstevel@tonic-gate 		return;
69410Sstevel@tonic-gate 	/*
69420Sstevel@tonic-gate 	 * We have held power in pre_unconfig, release it here.
69430Sstevel@tonic-gate 	 */
69440Sstevel@tonic-gate 	pm_rele_power(dip);
69450Sstevel@tonic-gate }
69460Sstevel@tonic-gate 
69470Sstevel@tonic-gate /*
69480Sstevel@tonic-gate  * Notify ppm of result of attach if there is a ppm that cares
69490Sstevel@tonic-gate  */
69500Sstevel@tonic-gate void
69510Sstevel@tonic-gate pm_post_attach(pm_ppm_cookie_t *cp, int ret)
69520Sstevel@tonic-gate {
69530Sstevel@tonic-gate 	int result;
69540Sstevel@tonic-gate 	power_req_t power_req;
69550Sstevel@tonic-gate 	dev_info_t	*dip;
69560Sstevel@tonic-gate 
69570Sstevel@tonic-gate 	if (cp->ppc_cmd != DDI_ATTACH)
69580Sstevel@tonic-gate 		return;
69590Sstevel@tonic-gate 
69600Sstevel@tonic-gate 	dip = cp->ppc_dip;
69610Sstevel@tonic-gate 
69620Sstevel@tonic-gate 	if (ret == DDI_SUCCESS) {
69630Sstevel@tonic-gate 		/*
69640Sstevel@tonic-gate 		 * Attach succeeded, so proceed to doing post-attach pm tasks
69650Sstevel@tonic-gate 		 */
69660Sstevel@tonic-gate 		if (PM_GET_PM_INFO(dip) == NULL)
69670Sstevel@tonic-gate 			(void) pm_start(dip);
69680Sstevel@tonic-gate 	} else {
69690Sstevel@tonic-gate 		/*
69700Sstevel@tonic-gate 		 * Attach may have got pm started before failing
69710Sstevel@tonic-gate 		 */
69720Sstevel@tonic-gate 		pm_stop(dip);
69730Sstevel@tonic-gate 	}
69740Sstevel@tonic-gate 
69750Sstevel@tonic-gate 	if (cp->ppc_ppm) {	/* if ppm driver claims the node */
69760Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_ATTACH;
69770Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
69780Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
69790Sstevel@tonic-gate 		ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
69800Sstevel@tonic-gate 		(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
69810Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
69820Sstevel@tonic-gate 	}
69830Sstevel@tonic-gate #ifdef DEBUG
69840Sstevel@tonic-gate 	else {
69850Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_ATTACH;
69860Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
69870Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
69880Sstevel@tonic-gate 		(void) pm_ctlops(NULL, cp->ppc_dip,
69890Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
69900Sstevel@tonic-gate 	}
69910Sstevel@tonic-gate #endif
69920Sstevel@tonic-gate }
69930Sstevel@tonic-gate 
69940Sstevel@tonic-gate /*
69950Sstevel@tonic-gate  * Notify ppm of attach action.  Parent is already held at full power by
69960Sstevel@tonic-gate  * probe action.
69970Sstevel@tonic-gate  */
69980Sstevel@tonic-gate void
69990Sstevel@tonic-gate pm_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, pm_ppm_cookie_t *cp)
70000Sstevel@tonic-gate {
70010Sstevel@tonic-gate 	int result;
70020Sstevel@tonic-gate 	power_req_t power_req;
70030Sstevel@tonic-gate 
70040Sstevel@tonic-gate 	bzero(cp, sizeof (*cp));
70050Sstevel@tonic-gate 	cp->ppc_dip = dip;
70060Sstevel@tonic-gate 	cp->ppc_cmd = (int)cmd;
70070Sstevel@tonic-gate 
70080Sstevel@tonic-gate 	switch (cmd) {
70090Sstevel@tonic-gate 	case DDI_DETACH:
70100Sstevel@tonic-gate 		pm_detaching(dip);		/* suspend pm while detaching */
70110Sstevel@tonic-gate 		if (pm_ppm_claimed(dip)) {	/* if ppm driver claims node */
70120Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_DETACH;
70130Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
70140Sstevel@tonic-gate 			ASSERT(PPM(dip));
70150Sstevel@tonic-gate 			(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
70160Sstevel@tonic-gate 			    &power_req, &result);
70170Sstevel@tonic-gate 			cp->ppc_ppm = PPM(dip);
70180Sstevel@tonic-gate 		} else {
70190Sstevel@tonic-gate #ifdef DEBUG
70200Sstevel@tonic-gate 			/* pass to the default handler so we can debug things */
70210Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_DETACH;
70220Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
70230Sstevel@tonic-gate 			(void) pm_ctlops(NULL, dip,
70240Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
70250Sstevel@tonic-gate #endif
70260Sstevel@tonic-gate 			cp->ppc_ppm = NULL;
70270Sstevel@tonic-gate 		}
70280Sstevel@tonic-gate 		break;
70290Sstevel@tonic-gate 
70300Sstevel@tonic-gate 	default:
70310Sstevel@tonic-gate 		break;
70320Sstevel@tonic-gate 	}
70330Sstevel@tonic-gate }
70340Sstevel@tonic-gate 
70350Sstevel@tonic-gate /*
70360Sstevel@tonic-gate  * Dip is either a leaf node that exported "no-involuntary-power-cycles" prop.,
70370Sstevel@tonic-gate  * (if devi_pm_noinvol count is 0) or an ancestor of such a node.  We need to
70380Sstevel@tonic-gate  * make an entry to record the details, which includes certain flag settings.
70390Sstevel@tonic-gate  */
70400Sstevel@tonic-gate static void
70410Sstevel@tonic-gate pm_record_invol_path(char *path, int flags, int noinvolpm, int volpmd,
70420Sstevel@tonic-gate     int wasvolpmd, major_t major)
70430Sstevel@tonic-gate {
70440Sstevel@tonic-gate 	PMD_FUNC(pmf, "record_invol_path")
70450Sstevel@tonic-gate 	major_t pm_path_to_major(char *);
70460Sstevel@tonic-gate 	size_t plen;
70470Sstevel@tonic-gate 	pm_noinvol_t *ip, *np, *pp;
70480Sstevel@tonic-gate 	pp = NULL;
70490Sstevel@tonic-gate 
70500Sstevel@tonic-gate 	plen = strlen(path) + 1;
70510Sstevel@tonic-gate 	np = kmem_zalloc(sizeof (*np), KM_SLEEP);
70520Sstevel@tonic-gate 	np->ni_size = plen;
70530Sstevel@tonic-gate 	np->ni_path = kmem_alloc(plen, KM_SLEEP);
70540Sstevel@tonic-gate 	np->ni_noinvolpm = noinvolpm;
70550Sstevel@tonic-gate 	np->ni_volpmd = volpmd;
70560Sstevel@tonic-gate 	np->ni_wasvolpmd = wasvolpmd;
70570Sstevel@tonic-gate 	np->ni_flags = flags;
70580Sstevel@tonic-gate 	(void) strcpy(np->ni_path, path);
70590Sstevel@tonic-gate 	/*
70600Sstevel@tonic-gate 	 * If we haven't actually seen the node attached, it is hard to figure
70610Sstevel@tonic-gate 	 * out its major.  If we could hold the node by path, we would be much
70620Sstevel@tonic-gate 	 * happier here.
70630Sstevel@tonic-gate 	 */
70640Sstevel@tonic-gate 	if (major == (major_t)-1) {
70650Sstevel@tonic-gate 		np->ni_major = pm_path_to_major(path);
70660Sstevel@tonic-gate 	} else {
70670Sstevel@tonic-gate 		np->ni_major = major;
70680Sstevel@tonic-gate 	}
70690Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
70700Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
70710Sstevel@tonic-gate 		int comp = strcmp(path, ip->ni_path);
70720Sstevel@tonic-gate 		if (comp < 0) {
70730Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: %s insert before %s\n",
70740Sstevel@tonic-gate 			    pmf, path, ip->ni_path))
70750Sstevel@tonic-gate 			/* insert before current entry */
70760Sstevel@tonic-gate 			np->ni_next = ip;
70770Sstevel@tonic-gate 			if (pp) {
70780Sstevel@tonic-gate 				pp->ni_next = np;
70790Sstevel@tonic-gate 			} else {
70800Sstevel@tonic-gate 				pm_noinvol_head = np;
70810Sstevel@tonic-gate 			}
70820Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
70830Sstevel@tonic-gate #ifdef DEBUG
70840Sstevel@tonic-gate 			if (pm_debug & PMD_NOINVOL)
70850Sstevel@tonic-gate 				pr_noinvol("record_invol_path exit0");
70860Sstevel@tonic-gate #endif
70870Sstevel@tonic-gate 			return;
70880Sstevel@tonic-gate 		} else if (comp == 0) {
70890Sstevel@tonic-gate 			panic("%s already in pm_noinvol list", path);
70900Sstevel@tonic-gate 		}
70910Sstevel@tonic-gate 	}
70920Sstevel@tonic-gate 	/*
70930Sstevel@tonic-gate 	 * If we did not find an entry in the list that this should go before,
70940Sstevel@tonic-gate 	 * then it must go at the end
70950Sstevel@tonic-gate 	 */
70960Sstevel@tonic-gate 	if (pp) {
70970Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: %s append after %s\n", pmf, path,
70980Sstevel@tonic-gate 		    pp->ni_path))
70990Sstevel@tonic-gate 		ASSERT(pp->ni_next == 0);
71000Sstevel@tonic-gate 		pp->ni_next = np;
71010Sstevel@tonic-gate 	} else {
71020Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: %s added to end-of-list\n", pmf, path))
71030Sstevel@tonic-gate 		ASSERT(!pm_noinvol_head);
71040Sstevel@tonic-gate 		pm_noinvol_head = np;
71050Sstevel@tonic-gate 	}
71060Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
71070Sstevel@tonic-gate #ifdef DEBUG
71080Sstevel@tonic-gate 	if (pm_debug & PMD_NOINVOL)
71090Sstevel@tonic-gate 		pr_noinvol("record_invol_path exit");
71100Sstevel@tonic-gate #endif
71110Sstevel@tonic-gate }
71120Sstevel@tonic-gate 
71130Sstevel@tonic-gate void
71140Sstevel@tonic-gate pm_record_invol(dev_info_t *dip)
71150Sstevel@tonic-gate {
71160Sstevel@tonic-gate 	char *pathbuf;
71170Sstevel@tonic-gate 	int pm_all_components_off(dev_info_t *);
71180Sstevel@tonic-gate 	int volpmd = (PM_NUMCMPTS(dip) > 0) && pm_all_components_off(dip);
71190Sstevel@tonic-gate 
71200Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
71210Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
71220Sstevel@tonic-gate 
71230Sstevel@tonic-gate 	pm_record_invol_path(pathbuf, (DEVI(dip)->devi_pm_flags &
71240Sstevel@tonic-gate 	    (PMC_NO_INVOL | PMC_CONSOLE_FB)), DEVI(dip)->devi_pm_noinvolpm,
71250Sstevel@tonic-gate 	    DEVI(dip)->devi_pm_volpmd, volpmd, PM_MAJOR(dip));
71260Sstevel@tonic-gate 
71270Sstevel@tonic-gate 	/*
71280Sstevel@tonic-gate 	 * If this child's detach will be holding up its ancestors, then we
71290Sstevel@tonic-gate 	 * allow for an exception to that if all children of this type have
71300Sstevel@tonic-gate 	 * gone down voluntarily.
71310Sstevel@tonic-gate 	 * Now walk down the tree incrementing devi_pm_noinvolpm
71320Sstevel@tonic-gate 	 */
71330Sstevel@tonic-gate 	(void) pm_noinvol_update(PM_BP_NOINVOL_DETACH, 0, volpmd, pathbuf,
71340Sstevel@tonic-gate 	    dip);
71350Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
71360Sstevel@tonic-gate }
71370Sstevel@tonic-gate 
71380Sstevel@tonic-gate void
71390Sstevel@tonic-gate pm_post_detach(pm_ppm_cookie_t *cp, int ret)
71400Sstevel@tonic-gate {
71410Sstevel@tonic-gate 	dev_info_t *dip = cp->ppc_dip;
71420Sstevel@tonic-gate 	int result;
71430Sstevel@tonic-gate 	power_req_t power_req;
71440Sstevel@tonic-gate 
71450Sstevel@tonic-gate 	switch (cp->ppc_cmd) {
71460Sstevel@tonic-gate 	case DDI_DETACH:
71470Sstevel@tonic-gate 		if (cp->ppc_ppm) {	/* if ppm driver claims the node */
71480Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_POST_DETACH;
71490Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = cp->ppc_dip;
71500Sstevel@tonic-gate 			power_req.req.ppm_config_req.result = ret;
71510Sstevel@tonic-gate 			ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
71520Sstevel@tonic-gate 			(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
71530Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
71540Sstevel@tonic-gate 		}
71550Sstevel@tonic-gate #ifdef DEBUG
71560Sstevel@tonic-gate 		else {
71570Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_POST_DETACH;
71580Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = cp->ppc_dip;
71590Sstevel@tonic-gate 			power_req.req.ppm_config_req.result = ret;
71600Sstevel@tonic-gate 			(void) pm_ctlops(NULL, cp->ppc_dip,
71610Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
71620Sstevel@tonic-gate 		}
71630Sstevel@tonic-gate #endif
71640Sstevel@tonic-gate 		if (ret == DDI_SUCCESS) {
71650Sstevel@tonic-gate 			/*
71660Sstevel@tonic-gate 			 * For hotplug detach we assume it is *really* gone
71670Sstevel@tonic-gate 			 */
71680Sstevel@tonic-gate 			if (cp->ppc_cmd == DDI_DETACH &&
71690Sstevel@tonic-gate 			    ((DEVI(dip)->devi_pm_flags &
71700Sstevel@tonic-gate 			    (PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
71710Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_noinvolpm))
71720Sstevel@tonic-gate 				pm_record_invol(dip);
71730Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags &=
71740Sstevel@tonic-gate 			    ~(PMC_NO_INVOL | PMC_NOINVOL_DONE);
71750Sstevel@tonic-gate 
71760Sstevel@tonic-gate 			/*
71770Sstevel@tonic-gate 			 * If console fb is detaching, then we don't need to
71780Sstevel@tonic-gate 			 * worry any more about it going off (pm_detaching has
71790Sstevel@tonic-gate 			 * brought up all components)
71800Sstevel@tonic-gate 			 */
71810Sstevel@tonic-gate 			if (PM_IS_CFB(dip)) {
71820Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
71830Sstevel@tonic-gate 				ASSERT(cfb_dip_detaching);
71840Sstevel@tonic-gate 				ASSERT(cfb_dip == NULL);
71850Sstevel@tonic-gate 				ASSERT(pm_cfb_comps_off == 0);
71860Sstevel@tonic-gate 				cfb_dip_detaching = NULL;
71870Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
71880Sstevel@tonic-gate 			}
71890Sstevel@tonic-gate 			pm_stop(dip);	/* make it permanent */
71900Sstevel@tonic-gate 		} else {
71910Sstevel@tonic-gate 			if (PM_IS_CFB(dip)) {
71920Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
71930Sstevel@tonic-gate 				ASSERT(cfb_dip_detaching);
71940Sstevel@tonic-gate 				ASSERT(cfb_dip == NULL);
71950Sstevel@tonic-gate 				ASSERT(pm_cfb_comps_off == 0);
71960Sstevel@tonic-gate 				cfb_dip = cfb_dip_detaching;
71970Sstevel@tonic-gate 				cfb_dip_detaching = NULL;
71980Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
71990Sstevel@tonic-gate 			}
72000Sstevel@tonic-gate 			pm_detach_failed(dip);	/* resume power management */
72010Sstevel@tonic-gate 		}
72020Sstevel@tonic-gate 		break;
72030Sstevel@tonic-gate 	case DDI_PM_SUSPEND:
72040Sstevel@tonic-gate 		break;
72050Sstevel@tonic-gate 	case DDI_SUSPEND:
72060Sstevel@tonic-gate 		break;				/* legal, but nothing to do */
72070Sstevel@tonic-gate 	default:
72080Sstevel@tonic-gate #ifdef DEBUG
72090Sstevel@tonic-gate 		panic("pm_post_detach: unrecognized cmd %d for detach",
72100Sstevel@tonic-gate 		    cp->ppc_cmd);
72110Sstevel@tonic-gate 		/*NOTREACHED*/
72120Sstevel@tonic-gate #else
72130Sstevel@tonic-gate 		break;
72140Sstevel@tonic-gate #endif
72150Sstevel@tonic-gate 	}
72160Sstevel@tonic-gate }
72170Sstevel@tonic-gate 
72180Sstevel@tonic-gate /*
72190Sstevel@tonic-gate  * Called after vfs_mountroot has got the clock started to fix up timestamps
72200Sstevel@tonic-gate  * that were set when root bush drivers attached.  hresttime was 0 then, so the
72210Sstevel@tonic-gate  * devices look busy but have a 0 busycnt
72220Sstevel@tonic-gate  */
72230Sstevel@tonic-gate int
72240Sstevel@tonic-gate pm_adjust_timestamps(dev_info_t *dip, void *arg)
72250Sstevel@tonic-gate {
72260Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
72270Sstevel@tonic-gate 
72280Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
72290Sstevel@tonic-gate 	struct pm_component *cp;
72300Sstevel@tonic-gate 	int i;
72310Sstevel@tonic-gate 
72320Sstevel@tonic-gate 	if (!info)
72330Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
72340Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
72350Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
72360Sstevel@tonic-gate 		cp = PM_CP(dip, i);
72370Sstevel@tonic-gate 		if (cp->pmc_timestamp == 0 && cp->pmc_busycount == 0)
72380Sstevel@tonic-gate 			cp->pmc_timestamp = gethrestime_sec();
72390Sstevel@tonic-gate 	}
72400Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
72410Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
72420Sstevel@tonic-gate }
72430Sstevel@tonic-gate 
72440Sstevel@tonic-gate /*
72450Sstevel@tonic-gate  * Called at attach time to see if the device being attached has a record in
72460Sstevel@tonic-gate  * the no involuntary power cycles list.  If so, we do some bookkeeping on the
72470Sstevel@tonic-gate  * parents and set a flag in the dip
72480Sstevel@tonic-gate  */
72490Sstevel@tonic-gate void
72500Sstevel@tonic-gate pm_noinvol_specd(dev_info_t *dip)
72510Sstevel@tonic-gate {
72520Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_specd")
72530Sstevel@tonic-gate 	char *pathbuf;
72540Sstevel@tonic-gate 	pm_noinvol_t *ip, *pp = NULL;
72550Sstevel@tonic-gate 	int wasvolpmd;
72560Sstevel@tonic-gate 	int found = 0;
72570Sstevel@tonic-gate 
72580Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_NOINVOL_DONE)
72590Sstevel@tonic-gate 		return;
72600Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |=  PMC_NOINVOL_DONE;
72610Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
72620Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
72630Sstevel@tonic-gate 
72640Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
72650Sstevel@tonic-gate 	DEVI(dip)->devi_pm_volpmd = 0;
72660Sstevel@tonic-gate 	DEVI(dip)->devi_pm_noinvolpm = 0;
72670Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
72680Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
72690Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n",
72700Sstevel@tonic-gate 		    pmf, pathbuf, ip->ni_path))
72710Sstevel@tonic-gate 		if (strcmp(pathbuf, ip->ni_path) == 0) {
72720Sstevel@tonic-gate 			found++;
72730Sstevel@tonic-gate 			break;
72740Sstevel@tonic-gate 		}
72750Sstevel@tonic-gate 	}
72760Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
72770Sstevel@tonic-gate 	if (!found) {
72780Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
72790Sstevel@tonic-gate 		kmem_free(pathbuf, MAXPATHLEN);
72800Sstevel@tonic-gate 		return;
72810Sstevel@tonic-gate 	}
72820Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
72830Sstevel@tonic-gate 	pp = NULL;
72840Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
72850Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n",
72860Sstevel@tonic-gate 		    pmf, pathbuf, ip->ni_path))
72870Sstevel@tonic-gate 		if (strcmp(pathbuf, ip->ni_path) == 0) {
72880Sstevel@tonic-gate 			ip->ni_flags &= ~PMC_DRIVER_REMOVED;
72890Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= ip->ni_flags;
72900Sstevel@tonic-gate 			/*
72910Sstevel@tonic-gate 			 * Handle special case of console fb
72920Sstevel@tonic-gate 			 */
72930Sstevel@tonic-gate 			if (PM_IS_CFB(dip)) {
72940Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
72950Sstevel@tonic-gate 				cfb_dip = dip;
72960Sstevel@tonic-gate 				PMD(PMD_CFB, ("%s: %s@%s(%s#%d) setting "
72970Sstevel@tonic-gate 				    "cfb_dip\n", pmf, PM_DEVICE(dip)))
72980Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
72990Sstevel@tonic-gate 			}
73000Sstevel@tonic-gate 			DEVI(dip)->devi_pm_noinvolpm = ip->ni_noinvolpm;
73010Sstevel@tonic-gate 			ASSERT((DEVI(dip)->devi_pm_flags &
73020Sstevel@tonic-gate 			    (PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
73030Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_noinvolpm);
73040Sstevel@tonic-gate 			DEVI(dip)->devi_pm_volpmd = ip->ni_volpmd;
73050Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: noinvol=%d, volpmd=%d, "
73060Sstevel@tonic-gate 			    "wasvolpmd=%d, flags=%x, path=%s\n", pmf,
73070Sstevel@tonic-gate 			    ip->ni_noinvolpm, ip->ni_volpmd,
73080Sstevel@tonic-gate 			    ip->ni_wasvolpmd, ip->ni_flags, ip->ni_path))
73090Sstevel@tonic-gate 			/*
73100Sstevel@tonic-gate 			 * free the entry in hopes the list will now be empty
73110Sstevel@tonic-gate 			 * and we won't have to search it any more until the
73120Sstevel@tonic-gate 			 * device detaches
73130Sstevel@tonic-gate 			 */
73140Sstevel@tonic-gate 			if (pp) {
73150Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: free %s, prev %s\n",
73160Sstevel@tonic-gate 				    pmf, ip->ni_path, pp->ni_path))
73170Sstevel@tonic-gate 				pp->ni_next = ip->ni_next;
73180Sstevel@tonic-gate 			} else {
73190Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: free %s head\n",
73200Sstevel@tonic-gate 				    pmf, ip->ni_path))
73210Sstevel@tonic-gate 				ASSERT(pm_noinvol_head == ip);
73220Sstevel@tonic-gate 				pm_noinvol_head = ip->ni_next;
73230Sstevel@tonic-gate 			}
73240Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
73250Sstevel@tonic-gate 			wasvolpmd = ip->ni_wasvolpmd;
73260Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
73270Sstevel@tonic-gate 			kmem_free(ip->ni_path, ip->ni_size);
73280Sstevel@tonic-gate 			kmem_free(ip, sizeof (*ip));
73290Sstevel@tonic-gate 			/*
73300Sstevel@tonic-gate 			 * Now walk up the tree decrementing devi_pm_noinvolpm
73310Sstevel@tonic-gate 			 * (and volpmd if appropriate)
73320Sstevel@tonic-gate 			 */
73330Sstevel@tonic-gate 			(void) pm_noinvol_update(PM_BP_NOINVOL_ATTACH, 0,
73340Sstevel@tonic-gate 			    wasvolpmd, pathbuf, dip);
73350Sstevel@tonic-gate #ifdef DEBUG
73360Sstevel@tonic-gate 			if (pm_debug & PMD_NOINVOL)
73370Sstevel@tonic-gate 				pr_noinvol("noinvol_specd exit");
73380Sstevel@tonic-gate #endif
73390Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
73400Sstevel@tonic-gate 			return;
73410Sstevel@tonic-gate 		}
73420Sstevel@tonic-gate 	}
73430Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
73440Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
73450Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
73460Sstevel@tonic-gate }
73470Sstevel@tonic-gate 
73480Sstevel@tonic-gate int
73490Sstevel@tonic-gate pm_all_components_off(dev_info_t *dip)
73500Sstevel@tonic-gate {
73510Sstevel@tonic-gate 	int i;
73520Sstevel@tonic-gate 	pm_component_t *cp;
73530Sstevel@tonic-gate 
73540Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
73550Sstevel@tonic-gate 		cp = PM_CP(dip, i);
73560Sstevel@tonic-gate 		if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN ||
73570Sstevel@tonic-gate 		    cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr])
73580Sstevel@tonic-gate 			return (0);
73590Sstevel@tonic-gate 	}
73600Sstevel@tonic-gate 	return (1);	/* all off */
73610Sstevel@tonic-gate }
73620Sstevel@tonic-gate 
73630Sstevel@tonic-gate /*
73640Sstevel@tonic-gate  * Make sure that all "no involuntary power cycles" devices are attached.
73650Sstevel@tonic-gate  * Called before doing a cpr suspend to make sure the driver has a say about
73660Sstevel@tonic-gate  * the power cycle
73670Sstevel@tonic-gate  */
73680Sstevel@tonic-gate int
73690Sstevel@tonic-gate pm_reattach_noinvol(void)
73700Sstevel@tonic-gate {
73710Sstevel@tonic-gate 	PMD_FUNC(pmf, "reattach_noinvol")
73720Sstevel@tonic-gate 	pm_noinvol_t *ip;
73730Sstevel@tonic-gate 	char *path;
73740Sstevel@tonic-gate 	dev_info_t *dip;
73750Sstevel@tonic-gate 
73760Sstevel@tonic-gate 	/*
73770Sstevel@tonic-gate 	 * Prevent the modunload thread from unloading any modules until we
73780Sstevel@tonic-gate 	 * have completely stopped all kernel threads.
73790Sstevel@tonic-gate 	 */
73800Sstevel@tonic-gate 	modunload_disable();
73810Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
73820Sstevel@tonic-gate 		/*
73830Sstevel@tonic-gate 		 * Forget we'v ever seen any entry
73840Sstevel@tonic-gate 		 */
73850Sstevel@tonic-gate 		ip->ni_persistent = 0;
73860Sstevel@tonic-gate 	}
73870Sstevel@tonic-gate restart:
73880Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
73890Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
73900Sstevel@tonic-gate 		major_t maj;
73910Sstevel@tonic-gate 		maj = ip->ni_major;
73920Sstevel@tonic-gate 		path = ip->ni_path;
73930Sstevel@tonic-gate 		if (path != NULL && !(ip->ni_flags & PMC_DRIVER_REMOVED)) {
73940Sstevel@tonic-gate 			if (ip->ni_persistent) {
73950Sstevel@tonic-gate 				/*
73960Sstevel@tonic-gate 				 * If we weren't able to make this entry
73970Sstevel@tonic-gate 				 * go away, then we give up, as
73980Sstevel@tonic-gate 				 * holding/attaching the driver ought to have
73990Sstevel@tonic-gate 				 * resulted in this entry being deleted
74000Sstevel@tonic-gate 				 */
74010Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: can't reattach %s "
74020Sstevel@tonic-gate 				    "(%s|%d)\n", pmf, ip->ni_path,
74030Sstevel@tonic-gate 				    ddi_major_to_name(maj), (int)maj))
74040Sstevel@tonic-gate 				cmn_err(CE_WARN, "cpr: unable to reattach %s ",
74050Sstevel@tonic-gate 				    ip->ni_path);
74060Sstevel@tonic-gate 				modunload_enable();
74070Sstevel@tonic-gate 				rw_exit(&pm_noinvol_rwlock);
74080Sstevel@tonic-gate 				return (0);
74090Sstevel@tonic-gate 			}
74100Sstevel@tonic-gate 			ip->ni_persistent++;
74110Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
74120Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: holding %s\n", pmf, path))
74130Sstevel@tonic-gate 			dip = e_ddi_hold_devi_by_path(path, 0);
74140Sstevel@tonic-gate 			if (dip == NULL) {
74150Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: can't hold (%s|%d)\n",
74160Sstevel@tonic-gate 				    pmf, path, (int)maj))
74170Sstevel@tonic-gate 				cmn_err(CE_WARN, "cpr: unable to hold %s "
74180Sstevel@tonic-gate 				    "driver", path);
74190Sstevel@tonic-gate 				modunload_enable();
74200Sstevel@tonic-gate 				return (0);
74210Sstevel@tonic-gate 			} else {
74220Sstevel@tonic-gate 				PMD(PMD_DHR, ("%s: release %s\n", pmf, path))
74230Sstevel@tonic-gate 				/*
74240Sstevel@tonic-gate 				 * Since the modunload thread is stopped, we
74250Sstevel@tonic-gate 				 * don't have to keep the driver held, which
74260Sstevel@tonic-gate 				 * saves a ton of bookkeeping
74270Sstevel@tonic-gate 				 */
74280Sstevel@tonic-gate 				ddi_release_devi(dip);
74290Sstevel@tonic-gate 				goto restart;
74300Sstevel@tonic-gate 			}
74310Sstevel@tonic-gate 		} else {
74320Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: skip %s; unknown major\n",
74330Sstevel@tonic-gate 			    pmf, ip->ni_path))
74340Sstevel@tonic-gate 			continue;
74350Sstevel@tonic-gate 		}
74360Sstevel@tonic-gate 	}
74370Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
74380Sstevel@tonic-gate 	return (1);
74390Sstevel@tonic-gate }
74400Sstevel@tonic-gate 
74410Sstevel@tonic-gate void
74420Sstevel@tonic-gate pm_reattach_noinvol_fini(void)
74430Sstevel@tonic-gate {
74440Sstevel@tonic-gate 	modunload_enable();
74450Sstevel@tonic-gate }
74460Sstevel@tonic-gate 
74470Sstevel@tonic-gate /*
74480Sstevel@tonic-gate  * Display pm support code
74490Sstevel@tonic-gate  */
74500Sstevel@tonic-gate 
74510Sstevel@tonic-gate 
74520Sstevel@tonic-gate /*
74530Sstevel@tonic-gate  * console frame-buffer power-mgmt gets enabled when debugging
74540Sstevel@tonic-gate  * services are not present or console fbpm override is set
74550Sstevel@tonic-gate  */
74560Sstevel@tonic-gate void
74570Sstevel@tonic-gate pm_cfb_setup(const char *stdout_path)
74580Sstevel@tonic-gate {
74590Sstevel@tonic-gate 	PMD_FUNC(pmf, "cfb_setup")
74600Sstevel@tonic-gate 	extern int obpdebug;
74610Sstevel@tonic-gate 	char *devname;
74620Sstevel@tonic-gate 	dev_info_t *dip;
74630Sstevel@tonic-gate 	int devname_len;
74640Sstevel@tonic-gate 	extern dev_info_t *fbdip;
74650Sstevel@tonic-gate 
74660Sstevel@tonic-gate 	/*
74670Sstevel@tonic-gate 	 * By virtue of this function being called (from consconfig),
74680Sstevel@tonic-gate 	 * we know stdout is a framebuffer.
74690Sstevel@tonic-gate 	 */
74700Sstevel@tonic-gate 	stdout_is_framebuffer = 1;
74710Sstevel@tonic-gate 
74720Sstevel@tonic-gate 	if (obpdebug || (boothowto & RB_DEBUG)) {
74730Sstevel@tonic-gate 		if (pm_cfb_override == 0) {
74740Sstevel@tonic-gate 			/*
74750Sstevel@tonic-gate 			 * Console is frame buffer, but we want to suppress
74760Sstevel@tonic-gate 			 * pm on it because of debugging setup
74770Sstevel@tonic-gate 			 */
74780Sstevel@tonic-gate 			pm_cfb_enabled = 0;
74790Sstevel@tonic-gate 			cmn_err(CE_NOTE, "Kernel debugger present: disabling "
74800Sstevel@tonic-gate 			    "console power management.");
74810Sstevel@tonic-gate 			/*
74820Sstevel@tonic-gate 			 * however, we still need to know which is the console
74830Sstevel@tonic-gate 			 * fb in order to suppress pm on it
74840Sstevel@tonic-gate 			 */
74850Sstevel@tonic-gate 		} else {
74860Sstevel@tonic-gate 			cmn_err(CE_WARN, "Kernel debugger present: see "
74870Sstevel@tonic-gate 			    "kmdb(1M) for interaction with power management.");
74880Sstevel@tonic-gate 		}
74890Sstevel@tonic-gate 	}
74900Sstevel@tonic-gate #ifdef DEBUG
74910Sstevel@tonic-gate 	/*
74920Sstevel@tonic-gate 	 * IF console is fb and is power managed, don't do prom_printfs from
74930Sstevel@tonic-gate 	 * pm debug macro
74940Sstevel@tonic-gate 	 */
74950Sstevel@tonic-gate 	if (pm_cfb_enabled) {
74960Sstevel@tonic-gate 		if (pm_debug)
74970Sstevel@tonic-gate 			prom_printf("pm debug output will be to log only\n");
74980Sstevel@tonic-gate 		pm_divertdebug++;
74990Sstevel@tonic-gate 	}
75000Sstevel@tonic-gate #endif
75010Sstevel@tonic-gate 	devname = i_ddi_strdup((char *)stdout_path, KM_SLEEP);
75020Sstevel@tonic-gate 	devname_len = strlen(devname) + 1;
75030Sstevel@tonic-gate 	PMD(PMD_CFB, ("%s: stripped %s\n", pmf, devname))
75040Sstevel@tonic-gate 	/* if the driver is attached */
75050Sstevel@tonic-gate 	if ((dip = fbdip) != NULL) {
75060Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: attached: %s@%s(%s#%d)\n", pmf,
75070Sstevel@tonic-gate 		    PM_DEVICE(dip)))
75080Sstevel@tonic-gate 		/*
75090Sstevel@tonic-gate 		 * We set up here as if the driver were power manageable in case
75100Sstevel@tonic-gate 		 * we get a later attach of a pm'able driver (which would result
75110Sstevel@tonic-gate 		 * in a panic later)
75120Sstevel@tonic-gate 		 */
75130Sstevel@tonic-gate 		cfb_dip = dip;
75140Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags |= (PMC_CONSOLE_FB | PMC_NO_INVOL);
75150Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: cfb_dip -> %s@%s(%s#%d)\n", pmf,
75160Sstevel@tonic-gate 		    PM_DEVICE(dip)))
75170Sstevel@tonic-gate #ifdef DEBUG
75180Sstevel@tonic-gate 		if (!(PM_GET_PM_INFO(dip) != NULL && PM_NUMCMPTS(dip))) {
75190Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d) not power-managed\n",
75200Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
75210Sstevel@tonic-gate 		}
75220Sstevel@tonic-gate #endif
75230Sstevel@tonic-gate 	} else {
75240Sstevel@tonic-gate 		char *ep;
75250Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: pntd %s failed\n", pmf, devname))
75260Sstevel@tonic-gate 		pm_record_invol_path(devname,
75270Sstevel@tonic-gate 		    (PMC_CONSOLE_FB | PMC_NO_INVOL), 1, 0, 0,
75280Sstevel@tonic-gate 		    (major_t)-1);
75290Sstevel@tonic-gate 		for (ep = strrchr(devname, '/'); ep != devname;
75300Sstevel@tonic-gate 		    ep = strrchr(devname, '/')) {
75310Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: devname %s\n", pmf, devname))
75320Sstevel@tonic-gate 			*ep = '\0';
75330Sstevel@tonic-gate 			dip = pm_name_to_dip(devname, 0);
75340Sstevel@tonic-gate 			if (dip != NULL) {
75350Sstevel@tonic-gate 				/*
75360Sstevel@tonic-gate 				 * Walk up the tree incrementing
75370Sstevel@tonic-gate 				 * devi_pm_noinvolpm
75380Sstevel@tonic-gate 				 */
75390Sstevel@tonic-gate 				(void) pm_noinvol_update(PM_BP_NOINVOL_CFB,
75400Sstevel@tonic-gate 				    0, 0, devname, dip);
75410Sstevel@tonic-gate 				break;
75420Sstevel@tonic-gate 			} else {
75430Sstevel@tonic-gate 				pm_record_invol_path(devname,
75440Sstevel@tonic-gate 				    PMC_NO_INVOL, 1, 0, 0, (major_t)-1);
75450Sstevel@tonic-gate 			}
75460Sstevel@tonic-gate 		}
75470Sstevel@tonic-gate 	}
75480Sstevel@tonic-gate 	kmem_free(devname, devname_len);
75490Sstevel@tonic-gate }
75500Sstevel@tonic-gate 
75510Sstevel@tonic-gate void
75520Sstevel@tonic-gate pm_cfb_rele(void)
75530Sstevel@tonic-gate {
75540Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
75550Sstevel@tonic-gate 	/*
75560Sstevel@tonic-gate 	 * this call isn't using the console any  more, it is ok to take it
75570Sstevel@tonic-gate 	 * down if the count goes to 0
75580Sstevel@tonic-gate 	 */
75590Sstevel@tonic-gate 	cfb_inuse--;
75600Sstevel@tonic-gate 	mutex_exit(&pm_cfb_lock);
75610Sstevel@tonic-gate }
75620Sstevel@tonic-gate 
75630Sstevel@tonic-gate /*
75640Sstevel@tonic-gate  * software interrupt handler for fbpm; this function exists because we can't
75650Sstevel@tonic-gate  * bring up the frame buffer power from above lock level.  So if we need to,
75660Sstevel@tonic-gate  * we instead schedule a softint that runs this routine and takes us into
75670Sstevel@tonic-gate  * debug_enter (a bit delayed from the original request, but avoiding a panic).
75680Sstevel@tonic-gate  */
75690Sstevel@tonic-gate static uint_t
75700Sstevel@tonic-gate pm_cfb_softint(caddr_t int_handler_arg)
75710Sstevel@tonic-gate {
75720Sstevel@tonic-gate 	_NOTE(ARGUNUSED(int_handler_arg))
75730Sstevel@tonic-gate 	int rval = DDI_INTR_UNCLAIMED;
75740Sstevel@tonic-gate 
75750Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
75760Sstevel@tonic-gate 	if (pm_soft_pending) {
75770Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
75780Sstevel@tonic-gate 		debug_enter((char *)NULL);
75790Sstevel@tonic-gate 		/* acquired in debug_enter before calling pm_cfb_trigger */
75800Sstevel@tonic-gate 		pm_cfb_rele();
75810Sstevel@tonic-gate 		mutex_enter(&pm_cfb_lock);
75820Sstevel@tonic-gate 		pm_soft_pending = 0;
75830Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
75840Sstevel@tonic-gate 		rval = DDI_INTR_CLAIMED;
75850Sstevel@tonic-gate 	} else
75860Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
75870Sstevel@tonic-gate 
75880Sstevel@tonic-gate 	return (rval);
75890Sstevel@tonic-gate }
75900Sstevel@tonic-gate 
75910Sstevel@tonic-gate void
75920Sstevel@tonic-gate pm_cfb_setup_intr(void)
75930Sstevel@tonic-gate {
75940Sstevel@tonic-gate 	PMD_FUNC(pmf, "cfb_setup_intr")
75950Sstevel@tonic-gate 	extern void prom_set_outfuncs(void (*)(void), void (*)(void));
75960Sstevel@tonic-gate 	void pm_cfb_check_and_powerup(void);
75970Sstevel@tonic-gate 
75980Sstevel@tonic-gate 	if (!stdout_is_framebuffer) {
75990Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: console not fb\n", pmf))
76000Sstevel@tonic-gate 		return;
76010Sstevel@tonic-gate 	}
76020Sstevel@tonic-gate 	mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
76030Sstevel@tonic-gate #ifdef DEBUG
76040Sstevel@tonic-gate 	mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
76050Sstevel@tonic-gate #endif
76060Sstevel@tonic-gate 	/*
76070Sstevel@tonic-gate 	 * setup software interrupt handler
76080Sstevel@tonic-gate 	 */
76090Sstevel@tonic-gate 	if (ddi_add_softintr(ddi_root_node(), DDI_SOFTINT_HIGH, &pm_soft_id,
76100Sstevel@tonic-gate 	    NULL, NULL, pm_cfb_softint, NULL) != DDI_SUCCESS)
76110Sstevel@tonic-gate 		panic("pm: unable to register soft intr.");
76120Sstevel@tonic-gate 
76130Sstevel@tonic-gate 	prom_set_outfuncs(pm_cfb_check_and_powerup, pm_cfb_rele);
76140Sstevel@tonic-gate }
76150Sstevel@tonic-gate 
76160Sstevel@tonic-gate /*
76170Sstevel@tonic-gate  * Checks to see if it is safe to write to the console wrt power management
76180Sstevel@tonic-gate  * (i.e. if the console is a framebuffer, then it must be at full power)
76190Sstevel@tonic-gate  * returns 1 when power is off (power-up is needed)
76200Sstevel@tonic-gate  * returns 0 when power is on (power-up not needed)
76210Sstevel@tonic-gate  */
76220Sstevel@tonic-gate int
76230Sstevel@tonic-gate pm_cfb_check_and_hold(void)
76240Sstevel@tonic-gate {
76250Sstevel@tonic-gate 	/*
76260Sstevel@tonic-gate 	 * cfb_dip is set iff console is a power manageable frame buffer
76270Sstevel@tonic-gate 	 * device
76280Sstevel@tonic-gate 	 */
76290Sstevel@tonic-gate 	extern int modrootloaded;
76300Sstevel@tonic-gate 
76310Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
76320Sstevel@tonic-gate 	cfb_inuse++;
76330Sstevel@tonic-gate 	ASSERT(cfb_inuse);	/* wrap? */
76340Sstevel@tonic-gate 	if (modrootloaded && cfb_dip) {
76350Sstevel@tonic-gate 		/*
76360Sstevel@tonic-gate 		 * don't power down the frame buffer, the prom is using it
76370Sstevel@tonic-gate 		 */
76380Sstevel@tonic-gate 		if (pm_cfb_comps_off) {
76390Sstevel@tonic-gate 			mutex_exit(&pm_cfb_lock);
76400Sstevel@tonic-gate 			return (1);
76410Sstevel@tonic-gate 		}
76420Sstevel@tonic-gate 	}
76430Sstevel@tonic-gate 	mutex_exit(&pm_cfb_lock);
76440Sstevel@tonic-gate 	return (0);
76450Sstevel@tonic-gate }
76460Sstevel@tonic-gate 
76470Sstevel@tonic-gate /*
76480Sstevel@tonic-gate  * turn on cfb power (which is known to be off).
76490Sstevel@tonic-gate  * Must be called below lock level!
76500Sstevel@tonic-gate  */
76510Sstevel@tonic-gate void
76520Sstevel@tonic-gate pm_cfb_powerup(void)
76530Sstevel@tonic-gate {
76540Sstevel@tonic-gate 	pm_info_t *info;
76550Sstevel@tonic-gate 	int norm;
76560Sstevel@tonic-gate 	int ccount, ci;
76570Sstevel@tonic-gate 	int unused;
76580Sstevel@tonic-gate #ifdef DEBUG
76590Sstevel@tonic-gate 	/*
76600Sstevel@tonic-gate 	 * Can't reenter prom_prekern, so suppress pm debug messages
76610Sstevel@tonic-gate 	 * (still go to circular buffer).
76620Sstevel@tonic-gate 	 */
76630Sstevel@tonic-gate 	mutex_enter(&pm_debug_lock);
76640Sstevel@tonic-gate 	pm_divertdebug++;
76650Sstevel@tonic-gate 	mutex_exit(&pm_debug_lock);
76660Sstevel@tonic-gate #endif
76670Sstevel@tonic-gate 	info = PM_GET_PM_INFO(cfb_dip);
76680Sstevel@tonic-gate 	ASSERT(info);
76690Sstevel@tonic-gate 
76700Sstevel@tonic-gate 	ccount = PM_NUMCMPTS(cfb_dip);
76710Sstevel@tonic-gate 	for (ci = 0; ci < ccount; ci++) {
76720Sstevel@tonic-gate 		norm = pm_get_normal_power(cfb_dip, ci);
76730Sstevel@tonic-gate 		(void) pm_set_power(cfb_dip, ci, norm, PM_LEVEL_UPONLY,
76740Sstevel@tonic-gate 		    PM_CANBLOCK_BYPASS, 0, &unused);
76750Sstevel@tonic-gate 	}
76760Sstevel@tonic-gate #ifdef DEBUG
76770Sstevel@tonic-gate 	mutex_enter(&pm_debug_lock);
76780Sstevel@tonic-gate 	pm_divertdebug--;
76790Sstevel@tonic-gate 	mutex_exit(&pm_debug_lock);
76800Sstevel@tonic-gate #endif
76810Sstevel@tonic-gate }
76820Sstevel@tonic-gate 
76830Sstevel@tonic-gate /*
76840Sstevel@tonic-gate  * Check if the console framebuffer is powered up.  If not power it up.
76850Sstevel@tonic-gate  * Note: Calling pm_cfb_check_and_hold has put a hold on the power state which
76860Sstevel@tonic-gate  * must be released by calling pm_cfb_rele when the console fb operation
76870Sstevel@tonic-gate  * is completed.
76880Sstevel@tonic-gate  */
76890Sstevel@tonic-gate void
76900Sstevel@tonic-gate pm_cfb_check_and_powerup(void)
76910Sstevel@tonic-gate {
76920Sstevel@tonic-gate 	if (pm_cfb_check_and_hold())
76930Sstevel@tonic-gate 		pm_cfb_powerup();
76940Sstevel@tonic-gate }
76950Sstevel@tonic-gate 
76960Sstevel@tonic-gate /*
76970Sstevel@tonic-gate  * Trigger a low level interrupt to power up console frame buffer.
76980Sstevel@tonic-gate  */
76990Sstevel@tonic-gate void
77000Sstevel@tonic-gate pm_cfb_trigger(void)
77010Sstevel@tonic-gate {
77020Sstevel@tonic-gate 	if (cfb_dip == NULL)
77030Sstevel@tonic-gate 		return;
77040Sstevel@tonic-gate 
77050Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
77060Sstevel@tonic-gate 	/*
77070Sstevel@tonic-gate 	 * If machine appears to be hung, pulling the keyboard connector of
77080Sstevel@tonic-gate 	 * the console will cause a high level interrupt and go to debug_enter.
77090Sstevel@tonic-gate 	 * But, if the fb is powered down, this routine will be called to bring
77100Sstevel@tonic-gate 	 * it up (by generating a softint to do the work).  If soft interrupts
77110Sstevel@tonic-gate 	 * are not running, and the keyboard connector is pulled again, the
77120Sstevel@tonic-gate 	 * following code detects this condition and calls panic which allows
77130Sstevel@tonic-gate 	 * the fb to be brought up from high level.
77140Sstevel@tonic-gate 	 *
77150Sstevel@tonic-gate 	 * If two nearly simultaneous calls to debug_enter occur (both from
77160Sstevel@tonic-gate 	 * high level) the code described above will cause a panic.
77170Sstevel@tonic-gate 	 */
77180Sstevel@tonic-gate 	if (lbolt <= pm_soft_pending) {
77190Sstevel@tonic-gate 		panicstr = "pm_cfb_trigger: lbolt not advancing";
77200Sstevel@tonic-gate 		panic(panicstr);	/* does a power up at any intr level */
77210Sstevel@tonic-gate 		/* NOTREACHED */
77220Sstevel@tonic-gate 	}
77230Sstevel@tonic-gate 	pm_soft_pending = lbolt;
77240Sstevel@tonic-gate 	mutex_exit(&pm_cfb_lock);
77250Sstevel@tonic-gate 	ddi_trigger_softintr(pm_soft_id);
77260Sstevel@tonic-gate }
77270Sstevel@tonic-gate 
77280Sstevel@tonic-gate major_t
77290Sstevel@tonic-gate pm_path_to_major(char *path)
77300Sstevel@tonic-gate {
77310Sstevel@tonic-gate 	PMD_FUNC(pmf, "path_to_major")
77320Sstevel@tonic-gate 	char *np, *ap, *bp;
77330Sstevel@tonic-gate 	major_t ret;
77340Sstevel@tonic-gate 	size_t len;
77350Sstevel@tonic-gate 	static major_t i_path_to_major(char *, char *);
77360Sstevel@tonic-gate 
77370Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s\n", pmf, path))
77380Sstevel@tonic-gate 
77390Sstevel@tonic-gate 	np = strrchr(path, '/');
77400Sstevel@tonic-gate 	if (np != NULL)
77410Sstevel@tonic-gate 		np++;
77420Sstevel@tonic-gate 	else
77430Sstevel@tonic-gate 		np = path;
77440Sstevel@tonic-gate 	len = strlen(np) + 1;
77450Sstevel@tonic-gate 	bp = kmem_alloc(len, KM_SLEEP);
77460Sstevel@tonic-gate 	(void) strcpy(bp, np);
77470Sstevel@tonic-gate 	if ((ap = strchr(bp, '@')) != NULL) {
77480Sstevel@tonic-gate 		*ap = '\0';
77490Sstevel@tonic-gate 	}
77500Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %d\n", pmf, ddi_name_to_major(np)))
77510Sstevel@tonic-gate 	ret = i_path_to_major(path, np);
77520Sstevel@tonic-gate 	kmem_free(bp, len);
77530Sstevel@tonic-gate 	return (ret);
77540Sstevel@tonic-gate }
77550Sstevel@tonic-gate 
77560Sstevel@tonic-gate #ifdef DEBUG
77570Sstevel@tonic-gate 
77580Sstevel@tonic-gate char *pm_msgp;
77590Sstevel@tonic-gate char *pm_bufend;
77600Sstevel@tonic-gate char *pm_msgbuf = NULL;
77610Sstevel@tonic-gate int   pm_logpages = 2;
77620Sstevel@tonic-gate 
77630Sstevel@tonic-gate #define	PMLOGPGS	pm_logpages
77640Sstevel@tonic-gate 
77650Sstevel@tonic-gate /*PRINTFLIKE1*/
77660Sstevel@tonic-gate void
77670Sstevel@tonic-gate pm_log(const char *fmt, ...)
77680Sstevel@tonic-gate {
77690Sstevel@tonic-gate 	va_list adx;
77700Sstevel@tonic-gate 	size_t size;
77710Sstevel@tonic-gate 
77720Sstevel@tonic-gate 	mutex_enter(&pm_debug_lock);
77730Sstevel@tonic-gate 	if (pm_msgbuf == NULL) {
77740Sstevel@tonic-gate 		pm_msgbuf = kmem_zalloc(mmu_ptob(PMLOGPGS), KM_SLEEP);
77750Sstevel@tonic-gate 		pm_bufend = pm_msgbuf + mmu_ptob(PMLOGPGS) - 1;
77760Sstevel@tonic-gate 		pm_msgp = pm_msgbuf;
77770Sstevel@tonic-gate 	}
77780Sstevel@tonic-gate 	va_start(adx, fmt);
77790Sstevel@tonic-gate 	size = vsnprintf(NULL, 0, fmt, adx) + 1;
77800Sstevel@tonic-gate 	va_end(adx);
77810Sstevel@tonic-gate 	va_start(adx, fmt);
77820Sstevel@tonic-gate 	if (size > (pm_bufend - pm_msgp)) {		/* wraps */
77830Sstevel@tonic-gate 		bzero(pm_msgp, pm_bufend - pm_msgp);
77840Sstevel@tonic-gate 		(void) vsnprintf(pm_msgbuf, size, fmt, adx);
77850Sstevel@tonic-gate 		if (!pm_divertdebug)
77860Sstevel@tonic-gate 			prom_printf("%s", pm_msgp);
77870Sstevel@tonic-gate 		pm_msgp = pm_msgbuf + size;
77880Sstevel@tonic-gate 	} else {
77890Sstevel@tonic-gate 		(void) vsnprintf(pm_msgp, size, fmt, adx);
77900Sstevel@tonic-gate 		if (!pm_divertdebug)
77910Sstevel@tonic-gate 			prom_printf("%s", pm_msgp);
77920Sstevel@tonic-gate 		pm_msgp += size;
77930Sstevel@tonic-gate 	}
77940Sstevel@tonic-gate 	va_end(adx);
77950Sstevel@tonic-gate 	mutex_exit(&pm_debug_lock);
77960Sstevel@tonic-gate }
77970Sstevel@tonic-gate #endif	/* DEBUG */
77980Sstevel@tonic-gate 
77990Sstevel@tonic-gate /*
78000Sstevel@tonic-gate  * We want to save the state of any directly pm'd devices over the suspend/
78010Sstevel@tonic-gate  * resume process so that we can put them back the way the controlling
78020Sstevel@tonic-gate  * process left them.
78030Sstevel@tonic-gate  */
78040Sstevel@tonic-gate void
78050Sstevel@tonic-gate pm_save_direct_levels(void)
78060Sstevel@tonic-gate {
78070Sstevel@tonic-gate 	pm_processes_stopped = 1;
78080Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_save_direct_lvl_walk, 0);
78090Sstevel@tonic-gate }
78100Sstevel@tonic-gate 
78110Sstevel@tonic-gate static int
78120Sstevel@tonic-gate pm_save_direct_lvl_walk(dev_info_t *dip, void *arg)
78130Sstevel@tonic-gate {
78140Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
78150Sstevel@tonic-gate 	int i;
78160Sstevel@tonic-gate 	int *ip;
78170Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
78180Sstevel@tonic-gate 
78190Sstevel@tonic-gate 	if (!info)
78200Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
78210Sstevel@tonic-gate 
78220Sstevel@tonic-gate 	if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) {
78230Sstevel@tonic-gate 		if (PM_NUMCMPTS(dip) > 2) {
78240Sstevel@tonic-gate 			info->pmi_lp = kmem_alloc(PM_NUMCMPTS(dip) *
78250Sstevel@tonic-gate 			    sizeof (int), KM_SLEEP);
78260Sstevel@tonic-gate 			ip = info->pmi_lp;
78270Sstevel@tonic-gate 		} else {
78280Sstevel@tonic-gate 			ip = info->pmi_levels;
78290Sstevel@tonic-gate 		}
78300Sstevel@tonic-gate 		/* autopm and processes are stopped, ok not to lock power */
78310Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
78320Sstevel@tonic-gate 			*ip++ = PM_CURPOWER(dip, i);
78330Sstevel@tonic-gate 		/*
78340Sstevel@tonic-gate 		 * There is a small window between stopping the
78350Sstevel@tonic-gate 		 * processes and setting pm_processes_stopped where
78360Sstevel@tonic-gate 		 * a driver could get hung up in a pm_raise_power()
78370Sstevel@tonic-gate 		 * call.  Free any such driver now.
78380Sstevel@tonic-gate 		 */
78390Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
78400Sstevel@tonic-gate 	}
78410Sstevel@tonic-gate 
78420Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
78430Sstevel@tonic-gate }
78440Sstevel@tonic-gate 
78450Sstevel@tonic-gate void
78460Sstevel@tonic-gate pm_restore_direct_levels(void)
78470Sstevel@tonic-gate {
78480Sstevel@tonic-gate 	/*
78490Sstevel@tonic-gate 	 * If cpr didn't call pm_save_direct_levels, (because stopping user
78500Sstevel@tonic-gate 	 * threads failed) then we don't want to try to restore them
78510Sstevel@tonic-gate 	 */
78520Sstevel@tonic-gate 	if (!pm_processes_stopped)
78530Sstevel@tonic-gate 		return;
78540Sstevel@tonic-gate 
78550Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_restore_direct_lvl_walk, 0);
78560Sstevel@tonic-gate 	pm_processes_stopped = 0;
78570Sstevel@tonic-gate }
78580Sstevel@tonic-gate 
78590Sstevel@tonic-gate static int
78600Sstevel@tonic-gate pm_restore_direct_lvl_walk(dev_info_t *dip, void *arg)
78610Sstevel@tonic-gate {
78620Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
78630Sstevel@tonic-gate 	PMD_FUNC(pmf, "restore_direct_lvl_walk")
78640Sstevel@tonic-gate 	int i, nc, result;
78650Sstevel@tonic-gate 	int *ip;
78660Sstevel@tonic-gate 
78670Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
78680Sstevel@tonic-gate 	if (!info)
78690Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
78700Sstevel@tonic-gate 
78710Sstevel@tonic-gate 	if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) {
78720Sstevel@tonic-gate 		if ((nc = PM_NUMCMPTS(dip)) > 2) {
78730Sstevel@tonic-gate 			ip = &info->pmi_lp[nc - 1];
78740Sstevel@tonic-gate 		} else {
78750Sstevel@tonic-gate 			ip = &info->pmi_levels[nc - 1];
78760Sstevel@tonic-gate 		}
78770Sstevel@tonic-gate 		/*
78780Sstevel@tonic-gate 		 * Because fb drivers fail attempts to turn off the
78790Sstevel@tonic-gate 		 * fb when the monitor is on, but treat a request to
78800Sstevel@tonic-gate 		 * turn on the monitor as a request to turn on the
78810Sstevel@tonic-gate 		 * fb too, we process components in descending order
78820Sstevel@tonic-gate 		 * Because autopm is disabled and processes aren't
78830Sstevel@tonic-gate 		 * running, it is ok to examine current power outside
78840Sstevel@tonic-gate 		 * of the power lock
78850Sstevel@tonic-gate 		 */
78860Sstevel@tonic-gate 		for (i = nc - 1; i >= 0; i--, ip--) {
78870Sstevel@tonic-gate 			if (PM_CURPOWER(dip, i) == *ip)
78880Sstevel@tonic-gate 				continue;
78890Sstevel@tonic-gate 			if (pm_set_power(dip, i, *ip, PM_LEVEL_EXACT,
78900Sstevel@tonic-gate 			    PM_CANBLOCK_BYPASS, 0, &result) !=
78910Sstevel@tonic-gate 				DDI_SUCCESS) {
78920Sstevel@tonic-gate 				cmn_err(CE_WARN, "cpr: unable "
78930Sstevel@tonic-gate 				    "to restore power level of "
78940Sstevel@tonic-gate 				    "component %d of directly "
78950Sstevel@tonic-gate 				    "power manged device %s@%s"
78960Sstevel@tonic-gate 				    " to %d",
78970Sstevel@tonic-gate 				    i, PM_NAME(dip),
78980Sstevel@tonic-gate 				    PM_ADDR(dip), *ip);
78990Sstevel@tonic-gate 				PMD(PMD_FAIL, ("%s: failed to restore "
79000Sstevel@tonic-gate 				    "%s@%s(%s#%d)[%d] exact(%d)->%d, "
79010Sstevel@tonic-gate 				    "errno %d\n", pmf, PM_DEVICE(dip), i,
79020Sstevel@tonic-gate 				    PM_CURPOWER(dip, i), *ip, result))
79030Sstevel@tonic-gate 			}
79040Sstevel@tonic-gate 		}
79050Sstevel@tonic-gate 		if (nc > 2) {
79060Sstevel@tonic-gate 			kmem_free(info->pmi_lp, nc * sizeof (int));
79070Sstevel@tonic-gate 			info->pmi_lp = NULL;
79080Sstevel@tonic-gate 		}
79090Sstevel@tonic-gate 	}
79100Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
79110Sstevel@tonic-gate }
79120Sstevel@tonic-gate 
79130Sstevel@tonic-gate /*
79140Sstevel@tonic-gate  * Stolen from the bootdev module
79150Sstevel@tonic-gate  * attempt to convert a path to a major number
79160Sstevel@tonic-gate  */
79170Sstevel@tonic-gate static major_t
79180Sstevel@tonic-gate i_path_to_major(char *path, char *leaf_name)
79190Sstevel@tonic-gate {
79200Sstevel@tonic-gate 	extern major_t path_to_major(char *pathname);
79210Sstevel@tonic-gate 	major_t maj;
79220Sstevel@tonic-gate 
79230Sstevel@tonic-gate 	if ((maj = path_to_major(path)) == (major_t)-1) {
79240Sstevel@tonic-gate 		maj = ddi_name_to_major(leaf_name);
79250Sstevel@tonic-gate 	}
79260Sstevel@tonic-gate 
79270Sstevel@tonic-gate 	return (maj);
79280Sstevel@tonic-gate }
79290Sstevel@tonic-gate 
79300Sstevel@tonic-gate /*
79310Sstevel@tonic-gate  * When user calls rem_drv, we need to forget no-involuntary-power-cycles state
79320Sstevel@tonic-gate  * An entry in the list means that the device is detached, so we need to
79330Sstevel@tonic-gate  * adjust its ancestors as if they had just seen this attach, and any detached
79340Sstevel@tonic-gate  * ancestors need to have their list entries adjusted.
79350Sstevel@tonic-gate  */
79360Sstevel@tonic-gate void
79370Sstevel@tonic-gate pm_driver_removed(major_t major)
79380Sstevel@tonic-gate {
79390Sstevel@tonic-gate 	static void i_pm_driver_removed(major_t major);
79400Sstevel@tonic-gate 
79410Sstevel@tonic-gate 	/*
79420Sstevel@tonic-gate 	 * Serialize removal of drivers. This is to keep ancestors of
79430Sstevel@tonic-gate 	 * a node that is being deleted from getting deleted and added back
79440Sstevel@tonic-gate 	 * with different counters.
79450Sstevel@tonic-gate 	 */
79460Sstevel@tonic-gate 	mutex_enter(&pm_remdrv_lock);
79470Sstevel@tonic-gate 	i_pm_driver_removed(major);
79480Sstevel@tonic-gate 	mutex_exit(&pm_remdrv_lock);
79490Sstevel@tonic-gate }
79500Sstevel@tonic-gate 
79510Sstevel@tonic-gate /*
79520Sstevel@tonic-gate  * This routine is called recursively by pm_noinvol_process_ancestors()
79530Sstevel@tonic-gate  */
79540Sstevel@tonic-gate static void
79550Sstevel@tonic-gate i_pm_driver_removed(major_t major)
79560Sstevel@tonic-gate {
79570Sstevel@tonic-gate 	PMD_FUNC(pmf, "driver_removed")
79580Sstevel@tonic-gate 	static void adjust_ancestors(char *, int);
79590Sstevel@tonic-gate 	static int pm_is_noinvol_ancestor(pm_noinvol_t *);
79600Sstevel@tonic-gate 	static void pm_noinvol_process_ancestors(char *);
79610Sstevel@tonic-gate 	pm_noinvol_t *ip, *pp = NULL;
79620Sstevel@tonic-gate 	int wasvolpmd;
79630Sstevel@tonic-gate 	ASSERT(major != (major_t)-1);
79640Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s\n", pmf, ddi_major_to_name(major)))
79650Sstevel@tonic-gate again:
79660Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
79670Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
79680Sstevel@tonic-gate 		if (major != ip->ni_major)
79690Sstevel@tonic-gate 			continue;
79700Sstevel@tonic-gate 		/*
79710Sstevel@tonic-gate 		 * If it is an ancestor of no-invol node, which is
79720Sstevel@tonic-gate 		 * not removed, skip it. This is to cover the case of
79730Sstevel@tonic-gate 		 * ancestor removed without removing its descendants.
79740Sstevel@tonic-gate 		 */
79750Sstevel@tonic-gate 		if (pm_is_noinvol_ancestor(ip)) {
79760Sstevel@tonic-gate 			ip->ni_flags |= PMC_DRIVER_REMOVED;
79770Sstevel@tonic-gate 			continue;
79780Sstevel@tonic-gate 		}
79790Sstevel@tonic-gate 		wasvolpmd = ip->ni_wasvolpmd;
79800Sstevel@tonic-gate 		/*
79810Sstevel@tonic-gate 		 * remove the entry from the list
79820Sstevel@tonic-gate 		 */
79830Sstevel@tonic-gate 		if (pp) {
79840Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: freeing %s, prev is %s\n",
79850Sstevel@tonic-gate 			    pmf, ip->ni_path, pp->ni_path))
79860Sstevel@tonic-gate 			pp->ni_next = ip->ni_next;
79870Sstevel@tonic-gate 		} else {
79880Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: free %s head\n", pmf,
79890Sstevel@tonic-gate 			    ip->ni_path))
79900Sstevel@tonic-gate 			ASSERT(pm_noinvol_head == ip);
79910Sstevel@tonic-gate 			pm_noinvol_head = ip->ni_next;
79920Sstevel@tonic-gate 		}
79930Sstevel@tonic-gate 		rw_exit(&pm_noinvol_rwlock);
79940Sstevel@tonic-gate 		adjust_ancestors(ip->ni_path, wasvolpmd);
79950Sstevel@tonic-gate 		/*
79960Sstevel@tonic-gate 		 * Had an ancestor been removed before this node, it would have
79970Sstevel@tonic-gate 		 * been skipped. Adjust the no-invol counters for such skipped
79980Sstevel@tonic-gate 		 * ancestors.
79990Sstevel@tonic-gate 		 */
80000Sstevel@tonic-gate 		pm_noinvol_process_ancestors(ip->ni_path);
80010Sstevel@tonic-gate 		kmem_free(ip->ni_path, ip->ni_size);
80020Sstevel@tonic-gate 		kmem_free(ip, sizeof (*ip));
80030Sstevel@tonic-gate 		goto again;
80040Sstevel@tonic-gate 	}
80050Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
80060Sstevel@tonic-gate }
80070Sstevel@tonic-gate 
80080Sstevel@tonic-gate /*
80090Sstevel@tonic-gate  * returns 1, if *aip is a ancestor of a no-invol node
80100Sstevel@tonic-gate  *	   0, otherwise
80110Sstevel@tonic-gate  */
80120Sstevel@tonic-gate static int
80130Sstevel@tonic-gate pm_is_noinvol_ancestor(pm_noinvol_t *aip)
80140Sstevel@tonic-gate {
80150Sstevel@tonic-gate 	pm_noinvol_t *ip;
80160Sstevel@tonic-gate 
80170Sstevel@tonic-gate 	ASSERT(strlen(aip->ni_path) != 0);
80180Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
80190Sstevel@tonic-gate 		if (ip == aip)
80200Sstevel@tonic-gate 			continue;
80210Sstevel@tonic-gate 		/*
80220Sstevel@tonic-gate 		 * To be an ancestor, the path must be an initial substring of
80230Sstevel@tonic-gate 		 * the descendent, and end just before a '/' in the
80240Sstevel@tonic-gate 		 * descendent's path.
80250Sstevel@tonic-gate 		 */
80260Sstevel@tonic-gate 		if ((strstr(ip->ni_path, aip->ni_path) == ip->ni_path) &&
80270Sstevel@tonic-gate 		    (ip->ni_path[strlen(aip->ni_path)] == '/'))
80280Sstevel@tonic-gate 			return (1);
80290Sstevel@tonic-gate 	}
80300Sstevel@tonic-gate 	return (0);
80310Sstevel@tonic-gate }
80320Sstevel@tonic-gate 
80330Sstevel@tonic-gate #define	PM_MAJOR(dip) ddi_name_to_major(ddi_binding_name(dip))
80340Sstevel@tonic-gate /*
80350Sstevel@tonic-gate  * scan through the pm_noinvolpm list adjusting ancestors of the current
80360Sstevel@tonic-gate  * node;  Modifies string *path.
80370Sstevel@tonic-gate  */
80380Sstevel@tonic-gate static void
80390Sstevel@tonic-gate adjust_ancestors(char *path, int wasvolpmd)
80400Sstevel@tonic-gate {
80410Sstevel@tonic-gate 	PMD_FUNC(pmf, "adjust_ancestors")
80420Sstevel@tonic-gate 	char *cp;
80430Sstevel@tonic-gate 	pm_noinvol_t *lp;
80440Sstevel@tonic-gate 	pm_noinvol_t *pp = NULL;
80450Sstevel@tonic-gate 	major_t locked = (major_t)UINT_MAX;
80460Sstevel@tonic-gate 	dev_info_t *dip;
80470Sstevel@tonic-gate 	char	*pathbuf;
80480Sstevel@tonic-gate 	size_t pathbuflen = strlen(path) + 1;
80490Sstevel@tonic-gate 
80500Sstevel@tonic-gate 	/*
80510Sstevel@tonic-gate 	 * First we look up the ancestor's dip.  If we find it, then we
80520Sstevel@tonic-gate 	 * adjust counts up the tree
80530Sstevel@tonic-gate 	 */
80540Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s wasvolpmd %d\n", pmf, path, wasvolpmd))
80550Sstevel@tonic-gate 	pathbuf = kmem_alloc(pathbuflen, KM_SLEEP);
80560Sstevel@tonic-gate 	(void) strcpy(pathbuf, path);
80570Sstevel@tonic-gate 	cp = strrchr(pathbuf, '/');
80580Sstevel@tonic-gate 	if (cp == NULL)	{
80590Sstevel@tonic-gate 		/* if no ancestors, then nothing to do */
80600Sstevel@tonic-gate 		kmem_free(pathbuf, pathbuflen);
80610Sstevel@tonic-gate 		return;
80620Sstevel@tonic-gate 	}
80630Sstevel@tonic-gate 	*cp = '\0';
80640Sstevel@tonic-gate 	dip = pm_name_to_dip(pathbuf, 1);
80650Sstevel@tonic-gate 	if (dip != NULL) {
80660Sstevel@tonic-gate 		locked = PM_MAJOR(dip);
80670Sstevel@tonic-gate 
80680Sstevel@tonic-gate 		(void) pm_noinvol_update(PM_BP_NOINVOL_REMDRV, 0, wasvolpmd,
80690Sstevel@tonic-gate 		    path, dip);
80700Sstevel@tonic-gate 
80710Sstevel@tonic-gate 		if (locked != (major_t)UINT_MAX)
80720Sstevel@tonic-gate 			ddi_release_devi(dip);
80730Sstevel@tonic-gate 	} else {
80740Sstevel@tonic-gate 		char *apath;
80750Sstevel@tonic-gate 		size_t len = strlen(pathbuf) + 1;
80760Sstevel@tonic-gate 		int  lock_held = 1;
80770Sstevel@tonic-gate 
80780Sstevel@tonic-gate 		/*
80790Sstevel@tonic-gate 		 * Now check for ancestors that exist only in the list
80800Sstevel@tonic-gate 		 */
80810Sstevel@tonic-gate 		apath = kmem_alloc(len, KM_SLEEP);
80820Sstevel@tonic-gate 		(void) strcpy(apath, pathbuf);
80830Sstevel@tonic-gate 		rw_enter(&pm_noinvol_rwlock, RW_WRITER);
80840Sstevel@tonic-gate 		for (lp = pm_noinvol_head; lp; pp = lp, lp = lp->ni_next) {
80850Sstevel@tonic-gate 			/*
80860Sstevel@tonic-gate 			 * This can only happen once.  Since we have to drop
80870Sstevel@tonic-gate 			 * the lock, we need to extract the relevant info.
80880Sstevel@tonic-gate 			 */
80890Sstevel@tonic-gate 			if (strcmp(pathbuf, lp->ni_path) == 0) {
80900Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: %s no %d -> %d\n", pmf,
80910Sstevel@tonic-gate 				    lp->ni_path, lp->ni_noinvolpm,
80920Sstevel@tonic-gate 				    lp->ni_noinvolpm - 1))
80930Sstevel@tonic-gate 				lp->ni_noinvolpm--;
80940Sstevel@tonic-gate 				if (wasvolpmd && lp->ni_volpmd) {
80950Sstevel@tonic-gate 					PMD(PMD_NOINVOL, ("%s: %s vol %d -> "
80960Sstevel@tonic-gate 					    "%d\n", pmf, lp->ni_path,
80970Sstevel@tonic-gate 					    lp->ni_volpmd, lp->ni_volpmd - 1))
80980Sstevel@tonic-gate 					lp->ni_volpmd--;
80990Sstevel@tonic-gate 				}
81000Sstevel@tonic-gate 				/*
81010Sstevel@tonic-gate 				 * remove the entry from the list, if there
81020Sstevel@tonic-gate 				 * are no more no-invol descendants and node
81030Sstevel@tonic-gate 				 * itself is not a no-invol node.
81040Sstevel@tonic-gate 				 */
81050Sstevel@tonic-gate 				if (!(lp->ni_noinvolpm ||
81060Sstevel@tonic-gate 				    (lp->ni_flags & PMC_NO_INVOL))) {
81070Sstevel@tonic-gate 					ASSERT(lp->ni_volpmd == 0);
81080Sstevel@tonic-gate 					if (pp) {
81090Sstevel@tonic-gate 						PMD(PMD_NOINVOL, ("%s: freeing "
81100Sstevel@tonic-gate 						    "%s, prev is %s\n", pmf,
81110Sstevel@tonic-gate 						    lp->ni_path, pp->ni_path))
81120Sstevel@tonic-gate 						pp->ni_next = lp->ni_next;
81130Sstevel@tonic-gate 					} else {
81140Sstevel@tonic-gate 						PMD(PMD_NOINVOL, ("%s: free %s "
81150Sstevel@tonic-gate 						    "head\n", pmf, lp->ni_path))
81160Sstevel@tonic-gate 						ASSERT(pm_noinvol_head == lp);
81170Sstevel@tonic-gate 						pm_noinvol_head = lp->ni_next;
81180Sstevel@tonic-gate 					}
81190Sstevel@tonic-gate 					lock_held = 0;
81200Sstevel@tonic-gate 					rw_exit(&pm_noinvol_rwlock);
81210Sstevel@tonic-gate 					adjust_ancestors(apath, wasvolpmd);
81220Sstevel@tonic-gate 					/* restore apath */
81230Sstevel@tonic-gate 					(void) strcpy(apath, pathbuf);
81240Sstevel@tonic-gate 					kmem_free(lp->ni_path, lp->ni_size);
81250Sstevel@tonic-gate 					kmem_free(lp, sizeof (*lp));
81260Sstevel@tonic-gate 				}
81270Sstevel@tonic-gate 				break;
81280Sstevel@tonic-gate 			}
81290Sstevel@tonic-gate 		}
81300Sstevel@tonic-gate 		if (lock_held)
81310Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
81320Sstevel@tonic-gate 		adjust_ancestors(apath, wasvolpmd);
81330Sstevel@tonic-gate 		kmem_free(apath, len);
81340Sstevel@tonic-gate 	}
81350Sstevel@tonic-gate 	kmem_free(pathbuf, pathbuflen);
81360Sstevel@tonic-gate }
81370Sstevel@tonic-gate 
81380Sstevel@tonic-gate /*
81390Sstevel@tonic-gate  * Do no-invol processing for any ancestors i.e. adjust counters of ancestors,
81400Sstevel@tonic-gate  * which were skipped even though their drivers were removed.
81410Sstevel@tonic-gate  */
81420Sstevel@tonic-gate static void
81430Sstevel@tonic-gate pm_noinvol_process_ancestors(char *path)
81440Sstevel@tonic-gate {
81450Sstevel@tonic-gate 	pm_noinvol_t *lp;
81460Sstevel@tonic-gate 
81470Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
81480Sstevel@tonic-gate 	for (lp = pm_noinvol_head; lp; lp = lp->ni_next) {
81490Sstevel@tonic-gate 		if (strstr(path, lp->ni_path) &&
81500Sstevel@tonic-gate 		    (lp->ni_flags & PMC_DRIVER_REMOVED)) {
81510Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
81520Sstevel@tonic-gate 			i_pm_driver_removed(lp->ni_major);
81530Sstevel@tonic-gate 			return;
81540Sstevel@tonic-gate 		}
81550Sstevel@tonic-gate 	}
81560Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
81570Sstevel@tonic-gate }
81580Sstevel@tonic-gate 
81590Sstevel@tonic-gate /*
81600Sstevel@tonic-gate  * Returns true if (detached) device needs to be kept up because it exported the
81610Sstevel@tonic-gate  * "no-involuntary-power-cycles" property or we're pretending it did (console
81620Sstevel@tonic-gate  * fb case) or it is an ancestor of such a device and has used up the "one
81630Sstevel@tonic-gate  * free cycle" allowed when all such leaf nodes have voluntarily powered down
81640Sstevel@tonic-gate  * upon detach.  In any event, we need an exact hit on the path or we return
81650Sstevel@tonic-gate  * false.
81660Sstevel@tonic-gate  */
81670Sstevel@tonic-gate int
81680Sstevel@tonic-gate pm_noinvol_detached(char *path)
81690Sstevel@tonic-gate {
81700Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_detached")
81710Sstevel@tonic-gate 	pm_noinvol_t *ip;
81720Sstevel@tonic-gate 	int ret = 0;
81730Sstevel@tonic-gate 
81740Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
81750Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
81760Sstevel@tonic-gate 		if (strcmp(path, ip->ni_path) == 0) {
81770Sstevel@tonic-gate 			if (ip->ni_flags & PMC_CONSOLE_FB) {
81780Sstevel@tonic-gate 				PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB "
81790Sstevel@tonic-gate 				    "%s\n", pmf, path))
81800Sstevel@tonic-gate 				ret = 1;
81810Sstevel@tonic-gate 				break;
81820Sstevel@tonic-gate 			}
81830Sstevel@tonic-gate #ifdef	DEBUG
81840Sstevel@tonic-gate 			if (ip->ni_noinvolpm != ip->ni_volpmd)
81850Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s"
81860Sstevel@tonic-gate 				    "\n", pmf, ip->ni_noinvolpm, ip->ni_volpmd,
81870Sstevel@tonic-gate 				    path))
81880Sstevel@tonic-gate #endif
81890Sstevel@tonic-gate 			ret = (ip->ni_noinvolpm != ip->ni_volpmd);
81900Sstevel@tonic-gate 			break;
81910Sstevel@tonic-gate 		}
81920Sstevel@tonic-gate 	}
81930Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
81940Sstevel@tonic-gate 	return (ret);
81950Sstevel@tonic-gate }
81960Sstevel@tonic-gate 
81970Sstevel@tonic-gate int
81980Sstevel@tonic-gate pm_is_cfb(dev_info_t *dip)
81990Sstevel@tonic-gate {
82000Sstevel@tonic-gate 	return (dip == cfb_dip);
82010Sstevel@tonic-gate }
82020Sstevel@tonic-gate 
82030Sstevel@tonic-gate #ifdef	DEBUG
82040Sstevel@tonic-gate /*
82050Sstevel@tonic-gate  * Return true if all components of the console frame buffer are at
82060Sstevel@tonic-gate  * "normal" power, i.e., fully on.  For the case where the console is not
82070Sstevel@tonic-gate  * a framebuffer, we also return true
82080Sstevel@tonic-gate  */
82090Sstevel@tonic-gate int
82100Sstevel@tonic-gate pm_cfb_is_up(void)
82110Sstevel@tonic-gate {
82120Sstevel@tonic-gate 	return (pm_cfb_comps_off == 0);
82130Sstevel@tonic-gate }
82140Sstevel@tonic-gate #endif
82150Sstevel@tonic-gate 
82160Sstevel@tonic-gate /*
82170Sstevel@tonic-gate  * Preventing scan from powering down the node by incrementing the
82180Sstevel@tonic-gate  * kidsupcnt.
82190Sstevel@tonic-gate  */
82200Sstevel@tonic-gate void
82210Sstevel@tonic-gate pm_hold_power(dev_info_t *dip)
82220Sstevel@tonic-gate {
82230Sstevel@tonic-gate 	e_pm_hold_rele_power(dip, 1);
82240Sstevel@tonic-gate }
82250Sstevel@tonic-gate 
82260Sstevel@tonic-gate /*
82270Sstevel@tonic-gate  * Releasing the hold by decrementing the kidsupcnt allowing scan
82280Sstevel@tonic-gate  * to power down the node if all conditions are met.
82290Sstevel@tonic-gate  */
82300Sstevel@tonic-gate void
82310Sstevel@tonic-gate pm_rele_power(dev_info_t *dip)
82320Sstevel@tonic-gate {
82330Sstevel@tonic-gate 	e_pm_hold_rele_power(dip, -1);
82340Sstevel@tonic-gate }
82350Sstevel@tonic-gate 
82360Sstevel@tonic-gate /*
82370Sstevel@tonic-gate  * A wrapper of pm_all_to_normal() to power up a dip
82380Sstevel@tonic-gate  * to its normal level
82390Sstevel@tonic-gate  */
82400Sstevel@tonic-gate int
82410Sstevel@tonic-gate pm_powerup(dev_info_t *dip)
82420Sstevel@tonic-gate {
82430Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_powerup")
82440Sstevel@tonic-gate 
82450Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
82460Sstevel@tonic-gate 	ASSERT(!(servicing_interrupt()));
82470Sstevel@tonic-gate 
82480Sstevel@tonic-gate 	/*
82490Sstevel@tonic-gate 	 * in case this node is not already participating pm
82500Sstevel@tonic-gate 	 */
82510Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip)) {
82520Sstevel@tonic-gate 		if (!DEVI_IS_ATTACHING(dip))
82530Sstevel@tonic-gate 			return (DDI_SUCCESS);
82540Sstevel@tonic-gate 		if (pm_start(dip) != DDI_SUCCESS)
82550Sstevel@tonic-gate 			return (DDI_FAILURE);
82560Sstevel@tonic-gate 		if (!PM_GET_PM_INFO(dip))
82570Sstevel@tonic-gate 			return (DDI_SUCCESS);
82580Sstevel@tonic-gate 	}
82590Sstevel@tonic-gate 
82600Sstevel@tonic-gate 	return (pm_all_to_normal(dip, PM_CANBLOCK_BLOCK));
82610Sstevel@tonic-gate }
82620Sstevel@tonic-gate 
82630Sstevel@tonic-gate int
82640Sstevel@tonic-gate pm_rescan_walk(dev_info_t *dip, void *arg)
82650Sstevel@tonic-gate {
82660Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
82670Sstevel@tonic-gate 
82680Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
82690Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
82700Sstevel@tonic-gate 
82710Sstevel@tonic-gate 	/*
82720Sstevel@tonic-gate 	 * Currently pm_cpr_callb/resume code is the only caller
82730Sstevel@tonic-gate 	 * and it needs to make sure that stopped scan get
82740Sstevel@tonic-gate 	 * reactivated. Otherwise, rescan walk needn't reactive
82750Sstevel@tonic-gate 	 * stopped scan.
82760Sstevel@tonic-gate 	 */
82770Sstevel@tonic-gate 	pm_scan_init(dip);
82780Sstevel@tonic-gate 
82790Sstevel@tonic-gate 	(void) pm_rescan(dip);
82800Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
82810Sstevel@tonic-gate }
82820Sstevel@tonic-gate 
82830Sstevel@tonic-gate static dev_info_t *
82840Sstevel@tonic-gate pm_get_next_descendent(dev_info_t *dip, dev_info_t *tdip)
82850Sstevel@tonic-gate {
82860Sstevel@tonic-gate 	dev_info_t *wdip, *pdip;
82870Sstevel@tonic-gate 
82880Sstevel@tonic-gate 	for (wdip = tdip; wdip != dip; wdip = pdip) {
82890Sstevel@tonic-gate 		pdip = ddi_get_parent(wdip);
82900Sstevel@tonic-gate 		if (pdip == dip)
82910Sstevel@tonic-gate 			return (wdip);
82920Sstevel@tonic-gate 	}
82930Sstevel@tonic-gate 	return (NULL);
82940Sstevel@tonic-gate }
82950Sstevel@tonic-gate 
82960Sstevel@tonic-gate int
82970Sstevel@tonic-gate pm_busop_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
82980Sstevel@tonic-gate     void *arg, void *result)
82990Sstevel@tonic-gate {
83000Sstevel@tonic-gate 	PMD_FUNC(pmf, "bp_bus_power")
83010Sstevel@tonic-gate 	dev_info_t	*cdip;
83020Sstevel@tonic-gate 	pm_info_t	*cinfo;
83030Sstevel@tonic-gate 	pm_bp_child_pwrchg_t	*bpc;
83040Sstevel@tonic-gate 	pm_sp_misc_t		*pspm;
83050Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t *bpn;
83060Sstevel@tonic-gate 	pm_bp_child_pwrchg_t new_bpc;
83070Sstevel@tonic-gate 	pm_bp_noinvol_t *bpi;
83080Sstevel@tonic-gate 	dev_info_t *tdip;
83090Sstevel@tonic-gate 	char *pathbuf;
83100Sstevel@tonic-gate 	int		ret = DDI_SUCCESS;
83110Sstevel@tonic-gate 	int		errno = 0;
83120Sstevel@tonic-gate 	pm_component_t *cp;
83130Sstevel@tonic-gate 
83140Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip),
83150Sstevel@tonic-gate 	    pm_decode_op(op)))
83160Sstevel@tonic-gate 	switch (op) {
83170Sstevel@tonic-gate 	case BUS_POWER_CHILD_PWRCHG:
83180Sstevel@tonic-gate 		bpc = (pm_bp_child_pwrchg_t *)arg;
83190Sstevel@tonic-gate 		pspm = (pm_sp_misc_t *)bpc->bpc_private;
83200Sstevel@tonic-gate 		tdip = bpc->bpc_dip;
83210Sstevel@tonic-gate 		cdip = pm_get_next_descendent(dip, tdip);
83220Sstevel@tonic-gate 		cinfo = PM_GET_PM_INFO(cdip);
83230Sstevel@tonic-gate 		if (cdip != tdip) {
83240Sstevel@tonic-gate 			/*
83250Sstevel@tonic-gate 			 * If the node is an involved parent, it needs to
83260Sstevel@tonic-gate 			 * power up the node as it is needed.  There is nothing
83270Sstevel@tonic-gate 			 * else the framework can do here.
83280Sstevel@tonic-gate 			 */
83290Sstevel@tonic-gate 			if (PM_WANTS_NOTIFICATION(cdip)) {
83300Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: call bus_power for "
83310Sstevel@tonic-gate 				    "%s@%s(%s#%d)\n", pmf, PM_DEVICE(cdip)))
83320Sstevel@tonic-gate 				return ((*PM_BUS_POWER_FUNC(cdip))(cdip,
83330Sstevel@tonic-gate 				    impl_arg, op, arg, result));
83340Sstevel@tonic-gate 			}
83350Sstevel@tonic-gate 			ASSERT(pspm->pspm_direction == PM_LEVEL_UPONLY ||
83360Sstevel@tonic-gate 			    pspm->pspm_direction == PM_LEVEL_DOWNONLY ||
83370Sstevel@tonic-gate 			    pspm->pspm_direction == PM_LEVEL_EXACT);
83380Sstevel@tonic-gate 			/*
83390Sstevel@tonic-gate 			 * we presume that the parent needs to be up in
83400Sstevel@tonic-gate 			 * order for the child to change state (either
83410Sstevel@tonic-gate 			 * because it must already be on if the child is on
83420Sstevel@tonic-gate 			 * (and the pm_all_to_normal_nexus() will be a nop)
83430Sstevel@tonic-gate 			 * or because it will need to be on for the child
83440Sstevel@tonic-gate 			 * to come on; so we make the call regardless
83450Sstevel@tonic-gate 			 */
83460Sstevel@tonic-gate 			pm_hold_power(cdip);
83470Sstevel@tonic-gate 			if (cinfo) {
83480Sstevel@tonic-gate 				pm_canblock_t canblock = pspm->pspm_canblock;
83490Sstevel@tonic-gate 				ret = pm_all_to_normal_nexus(cdip, canblock);
83500Sstevel@tonic-gate 				if (ret != DDI_SUCCESS) {
83510Sstevel@tonic-gate 					pm_rele_power(cdip);
83520Sstevel@tonic-gate 					return (ret);
83530Sstevel@tonic-gate 				}
83540Sstevel@tonic-gate 			}
83550Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: walk down to %s@%s(%s#%d)\n", pmf,
83560Sstevel@tonic-gate 			    PM_DEVICE(cdip)))
83570Sstevel@tonic-gate 			ret = pm_busop_bus_power(cdip, impl_arg, op, arg,
83580Sstevel@tonic-gate 			    result);
83590Sstevel@tonic-gate 			pm_rele_power(cdip);
83600Sstevel@tonic-gate 		} else {
83610Sstevel@tonic-gate 			ret = pm_busop_set_power(cdip, impl_arg, op, arg,
83620Sstevel@tonic-gate 			    result);
83630Sstevel@tonic-gate 		}
83640Sstevel@tonic-gate 		return (ret);
83650Sstevel@tonic-gate 
83660Sstevel@tonic-gate 	case BUS_POWER_NEXUS_PWRUP:
83670Sstevel@tonic-gate 		bpn = (pm_bp_nexus_pwrup_t *)arg;
83680Sstevel@tonic-gate 		pspm = (pm_sp_misc_t *)bpn->bpn_private;
83690Sstevel@tonic-gate 
83700Sstevel@tonic-gate 		if (!e_pm_valid_info(dip, NULL) ||
83710Sstevel@tonic-gate 		    !e_pm_valid_comp(dip, bpn->bpn_comp, &cp) ||
83720Sstevel@tonic-gate 		    !e_pm_valid_power(dip, bpn->bpn_comp, bpn->bpn_level)) {
83730Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: %s@%s(%s#%d) has no pm info; EIO\n",
83740Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
83750Sstevel@tonic-gate 			*pspm->pspm_errnop = EIO;
83760Sstevel@tonic-gate 			*(int *)result = DDI_FAILURE;
83770Sstevel@tonic-gate 			return (DDI_FAILURE);
83780Sstevel@tonic-gate 		}
83790Sstevel@tonic-gate 
83800Sstevel@tonic-gate 		ASSERT(bpn->bpn_dip == dip);
83810Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: nexus powerup for %s@%s(%s#%d)\n", pmf,
83820Sstevel@tonic-gate 		    PM_DEVICE(dip)))
83830Sstevel@tonic-gate 		new_bpc.bpc_dip = dip;
83840Sstevel@tonic-gate 		pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
83850Sstevel@tonic-gate 		new_bpc.bpc_path = ddi_pathname(dip, pathbuf);
83860Sstevel@tonic-gate 		new_bpc.bpc_comp = bpn->bpn_comp;
83870Sstevel@tonic-gate 		new_bpc.bpc_olevel = PM_CURPOWER(dip, bpn->bpn_comp);
83880Sstevel@tonic-gate 		new_bpc.bpc_nlevel = bpn->bpn_level;
83890Sstevel@tonic-gate 		new_bpc.bpc_private = bpn->bpn_private;
83900Sstevel@tonic-gate 		((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_direction =
83910Sstevel@tonic-gate 		    PM_LEVEL_UPONLY;
83920Sstevel@tonic-gate 		((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_errnop =
83930Sstevel@tonic-gate 		    &errno;
83940Sstevel@tonic-gate 		ret = pm_busop_set_power(dip, impl_arg, BUS_POWER_CHILD_PWRCHG,
83950Sstevel@tonic-gate 		    (void *)&new_bpc, result);
83960Sstevel@tonic-gate 		kmem_free(pathbuf, MAXPATHLEN);
83970Sstevel@tonic-gate 		return (ret);
83980Sstevel@tonic-gate 
83990Sstevel@tonic-gate 	case BUS_POWER_NOINVOL:
84000Sstevel@tonic-gate 		bpi = (pm_bp_noinvol_t *)arg;
84010Sstevel@tonic-gate 		tdip = bpi->bpni_dip;
84020Sstevel@tonic-gate 		cdip = pm_get_next_descendent(dip, tdip);
84030Sstevel@tonic-gate 
84040Sstevel@tonic-gate 		/* In case of rem_drv, the leaf node has been removed */
84050Sstevel@tonic-gate 		if (cdip == NULL)
84060Sstevel@tonic-gate 			return (DDI_SUCCESS);
84070Sstevel@tonic-gate 
84080Sstevel@tonic-gate 		cinfo = PM_GET_PM_INFO(cdip);
84090Sstevel@tonic-gate 		if (cdip != tdip) {
84100Sstevel@tonic-gate 			if (PM_WANTS_NOTIFICATION(cdip)) {
84110Sstevel@tonic-gate 				PMD(PMD_NOINVOL,
84120Sstevel@tonic-gate 				    ("%s: call bus_power for %s@%s(%s#%d)\n",
84130Sstevel@tonic-gate 				    pmf, PM_DEVICE(cdip)))
84140Sstevel@tonic-gate 				ret = (*PM_BUS_POWER_FUNC(cdip))
84150Sstevel@tonic-gate 				    (cdip, NULL, op, arg, result);
84160Sstevel@tonic-gate 				if ((cinfo) && (ret == DDI_SUCCESS))
84170Sstevel@tonic-gate 					(void) pm_noinvol_update_node(cdip,
84180Sstevel@tonic-gate 					    bpi);
84190Sstevel@tonic-gate 				return (ret);
84200Sstevel@tonic-gate 			} else {
84210Sstevel@tonic-gate 				PMD(PMD_NOINVOL,
84220Sstevel@tonic-gate 				    ("%s: walk down to %s@%s(%s#%d)\n", pmf,
84230Sstevel@tonic-gate 				    PM_DEVICE(cdip)))
84240Sstevel@tonic-gate 				ret = pm_busop_bus_power(cdip, NULL, op,
84250Sstevel@tonic-gate 				    arg, result);
84260Sstevel@tonic-gate 				/*
84270Sstevel@tonic-gate 				 * Update the current node.
84280Sstevel@tonic-gate 				 */
84290Sstevel@tonic-gate 				if ((cinfo) && (ret == DDI_SUCCESS))
84300Sstevel@tonic-gate 					(void) pm_noinvol_update_node(cdip,
84310Sstevel@tonic-gate 					    bpi);
84320Sstevel@tonic-gate 				return (ret);
84330Sstevel@tonic-gate 			}
84340Sstevel@tonic-gate 		} else {
84350Sstevel@tonic-gate 			/*
84360Sstevel@tonic-gate 			 * For attach, detach, power up:
84370Sstevel@tonic-gate 			 * Do nothing for leaf node since its
84380Sstevel@tonic-gate 			 * counts are already updated.
84390Sstevel@tonic-gate 			 * For CFB and driver removal, since the
84400Sstevel@tonic-gate 			 * path and the target dip passed in is up to and incl.
84410Sstevel@tonic-gate 			 * the immediate ancestor, need to do the update.
84420Sstevel@tonic-gate 			 */
84430Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: target %s@%s(%s#%d) is "
84440Sstevel@tonic-gate 			    "reached\n", pmf, PM_DEVICE(cdip)))
84450Sstevel@tonic-gate 			if (cinfo && ((bpi->bpni_cmd == PM_BP_NOINVOL_REMDRV) ||
84460Sstevel@tonic-gate 			    (bpi->bpni_cmd == PM_BP_NOINVOL_CFB)))
84470Sstevel@tonic-gate 				(void) pm_noinvol_update_node(cdip, bpi);
84480Sstevel@tonic-gate 			return (DDI_SUCCESS);
84490Sstevel@tonic-gate 		}
84500Sstevel@tonic-gate 
84510Sstevel@tonic-gate 	default:
84520Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: operation %d is not supported!\n", pmf, op))
84530Sstevel@tonic-gate 		return (DDI_FAILURE);
84540Sstevel@tonic-gate 	}
84550Sstevel@tonic-gate }
84560Sstevel@tonic-gate 
84570Sstevel@tonic-gate static int
84580Sstevel@tonic-gate pm_busop_set_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
84590Sstevel@tonic-gate     void *arg, void *resultp)
84600Sstevel@tonic-gate {
84610Sstevel@tonic-gate 	_NOTE(ARGUNUSED(impl_arg))
84620Sstevel@tonic-gate 	PMD_FUNC(pmf, "bp_set_power")
84630Sstevel@tonic-gate 	pm_ppm_devlist_t *devl;
84640Sstevel@tonic-gate 	int clevel, circ;
84650Sstevel@tonic-gate #ifdef	DEBUG
84660Sstevel@tonic-gate 	int circ_db, ccirc_db;
84670Sstevel@tonic-gate #endif
84680Sstevel@tonic-gate 	int ret = DDI_SUCCESS;
84690Sstevel@tonic-gate 	dev_info_t *cdip;
84700Sstevel@tonic-gate 	pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
84710Sstevel@tonic-gate 	pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private;
84720Sstevel@tonic-gate 	pm_canblock_t canblock = pspm->pspm_canblock;
84730Sstevel@tonic-gate 	int scan = pspm->pspm_scan;
84740Sstevel@tonic-gate 	int comp = bpc->bpc_comp;
84750Sstevel@tonic-gate 	int olevel = bpc->bpc_olevel;
84760Sstevel@tonic-gate 	int nlevel = bpc->bpc_nlevel;
84770Sstevel@tonic-gate 	int comps_off_incr = 0;
84780Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
84790Sstevel@tonic-gate 	int dodeps;
84800Sstevel@tonic-gate 	int direction = pspm->pspm_direction;
84810Sstevel@tonic-gate 	int *errnop = pspm->pspm_errnop;
84820Sstevel@tonic-gate 	char *dir = pm_decode_direction(direction);
84830Sstevel@tonic-gate 	int *iresp = (int *)resultp;
84840Sstevel@tonic-gate 	time_t	idletime, thresh;
84850Sstevel@tonic-gate 	pm_component_t *cp = PM_CP(dip, comp);
84860Sstevel@tonic-gate 	int work_type;
84870Sstevel@tonic-gate 
84880Sstevel@tonic-gate 	*iresp = DDI_SUCCESS;
84890Sstevel@tonic-gate 	*errnop = 0;
84900Sstevel@tonic-gate 	ASSERT(op == BUS_POWER_CHILD_PWRCHG);
84910Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip),
84920Sstevel@tonic-gate 	    pm_decode_op(op)))
84930Sstevel@tonic-gate 
84940Sstevel@tonic-gate 	/*
84950Sstevel@tonic-gate 	 * The following set of conditions indicate we are here to handle a
84960Sstevel@tonic-gate 	 * driver's pm_[raise|lower]_power request, but the device is being
84970Sstevel@tonic-gate 	 * power managed (PM_DIRECT_PM) by a user process.  For that case
84980Sstevel@tonic-gate 	 * we want to pm_block and pass a status back to the caller based
84990Sstevel@tonic-gate 	 * on whether the controlling process's next activity on the device
85000Sstevel@tonic-gate 	 * matches the current request or not.  This distinction tells
85010Sstevel@tonic-gate 	 * downstream functions to avoid calling into a driver or changing
85020Sstevel@tonic-gate 	 * the framework's power state.  To actually block, we need:
85030Sstevel@tonic-gate 	 *
85040Sstevel@tonic-gate 	 * PM_ISDIRECT(dip)
85050Sstevel@tonic-gate 	 *	no reason to block unless a process is directly controlling dev
85060Sstevel@tonic-gate 	 * direction != PM_LEVEL_EXACT
85070Sstevel@tonic-gate 	 *	EXACT is used by controlling proc's PM_SET_CURRENT_POWER ioctl
85080Sstevel@tonic-gate 	 * !pm_processes_stopped
85090Sstevel@tonic-gate 	 *	don't block if controlling proc already be stopped for cpr
85100Sstevel@tonic-gate 	 * canblock != PM_CANBLOCK_BYPASS
85110Sstevel@tonic-gate 	 *	our caller must not have explicitly prevented blocking
85120Sstevel@tonic-gate 	 */
85130Sstevel@tonic-gate 	if (direction != PM_LEVEL_EXACT && canblock != PM_CANBLOCK_BYPASS) {
85140Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
85150Sstevel@tonic-gate 		while (PM_ISDIRECT(dip) && !pm_processes_stopped) {
85160Sstevel@tonic-gate 			/* releases dip lock */
85170Sstevel@tonic-gate 			ret = pm_busop_match_request(dip, bpc);
85180Sstevel@tonic-gate 			if (ret == EAGAIN) {
85190Sstevel@tonic-gate 				PM_LOCK_DIP(dip);
85200Sstevel@tonic-gate 				continue;
85210Sstevel@tonic-gate 			}
85220Sstevel@tonic-gate 			return (*iresp = ret);
85230Sstevel@tonic-gate 		}
85240Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
85250Sstevel@tonic-gate 	}
85260Sstevel@tonic-gate 	/* BC device is never scanned, so power will stick until we are done */
85270Sstevel@tonic-gate 	if (PM_ISBC(dip) && comp != 0 && nlevel != 0 &&
85280Sstevel@tonic-gate 	    direction != PM_LEVEL_DOWNONLY) {
85290Sstevel@tonic-gate 		int nrmpwr0 = pm_get_normal_power(dip, 0);
85300Sstevel@tonic-gate 		if (pm_set_power(dip, 0, nrmpwr0, direction,
85310Sstevel@tonic-gate 		    canblock, 0, resultp) != DDI_SUCCESS) {
85320Sstevel@tonic-gate 			/* *resultp set by pm_set_power */
85330Sstevel@tonic-gate 			return (DDI_FAILURE);
85340Sstevel@tonic-gate 		}
85350Sstevel@tonic-gate 	}
85360Sstevel@tonic-gate 	if (PM_WANTS_NOTIFICATION(pdip)) {
85370Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: pre_notify %s@%s(%s#%d) for child "
85380Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", pmf, PM_DEVICE(pdip), PM_DEVICE(dip)))
85390Sstevel@tonic-gate 		ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
85400Sstevel@tonic-gate 		    BUS_POWER_PRE_NOTIFICATION, bpc, resultp);
85410Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
85420Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: failed to pre_notify %s@%s(%s#%d)\n",
85430Sstevel@tonic-gate 			    pmf, PM_DEVICE(pdip)))
85440Sstevel@tonic-gate 			return (DDI_FAILURE);
85450Sstevel@tonic-gate 		}
85460Sstevel@tonic-gate 	} else {
85470Sstevel@tonic-gate 		/*
85480Sstevel@tonic-gate 		 * Since we don't know what the actual power level is,
85490Sstevel@tonic-gate 		 * we place a power hold on the parent no matter what
85500Sstevel@tonic-gate 		 * component and level is changing.
85510Sstevel@tonic-gate 		 */
85520Sstevel@tonic-gate 		pm_hold_power(pdip);
85530Sstevel@tonic-gate 	}
85540Sstevel@tonic-gate 	PM_LOCK_POWER(dip, &circ);
85550Sstevel@tonic-gate 	clevel = PM_CURPOWER(dip, comp);
85560Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, olvl=%d, nlvl=%d, clvl=%d, "
85570Sstevel@tonic-gate 	    "dir=%s\n", pmf, PM_DEVICE(dip), comp, bpc->bpc_olevel, nlevel,
85580Sstevel@tonic-gate 	    clevel, dir))
85590Sstevel@tonic-gate 	switch (direction) {
85600Sstevel@tonic-gate 	case PM_LEVEL_UPONLY:
85610Sstevel@tonic-gate 		/* Powering up */
85620Sstevel@tonic-gate 		if (clevel >= nlevel) {
85630Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: current level is already "
85640Sstevel@tonic-gate 			    "at or above the requested level.\n", pmf))
85650Sstevel@tonic-gate 			*iresp = DDI_SUCCESS;
85660Sstevel@tonic-gate 			ret = DDI_SUCCESS;
85670Sstevel@tonic-gate 			goto post_notify;
85680Sstevel@tonic-gate 		}
85690Sstevel@tonic-gate 		break;
85700Sstevel@tonic-gate 	case PM_LEVEL_EXACT:
85710Sstevel@tonic-gate 		/* specific level request */
85720Sstevel@tonic-gate 		if (clevel == nlevel && !PM_ISBC(dip)) {
85730Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: current level is already "
85740Sstevel@tonic-gate 			    "at the requested level.\n", pmf))
85750Sstevel@tonic-gate 			*iresp = DDI_SUCCESS;
85760Sstevel@tonic-gate 			ret = DDI_SUCCESS;
85770Sstevel@tonic-gate 			goto post_notify;
85780Sstevel@tonic-gate 		} else if (PM_IS_CFB(dip) && (nlevel < clevel)) {
85790Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: powerdown of console\n", pmf))
85800Sstevel@tonic-gate 			if (!pm_cfb_enabled) {
85810Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_CFB,
85820Sstevel@tonic-gate 				    ("%s: !pm_cfb_enabled, fails\n", pmf))
85830Sstevel@tonic-gate 				*errnop = EINVAL;
85840Sstevel@tonic-gate 				*iresp = DDI_FAILURE;
85850Sstevel@tonic-gate 				ret = DDI_FAILURE;
85860Sstevel@tonic-gate 				goto post_notify;
85870Sstevel@tonic-gate 			}
85880Sstevel@tonic-gate 			mutex_enter(&pm_cfb_lock);
85890Sstevel@tonic-gate 			while (cfb_inuse) {
85900Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
85910Sstevel@tonic-gate 				if (delay_sig(1) == EINTR) {
85920Sstevel@tonic-gate 					ret = DDI_FAILURE;
85930Sstevel@tonic-gate 					*iresp = DDI_FAILURE;
85940Sstevel@tonic-gate 					*errnop = EINTR;
85950Sstevel@tonic-gate 					goto post_notify;
85960Sstevel@tonic-gate 				}
85970Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
85980Sstevel@tonic-gate 			}
85990Sstevel@tonic-gate 			mutex_exit(&pm_cfb_lock);
86000Sstevel@tonic-gate 		}
86010Sstevel@tonic-gate 		break;
86020Sstevel@tonic-gate 	case PM_LEVEL_DOWNONLY:
86030Sstevel@tonic-gate 		/* Powering down */
86040Sstevel@tonic-gate 		thresh = cur_threshold(dip, comp);
86050Sstevel@tonic-gate 		idletime = gethrestime_sec() - cp->pmc_timestamp;
86060Sstevel@tonic-gate 		if (scan && ((PM_KUC(dip) != 0) ||
86071316Sosaeed 		    (cp->pmc_busycount > 0) ||
86081316Sosaeed 		    ((idletime < thresh) && !PM_IS_PID(dip)))) {
86090Sstevel@tonic-gate #ifdef	DEBUG
86100Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_kidsupcnt != 0)
86110Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: scan failed: "
86120Sstevel@tonic-gate 				    "kidsupcnt != 0\n", pmf))
86130Sstevel@tonic-gate 			if (cp->pmc_busycount > 0)
86140Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: scan failed: "
86150Sstevel@tonic-gate 				    "device become busy\n", pmf))
86160Sstevel@tonic-gate 			if (idletime < thresh)
86170Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: scan failed: device "
86180Sstevel@tonic-gate 				    "hasn't been idle long enough\n", pmf))
86190Sstevel@tonic-gate #endif
86200Sstevel@tonic-gate 			*iresp = DDI_FAILURE;
86210Sstevel@tonic-gate 			*errnop = EBUSY;
86220Sstevel@tonic-gate 			ret = DDI_FAILURE;
86230Sstevel@tonic-gate 			goto post_notify;
86240Sstevel@tonic-gate 		} else if (clevel != PM_LEVEL_UNKNOWN && clevel <= nlevel) {
86250Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: current level is already at "
86260Sstevel@tonic-gate 			    "or below the requested level.\n", pmf))
86270Sstevel@tonic-gate 			*iresp = DDI_SUCCESS;
86280Sstevel@tonic-gate 			ret = DDI_SUCCESS;
86290Sstevel@tonic-gate 			goto post_notify;
86300Sstevel@tonic-gate 		}
86310Sstevel@tonic-gate 		break;
86320Sstevel@tonic-gate 	}
86330Sstevel@tonic-gate 
86340Sstevel@tonic-gate 	if (PM_IS_CFB(dip) && (comps_off_incr =
86350Sstevel@tonic-gate 	    calc_cfb_comps_incr(dip, comp, clevel, nlevel)) > 0) {
86360Sstevel@tonic-gate 		/*
86370Sstevel@tonic-gate 		 * Pre-adjust pm_cfb_comps_off if lowering a console fb
86380Sstevel@tonic-gate 		 * component from full power.  Remember that we tried to
86390Sstevel@tonic-gate 		 * lower power in case it fails and we need to back out
86400Sstevel@tonic-gate 		 * the adjustment.
86410Sstevel@tonic-gate 		 */
86420Sstevel@tonic-gate 		update_comps_off(comps_off_incr, dip);
86430Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d cfb_comps_off->%d\n",
86440Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), comp, clevel, nlevel,
86450Sstevel@tonic-gate 		    pm_cfb_comps_off))
86460Sstevel@tonic-gate 	}
86470Sstevel@tonic-gate 
86480Sstevel@tonic-gate 	if ((*iresp = power_dev(dip,
86490Sstevel@tonic-gate 	    comp, nlevel, clevel, canblock, &devl)) == DDI_SUCCESS) {
86500Sstevel@tonic-gate #ifdef DEBUG
86510Sstevel@tonic-gate 		/*
86520Sstevel@tonic-gate 		 * All descendents of this node should already be powered off.
86530Sstevel@tonic-gate 		 */
86540Sstevel@tonic-gate 		if (PM_CURPOWER(dip, comp) == 0) {
86550Sstevel@tonic-gate 			pm_desc_pwrchk_t pdpchk;
86560Sstevel@tonic-gate 			pdpchk.pdpc_dip = dip;
86570Sstevel@tonic-gate 			pdpchk.pdpc_par_involved = PM_WANTS_NOTIFICATION(dip);
86580Sstevel@tonic-gate 			ndi_devi_enter(dip, &circ_db);
86590Sstevel@tonic-gate 			for (cdip = ddi_get_child(dip); cdip != NULL;
86600Sstevel@tonic-gate 			    cdip = ddi_get_next_sibling(cdip)) {
86610Sstevel@tonic-gate 				ndi_devi_enter(cdip, &ccirc_db);
86620Sstevel@tonic-gate 				ddi_walk_devs(cdip, pm_desc_pwrchk_walk,
86630Sstevel@tonic-gate 				    (void *)&pdpchk);
86640Sstevel@tonic-gate 				ndi_devi_exit(cdip, ccirc_db);
86650Sstevel@tonic-gate 			}
86660Sstevel@tonic-gate 			ndi_devi_exit(dip, circ_db);
86670Sstevel@tonic-gate 		}
86680Sstevel@tonic-gate #endif
86690Sstevel@tonic-gate 		/*
86700Sstevel@tonic-gate 		 * Post-adjust pm_cfb_comps_off if we brought an fb component
86710Sstevel@tonic-gate 		 * back up to full power.
86720Sstevel@tonic-gate 		 */
86730Sstevel@tonic-gate 		if (PM_IS_CFB(dip) && comps_off_incr < 0) {
86740Sstevel@tonic-gate 			update_comps_off(comps_off_incr, dip);
86750Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d "
86760Sstevel@tonic-gate 			    "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
86770Sstevel@tonic-gate 			    comp, clevel, nlevel, pm_cfb_comps_off))
86780Sstevel@tonic-gate 		}
86790Sstevel@tonic-gate 		dodeps = 0;
86800Sstevel@tonic-gate 		if (POWERING_OFF(clevel, nlevel)) {
86810Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
86820Sstevel@tonic-gate 				dodeps = (comp == 0);
86830Sstevel@tonic-gate 			} else {
86840Sstevel@tonic-gate 				int i;
86850Sstevel@tonic-gate 				dodeps = 1;
86860Sstevel@tonic-gate 				for (i = 0; i < PM_NUMCMPTS(dip); i++) {
86870Sstevel@tonic-gate 					/* if some component still on */
86880Sstevel@tonic-gate 					if (PM_CURPOWER(dip, i)) {
86890Sstevel@tonic-gate 						dodeps = 0;
86900Sstevel@tonic-gate 						break;
86910Sstevel@tonic-gate 					}
86920Sstevel@tonic-gate 				}
86930Sstevel@tonic-gate 			}
86940Sstevel@tonic-gate 			if (dodeps)
86950Sstevel@tonic-gate 				work_type = PM_DEP_WK_POWER_OFF;
86960Sstevel@tonic-gate 		} else if (POWERING_ON(clevel, nlevel)) {
86970Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
86980Sstevel@tonic-gate 				dodeps = (comp == 0);
86990Sstevel@tonic-gate 			} else {
87000Sstevel@tonic-gate 				int i;
87010Sstevel@tonic-gate 				dodeps = 1;
87020Sstevel@tonic-gate 				for (i = 0; i < PM_NUMCMPTS(dip); i++) {
87030Sstevel@tonic-gate 					if (i == comp)
87040Sstevel@tonic-gate 						continue;
87050Sstevel@tonic-gate 					if (PM_CURPOWER(dip, i) > 0) {
87060Sstevel@tonic-gate 						dodeps = 0;
87070Sstevel@tonic-gate 						break;
87080Sstevel@tonic-gate 					}
87090Sstevel@tonic-gate 				}
87100Sstevel@tonic-gate 			}
87110Sstevel@tonic-gate 			if (dodeps)
87120Sstevel@tonic-gate 				work_type = PM_DEP_WK_POWER_ON;
87130Sstevel@tonic-gate 		}
87140Sstevel@tonic-gate 
87150Sstevel@tonic-gate 		if (dodeps) {
87160Sstevel@tonic-gate 			char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
87170Sstevel@tonic-gate 
87180Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
87190Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(work_type, pathbuf, NULL,
87200Sstevel@tonic-gate 			    PM_DEP_NOWAIT, NULL, 0);
87210Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
87220Sstevel@tonic-gate 		}
87230Sstevel@tonic-gate 		if ((PM_CURPOWER(dip, comp) == nlevel) && pm_watchers()) {
87240Sstevel@tonic-gate 			int old;
87250Sstevel@tonic-gate 
87260Sstevel@tonic-gate 			/* If old power cached during deadlock, use it. */
87270Sstevel@tonic-gate 			old = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
87280Sstevel@tonic-gate 			    cp->pmc_phc_pwr : olevel);
87290Sstevel@tonic-gate 			mutex_enter(&pm_rsvp_lock);
87300Sstevel@tonic-gate 			pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, nlevel,
87310Sstevel@tonic-gate 			    old, canblock);
87320Sstevel@tonic-gate 			pm_enqueue_notify_others(&devl, canblock);
87330Sstevel@tonic-gate 			mutex_exit(&pm_rsvp_lock);
87340Sstevel@tonic-gate 		}
87350Sstevel@tonic-gate 
87360Sstevel@tonic-gate 		/*
87370Sstevel@tonic-gate 		 * If we are coming from a scan, don't do it again,
87380Sstevel@tonic-gate 		 * else we can have infinite loops.
87390Sstevel@tonic-gate 		 */
87400Sstevel@tonic-gate 		if (!scan)
87410Sstevel@tonic-gate 			pm_rescan(dip);
87420Sstevel@tonic-gate 	} else {
87430Sstevel@tonic-gate 		/* if we incremented pm_comps_off_count, but failed */
87440Sstevel@tonic-gate 		if (comps_off_incr > 0) {
87450Sstevel@tonic-gate 			update_comps_off(-comps_off_incr, dip);
87460Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d "
87470Sstevel@tonic-gate 			    "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
87480Sstevel@tonic-gate 			    comp, clevel, nlevel, pm_cfb_comps_off))
87490Sstevel@tonic-gate 		}
87500Sstevel@tonic-gate 		*errnop = EIO;
87510Sstevel@tonic-gate 	}
87520Sstevel@tonic-gate 
87530Sstevel@tonic-gate post_notify:
87540Sstevel@tonic-gate 	/*
87550Sstevel@tonic-gate 	 * This thread may have been in deadlock with pm_power_has_changed.
87560Sstevel@tonic-gate 	 * Before releasing power lock, clear the flag which marks this
87570Sstevel@tonic-gate 	 * condition.
87580Sstevel@tonic-gate 	 */
87590Sstevel@tonic-gate 	cp->pmc_flags &= ~PM_PHC_WHILE_SET_POWER;
87600Sstevel@tonic-gate 
87610Sstevel@tonic-gate 	/*
87620Sstevel@tonic-gate 	 * Update the old power level in the bus power structure with the
87630Sstevel@tonic-gate 	 * actual power level before the transition was made to the new level.
87640Sstevel@tonic-gate 	 * Some involved parents depend on this information to keep track of
87650Sstevel@tonic-gate 	 * their children's power transition.
87660Sstevel@tonic-gate 	 */
87670Sstevel@tonic-gate 	if (*iresp != DDI_FAILURE)
87680Sstevel@tonic-gate 		bpc->bpc_olevel = clevel;
87690Sstevel@tonic-gate 
87700Sstevel@tonic-gate 	if (PM_WANTS_NOTIFICATION(pdip)) {
87710Sstevel@tonic-gate 		ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
87720Sstevel@tonic-gate 		    BUS_POWER_POST_NOTIFICATION, bpc, resultp);
87730Sstevel@tonic-gate 		PM_UNLOCK_POWER(dip, circ);
87740Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: post_notify %s@%s(%s#%d) for "
87750Sstevel@tonic-gate 		    "child %s@%s(%s#%d), ret=%d\n", pmf, PM_DEVICE(pdip),
87760Sstevel@tonic-gate 		    PM_DEVICE(dip), ret))
87770Sstevel@tonic-gate 	} else {
87780Sstevel@tonic-gate 		nlevel = cur_power(cp); /* in case phc deadlock updated pwr */
87790Sstevel@tonic-gate 		PM_UNLOCK_POWER(dip, circ);
87800Sstevel@tonic-gate 		/*
87810Sstevel@tonic-gate 		 * Now that we know what power transition has occurred
87820Sstevel@tonic-gate 		 * (if any), release the power hold.  Leave the hold
87830Sstevel@tonic-gate 		 * in effect in the case of OFF->ON transition.
87840Sstevel@tonic-gate 		 */
87850Sstevel@tonic-gate 		if (!(clevel == 0 && nlevel > 0 &&
87860Sstevel@tonic-gate 		    (!PM_ISBC(dip) || comp == 0)))
87870Sstevel@tonic-gate 			pm_rele_power(pdip);
87880Sstevel@tonic-gate 		/*
87890Sstevel@tonic-gate 		 * If the power transition was an ON->OFF transition,
87900Sstevel@tonic-gate 		 * remove the power hold from the parent.
87910Sstevel@tonic-gate 		 */
87920Sstevel@tonic-gate 		if ((clevel > 0 || clevel == PM_LEVEL_UNKNOWN) &&
87930Sstevel@tonic-gate 		    nlevel == 0 && (!PM_ISBC(dip) || comp == 0))
87940Sstevel@tonic-gate 			pm_rele_power(pdip);
87950Sstevel@tonic-gate 	}
87960Sstevel@tonic-gate 	if (*iresp != DDI_SUCCESS || ret != DDI_SUCCESS)
87970Sstevel@tonic-gate 		return (DDI_FAILURE);
87980Sstevel@tonic-gate 	else
87990Sstevel@tonic-gate 		return (DDI_SUCCESS);
88000Sstevel@tonic-gate }
88010Sstevel@tonic-gate 
88020Sstevel@tonic-gate /*
88030Sstevel@tonic-gate  * If an app (SunVTS or Xsun) has taken control, then block until it
88040Sstevel@tonic-gate  * gives it up or makes the requested power level change, unless
88050Sstevel@tonic-gate  * we have other instructions about blocking.  Returns DDI_SUCCESS,
88060Sstevel@tonic-gate  * DDI_FAILURE or EAGAIN (owner released device from directpm).
88070Sstevel@tonic-gate  */
88080Sstevel@tonic-gate static int
88090Sstevel@tonic-gate pm_busop_match_request(dev_info_t *dip, void *arg)
88100Sstevel@tonic-gate {
88110Sstevel@tonic-gate 	PMD_FUNC(pmf, "bp_match_request")
88120Sstevel@tonic-gate 	pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
88130Sstevel@tonic-gate 	pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private;
88140Sstevel@tonic-gate 	int comp = bpc->bpc_comp;
88150Sstevel@tonic-gate 	int nlevel = bpc->bpc_nlevel;
88160Sstevel@tonic-gate 	pm_canblock_t canblock = pspm->pspm_canblock;
88170Sstevel@tonic-gate 	int direction = pspm->pspm_direction;
88180Sstevel@tonic-gate 	int clevel, circ;
88190Sstevel@tonic-gate 
88200Sstevel@tonic-gate 	ASSERT(PM_IAM_LOCKING_DIP(dip));
88210Sstevel@tonic-gate 	PM_LOCK_POWER(dip, &circ);
88220Sstevel@tonic-gate 	clevel = PM_CURPOWER(dip, comp);
88230Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, nlvl=%d, clvl=%d\n",
88240Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), comp, nlevel, clevel))
88250Sstevel@tonic-gate 	if (direction == PM_LEVEL_UPONLY) {
88260Sstevel@tonic-gate 		if (clevel >= nlevel) {
88270Sstevel@tonic-gate 			PM_UNLOCK_POWER(dip, circ);
88280Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
88290Sstevel@tonic-gate 			return (DDI_SUCCESS);
88300Sstevel@tonic-gate 		}
88310Sstevel@tonic-gate 	} else if (clevel == nlevel) {
88320Sstevel@tonic-gate 		PM_UNLOCK_POWER(dip, circ);
88330Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
88340Sstevel@tonic-gate 		return (DDI_SUCCESS);
88350Sstevel@tonic-gate 	}
88360Sstevel@tonic-gate 	if (canblock == PM_CANBLOCK_FAIL) {
88370Sstevel@tonic-gate 		PM_UNLOCK_POWER(dip, circ);
88380Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
88390Sstevel@tonic-gate 		return (DDI_FAILURE);
88400Sstevel@tonic-gate 	}
88410Sstevel@tonic-gate 	if (canblock == PM_CANBLOCK_BLOCK) {
88420Sstevel@tonic-gate 		/*
88430Sstevel@tonic-gate 		 * To avoid a deadlock, we must not hold the
88440Sstevel@tonic-gate 		 * power lock when we pm_block.
88450Sstevel@tonic-gate 		 */
88460Sstevel@tonic-gate 		PM_UNLOCK_POWER(dip, circ);
88470Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: blocking\n", pmf))
88480Sstevel@tonic-gate 		    /* pm_block releases dip lock */
88490Sstevel@tonic-gate 		    switch (pm_block(dip, comp, nlevel, clevel)) {
88500Sstevel@tonic-gate 		    case PMP_RELEASE:
88510Sstevel@tonic-gate 				return (EAGAIN);
88520Sstevel@tonic-gate 		    case PMP_SUCCEED:
88530Sstevel@tonic-gate 				return (DDI_SUCCESS);
88540Sstevel@tonic-gate 		    case PMP_FAIL:
88550Sstevel@tonic-gate 				return (DDI_FAILURE);
88560Sstevel@tonic-gate 		    }
88570Sstevel@tonic-gate 	} else {
88580Sstevel@tonic-gate 		ASSERT(0);
88590Sstevel@tonic-gate 	}
88600Sstevel@tonic-gate 	_NOTE(NOTREACHED);
88610Sstevel@tonic-gate 	return (DDI_FAILURE);	/* keep gcc happy */
88620Sstevel@tonic-gate }
88630Sstevel@tonic-gate 
88640Sstevel@tonic-gate static int
88650Sstevel@tonic-gate pm_all_to_normal_nexus(dev_info_t *dip, pm_canblock_t canblock)
88660Sstevel@tonic-gate {
88670Sstevel@tonic-gate 	PMD_FUNC(pmf, "all_to_normal_nexus")
88680Sstevel@tonic-gate 	int		*normal;
88690Sstevel@tonic-gate 	int		i, ncomps;
88700Sstevel@tonic-gate 	size_t		size;
88710Sstevel@tonic-gate 	int		changefailed = 0;
88720Sstevel@tonic-gate 	int		ret, result = DDI_SUCCESS;
88730Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t	bpn;
88740Sstevel@tonic-gate 	pm_sp_misc_t	pspm;
88750Sstevel@tonic-gate 
88760Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip));
88770Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
88780Sstevel@tonic-gate 	if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
88790Sstevel@tonic-gate 		PMD(PMD_ALLNORM, ("%s: can't get norm pwrs\n", pmf))
88800Sstevel@tonic-gate 		return (DDI_FAILURE);
88810Sstevel@tonic-gate 	}
88820Sstevel@tonic-gate 	ncomps = PM_NUMCMPTS(dip);
88830Sstevel@tonic-gate 	for (i = 0; i < ncomps; i++) {
88840Sstevel@tonic-gate 		bpn.bpn_dip = dip;
88850Sstevel@tonic-gate 		bpn.bpn_comp = i;
88860Sstevel@tonic-gate 		bpn.bpn_level = normal[i];
88870Sstevel@tonic-gate 		pspm.pspm_canblock = canblock;
88880Sstevel@tonic-gate 		pspm.pspm_scan = 0;
88890Sstevel@tonic-gate 		bpn.bpn_private = &pspm;
88900Sstevel@tonic-gate 		ret = pm_busop_bus_power(dip, NULL, BUS_POWER_NEXUS_PWRUP,
88910Sstevel@tonic-gate 		    (void *)&bpn, (void *)&result);
88920Sstevel@tonic-gate 		if (ret != DDI_SUCCESS || result != DDI_SUCCESS) {
88930Sstevel@tonic-gate 			PMD(PMD_FAIL | PMD_ALLNORM, ("%s: %s@%s(%s#%d)[%d] "
88940Sstevel@tonic-gate 			    "->%d failure result %d\n", pmf, PM_DEVICE(dip),
88950Sstevel@tonic-gate 			    i, normal[i], result))
88960Sstevel@tonic-gate 			changefailed++;
88970Sstevel@tonic-gate 		}
88980Sstevel@tonic-gate 	}
88990Sstevel@tonic-gate 	kmem_free(normal, size);
89000Sstevel@tonic-gate 	if (changefailed) {
89010Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) "
89020Sstevel@tonic-gate 		    "full power\n", pmf, changefailed, PM_DEVICE(dip)))
89030Sstevel@tonic-gate 		return (DDI_FAILURE);
89040Sstevel@tonic-gate 	}
89050Sstevel@tonic-gate 	return (DDI_SUCCESS);
89060Sstevel@tonic-gate }
89070Sstevel@tonic-gate 
89080Sstevel@tonic-gate int
89090Sstevel@tonic-gate pm_noinvol_update(int subcmd, int volpmd, int wasvolpmd, char *path,
89100Sstevel@tonic-gate     dev_info_t *tdip)
89110Sstevel@tonic-gate {
89120Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_update")
89130Sstevel@tonic-gate 	pm_bp_noinvol_t args;
89140Sstevel@tonic-gate 	int ret;
89150Sstevel@tonic-gate 	int result = DDI_SUCCESS;
89160Sstevel@tonic-gate 
89170Sstevel@tonic-gate 	args.bpni_path = path;
89180Sstevel@tonic-gate 	args.bpni_dip = tdip;
89190Sstevel@tonic-gate 	args.bpni_cmd = subcmd;
89200Sstevel@tonic-gate 	args.bpni_wasvolpmd = wasvolpmd;
89210Sstevel@tonic-gate 	args.bpni_volpmd = volpmd;
89220Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: update for path %s tdip %p subcmd %d "
89230Sstevel@tonic-gate 	    "volpmd %d wasvolpmd %d\n", pmf,
89240Sstevel@tonic-gate 	    path, (void *)tdip, subcmd, wasvolpmd, volpmd))
89250Sstevel@tonic-gate 	ret = pm_busop_bus_power(ddi_root_node(), NULL, BUS_POWER_NOINVOL,
89260Sstevel@tonic-gate 	    &args, &result);
89270Sstevel@tonic-gate 	return (ret);
89280Sstevel@tonic-gate }
89290Sstevel@tonic-gate 
89300Sstevel@tonic-gate void
89310Sstevel@tonic-gate pm_noinvol_update_node(dev_info_t *dip, pm_bp_noinvol_t *req)
89320Sstevel@tonic-gate {
89330Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_update_node")
89340Sstevel@tonic-gate 
89350Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
89360Sstevel@tonic-gate 	switch (req->bpni_cmd) {
89370Sstevel@tonic-gate 	case PM_BP_NOINVOL_ATTACH:
89380Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: PM_PB_NOINVOL_ATTACH %s@%s(%s#%d) "
89390Sstevel@tonic-gate 		    "noinvol %d->%d\n", pmf, PM_DEVICE(dip),
89400Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm,
89410Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm - 1))
89420Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_noinvolpm);
89430Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
89440Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm--;
89450Sstevel@tonic-gate 		if (req->bpni_wasvolpmd) {
89460Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_ATTACH "
89470Sstevel@tonic-gate 			    "%s@%s(%s#%d) volpmd %d->%d\n", pmf,
89480Sstevel@tonic-gate 			    PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd,
89490Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd - 1))
89500Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_volpmd)
89510Sstevel@tonic-gate 				DEVI(dip)->devi_pm_volpmd--;
89520Sstevel@tonic-gate 		}
89530Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
89540Sstevel@tonic-gate 		break;
89550Sstevel@tonic-gate 
89560Sstevel@tonic-gate 	case PM_BP_NOINVOL_DETACH:
89570Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH %s@%s(%s#%d) "
89580Sstevel@tonic-gate 		    "noinvolpm %d->%d\n", pmf, PM_DEVICE(dip),
89590Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm,
89600Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm + 1))
89610Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
89620Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm++;
89630Sstevel@tonic-gate 		if (req->bpni_wasvolpmd) {
89640Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH "
89650Sstevel@tonic-gate 			    "%s@%s(%s#%d) volpmd %d->%d\n", pmf,
89660Sstevel@tonic-gate 			    PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd,
89670Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd + 1))
89680Sstevel@tonic-gate 			DEVI(dip)->devi_pm_volpmd++;
89690Sstevel@tonic-gate 		}
89700Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
89710Sstevel@tonic-gate 		break;
89720Sstevel@tonic-gate 
89730Sstevel@tonic-gate 	case PM_BP_NOINVOL_REMDRV:
89740Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
89750Sstevel@tonic-gate 		    "noinvol %d->%d\n", pmf, PM_DEVICE(dip),
89760Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm,
89770Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm - 1))
89780Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_noinvolpm);
89790Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
89800Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm--;
89810Sstevel@tonic-gate 		if (req->bpni_wasvolpmd) {
89820Sstevel@tonic-gate 			PMD(PMD_NOINVOL,
89830Sstevel@tonic-gate 			    ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
89840Sstevel@tonic-gate 			    "volpmd %d->%d\n", pmf, PM_DEVICE(dip),
89850Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd,
89860Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd - 1))
89870Sstevel@tonic-gate 			/*
89880Sstevel@tonic-gate 			 * A power up could come in between and
89890Sstevel@tonic-gate 			 * clear the volpmd, if that's the case,
89900Sstevel@tonic-gate 			 * volpmd would be clear.
89910Sstevel@tonic-gate 			 */
89920Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_volpmd)
89930Sstevel@tonic-gate 				DEVI(dip)->devi_pm_volpmd--;
89940Sstevel@tonic-gate 		}
89950Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
89960Sstevel@tonic-gate 		break;
89970Sstevel@tonic-gate 
89980Sstevel@tonic-gate 	case PM_BP_NOINVOL_CFB:
89990Sstevel@tonic-gate 		PMD(PMD_NOINVOL,
90000Sstevel@tonic-gate 		    ("%s: PM_BP_NOIVOL_CFB %s@%s(%s#%d) noinvol %d->%d\n",
90010Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), DEVI(dip)->devi_pm_noinvolpm,
90020Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm + 1))
90030Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
90040Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm++;
90050Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
90060Sstevel@tonic-gate 		break;
90070Sstevel@tonic-gate 
90080Sstevel@tonic-gate 	case PM_BP_NOINVOL_POWER:
90090Sstevel@tonic-gate 		PMD(PMD_NOINVOL,
90100Sstevel@tonic-gate 		    ("%s: PM_BP_NOIVOL_PWR %s@%s(%s#%d) volpmd %d->%d\n",
90110Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip),
90120Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_volpmd, DEVI(dip)->devi_pm_volpmd -
90130Sstevel@tonic-gate 		    req->bpni_volpmd))
90140Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
90150Sstevel@tonic-gate 		DEVI(dip)->devi_pm_volpmd -= req->bpni_volpmd;
90160Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
90170Sstevel@tonic-gate 		break;
90180Sstevel@tonic-gate 
90190Sstevel@tonic-gate 	default:
90200Sstevel@tonic-gate 		break;
90210Sstevel@tonic-gate 	}
90220Sstevel@tonic-gate 
90230Sstevel@tonic-gate }
90240Sstevel@tonic-gate 
90250Sstevel@tonic-gate #ifdef DEBUG
90260Sstevel@tonic-gate static int
90270Sstevel@tonic-gate pm_desc_pwrchk_walk(dev_info_t *dip, void *arg)
90280Sstevel@tonic-gate {
90290Sstevel@tonic-gate 	PMD_FUNC(pmf, "desc_pwrchk")
90300Sstevel@tonic-gate 	pm_desc_pwrchk_t *pdpchk = (pm_desc_pwrchk_t *)arg;
90310Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
90320Sstevel@tonic-gate 	int i, curpwr, ce_level;
90330Sstevel@tonic-gate 
90340Sstevel@tonic-gate 	if (!info)
90350Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
90360Sstevel@tonic-gate 
90370Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
90380Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
90390Sstevel@tonic-gate 		if ((curpwr = PM_CURPOWER(dip, i)) == 0)
90400Sstevel@tonic-gate 			continue;
90410Sstevel@tonic-gate 		ce_level = (pdpchk->pdpc_par_involved == 0) ? CE_PANIC :
90420Sstevel@tonic-gate 		    CE_WARN;
90430Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d) is powered off while desc "
90440Sstevel@tonic-gate 		    "%s@%s(%s#%d)[%d] is at %d\n", pmf,
90450Sstevel@tonic-gate 		    PM_DEVICE(pdpchk->pdpc_dip), PM_DEVICE(dip), i, curpwr))
90460Sstevel@tonic-gate 		cmn_err(ce_level, "!device %s@%s(%s#%d) is powered on, "
90470Sstevel@tonic-gate 		    "while its ancestor, %s@%s(%s#%d), is powering off!",
90480Sstevel@tonic-gate 		    PM_DEVICE(dip), PM_DEVICE(pdpchk->pdpc_dip));
90490Sstevel@tonic-gate 	}
90500Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
90510Sstevel@tonic-gate }
90520Sstevel@tonic-gate #endif
90530Sstevel@tonic-gate 
90540Sstevel@tonic-gate /*
90550Sstevel@tonic-gate  * Record the fact that one thread is borrowing the lock on a device node.
90560Sstevel@tonic-gate  * Use is restricted to the case where the lending thread will block until
90570Sstevel@tonic-gate  * the borrowing thread (always curthread) completes.
90580Sstevel@tonic-gate  */
90590Sstevel@tonic-gate void
90600Sstevel@tonic-gate pm_borrow_lock(kthread_t *lender)
90610Sstevel@tonic-gate {
90620Sstevel@tonic-gate 	lock_loan_t *prev = &lock_loan_head;
90630Sstevel@tonic-gate 	lock_loan_t *cur = (lock_loan_t *)kmem_zalloc(sizeof (*cur), KM_SLEEP);
90640Sstevel@tonic-gate 
90650Sstevel@tonic-gate 	cur->pmlk_borrower = curthread;
90660Sstevel@tonic-gate 	cur->pmlk_lender = lender;
90670Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
90680Sstevel@tonic-gate 	cur->pmlk_next = prev->pmlk_next;
90690Sstevel@tonic-gate 	prev->pmlk_next = cur;
90700Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
90710Sstevel@tonic-gate }
90720Sstevel@tonic-gate 
90730Sstevel@tonic-gate /*
90740Sstevel@tonic-gate  * Return the borrowed lock.  A thread can borrow only one.
90750Sstevel@tonic-gate  */
90760Sstevel@tonic-gate void
90770Sstevel@tonic-gate pm_return_lock(void)
90780Sstevel@tonic-gate {
90790Sstevel@tonic-gate 	lock_loan_t *cur;
90800Sstevel@tonic-gate 	lock_loan_t *prev = &lock_loan_head;
90810Sstevel@tonic-gate 
90820Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
90830Sstevel@tonic-gate 	ASSERT(prev->pmlk_next != NULL);
90840Sstevel@tonic-gate 	for (cur = prev->pmlk_next; cur; prev = cur, cur = cur->pmlk_next)
90850Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
90860Sstevel@tonic-gate 			break;
90870Sstevel@tonic-gate 
90880Sstevel@tonic-gate 	ASSERT(cur != NULL);
90890Sstevel@tonic-gate 	prev->pmlk_next = cur->pmlk_next;
90900Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
90910Sstevel@tonic-gate 	kmem_free(cur, sizeof (*cur));
90920Sstevel@tonic-gate }
9093