xref: /illumos-gate/usr/src/uts/common/os/sunpm.c (revision d5ebc493)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5144dfaa9Scth  * Common Development and Distribution License (the "License").
6144dfaa9Scth  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
220e751525SEric Saxe  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
25e5f4d43eSGarrett D'Amore /*
26e5f4d43eSGarrett D'Amore  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
273fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
28e5f4d43eSGarrett D'Amore  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * sunpm.c builds sunpm.o	"power management framework"
327c478bd9Sstevel@tonic-gate  *	kernel-resident power management code.  Implements power management
337c478bd9Sstevel@tonic-gate  *	policy
347c478bd9Sstevel@tonic-gate  *	Assumes: all backwards compat. device components wake up on &
357c478bd9Sstevel@tonic-gate  *		 the pm_info pointer in dev_info is initially NULL
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * PM - (device) Power Management
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  * Each device may have 0 or more components.  If a device has no components,
407c478bd9Sstevel@tonic-gate  * then it can't be power managed.  Each component has 2 or more
417c478bd9Sstevel@tonic-gate  * power states.
427c478bd9Sstevel@tonic-gate  *
437c478bd9Sstevel@tonic-gate  * "Backwards Compatible" (bc) devices:
447c478bd9Sstevel@tonic-gate  * There are two different types of devices from the point of view of this
457c478bd9Sstevel@tonic-gate  * code.  The original type, left over from the original PM implementation on
467c478bd9Sstevel@tonic-gate  * the voyager platform are known in this code as "backwards compatible"
477c478bd9Sstevel@tonic-gate  * devices (PM_ISBC(dip) returns true).
487c478bd9Sstevel@tonic-gate  * They are recognized by the pm code by the lack of a pm-components property
497c478bd9Sstevel@tonic-gate  * and a call made by the driver to pm_create_components(9F).
507c478bd9Sstevel@tonic-gate  * For these devices, component 0 is special, and represents the power state
517c478bd9Sstevel@tonic-gate  * of the device.  If component 0 is to be set to power level 0 (off), then
527c478bd9Sstevel@tonic-gate  * the framework must first call into the driver's detach(9E) routine with
537c478bd9Sstevel@tonic-gate  * DDI_PM_SUSPEND, to get the driver to save the hardware state of the device.
547c478bd9Sstevel@tonic-gate  * After setting component 0 from 0 to a non-zero power level, a call must be
557c478bd9Sstevel@tonic-gate  * made into the driver's attach(9E) routine with DDI_PM_RESUME.
567c478bd9Sstevel@tonic-gate  *
577c478bd9Sstevel@tonic-gate  * Currently, the only way to get a bc device power managed is via a set of
587c478bd9Sstevel@tonic-gate  * ioctls (PM_DIRECT_PM, PM_SET_CURRENT_POWER) issued to /dev/pm.
597c478bd9Sstevel@tonic-gate  *
607c478bd9Sstevel@tonic-gate  * For non-bc devices, the driver describes the components by exporting a
617c478bd9Sstevel@tonic-gate  * pm-components(9P) property that tells how many components there are,
627c478bd9Sstevel@tonic-gate  * tells what each component's power state values are, and provides human
637c478bd9Sstevel@tonic-gate  * readable strings (currently unused) for each component name and power state.
647c478bd9Sstevel@tonic-gate  * Devices which export pm-components(9P) are automatically power managed
65bbf21555SRichard Lowe  * whenever autopm is enabled (via PM_START_PM ioctl issued by pmconfig(8)
66bbf21555SRichard Lowe  * after parsing power.conf(5)). The exception to this rule is that power
67c42872d4Smh  * manageable CPU devices may be automatically managed independently of autopm
68c42872d4Smh  * by either enabling or disabling (via PM_START_CPUPM and PM_STOP_CPUPM
69c42872d4Smh  * ioctls) cpupm. If the CPU devices are not managed independently, then they
70c42872d4Smh  * are managed by autopm. In either case, for automatically power managed
71c42872d4Smh  * devices, all components are considered independent of each other, and it is
72c42872d4Smh  * up to the driver to decide when a transition requires saving or restoring
73c42872d4Smh  * hardware state.
747c478bd9Sstevel@tonic-gate  *
757c478bd9Sstevel@tonic-gate  * Each device component also has a threshold time associated with each power
76bbf21555SRichard Lowe  * transition (see power.conf(5)), and a busy/idle state maintained by the
777c478bd9Sstevel@tonic-gate  * driver calling pm_idle_component(9F) and pm_busy_component(9F).
787c478bd9Sstevel@tonic-gate  * Components are created idle.
797c478bd9Sstevel@tonic-gate  *
807c478bd9Sstevel@tonic-gate  * The PM framework provides several functions:
81bbf21555SRichard Lowe  * -implement PM policy as described in power.conf(5)
82bbf21555SRichard Lowe  *  Policy is set by pmconfig(8) issuing pm ioctls based on power.conf(5).
837c478bd9Sstevel@tonic-gate  *  Policies consist of:
847c478bd9Sstevel@tonic-gate  *    -set threshold values (defaults if none provided by pmconfig)
857c478bd9Sstevel@tonic-gate  *    -set dependencies among devices
867c478bd9Sstevel@tonic-gate  *    -enable/disable autopm
87c42872d4Smh  *    -enable/disable cpupm
88c42872d4Smh  *    -turn down idle components based on thresholds (if autopm or cpupm is
89c42872d4Smh  *     enabled) (aka scanning)
907c478bd9Sstevel@tonic-gate  *    -maintain power states based on dependencies among devices
917c478bd9Sstevel@tonic-gate  *    -upon request, or when the frame buffer powers off, attempt to turn off
927c478bd9Sstevel@tonic-gate  *     all components that are idle or become idle over the next (10 sec)
937c478bd9Sstevel@tonic-gate  *     period in an attempt to get down to an EnergyStar compliant state
947c478bd9Sstevel@tonic-gate  *    -prevent powering off of a device which exported the
957c478bd9Sstevel@tonic-gate  *     pm-no-involuntary-power-cycles property without active involvement of
967c478bd9Sstevel@tonic-gate  *     the device's driver (so no removing power when the device driver is
977c478bd9Sstevel@tonic-gate  *     not attached)
987c478bd9Sstevel@tonic-gate  * -provide a mechanism for a device driver to request that a device's component
997c478bd9Sstevel@tonic-gate  *  be brought back to the power level necessary for the use of the device
1007c478bd9Sstevel@tonic-gate  * -allow a process to directly control the power levels of device components
1017c478bd9Sstevel@tonic-gate  *  (via ioctls issued to /dev/pm--see usr/src/uts/common/io/pm.c)
1027c478bd9Sstevel@tonic-gate  * -ensure that the console frame buffer is powered up before being referenced
1037c478bd9Sstevel@tonic-gate  *  via prom_printf() or other prom calls that might generate console output
1047c478bd9Sstevel@tonic-gate  * -maintain implicit dependencies (e.g. parent must be powered up if child is)
1057c478bd9Sstevel@tonic-gate  * -provide "backwards compatible" behavior for devices without pm-components
1067c478bd9Sstevel@tonic-gate  *  property
1077c478bd9Sstevel@tonic-gate  *
1087c478bd9Sstevel@tonic-gate  * Scanning:
109c42872d4Smh  * Whenever autopm or cpupm  is enabled, the framework attempts to bring each
110c42872d4Smh  * component of each managed device to its lowest power based on the threshold
111c42872d4Smh  * of idleness associated with each transition and the busy/idle state of the
112c42872d4Smh  * component.
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  * The actual work of this is done by pm_scan_dev(), which cycles through each
1157c478bd9Sstevel@tonic-gate  * component of a device, checking its idleness against its current threshold,
1167c478bd9Sstevel@tonic-gate  * and calling pm_set_power() as appropriate to change the power level.
1177c478bd9Sstevel@tonic-gate  * This function also indicates when it would next be profitable to scan the
1187c478bd9Sstevel@tonic-gate  * device again, and a new scan is scheduled after that time.
1197c478bd9Sstevel@tonic-gate  *
1207c478bd9Sstevel@tonic-gate  * Dependencies:
1217c478bd9Sstevel@tonic-gate  * It is possible to establish a dependency between the power states of two
1227c478bd9Sstevel@tonic-gate  * otherwise unrelated devices.  This is currently done to ensure that the
1237c478bd9Sstevel@tonic-gate  * cdrom is always up whenever the console framebuffer is up, so that the user
1247c478bd9Sstevel@tonic-gate  * can insert a cdrom and see a popup as a result.
1257c478bd9Sstevel@tonic-gate  *
126bbf21555SRichard Lowe  * The dependency terminology used in power.conf(5) is not easy to understand,
1277c478bd9Sstevel@tonic-gate  * so we've adopted a different terminology in the implementation.  We write
1287c478bd9Sstevel@tonic-gate  * of a "keeps up" and a "kept up" device.  A relationship can be established
1297c478bd9Sstevel@tonic-gate  * where one device keeps up another.  That means that if the keepsup device
1307c478bd9Sstevel@tonic-gate  * has any component that is at a non-zero power level, all components of the
1317c478bd9Sstevel@tonic-gate  * "kept up" device must be brought to full power.  This relationship is
1327c478bd9Sstevel@tonic-gate  * asynchronous.  When the keeping device is powered up, a request is queued
1337c478bd9Sstevel@tonic-gate  * to a worker thread to bring up the kept device.  The caller does not wait.
1347c478bd9Sstevel@tonic-gate  * Scan will not turn down a kept up device.
1357c478bd9Sstevel@tonic-gate  *
1367c478bd9Sstevel@tonic-gate  * Direct PM:
1377c478bd9Sstevel@tonic-gate  * A device may be directly power managed by a process.  If a device is
1387c478bd9Sstevel@tonic-gate  * directly pm'd, then it will not be scanned, and dependencies will not be
1397c478bd9Sstevel@tonic-gate  * enforced.  * If a directly pm'd device's driver requests a power change (via
1407c478bd9Sstevel@tonic-gate  * pm_raise_power(9F)), then the request is blocked and notification is sent
1417c478bd9Sstevel@tonic-gate  * to the controlling process, which must issue the requested power change for
1427c478bd9Sstevel@tonic-gate  * the driver to proceed.
1437c478bd9Sstevel@tonic-gate  *
1447c478bd9Sstevel@tonic-gate  */
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate #include <sys/types.h>
1477c478bd9Sstevel@tonic-gate #include <sys/errno.h>
1487c478bd9Sstevel@tonic-gate #include <sys/callb.h>		/* callback registration during CPR */
1497c478bd9Sstevel@tonic-gate #include <sys/conf.h>		/* driver flags and functions */
1507c478bd9Sstevel@tonic-gate #include <sys/open.h>		/* OTYP_CHR definition */
1517c478bd9Sstevel@tonic-gate #include <sys/stat.h>		/* S_IFCHR definition */
1527c478bd9Sstevel@tonic-gate #include <sys/pathname.h>	/* name -> dev_info xlation */
1537c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>	/* dev_info node fields */
1547c478bd9Sstevel@tonic-gate #include <sys/kmem.h>		/* memory alloc stuff */
1557c478bd9Sstevel@tonic-gate #include <sys/debug.h>
1567c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
1577c478bd9Sstevel@tonic-gate #include <sys/pm.h>
1587c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
1597c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
1607c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
1617c478bd9Sstevel@tonic-gate #include <sys/sunpm.h>
1627c478bd9Sstevel@tonic-gate #include <sys/epm.h>
1637c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
1647c478bd9Sstevel@tonic-gate #include <sys/mode.h>
1657c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
1667c478bd9Sstevel@tonic-gate #include <sys/promif.h>
1677c478bd9Sstevel@tonic-gate #include <sys/consdev.h>
1687c478bd9Sstevel@tonic-gate #include <sys/esunddi.h>
1697c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
1707c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
1717c478bd9Sstevel@tonic-gate #include <sys/note.h>
1727c478bd9Sstevel@tonic-gate #include <sys/taskq.h>
1737c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
1747c478bd9Sstevel@tonic-gate #include <sys/reboot.h>
1757c478bd9Sstevel@tonic-gate #include <sys/spl.h>
1767c478bd9Sstevel@tonic-gate #include <sys/disp.h>
1777c478bd9Sstevel@tonic-gate #include <sys/sobject.h>
1787c478bd9Sstevel@tonic-gate #include <sys/sunmdi.h>
1792df1fe9cSrandyf #include <sys/systm.h>
1802df1fe9cSrandyf #include <sys/cpuvar.h>
1812df1fe9cSrandyf #include <sys/cyclic.h>
1822df1fe9cSrandyf #include <sys/uadmin.h>
1832df1fe9cSrandyf #include <sys/srn.h>
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate /*
1877c478bd9Sstevel@tonic-gate  * PM LOCKING
1887c478bd9Sstevel@tonic-gate  *	The list of locks:
1897c478bd9Sstevel@tonic-gate  * Global pm mutex locks.
1907c478bd9Sstevel@tonic-gate  *
1917c478bd9Sstevel@tonic-gate  * pm_scan_lock:
1927c478bd9Sstevel@tonic-gate  *		It protects the timeout id of the scan thread, and the value
193c42872d4Smh  *		of autopm_enabled and cpupm.  This lock is not held
194c42872d4Smh  *		concurrently with any other PM locks.
1957c478bd9Sstevel@tonic-gate  *
1967c478bd9Sstevel@tonic-gate  * pm_clone_lock:	Protects the clone list and count of poll events
1977c478bd9Sstevel@tonic-gate  *		pending for the pm driver.
1987c478bd9Sstevel@tonic-gate  *		Lock ordering:
1997c478bd9Sstevel@tonic-gate  *			pm_clone_lock -> pm_pscc_interest_rwlock,
2007c478bd9Sstevel@tonic-gate  *			pm_clone_lock -> pm_pscc_direct_rwlock.
2017c478bd9Sstevel@tonic-gate  *
2027c478bd9Sstevel@tonic-gate  * pm_rsvp_lock:
2037c478bd9Sstevel@tonic-gate  *		Used to synchronize the data structures used for processes
2047c478bd9Sstevel@tonic-gate  *		to rendezvous with state change information when doing
2057c478bd9Sstevel@tonic-gate  *		direct PM.
2067c478bd9Sstevel@tonic-gate  *		Lock ordering:
2077c478bd9Sstevel@tonic-gate  *			pm_rsvp_lock -> pm_pscc_interest_rwlock,
2087c478bd9Sstevel@tonic-gate  *			pm_rsvp_lock -> pm_pscc_direct_rwlock,
2097c478bd9Sstevel@tonic-gate  *			pm_rsvp_lock -> pm_clone_lock.
2107c478bd9Sstevel@tonic-gate  *
2117c478bd9Sstevel@tonic-gate  * ppm_lock:	protects the list of registered ppm drivers
2127c478bd9Sstevel@tonic-gate  *		Lock ordering:
2137c478bd9Sstevel@tonic-gate  *			ppm_lock -> ppm driver unit_lock
2147c478bd9Sstevel@tonic-gate  *
2157c478bd9Sstevel@tonic-gate  * pm_compcnt_lock:
2167c478bd9Sstevel@tonic-gate  *		Protects count of components that are not at their lowest
2177c478bd9Sstevel@tonic-gate  *		power level.
2187c478bd9Sstevel@tonic-gate  *		Lock ordering:
2197c478bd9Sstevel@tonic-gate  *			pm_compcnt_lock -> ppm_lock.
2207c478bd9Sstevel@tonic-gate  *
2217c478bd9Sstevel@tonic-gate  * pm_dep_thread_lock:
2227c478bd9Sstevel@tonic-gate  *		Protects work list for pm_dep_thread.  Not taken concurrently
2237c478bd9Sstevel@tonic-gate  *		with any other pm lock.
2247c478bd9Sstevel@tonic-gate  *
2257c478bd9Sstevel@tonic-gate  * pm_remdrv_lock:
2267c478bd9Sstevel@tonic-gate  *		Serializes the operation of removing noinvol data structure
2277c478bd9Sstevel@tonic-gate  *		entries for a branch of the tree when a driver has been
2287c478bd9Sstevel@tonic-gate  *		removed from the system (modctl_rem_major).
2297c478bd9Sstevel@tonic-gate  *		Lock ordering:
2307c478bd9Sstevel@tonic-gate  *			pm_remdrv_lock -> pm_noinvol_rwlock.
2317c478bd9Sstevel@tonic-gate  *
2327c478bd9Sstevel@tonic-gate  * pm_cfb_lock: (High level spin lock)
2337c478bd9Sstevel@tonic-gate  *		Protects the count of how many components of the console
2347c478bd9Sstevel@tonic-gate  *		frame buffer are off (so we know if we have to bring up the
2357c478bd9Sstevel@tonic-gate  *		console as a result of a prom_printf, etc.
2367c478bd9Sstevel@tonic-gate  *		No other locks are taken while holding this lock.
2377c478bd9Sstevel@tonic-gate  *
2387c478bd9Sstevel@tonic-gate  * pm_loan_lock:
2397c478bd9Sstevel@tonic-gate  *		Protects the lock_loan list.  List is used to record that one
2407c478bd9Sstevel@tonic-gate  *		thread has acquired a power lock but has launched another thread
2417c478bd9Sstevel@tonic-gate  *		to complete its processing.  An entry in the list indicates that
2427c478bd9Sstevel@tonic-gate  *		the worker thread can borrow the lock held by the other thread,
2437c478bd9Sstevel@tonic-gate  *		which must block on the completion of the worker.  Use is
2447c478bd9Sstevel@tonic-gate  *		specific to module loading.
2457c478bd9Sstevel@tonic-gate  *		No other locks are taken while holding this lock.
2467c478bd9Sstevel@tonic-gate  *
2477c478bd9Sstevel@tonic-gate  * Global PM rwlocks
2487c478bd9Sstevel@tonic-gate  *
2497c478bd9Sstevel@tonic-gate  * pm_thresh_rwlock:
2507c478bd9Sstevel@tonic-gate  *		Protects the list of thresholds recorded for future use (when
2517c478bd9Sstevel@tonic-gate  *		devices attach).
2527c478bd9Sstevel@tonic-gate  *		Lock ordering:
2537c478bd9Sstevel@tonic-gate  *			pm_thresh_rwlock -> devi_pm_lock
2547c478bd9Sstevel@tonic-gate  *
2557c478bd9Sstevel@tonic-gate  * pm_noinvol_rwlock:
2567c478bd9Sstevel@tonic-gate  *		Protects list of detached nodes that had noinvol registered.
2577c478bd9Sstevel@tonic-gate  *		No other PM locks are taken while holding pm_noinvol_rwlock.
2587c478bd9Sstevel@tonic-gate  *
2597c478bd9Sstevel@tonic-gate  * pm_pscc_direct_rwlock:
2607c478bd9Sstevel@tonic-gate  *		Protects the list that maps devices being directly power
2617c478bd9Sstevel@tonic-gate  *		managed to the processes that manage them.
2627c478bd9Sstevel@tonic-gate  *		Lock ordering:
2637c478bd9Sstevel@tonic-gate  *			pm_pscc_direct_rwlock -> psce_lock
2647c478bd9Sstevel@tonic-gate  *
2657c478bd9Sstevel@tonic-gate  * pm_pscc_interest_rwlock;
2667c478bd9Sstevel@tonic-gate  *		Protects the list that maps state change events to processes
2677c478bd9Sstevel@tonic-gate  *		that want to know about them.
2687c478bd9Sstevel@tonic-gate  *		Lock ordering:
2697c478bd9Sstevel@tonic-gate  *			pm_pscc_interest_rwlock -> psce_lock
2707c478bd9Sstevel@tonic-gate  *
2717c478bd9Sstevel@tonic-gate  * per-dip locks:
2727c478bd9Sstevel@tonic-gate  *
2737c478bd9Sstevel@tonic-gate  * Each node has these per-dip locks, which are only used if the device is
2747c478bd9Sstevel@tonic-gate  * a candidate for power management (e.g. has pm components)
2757c478bd9Sstevel@tonic-gate  *
2767c478bd9Sstevel@tonic-gate  * devi_pm_lock:
2777c478bd9Sstevel@tonic-gate  *		Protects all power management state of the node except for
2787c478bd9Sstevel@tonic-gate  *		power level, which is protected by ndi_devi_enter().
2797c478bd9Sstevel@tonic-gate  *		Encapsulated in macros PM_LOCK_DIP()/PM_UNLOCK_DIP().
2807c478bd9Sstevel@tonic-gate  *		Lock ordering:
2817c478bd9Sstevel@tonic-gate  *			devi_pm_lock -> pm_rsvp_lock,
2827c478bd9Sstevel@tonic-gate  *			devi_pm_lock -> pm_dep_thread_lock,
2837c478bd9Sstevel@tonic-gate  *			devi_pm_lock -> pm_noinvol_rwlock,
2847c478bd9Sstevel@tonic-gate  *			devi_pm_lock -> power lock
2857c478bd9Sstevel@tonic-gate  *
2867c478bd9Sstevel@tonic-gate  * power lock (ndi_devi_enter()):
2877c478bd9Sstevel@tonic-gate  *		Since changing power level is possibly a slow operation (30
2887c478bd9Sstevel@tonic-gate  *		seconds to spin up a disk drive), this is locked separately.
2897c478bd9Sstevel@tonic-gate  *		Since a call into the driver to change the power level of one
2907c478bd9Sstevel@tonic-gate  *		component may result in a call back into the framework to change
2917c478bd9Sstevel@tonic-gate  *		the power level of another, this lock allows re-entrancy by
2927c478bd9Sstevel@tonic-gate  *		the same thread (ndi_devi_enter is used for this because
2937c478bd9Sstevel@tonic-gate  *		the USB framework uses ndi_devi_enter in its power entry point,
2947c478bd9Sstevel@tonic-gate  *		and use of any other lock would produce a deadlock.
2957c478bd9Sstevel@tonic-gate  *
2967c478bd9Sstevel@tonic-gate  * devi_pm_busy_lock:
2977c478bd9Sstevel@tonic-gate  *		This lock protects the integrity of the busy count.  It is
2987c478bd9Sstevel@tonic-gate  *		only taken by pm_busy_component() and pm_idle_component and
2997c478bd9Sstevel@tonic-gate  *		some code that adjust the busy time after the timer gets set
3007c478bd9Sstevel@tonic-gate  *		up or after a CPR operation.  It is per-dip to keep from
3017c478bd9Sstevel@tonic-gate  *		single-threading all the disk drivers on a system.
3027c478bd9Sstevel@tonic-gate  *		It could be per component instead, but most devices have
3037c478bd9Sstevel@tonic-gate  *		only one component.
3047c478bd9Sstevel@tonic-gate  *		No other PM locks are taken while holding this lock.
3057c478bd9Sstevel@tonic-gate  *
3067c478bd9Sstevel@tonic-gate  */
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate static int stdout_is_framebuffer;
3097c478bd9Sstevel@tonic-gate static kmutex_t	e_pm_power_lock;
3107c478bd9Sstevel@tonic-gate static kmutex_t pm_loan_lock;
3117c478bd9Sstevel@tonic-gate kmutex_t	pm_scan_lock;
3127c478bd9Sstevel@tonic-gate callb_id_t	pm_cpr_cb_id;
3137c478bd9Sstevel@tonic-gate callb_id_t	pm_panic_cb_id;
3147c478bd9Sstevel@tonic-gate callb_id_t	pm_halt_cb_id;
3157c478bd9Sstevel@tonic-gate int		pm_comps_notlowest;	/* no. of comps not at lowest power */
3167c478bd9Sstevel@tonic-gate int		pm_powering_down;	/* cpr is source of DDI_SUSPEND calls */
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate clock_t pm_id_ticks = 5;	/* ticks to wait before scan during idle-down */
319fc68e77cSmh clock_t pm_default_min_scan = PM_DEFAULT_MIN_SCAN;
320fc68e77cSmh clock_t pm_cpu_min_scan = PM_CPU_MIN_SCAN;
321fc68e77cSmh 
322fc68e77cSmh #define	PM_MIN_SCAN(dip)	(PM_ISCPU(dip) ? pm_cpu_min_scan : \
323fc68e77cSmh 				    pm_default_min_scan)
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate static int pm_busop_set_power(dev_info_t *,
3267c478bd9Sstevel@tonic-gate     void *, pm_bus_power_op_t, void *, void *);
3277c478bd9Sstevel@tonic-gate static int pm_busop_match_request(dev_info_t *, void *);
3287c478bd9Sstevel@tonic-gate static int pm_all_to_normal_nexus(dev_info_t *, pm_canblock_t);
3295cff7825Smh static void e_pm_set_max_power(dev_info_t *, int, int);
3305cff7825Smh static int e_pm_get_max_power(dev_info_t *, int);
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate /*
3337c478bd9Sstevel@tonic-gate  * Dependency Processing is done thru a seperate thread.
3347c478bd9Sstevel@tonic-gate  */
3357c478bd9Sstevel@tonic-gate kmutex_t	pm_dep_thread_lock;
3367c478bd9Sstevel@tonic-gate kcondvar_t	pm_dep_thread_cv;
3377c478bd9Sstevel@tonic-gate pm_dep_wk_t	*pm_dep_thread_workq = NULL;
3387c478bd9Sstevel@tonic-gate pm_dep_wk_t	*pm_dep_thread_tail = NULL;
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate /*
3417c478bd9Sstevel@tonic-gate  * Autopm  must be turned on by a PM_START_PM ioctl, so we don't end up
3427c478bd9Sstevel@tonic-gate  * power managing things in single user mode that have been suppressed via
3437c478bd9Sstevel@tonic-gate  * power.conf entries.  Protected by pm_scan_lock.
3447c478bd9Sstevel@tonic-gate  */
3457c478bd9Sstevel@tonic-gate int		autopm_enabled;
3467c478bd9Sstevel@tonic-gate 
347c42872d4Smh /*
348c42872d4Smh  * cpupm is turned on and off, by the PM_START_CPUPM and PM_STOP_CPUPM ioctls,
349c42872d4Smh  * to define the power management behavior of CPU devices separate from
350c42872d4Smh  * autopm. Protected by pm_scan_lock.
351c42872d4Smh  */
352c42872d4Smh pm_cpupm_t	cpupm = PM_CPUPM_NOTSET;
353c42872d4Smh 
3540e751525SEric Saxe /*
3550e751525SEric Saxe  * Defines the default mode of operation for CPU power management,
3560e751525SEric Saxe  * either the polling implementation, or the event based dispatcher driven
3570e751525SEric Saxe  * implementation.
3580e751525SEric Saxe  */
3590e751525SEric Saxe pm_cpupm_t	cpupm_default_mode = PM_CPUPM_EVENT;
3600e751525SEric Saxe 
3612df1fe9cSrandyf /*
3622df1fe9cSrandyf  * AutoS3 depends on autopm being enabled, and must be enabled by
3632df1fe9cSrandyf  * PM_START_AUTOS3 command.
3642df1fe9cSrandyf  */
3652df1fe9cSrandyf int		autoS3_enabled;
3662df1fe9cSrandyf 
3672df1fe9cSrandyf #if !defined(__sparc)
3682df1fe9cSrandyf /*
3692df1fe9cSrandyf  * on sparc these live in fillsysinfo.c
3702df1fe9cSrandyf  *
3712df1fe9cSrandyf  * If this variable is non-zero, cpr should return "not supported" when
3722df1fe9cSrandyf  * it is queried even though it would normally be supported on this platform.
3732df1fe9cSrandyf  */
3742df1fe9cSrandyf int cpr_supported_override;
3752df1fe9cSrandyf 
3762df1fe9cSrandyf /*
3772df1fe9cSrandyf  * Some platforms may need to support CPR even in the absence of
3782df1fe9cSrandyf  * having the correct platform id information.  If this
3792df1fe9cSrandyf  * variable is non-zero, cpr should proceed even in the absence
3802df1fe9cSrandyf  * of otherwise being qualified.
3812df1fe9cSrandyf  */
3822df1fe9cSrandyf int cpr_platform_enable = 0;
3832df1fe9cSrandyf 
3842df1fe9cSrandyf #endif
3852df1fe9cSrandyf 
3862df1fe9cSrandyf /*
3872df1fe9cSrandyf  * pm_S3_enabled indicates that we believe the platform can support S3,
388bbf21555SRichard Lowe  * which we get from pmconfig(8)
3892df1fe9cSrandyf  */
3902df1fe9cSrandyf int		pm_S3_enabled;
3912df1fe9cSrandyf 
3927c478bd9Sstevel@tonic-gate /*
3937c478bd9Sstevel@tonic-gate  * This flag is true while processes are stopped for a checkpoint/resume.
3947c478bd9Sstevel@tonic-gate  * Controlling processes of direct pm'd devices are not available to
3957c478bd9Sstevel@tonic-gate  * participate in power level changes, so we bypass them when this is set.
3967c478bd9Sstevel@tonic-gate  */
3977c478bd9Sstevel@tonic-gate static int	pm_processes_stopped;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate #ifdef	DEBUG
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate /*
4027c478bd9Sstevel@tonic-gate  * see common/sys/epm.h for PMD_* values
4037c478bd9Sstevel@tonic-gate  */
4042df1fe9cSrandyf 
4057c478bd9Sstevel@tonic-gate uint_t		pm_debug = 0;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate /*
4087c478bd9Sstevel@tonic-gate  * If pm_divertdebug is set, then no prom_printf calls will be made by
4097c478bd9Sstevel@tonic-gate  * PMD(), which will prevent debug output from bringing up the console
4107c478bd9Sstevel@tonic-gate  * frame buffer.  Clearing this variable before setting pm_debug will result
4117c478bd9Sstevel@tonic-gate  * in PMD output going to the console.
4127c478bd9Sstevel@tonic-gate  *
4137c478bd9Sstevel@tonic-gate  * pm_divertdebug is incremented in pm_set_power() if dip == cfb_dip to avoid
4147c478bd9Sstevel@tonic-gate  * deadlocks and decremented at the end of pm_set_power()
4157c478bd9Sstevel@tonic-gate  */
4167c478bd9Sstevel@tonic-gate uint_t		pm_divertdebug = 1;
4172df1fe9cSrandyf volatile uint_t pm_debug_to_console = 0;
4187c478bd9Sstevel@tonic-gate kmutex_t	pm_debug_lock;		/* protects pm_divertdebug */
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate void prdeps(char *);
4217c478bd9Sstevel@tonic-gate #endif
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate /* Globals */
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate /*
4267c478bd9Sstevel@tonic-gate  * List of recorded thresholds and dependencies
4277c478bd9Sstevel@tonic-gate  */
4287c478bd9Sstevel@tonic-gate pm_thresh_rec_t *pm_thresh_head;
4297c478bd9Sstevel@tonic-gate krwlock_t pm_thresh_rwlock;
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate pm_pdr_t *pm_dep_head;
4327c478bd9Sstevel@tonic-gate static int pm_unresolved_deps = 0;
4337c478bd9Sstevel@tonic-gate static int pm_prop_deps = 0;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate /*
4367c478bd9Sstevel@tonic-gate  * List of devices that exported no-involuntary-power-cycles property
4377c478bd9Sstevel@tonic-gate  */
4387c478bd9Sstevel@tonic-gate pm_noinvol_t *pm_noinvol_head;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate /*
4417c478bd9Sstevel@tonic-gate  * Locks used in noinvol processing
4427c478bd9Sstevel@tonic-gate  */
4437c478bd9Sstevel@tonic-gate krwlock_t pm_noinvol_rwlock;
4447c478bd9Sstevel@tonic-gate kmutex_t pm_remdrv_lock;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate int pm_default_idle_threshold = PM_DEFAULT_SYS_IDLENESS;
4477c478bd9Sstevel@tonic-gate int pm_system_idle_threshold;
448c42872d4Smh int pm_cpu_idle_threshold;
449c42872d4Smh 
4507c478bd9Sstevel@tonic-gate /*
4517c478bd9Sstevel@tonic-gate  * By default nexus has 0 threshold, and depends on its children to keep it up
4527c478bd9Sstevel@tonic-gate  */
4537c478bd9Sstevel@tonic-gate int pm_default_nexus_threshold = 0;
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate /*
4567c478bd9Sstevel@tonic-gate  * Data structures shared with common/io/pm.c
4577c478bd9Sstevel@tonic-gate  */
4587c478bd9Sstevel@tonic-gate kmutex_t	pm_clone_lock;
4597c478bd9Sstevel@tonic-gate kcondvar_t	pm_clones_cv[PM_MAX_CLONE];
4607c478bd9Sstevel@tonic-gate uint_t		pm_poll_cnt[PM_MAX_CLONE];	/* count of events for poll */
4617c478bd9Sstevel@tonic-gate unsigned char	pm_interest[PM_MAX_CLONE];
4627c478bd9Sstevel@tonic-gate struct pollhead	pm_pollhead;
4637c478bd9Sstevel@tonic-gate 
4642df1fe9cSrandyf /*
4652df1fe9cSrandyf  * Data structures shared with common/io/srn.c
4662df1fe9cSrandyf  */
4672df1fe9cSrandyf kmutex_t	srn_clone_lock;		/* protects srn_signal, srn_inuse */
4682df1fe9cSrandyf void (*srn_signal)(int type, int event);
4692df1fe9cSrandyf int srn_inuse;				/* stop srn detach */
4702df1fe9cSrandyf 
4717c478bd9Sstevel@tonic-gate extern int	hz;
4727c478bd9Sstevel@tonic-gate extern char	*platform_module_list[];
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate /*
4757c478bd9Sstevel@tonic-gate  * Wrappers for use in ddi_walk_devs
4767c478bd9Sstevel@tonic-gate  */
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate static int		pm_set_dev_thr_walk(dev_info_t *, void *);
4797c478bd9Sstevel@tonic-gate static int		pm_restore_direct_lvl_walk(dev_info_t *, void *);
4807c478bd9Sstevel@tonic-gate static int		pm_save_direct_lvl_walk(dev_info_t *, void *);
4817c478bd9Sstevel@tonic-gate static int		pm_discard_dep_walk(dev_info_t *, void *);
4827c478bd9Sstevel@tonic-gate #ifdef DEBUG
4837c478bd9Sstevel@tonic-gate static int		pm_desc_pwrchk_walk(dev_info_t *, void *);
4847c478bd9Sstevel@tonic-gate #endif
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate /*
4877c478bd9Sstevel@tonic-gate  * Routines for managing noinvol devices
4887c478bd9Sstevel@tonic-gate  */
4897c478bd9Sstevel@tonic-gate int			pm_noinvol_update(int, int, int, char *, dev_info_t *);
4907c478bd9Sstevel@tonic-gate void			pm_noinvol_update_node(dev_info_t *,
4917c478bd9Sstevel@tonic-gate 			    pm_bp_noinvol_t *req);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate kmutex_t pm_rsvp_lock;
4947c478bd9Sstevel@tonic-gate kmutex_t pm_compcnt_lock;
4957c478bd9Sstevel@tonic-gate krwlock_t pm_pscc_direct_rwlock;
4967c478bd9Sstevel@tonic-gate krwlock_t pm_pscc_interest_rwlock;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate #define	PSC_INTEREST	0	/* belongs to interest psc list */
4997c478bd9Sstevel@tonic-gate #define	PSC_DIRECT	1	/* belongs to direct psc list */
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate pscc_t *pm_pscc_interest;
5027c478bd9Sstevel@tonic-gate pscc_t *pm_pscc_direct;
5037c478bd9Sstevel@tonic-gate 
5045c066ec2SJerry Gilliam #define	PM_MAJOR(dip) ddi_driver_major(dip)
5055c066ec2SJerry Gilliam #define	PM_IS_NEXUS(dip) ((PM_MAJOR(dip) == DDI_MAJOR_T_NONE) ? 0 : \
5065c066ec2SJerry Gilliam 	NEXUS_DRV(devopsp[PM_MAJOR(dip)]))
5077c478bd9Sstevel@tonic-gate #define	POWERING_ON(old, new) ((old) == 0 && (new) != 0)
5087c478bd9Sstevel@tonic-gate #define	POWERING_OFF(old, new) ((old) != 0 && (new) == 0)
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate #define	PM_INCR_NOTLOWEST(dip) {					\
5117c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_compcnt_lock);					\
5127c478bd9Sstevel@tonic-gate 	if (!PM_IS_NEXUS(dip) ||					\
5137c478bd9Sstevel@tonic-gate 	    (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\
5147c478bd9Sstevel@tonic-gate 		if (pm_comps_notlowest == 0)				\
5157c478bd9Sstevel@tonic-gate 			pm_ppm_notify_all_lowest(dip, PM_NOT_ALL_LOWEST);\
5167c478bd9Sstevel@tonic-gate 		pm_comps_notlowest++;					\
5177c478bd9Sstevel@tonic-gate 		PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr notlowest->%d\n",\
5187c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), pm_comps_notlowest))		\
5197c478bd9Sstevel@tonic-gate 	}								\
5207c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_compcnt_lock);					\
5217c478bd9Sstevel@tonic-gate }
5227c478bd9Sstevel@tonic-gate #define	PM_DECR_NOTLOWEST(dip) {					\
5237c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_compcnt_lock);					\
5247c478bd9Sstevel@tonic-gate 	if (!PM_IS_NEXUS(dip) ||					\
5257c478bd9Sstevel@tonic-gate 	    (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\
5267c478bd9Sstevel@tonic-gate 		ASSERT(pm_comps_notlowest);				\
5277c478bd9Sstevel@tonic-gate 		pm_comps_notlowest--;					\
5287c478bd9Sstevel@tonic-gate 		PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr notlowest to "	\
5297c478bd9Sstevel@tonic-gate 			    "%d\n", pmf, PM_DEVICE(dip), pm_comps_notlowest))\
5307c478bd9Sstevel@tonic-gate 		if (pm_comps_notlowest == 0)				\
5317c478bd9Sstevel@tonic-gate 			pm_ppm_notify_all_lowest(dip, PM_ALL_LOWEST);	\
5327c478bd9Sstevel@tonic-gate 	}								\
5337c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_compcnt_lock);					\
5347c478bd9Sstevel@tonic-gate }
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate /*
5377c478bd9Sstevel@tonic-gate  * console frame-buffer power-management is not enabled when
5387c478bd9Sstevel@tonic-gate  * debugging services are present.  to override, set pm_cfb_override
5397c478bd9Sstevel@tonic-gate  * to non-zero.
5407c478bd9Sstevel@tonic-gate  */
5417c478bd9Sstevel@tonic-gate uint_t pm_cfb_comps_off = 0;	/* PM_LEVEL_UNKNOWN is considered on */
5427c478bd9Sstevel@tonic-gate kmutex_t pm_cfb_lock;
5437c478bd9Sstevel@tonic-gate int pm_cfb_enabled = 1;		/* non-zero allows pm of console frame buffer */
5447c478bd9Sstevel@tonic-gate #ifdef DEBUG
5457c478bd9Sstevel@tonic-gate int pm_cfb_override = 1;	/* non-zero allows pm of cfb with debuggers */
5467c478bd9Sstevel@tonic-gate #else
5477c478bd9Sstevel@tonic-gate int pm_cfb_override = 0;	/* non-zero allows pm of cfb with debuggers */
5487c478bd9Sstevel@tonic-gate #endif
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate static dev_info_t *cfb_dip = 0;
5517c478bd9Sstevel@tonic-gate static dev_info_t *cfb_dip_detaching = 0;
5527c478bd9Sstevel@tonic-gate uint_t cfb_inuse = 0;
5537c478bd9Sstevel@tonic-gate static ddi_softintr_t pm_soft_id;
554d3d50737SRafael Vanoni static boolean_t pm_soft_pending;
5557c478bd9Sstevel@tonic-gate int	pm_scans_disabled = 0;
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate /*
5587c478bd9Sstevel@tonic-gate  * A structure to record the fact that one thread has borrowed a lock held
5597c478bd9Sstevel@tonic-gate  * by another thread.  The context requires that the lender block on the
5607c478bd9Sstevel@tonic-gate  * completion of the borrower.
5617c478bd9Sstevel@tonic-gate  */
5627c478bd9Sstevel@tonic-gate typedef struct lock_loan {
5637c478bd9Sstevel@tonic-gate 	struct lock_loan	*pmlk_next;
5647c478bd9Sstevel@tonic-gate 	kthread_t		*pmlk_borrower;
5657c478bd9Sstevel@tonic-gate 	kthread_t		*pmlk_lender;
5667c478bd9Sstevel@tonic-gate 	dev_info_t		*pmlk_dip;
5677c478bd9Sstevel@tonic-gate } lock_loan_t;
5687c478bd9Sstevel@tonic-gate static lock_loan_t lock_loan_head;	/* list head is a dummy element */
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate #ifdef	DEBUG
5712df1fe9cSrandyf #ifdef	PMDDEBUG
5727c478bd9Sstevel@tonic-gate #define	PMD_FUNC(func, name)	char *(func) = (name);
5732df1fe9cSrandyf #else	/* !PMDDEBUG */
5747c478bd9Sstevel@tonic-gate #define	PMD_FUNC(func, name)
5752df1fe9cSrandyf #endif	/* PMDDEBUG */
5762df1fe9cSrandyf #else	/* !DEBUG */
5779681b4a1Skchow #define	PMD_FUNC(func, name)
5782df1fe9cSrandyf #endif	/* DEBUG */
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate /*
5827c478bd9Sstevel@tonic-gate  * Must be called before first device (including pseudo) attach
5837c478bd9Sstevel@tonic-gate  */
5847c478bd9Sstevel@tonic-gate void
pm_init_locks(void)5857c478bd9Sstevel@tonic-gate pm_init_locks(void)
5867c478bd9Sstevel@tonic-gate {
5877c478bd9Sstevel@tonic-gate 	mutex_init(&pm_scan_lock, NULL, MUTEX_DRIVER, NULL);
5887c478bd9Sstevel@tonic-gate 	mutex_init(&pm_rsvp_lock, NULL, MUTEX_DRIVER, NULL);
5897c478bd9Sstevel@tonic-gate 	mutex_init(&pm_compcnt_lock, NULL, MUTEX_DRIVER, NULL);
5907c478bd9Sstevel@tonic-gate 	mutex_init(&pm_dep_thread_lock, NULL, MUTEX_DRIVER, NULL);
5917c478bd9Sstevel@tonic-gate 	mutex_init(&pm_remdrv_lock, NULL, MUTEX_DRIVER, NULL);
5927c478bd9Sstevel@tonic-gate 	mutex_init(&pm_loan_lock, NULL, MUTEX_DRIVER, NULL);
5937c478bd9Sstevel@tonic-gate 	rw_init(&pm_thresh_rwlock, NULL, RW_DEFAULT, NULL);
5947c478bd9Sstevel@tonic-gate 	rw_init(&pm_noinvol_rwlock, NULL, RW_DEFAULT, NULL);
5957c478bd9Sstevel@tonic-gate 	cv_init(&pm_dep_thread_cv, NULL, CV_DEFAULT, NULL);
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate 
598d67944fbSScott Rotondo static int pm_reset_timestamps(dev_info_t *, void *);
599d67944fbSScott Rotondo 
6007c478bd9Sstevel@tonic-gate static boolean_t
pm_cpr_callb(void * arg,int code)6017c478bd9Sstevel@tonic-gate pm_cpr_callb(void *arg, int code)
6027c478bd9Sstevel@tonic-gate {
6037c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
6047c478bd9Sstevel@tonic-gate 	static int auto_save;
605c42872d4Smh 	static pm_cpupm_t cpupm_save;
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	switch (code) {
6087c478bd9Sstevel@tonic-gate 	case CB_CODE_CPR_CHKPT:
6097c478bd9Sstevel@tonic-gate 		/*
6107c478bd9Sstevel@tonic-gate 		 * Cancel scan or wait for scan in progress to finish
6117c478bd9Sstevel@tonic-gate 		 * Other threads may be trying to restart the scan, so we
6127c478bd9Sstevel@tonic-gate 		 * have to keep at it unil it sticks
6137c478bd9Sstevel@tonic-gate 		 */
6147c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
6157c478bd9Sstevel@tonic-gate 		ASSERT(!pm_scans_disabled);
6167c478bd9Sstevel@tonic-gate 		pm_scans_disabled = 1;
6177c478bd9Sstevel@tonic-gate 		auto_save = autopm_enabled;
6187c478bd9Sstevel@tonic-gate 		autopm_enabled = 0;
619c42872d4Smh 		cpupm_save = cpupm;
620c42872d4Smh 		cpupm = PM_CPUPM_NOTSET;
6217c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
6227c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_scan_stop_walk, NULL);
6237c478bd9Sstevel@tonic-gate 		break;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	case CB_CODE_CPR_RESUME:
6267c478bd9Sstevel@tonic-gate 		ASSERT(!autopm_enabled);
627c42872d4Smh 		ASSERT(cpupm == PM_CPUPM_NOTSET);
6287c478bd9Sstevel@tonic-gate 		ASSERT(pm_scans_disabled);
6297c478bd9Sstevel@tonic-gate 		pm_scans_disabled = 0;
6307c478bd9Sstevel@tonic-gate 		/*
6317c478bd9Sstevel@tonic-gate 		 * Call pm_reset_timestamps to reset timestamps of each
6327c478bd9Sstevel@tonic-gate 		 * device to the time when the system is resumed so that their
6337c478bd9Sstevel@tonic-gate 		 * idleness can be re-calculated. That's to avoid devices from
6347c478bd9Sstevel@tonic-gate 		 * being powered down right after resume if the system was in
6357c478bd9Sstevel@tonic-gate 		 * suspended mode long enough.
6367c478bd9Sstevel@tonic-gate 		 */
6377c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_reset_timestamps, NULL);
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		autopm_enabled = auto_save;
640c42872d4Smh 		cpupm = cpupm_save;
6417c478bd9Sstevel@tonic-gate 		/*
6427c478bd9Sstevel@tonic-gate 		 * If there is any auto-pm device, get the scanning
6437c478bd9Sstevel@tonic-gate 		 * going. Otherwise don't bother.
6447c478bd9Sstevel@tonic-gate 		 */
6457c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_rescan_walk, NULL);
6467c478bd9Sstevel@tonic-gate 		break;
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 	return (B_TRUE);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate /*
6527c478bd9Sstevel@tonic-gate  * This callback routine is called when there is a system panic.  This function
6537c478bd9Sstevel@tonic-gate  * exists for prototype matching.
6547c478bd9Sstevel@tonic-gate  */
6557c478bd9Sstevel@tonic-gate static boolean_t
pm_panic_callb(void * arg,int code)6567c478bd9Sstevel@tonic-gate pm_panic_callb(void *arg, int code)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg, code))
6597c478bd9Sstevel@tonic-gate 	void pm_cfb_check_and_powerup(void);
6607c478bd9Sstevel@tonic-gate 	PMD(PMD_CFB, ("pm_panic_callb\n"))
6617c478bd9Sstevel@tonic-gate 	pm_cfb_check_and_powerup();
6627c478bd9Sstevel@tonic-gate 	return (B_TRUE);
6637c478bd9Sstevel@tonic-gate }
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate static boolean_t
pm_halt_callb(void * arg,int code)6667c478bd9Sstevel@tonic-gate pm_halt_callb(void *arg, int code)
6677c478bd9Sstevel@tonic-gate {
6687c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg, code))
6692df1fe9cSrandyf 	return (B_TRUE);
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate 
672d67944fbSScott Rotondo static void pm_dep_thread(void);
673d67944fbSScott Rotondo 
6747c478bd9Sstevel@tonic-gate /*
6757c478bd9Sstevel@tonic-gate  * This needs to be called after the root and platform drivers are loaded
6767c478bd9Sstevel@tonic-gate  * and be single-threaded with respect to driver attach/detach
6777c478bd9Sstevel@tonic-gate  */
6787c478bd9Sstevel@tonic-gate void
pm_init(void)6797c478bd9Sstevel@tonic-gate pm_init(void)
6807c478bd9Sstevel@tonic-gate {
6817c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_init")
6827c478bd9Sstevel@tonic-gate 	char **mod;
6837c478bd9Sstevel@tonic-gate 	extern pri_t minclsyspri;
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	pm_comps_notlowest = 0;
6867c478bd9Sstevel@tonic-gate 	pm_system_idle_threshold = pm_default_idle_threshold;
687c42872d4Smh 	pm_cpu_idle_threshold = 0;
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	pm_cpr_cb_id = callb_add(pm_cpr_callb, (void *)NULL,
6907c478bd9Sstevel@tonic-gate 	    CB_CL_CPR_PM, "pm_cpr");
6917c478bd9Sstevel@tonic-gate 	pm_panic_cb_id = callb_add(pm_panic_callb, (void *)NULL,
6926152d498Smh 	    CB_CL_PANIC, "pm_panic");
6937c478bd9Sstevel@tonic-gate 	pm_halt_cb_id = callb_add(pm_halt_callb, (void *)NULL,
6946152d498Smh 	    CB_CL_HALT, "pm_halt");
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	/*
6977c478bd9Sstevel@tonic-gate 	 * Create a thread to do dependency processing.
6987c478bd9Sstevel@tonic-gate 	 */
6997c478bd9Sstevel@tonic-gate 	(void) thread_create(NULL, 0, (void (*)())pm_dep_thread, NULL, 0, &p0,
7007c478bd9Sstevel@tonic-gate 	    TS_RUN, minclsyspri);
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate 	/*
7037c478bd9Sstevel@tonic-gate 	 * loadrootmodules already loaded these ppm drivers, now get them
7047c478bd9Sstevel@tonic-gate 	 * attached so they can claim the root drivers as they attach
7057c478bd9Sstevel@tonic-gate 	 */
7067c478bd9Sstevel@tonic-gate 	for (mod = platform_module_list; *mod; mod++) {
7077c478bd9Sstevel@tonic-gate 		if (i_ddi_attach_hw_nodes(*mod) != DDI_SUCCESS) {
7087c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "!cannot load platform pm driver %s\n",
7097c478bd9Sstevel@tonic-gate 			    *mod);
7107c478bd9Sstevel@tonic-gate 		} else {
7117c478bd9Sstevel@tonic-gate 			PMD(PMD_DHR, ("%s: %s (%s)\n", pmf, *mod,
7127c478bd9Sstevel@tonic-gate 			    ddi_major_to_name(ddi_name_to_major(*mod))))
7137c478bd9Sstevel@tonic-gate 		}
7147c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate }
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate /*
718c42872d4Smh  * pm_scan_init - create pm scan data structure.  Called (if autopm or cpupm
719c42872d4Smh  * enabled) when device becomes power managed or after a failed detach and
720c42872d4Smh  * when autopm is started via PM_START_PM or PM_START_CPUPM ioctls, and after
721c42872d4Smh  * a CPR resume to get all the devices scanning again.
7227c478bd9Sstevel@tonic-gate  */
7237c478bd9Sstevel@tonic-gate void
pm_scan_init(dev_info_t * dip)7247c478bd9Sstevel@tonic-gate pm_scan_init(dev_info_t *dip)
7257c478bd9Sstevel@tonic-gate {
7267c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_init")
7277c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp;
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	ASSERT(!PM_ISBC(dip));
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
7327c478bd9Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
7337c478bd9Sstevel@tonic-gate 	if (!scanp) {
7347c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): create scan data\n",
7357c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
7367c478bd9Sstevel@tonic-gate 		scanp =  kmem_zalloc(sizeof (pm_scan_t), KM_SLEEP);
7377c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_scan = scanp;
7387c478bd9Sstevel@tonic-gate 	} else if (scanp->ps_scan_flags & PM_SCAN_STOP) {
7397c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): "
7407c478bd9Sstevel@tonic-gate 		    "clear PM_SCAN_STOP flag\n", pmf, PM_DEVICE(dip)))
7417c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~PM_SCAN_STOP;
7427c478bd9Sstevel@tonic-gate 	}
7437c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
7447c478bd9Sstevel@tonic-gate }
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate /*
7477c478bd9Sstevel@tonic-gate  * pm_scan_fini - remove pm scan data structure when stopping pm on the device
7487c478bd9Sstevel@tonic-gate  */
7497c478bd9Sstevel@tonic-gate void
pm_scan_fini(dev_info_t * dip)7507c478bd9Sstevel@tonic-gate pm_scan_fini(dev_info_t *dip)
7517c478bd9Sstevel@tonic-gate {
7527c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_fini")
7537c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
7567c478bd9Sstevel@tonic-gate 	ASSERT(!PM_ISBC(dip));
7577c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
7587c478bd9Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
7597c478bd9Sstevel@tonic-gate 	if (!scanp) {
7607c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
7617c478bd9Sstevel@tonic-gate 		return;
7627c478bd9Sstevel@tonic-gate 	}
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	ASSERT(!scanp->ps_scan_id && !(scanp->ps_scan_flags &
7657c478bd9Sstevel@tonic-gate 	    (PM_SCANNING | PM_SCAN_DISPATCHED | PM_SCAN_AGAIN)));
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	kmem_free(scanp, sizeof (pm_scan_t));
7687c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_scan = NULL;
7697c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
7707c478bd9Sstevel@tonic-gate }
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate /*
7737c478bd9Sstevel@tonic-gate  * Given a pointer to a component struct, return the current power level
7747c478bd9Sstevel@tonic-gate  * (struct contains index unless it is a continuous level).
7757c478bd9Sstevel@tonic-gate  * Located here in hopes of getting both this and dev_is_needed into the
7767c478bd9Sstevel@tonic-gate  * cache together
7777c478bd9Sstevel@tonic-gate  */
7787c478bd9Sstevel@tonic-gate static int
cur_power(pm_component_t * cp)7797c478bd9Sstevel@tonic-gate cur_power(pm_component_t *cp)
7807c478bd9Sstevel@tonic-gate {
7817c478bd9Sstevel@tonic-gate 	if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN)
7827c478bd9Sstevel@tonic-gate 		return (cp->pmc_cur_pwr);
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	return (cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]);
7857c478bd9Sstevel@tonic-gate }
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate static char *
pm_decode_direction(int direction)7887c478bd9Sstevel@tonic-gate pm_decode_direction(int direction)
7897c478bd9Sstevel@tonic-gate {
7907c478bd9Sstevel@tonic-gate 	switch (direction) {
7917c478bd9Sstevel@tonic-gate 	case PM_LEVEL_UPONLY:
7927c478bd9Sstevel@tonic-gate 		return ("up");
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	case PM_LEVEL_EXACT:
7957c478bd9Sstevel@tonic-gate 		return ("exact");
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	case PM_LEVEL_DOWNONLY:
7987c478bd9Sstevel@tonic-gate 		return ("down");
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	default:
8017c478bd9Sstevel@tonic-gate 		return ("INVALID DIRECTION");
8027c478bd9Sstevel@tonic-gate 	}
8037c478bd9Sstevel@tonic-gate }
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate char *
pm_decode_op(pm_bus_power_op_t op)8067c478bd9Sstevel@tonic-gate pm_decode_op(pm_bus_power_op_t op)
8077c478bd9Sstevel@tonic-gate {
8087c478bd9Sstevel@tonic-gate 	switch (op) {
8097c478bd9Sstevel@tonic-gate 	case BUS_POWER_CHILD_PWRCHG:
8107c478bd9Sstevel@tonic-gate 		return ("CHILD_PWRCHG");
8117c478bd9Sstevel@tonic-gate 	case BUS_POWER_NEXUS_PWRUP:
8127c478bd9Sstevel@tonic-gate 		return ("NEXUS_PWRUP");
8137c478bd9Sstevel@tonic-gate 	case BUS_POWER_PRE_NOTIFICATION:
8147c478bd9Sstevel@tonic-gate 		return ("PRE_NOTIFICATION");
8157c478bd9Sstevel@tonic-gate 	case BUS_POWER_POST_NOTIFICATION:
8167c478bd9Sstevel@tonic-gate 		return ("POST_NOTIFICATION");
8177c478bd9Sstevel@tonic-gate 	case BUS_POWER_HAS_CHANGED:
8187c478bd9Sstevel@tonic-gate 		return ("HAS_CHANGED");
8197c478bd9Sstevel@tonic-gate 	case BUS_POWER_NOINVOL:
8207c478bd9Sstevel@tonic-gate 		return ("NOINVOL");
8217c478bd9Sstevel@tonic-gate 	default:
8227c478bd9Sstevel@tonic-gate 		return ("UNKNOWN OP");
8237c478bd9Sstevel@tonic-gate 	}
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate /*
8277c478bd9Sstevel@tonic-gate  * Returns true if level is a possible (valid) power level for component
8287c478bd9Sstevel@tonic-gate  */
8297c478bd9Sstevel@tonic-gate int
e_pm_valid_power(dev_info_t * dip,int cmpt,int level)8307c478bd9Sstevel@tonic-gate e_pm_valid_power(dev_info_t *dip, int cmpt, int level)
8317c478bd9Sstevel@tonic-gate {
8327c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "e_pm_valid_power")
8337c478bd9Sstevel@tonic-gate 	pm_component_t *cp = PM_CP(dip, cmpt);
8347c478bd9Sstevel@tonic-gate 	int i;
8357c478bd9Sstevel@tonic-gate 	int *ip = cp->pmc_comp.pmc_lvals;
8367c478bd9Sstevel@tonic-gate 	int limit = cp->pmc_comp.pmc_numlevels;
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 	if (level < 0)
8397c478bd9Sstevel@tonic-gate 		return (0);
8407c478bd9Sstevel@tonic-gate 	for (i = 0; i < limit; i++) {
8417c478bd9Sstevel@tonic-gate 		if (level == *ip++)
8427c478bd9Sstevel@tonic-gate 			return (1);
8437c478bd9Sstevel@tonic-gate 	}
8447c478bd9Sstevel@tonic-gate #ifdef DEBUG
8457c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_FAIL) {
8467c478bd9Sstevel@tonic-gate 		ip = cp->pmc_comp.pmc_lvals;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		for (i = 0; i < limit; i++)
8497c478bd9Sstevel@tonic-gate 			PMD(PMD_FAIL, ("%s: index=%d, level=%d\n",
8507c478bd9Sstevel@tonic-gate 			    pmf, i, *ip++))
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate #endif
8537c478bd9Sstevel@tonic-gate 	return (0);
8547c478bd9Sstevel@tonic-gate }
8557c478bd9Sstevel@tonic-gate 
856d67944fbSScott Rotondo static int pm_start(dev_info_t *dip);
8577c478bd9Sstevel@tonic-gate /*
8587c478bd9Sstevel@tonic-gate  * Returns true if device is pm'd (after calling pm_start if need be)
8597c478bd9Sstevel@tonic-gate  */
8607c478bd9Sstevel@tonic-gate int
e_pm_valid_info(dev_info_t * dip,pm_info_t ** infop)8617c478bd9Sstevel@tonic-gate e_pm_valid_info(dev_info_t *dip, pm_info_t **infop)
8627c478bd9Sstevel@tonic-gate {
8637c478bd9Sstevel@tonic-gate 	pm_info_t *info;
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	/*
8667c478bd9Sstevel@tonic-gate 	 * Check if the device is power managed if not.
8677c478bd9Sstevel@tonic-gate 	 * To make the common case (device is power managed already)
8687c478bd9Sstevel@tonic-gate 	 * fast, we check without the lock.  If device is not already
8697c478bd9Sstevel@tonic-gate 	 * power managed, then we take the lock and the long route through
8707c478bd9Sstevel@tonic-gate 	 * go get it managed.  Devices never go unmanaged until they
8717c478bd9Sstevel@tonic-gate 	 * detach.
8727c478bd9Sstevel@tonic-gate 	 */
8737c478bd9Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
8747c478bd9Sstevel@tonic-gate 	if (!info) {
8757c478bd9Sstevel@tonic-gate 		if (!DEVI_IS_ATTACHING(dip)) {
8767c478bd9Sstevel@tonic-gate 			return (0);
8777c478bd9Sstevel@tonic-gate 		}
8787c478bd9Sstevel@tonic-gate 		if (pm_start(dip) != DDI_SUCCESS) {
8797c478bd9Sstevel@tonic-gate 			return (0);
8807c478bd9Sstevel@tonic-gate 		}
8817c478bd9Sstevel@tonic-gate 		info = PM_GET_PM_INFO(dip);
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 	ASSERT(info);
8847c478bd9Sstevel@tonic-gate 	if (infop != NULL)
8857c478bd9Sstevel@tonic-gate 		*infop = info;
8867c478bd9Sstevel@tonic-gate 	return (1);
8877c478bd9Sstevel@tonic-gate }
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate int
e_pm_valid_comp(dev_info_t * dip,int cmpt,pm_component_t ** cpp)8907c478bd9Sstevel@tonic-gate e_pm_valid_comp(dev_info_t *dip, int cmpt, pm_component_t **cpp)
8917c478bd9Sstevel@tonic-gate {
8927c478bd9Sstevel@tonic-gate 	if (cmpt >= 0 && cmpt < PM_NUMCMPTS(dip)) {
8937c478bd9Sstevel@tonic-gate 		if (cpp != NULL)
8947c478bd9Sstevel@tonic-gate 			*cpp = PM_CP(dip, cmpt);
8957c478bd9Sstevel@tonic-gate 		return (1);
8967c478bd9Sstevel@tonic-gate 	} else {
8977c478bd9Sstevel@tonic-gate 		return (0);
8987c478bd9Sstevel@tonic-gate 	}
8997c478bd9Sstevel@tonic-gate }
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate /*
9027c478bd9Sstevel@tonic-gate  * Internal guts of ddi_dev_is_needed and pm_raise/lower_power
9037c478bd9Sstevel@tonic-gate  */
9047c478bd9Sstevel@tonic-gate static int
dev_is_needed(dev_info_t * dip,int cmpt,int level,int direction)9057c478bd9Sstevel@tonic-gate dev_is_needed(dev_info_t *dip, int cmpt, int level, int direction)
9067c478bd9Sstevel@tonic-gate {
9077c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "din")
9087c478bd9Sstevel@tonic-gate 	pm_component_t *cp;
9097c478bd9Sstevel@tonic-gate 	char *pathbuf;
9107c478bd9Sstevel@tonic-gate 	int result;
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY);
9137c478bd9Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp) ||
9147c478bd9Sstevel@tonic-gate 	    !e_pm_valid_power(dip, cmpt, level))
9157c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	PMD(PMD_DIN, ("%s: %s@%s(%s#%d) cmpt=%d, dir=%s, new=%d, cur=%d\n",
9187c478bd9Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), cmpt, pm_decode_direction(direction),
9197c478bd9Sstevel@tonic-gate 	    level, cur_power(cp)))
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	if (pm_set_power(dip, cmpt, level,  direction,
9227c478bd9Sstevel@tonic-gate 	    PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) {
9237c478bd9Sstevel@tonic-gate 		if (direction == PM_LEVEL_UPONLY) {
9247c478bd9Sstevel@tonic-gate 			pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
9257c478bd9Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
9267c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "Device %s failed to power up.",
9277c478bd9Sstevel@tonic-gate 			    pathbuf);
9287c478bd9Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
9297c478bd9Sstevel@tonic-gate 		}
9307c478bd9Sstevel@tonic-gate 		PMD(PMD_DIN | PMD_FAIL, ("%s: %s@%s(%s#%d) [%d] %s->%d failed, "
9317c478bd9Sstevel@tonic-gate 		    "errno %d\n", pmf, PM_DEVICE(dip), cmpt,
9327c478bd9Sstevel@tonic-gate 		    pm_decode_direction(direction), level, result))
9337c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9347c478bd9Sstevel@tonic-gate 	}
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 	PMD(PMD_RESCAN | PMD_DIN, ("%s: pm_rescan %s@%s(%s#%d)\n", pmf,
9377c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip)))
9387c478bd9Sstevel@tonic-gate 	pm_rescan(dip);
9397c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
9407c478bd9Sstevel@tonic-gate }
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate /*
9437c478bd9Sstevel@tonic-gate  * We can get multiple pm_rescan() threads, if one of them discovers
9447c478bd9Sstevel@tonic-gate  * that no scan is running at the moment, it kicks it into action.
9457c478bd9Sstevel@tonic-gate  * Otherwise, it tells the current scanning thread to scan again when
9467c478bd9Sstevel@tonic-gate  * it is done by asserting the PM_SCAN_AGAIN flag. The PM_SCANNING and
9477c478bd9Sstevel@tonic-gate  * PM_SCAN_AGAIN flags are used to regulate scan, to make sure only one
9487c478bd9Sstevel@tonic-gate  * thread at a time runs the pm_scan_dev() code.
9497c478bd9Sstevel@tonic-gate  */
9507c478bd9Sstevel@tonic-gate void
pm_rescan(void * arg)9517c478bd9Sstevel@tonic-gate pm_rescan(void *arg)
9527c478bd9Sstevel@tonic-gate {
9537c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "rescan")
9547c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = (dev_info_t *)arg;
9557c478bd9Sstevel@tonic-gate 	pm_info_t	*info;
9567c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp;
9577c478bd9Sstevel@tonic-gate 	timeout_id_t	scanid;
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
9607c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
9617c478bd9Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
9627c478bd9Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
963c42872d4Smh 	if (pm_scans_disabled || !PM_SCANABLE(dip) || !info || !scanp ||
9647c478bd9Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_STOP)) {
9657c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
9667c478bd9Sstevel@tonic-gate 		return;
9677c478bd9Sstevel@tonic-gate 	}
9687c478bd9Sstevel@tonic-gate 	if (scanp->ps_scan_flags & PM_SCANNING) {
9697c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags |= PM_SCAN_AGAIN;
9707c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
9717c478bd9Sstevel@tonic-gate 		return;
9727c478bd9Sstevel@tonic-gate 	} else if (scanp->ps_scan_id) {
9737c478bd9Sstevel@tonic-gate 		scanid = scanp->ps_scan_id;
9747c478bd9Sstevel@tonic-gate 		scanp->ps_scan_id = 0;
9757c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): cancel timeout scanid %lx\n",
9767c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), (ulong_t)scanid))
9777c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
9787c478bd9Sstevel@tonic-gate 		(void) untimeout(scanid);
9797c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
9807c478bd9Sstevel@tonic-gate 	}
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	/*
9837c478bd9Sstevel@tonic-gate 	 * Dispatching pm_scan during attach time is risky due to the fact that
9847c478bd9Sstevel@tonic-gate 	 * attach might soon fail and dip dissolved, and panic may happen while
9857c478bd9Sstevel@tonic-gate 	 * attempting to stop scan. So schedule a pm_rescan instead.
9867c478bd9Sstevel@tonic-gate 	 * (Note that if either of the first two terms are true, taskq_dispatch
9877c478bd9Sstevel@tonic-gate 	 * will not be invoked).
9887c478bd9Sstevel@tonic-gate 	 *
9897c478bd9Sstevel@tonic-gate 	 * Multiple pm_scan dispatching is unecessary and costly to keep track
9907c478bd9Sstevel@tonic-gate 	 * of. The PM_SCAN_DISPATCHED flag is used between pm_rescan and pm_scan
9917c478bd9Sstevel@tonic-gate 	 * to regulate the dispatching.
9927c478bd9Sstevel@tonic-gate 	 *
9937c478bd9Sstevel@tonic-gate 	 * Scan is stopped before the device is detached (in pm_detaching())
9947c478bd9Sstevel@tonic-gate 	 * but it may get re-started during the post_detach processing if the
9957c478bd9Sstevel@tonic-gate 	 * driver fails to detach.
9967c478bd9Sstevel@tonic-gate 	 */
9977c478bd9Sstevel@tonic-gate 	if (DEVI_IS_ATTACHING(dip) ||
9987c478bd9Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_DISPATCHED) ||
999fc8ae2ecSToomas Soome 	    taskq_dispatch(system_taskq, pm_scan, (void *)dip, TQ_NOSLEEP) ==
1000fc8ae2ecSToomas Soome 	    TASKQID_INVALID) {
10017c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): attaching, pm_scan already "
10027c478bd9Sstevel@tonic-gate 		    "dispatched or dispatching failed\n", pmf, PM_DEVICE(dip)))
10037c478bd9Sstevel@tonic-gate 		if (scanp->ps_scan_id) {
10047c478bd9Sstevel@tonic-gate 			scanid = scanp->ps_scan_id;
10057c478bd9Sstevel@tonic-gate 			scanp->ps_scan_id = 0;
10067c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
10077c478bd9Sstevel@tonic-gate 			(void) untimeout(scanid);
10087c478bd9Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
10097c478bd9Sstevel@tonic-gate 			if (scanp->ps_scan_id) {
10107c478bd9Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): a competing "
10117c478bd9Sstevel@tonic-gate 				    "thread scheduled pm_rescan, scanid %lx\n",
10127c478bd9Sstevel@tonic-gate 				    pmf, PM_DEVICE(dip),
10137c478bd9Sstevel@tonic-gate 				    (ulong_t)scanp->ps_scan_id))
10147c478bd9Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
10157c478bd9Sstevel@tonic-gate 				return;
10167c478bd9Sstevel@tonic-gate 			}
10177c478bd9Sstevel@tonic-gate 		}
10187c478bd9Sstevel@tonic-gate 		scanp->ps_scan_id = timeout(pm_rescan, (void *)dip,
10197c478bd9Sstevel@tonic-gate 		    (scanp->ps_idle_down ? pm_id_ticks :
1020fc68e77cSmh 		    (PM_MIN_SCAN(dip) * hz)));
10217c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): scheduled next pm_rescan, "
10227c478bd9Sstevel@tonic-gate 		    "scanid %lx\n", pmf, PM_DEVICE(dip),
10237c478bd9Sstevel@tonic-gate 		    (ulong_t)scanp->ps_scan_id))
10247c478bd9Sstevel@tonic-gate 	} else {
10257c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: dispatched pm_scan for %s@%s(%s#%d)\n",
10267c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
10277c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags |= PM_SCAN_DISPATCHED;
10287c478bd9Sstevel@tonic-gate 	}
10297c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
10307c478bd9Sstevel@tonic-gate }
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate void
pm_scan(void * arg)10337c478bd9Sstevel@tonic-gate pm_scan(void *arg)
10347c478bd9Sstevel@tonic-gate {
10357c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan")
10367c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = (dev_info_t *)arg;
10377c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp;
10387c478bd9Sstevel@tonic-gate 	time_t		nextscan;
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
10437c478bd9Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
10447c478bd9Sstevel@tonic-gate 	ASSERT(scanp && PM_GET_PM_INFO(dip));
10457c478bd9Sstevel@tonic-gate 
1046c42872d4Smh 	if (pm_scans_disabled || !PM_SCANABLE(dip) ||
10477c478bd9Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_STOP)) {
10487c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~(PM_SCAN_AGAIN | PM_SCAN_DISPATCHED);
10497c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
10507c478bd9Sstevel@tonic-gate 		return;
10517c478bd9Sstevel@tonic-gate 	}
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	if (scanp->ps_idle_down) {
10547c478bd9Sstevel@tonic-gate 		/*
10557c478bd9Sstevel@tonic-gate 		 * make sure we remember idledown was in affect until
10567c478bd9Sstevel@tonic-gate 		 * we've completed the scan
10577c478bd9Sstevel@tonic-gate 		 */
10587c478bd9Sstevel@tonic-gate 		PMID_SET_SCANS(scanp->ps_idle_down)
10597c478bd9Sstevel@tonic-gate 		PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown starts "
10607c478bd9Sstevel@tonic-gate 		    "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down))
10617c478bd9Sstevel@tonic-gate 	}
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	/* possible having two threads running pm_scan() */
10647c478bd9Sstevel@tonic-gate 	if (scanp->ps_scan_flags & PM_SCANNING) {
10657c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags |= PM_SCAN_AGAIN;
10667c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: scanning, will scan %s@%s(%s#%d) again\n",
10677c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
10687c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED;
10697c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
10707c478bd9Sstevel@tonic-gate 		return;
10717c478bd9Sstevel@tonic-gate 	}
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	scanp->ps_scan_flags |= PM_SCANNING;
10747c478bd9Sstevel@tonic-gate 	scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED;
10757c478bd9Sstevel@tonic-gate 	do {
10767c478bd9Sstevel@tonic-gate 		scanp->ps_scan_flags &= ~PM_SCAN_AGAIN;
10777c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
10787c478bd9Sstevel@tonic-gate 		nextscan = pm_scan_dev(dip);
10797c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
10807c478bd9Sstevel@tonic-gate 	} while (scanp->ps_scan_flags & PM_SCAN_AGAIN);
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	ASSERT(scanp->ps_scan_flags & PM_SCANNING);
10837c478bd9Sstevel@tonic-gate 	scanp->ps_scan_flags &= ~PM_SCANNING;
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	if (scanp->ps_idle_down) {
10867c478bd9Sstevel@tonic-gate 		scanp->ps_idle_down &= ~PMID_SCANS;
10877c478bd9Sstevel@tonic-gate 		PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown ends "
10887c478bd9Sstevel@tonic-gate 		    "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down))
10897c478bd9Sstevel@tonic-gate 	}
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	/* schedule for next idle check */
10927c478bd9Sstevel@tonic-gate 	if (nextscan != LONG_MAX) {
10937c478bd9Sstevel@tonic-gate 		if (nextscan > (LONG_MAX / hz))
10947c478bd9Sstevel@tonic-gate 			nextscan = (LONG_MAX - 1) / hz;
10957c478bd9Sstevel@tonic-gate 		if (scanp->ps_scan_id) {
10967c478bd9Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): while scanning "
10977c478bd9Sstevel@tonic-gate 			    "another rescan scheduled scanid(%lx)\n", pmf,
10987c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip), (ulong_t)scanp->ps_scan_id))
10997c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
11007c478bd9Sstevel@tonic-gate 			return;
11017c478bd9Sstevel@tonic-gate 		} else if (!(scanp->ps_scan_flags & PM_SCAN_STOP)) {
11027c478bd9Sstevel@tonic-gate 			scanp->ps_scan_id = timeout(pm_rescan, (void *)dip,
11037c478bd9Sstevel@tonic-gate 			    (clock_t)(nextscan * hz));
11047c478bd9Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: nextscan for %s@%s(%s#%d) in "
11057c478bd9Sstevel@tonic-gate 			    "%lx sec, scanid(%lx) \n", pmf, PM_DEVICE(dip),
11067c478bd9Sstevel@tonic-gate 			    (ulong_t)nextscan, (ulong_t)scanp->ps_scan_id))
11077c478bd9Sstevel@tonic-gate 		}
11087c478bd9Sstevel@tonic-gate 	}
11097c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
11107c478bd9Sstevel@tonic-gate }
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate void
pm_get_timestamps(dev_info_t * dip,time_t * valuep)11137c478bd9Sstevel@tonic-gate pm_get_timestamps(dev_info_t *dip, time_t *valuep)
11147c478bd9Sstevel@tonic-gate {
11157c478bd9Sstevel@tonic-gate 	int components = PM_NUMCMPTS(dip);
11167c478bd9Sstevel@tonic-gate 	int i;
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 	ASSERT(components > 0);
11197c478bd9Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);	/* so we get a consistent view */
11207c478bd9Sstevel@tonic-gate 	for (i = 0; i < components; i++) {
11217c478bd9Sstevel@tonic-gate 		valuep[i] = PM_CP(dip, i)->pmc_timestamp;
11227c478bd9Sstevel@tonic-gate 	}
11237c478bd9Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
11247c478bd9Sstevel@tonic-gate }
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate /*
11277c478bd9Sstevel@tonic-gate  * Returns true if device needs to be kept up because it exported the
11287c478bd9Sstevel@tonic-gate  * "no-involuntary-power-cycles" property or we're pretending it did (console
11297c478bd9Sstevel@tonic-gate  * fb case) or it is an ancestor of such a device and has used up the "one
11307c478bd9Sstevel@tonic-gate  * free cycle" allowed when all such leaf nodes have voluntarily powered down
11317c478bd9Sstevel@tonic-gate  * upon detach
11327c478bd9Sstevel@tonic-gate  */
11337c478bd9Sstevel@tonic-gate int
pm_noinvol(dev_info_t * dip)11347c478bd9Sstevel@tonic-gate pm_noinvol(dev_info_t *dip)
11357c478bd9Sstevel@tonic-gate {
11367c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol")
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	/*
11397c478bd9Sstevel@tonic-gate 	 * This doesn't change over the life of a driver, so no locking needed
11407c478bd9Sstevel@tonic-gate 	 */
11417c478bd9Sstevel@tonic-gate 	if (PM_IS_CFB(dip)) {
11427c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB %s@%s(%s#%d)\n",
11437c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
11447c478bd9Sstevel@tonic-gate 		return (1);
11457c478bd9Sstevel@tonic-gate 	}
11467c478bd9Sstevel@tonic-gate 	/*
11477c478bd9Sstevel@tonic-gate 	 * Not an issue if no such kids
11487c478bd9Sstevel@tonic-gate 	 */
11497c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_noinvolpm == 0) {
11507c478bd9Sstevel@tonic-gate #ifdef DEBUG
11517c478bd9Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_volpmd != 0) {
11527c478bd9Sstevel@tonic-gate 			dev_info_t *pdip = dip;
11537c478bd9Sstevel@tonic-gate 			do {
11547c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d) noinvol %d "
11557c478bd9Sstevel@tonic-gate 				    "volpmd %d\n", pmf, PM_DEVICE(pdip),
11567c478bd9Sstevel@tonic-gate 				    DEVI(pdip)->devi_pm_noinvolpm,
11577c478bd9Sstevel@tonic-gate 				    DEVI(pdip)->devi_pm_volpmd))
11587c478bd9Sstevel@tonic-gate 				pdip = ddi_get_parent(pdip);
11597c478bd9Sstevel@tonic-gate 			} while (pdip);
11607c478bd9Sstevel@tonic-gate 		}
11617c478bd9Sstevel@tonic-gate #endif
11627c478bd9Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_volpmd == 0);
11637c478bd9Sstevel@tonic-gate 		return (0);
11647c478bd9Sstevel@tonic-gate 	}
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	/*
11677c478bd9Sstevel@tonic-gate 	 * Since we now maintain the counts correct at every node, we no longer
11687c478bd9Sstevel@tonic-gate 	 * need to look up the tree.  An ancestor cannot use up the free cycle
11697c478bd9Sstevel@tonic-gate 	 * without the children getting their counts adjusted.
11707c478bd9Sstevel@tonic-gate 	 */
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate #ifdef	DEBUG
11737c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd)
11747c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s@%s(%s#%d)\n", pmf,
11757c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm, DEVI(dip)->devi_pm_volpmd,
11767c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
11777c478bd9Sstevel@tonic-gate #endif
11787c478bd9Sstevel@tonic-gate 	return (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd);
11797c478bd9Sstevel@tonic-gate }
11807c478bd9Sstevel@tonic-gate 
1181d67944fbSScott Rotondo static int	cur_threshold(dev_info_t *, int);
1182d67944fbSScott Rotondo static int	pm_next_lower_power(pm_component_t *, int);
1183d67944fbSScott Rotondo 
11847c478bd9Sstevel@tonic-gate /*
11857c478bd9Sstevel@tonic-gate  * This function performs the actual scanning of the device.
11867c478bd9Sstevel@tonic-gate  * It attempts to power off the indicated device's components if they have
11877c478bd9Sstevel@tonic-gate  * been idle and other restrictions are met.
11887c478bd9Sstevel@tonic-gate  * pm_scan_dev calculates and returns when the next scan should happen for
11897c478bd9Sstevel@tonic-gate  * this device.
11907c478bd9Sstevel@tonic-gate  */
11917c478bd9Sstevel@tonic-gate time_t
pm_scan_dev(dev_info_t * dip)11927c478bd9Sstevel@tonic-gate pm_scan_dev(dev_info_t *dip)
11937c478bd9Sstevel@tonic-gate {
11947c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_dev")
11957c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp;
11967c478bd9Sstevel@tonic-gate 	time_t		*timestamp, idletime, now, thresh;
11977c478bd9Sstevel@tonic-gate 	time_t		timeleft = 0;
11989681b4a1Skchow #ifdef PMDDEBUG
11999681b4a1Skchow 	int		curpwr;
12009681b4a1Skchow #endif
12019681b4a1Skchow 	int		i, nxtpwr, pwrndx, unused;
12027c478bd9Sstevel@tonic-gate 	size_t		size;
12037c478bd9Sstevel@tonic-gate 	pm_component_t	 *cp;
12047c478bd9Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
1205fc68e77cSmh 	clock_t		min_scan = pm_default_min_scan;
12067c478bd9Sstevel@tonic-gate 
12077c478bd9Sstevel@tonic-gate 	/*
12087c478bd9Sstevel@tonic-gate 	 * skip attaching device
12097c478bd9Sstevel@tonic-gate 	 */
12107c478bd9Sstevel@tonic-gate 	if (DEVI_IS_ATTACHING(dip)) {
12117c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) is attaching, timeleft(%lx)\n",
1212fc68e77cSmh 		    pmf, PM_DEVICE(dip), min_scan))
1213fc68e77cSmh 		return (min_scan);
12147c478bd9Sstevel@tonic-gate 	}
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
12177c478bd9Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
1218fc68e77cSmh 	min_scan = PM_MIN_SCAN(dip);
12197c478bd9Sstevel@tonic-gate 	ASSERT(scanp && PM_GET_PM_INFO(dip));
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
12227c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): kuc is %d\n", pmf, PM_DEVICE(dip),
12237c478bd9Sstevel@tonic-gate 	    PM_KUC(dip)))
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	/* no scan under the following conditions */
1226c42872d4Smh 	if (pm_scans_disabled || !PM_SCANABLE(dip) ||
12277c478bd9Sstevel@tonic-gate 	    (scanp->ps_scan_flags & PM_SCAN_STOP) ||
12287c478bd9Sstevel@tonic-gate 	    (PM_KUC(dip) != 0) ||
12297c478bd9Sstevel@tonic-gate 	    PM_ISDIRECT(dip) || pm_noinvol(dip)) {
12307c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
12317c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: [END, %s@%s(%s#%d)] no scan, "
1232c42872d4Smh 		    "scan_disabled(%d), apm_enabled(%d), cpupm(%d), "
1233c42872d4Smh 		    "kuc(%d), %s directpm, %s pm_noinvol\n",
1234c42872d4Smh 		    pmf, PM_DEVICE(dip), pm_scans_disabled, autopm_enabled,
1235c42872d4Smh 		    cpupm, PM_KUC(dip),
12367c478bd9Sstevel@tonic-gate 		    PM_ISDIRECT(dip) ? "is" : "is not",
12377c478bd9Sstevel@tonic-gate 		    pm_noinvol(dip) ? "is" : "is not"))
12387c478bd9Sstevel@tonic-gate 		return (LONG_MAX);
12397c478bd9Sstevel@tonic-gate 	}
12407c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
12417c478bd9Sstevel@tonic-gate 
12423fe80ca4SDan Cross 	if (!ndi_devi_tryenter(pdip)) {
12437c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) can't hold pdip",
12447c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(pdip)))
12457c478bd9Sstevel@tonic-gate 		return ((time_t)1);
12467c478bd9Sstevel@tonic-gate 	}
12477c478bd9Sstevel@tonic-gate 	now = gethrestime_sec();
12487c478bd9Sstevel@tonic-gate 	size = PM_NUMCMPTS(dip) * sizeof (time_t);
12497c478bd9Sstevel@tonic-gate 	timestamp = kmem_alloc(size, KM_SLEEP);
12507c478bd9Sstevel@tonic-gate 	pm_get_timestamps(dip, timestamp);
12517c478bd9Sstevel@tonic-gate 
12527c478bd9Sstevel@tonic-gate 	/*
12537c478bd9Sstevel@tonic-gate 	 * Since we removed support for backwards compatible devices,
12547c478bd9Sstevel@tonic-gate 	 * (see big comment at top of file)
12557c478bd9Sstevel@tonic-gate 	 * it is no longer required to deal with component 0 last.
12567c478bd9Sstevel@tonic-gate 	 */
12577c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
12587c478bd9Sstevel@tonic-gate 		/*
12597c478bd9Sstevel@tonic-gate 		 * If already off (an optimization, perhaps)
12607c478bd9Sstevel@tonic-gate 		 */
12617c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
12627c478bd9Sstevel@tonic-gate 		pwrndx = cp->pmc_cur_pwr;
12639681b4a1Skchow #ifdef PMDDEBUG
12647c478bd9Sstevel@tonic-gate 		curpwr = (pwrndx == PM_LEVEL_UNKNOWN) ?
12657c478bd9Sstevel@tonic-gate 		    PM_LEVEL_UNKNOWN :
12667c478bd9Sstevel@tonic-gate 		    cp->pmc_comp.pmc_lvals[pwrndx];
12679681b4a1Skchow #endif
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 		if (pwrndx == 0) {
12707c478bd9Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d off or "
12717c478bd9Sstevel@tonic-gate 			    "lowest\n", pmf, PM_DEVICE(dip), i))
12727c478bd9Sstevel@tonic-gate 			/* skip device if off or at its lowest */
12737c478bd9Sstevel@tonic-gate 			continue;
12747c478bd9Sstevel@tonic-gate 		}
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 		thresh = cur_threshold(dip, i);		/* comp i threshold */
12777c478bd9Sstevel@tonic-gate 		if ((timestamp[i] == 0) || (cp->pmc_busycount > 0)) {
12787c478bd9Sstevel@tonic-gate 			/* were busy or newly became busy by another thread */
12797c478bd9Sstevel@tonic-gate 			if (timeleft == 0)
1280fc68e77cSmh 				timeleft = max(thresh, min_scan);
12817c478bd9Sstevel@tonic-gate 			else
12827c478bd9Sstevel@tonic-gate 				timeleft = min(
1283fc68e77cSmh 				    timeleft, max(thresh, min_scan));
12847c478bd9Sstevel@tonic-gate 			continue;
12857c478bd9Sstevel@tonic-gate 		}
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 		idletime = now - timestamp[i];		/* idle time */
12887c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d idle time %lx\n",
12897c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), i, idletime))
12907c478bd9Sstevel@tonic-gate 		if (idletime >= thresh || PM_IS_PID(dip)) {
12917c478bd9Sstevel@tonic-gate 			nxtpwr = pm_next_lower_power(cp, pwrndx);
12927c478bd9Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, %d->%d\n",
12937c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip), i, curpwr, nxtpwr))
12947c478bd9Sstevel@tonic-gate 			if (pm_set_power(dip, i, nxtpwr, PM_LEVEL_DOWNONLY,
12957c478bd9Sstevel@tonic-gate 			    PM_CANBLOCK_FAIL, 1, &unused) != DDI_SUCCESS &&
12967c478bd9Sstevel@tonic-gate 			    PM_CURPOWER(dip, i) != nxtpwr) {
12977c478bd9Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
12987c478bd9Sstevel@tonic-gate 				    "%d->%d Failed\n", pmf, PM_DEVICE(dip),
12997c478bd9Sstevel@tonic-gate 				    i, curpwr, nxtpwr))
1300fc68e77cSmh 				timeleft = min_scan;
13017c478bd9Sstevel@tonic-gate 				continue;
13027c478bd9Sstevel@tonic-gate 			} else {
13037c478bd9Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
13047c478bd9Sstevel@tonic-gate 				    "%d->%d, GOOD curpwr %d\n", pmf,
13057c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip), i, curpwr, nxtpwr,
13067c478bd9Sstevel@tonic-gate 				    cur_power(cp)))
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 				if (nxtpwr == 0)	/* component went off */
13097c478bd9Sstevel@tonic-gate 					continue;
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 				/*
13127c478bd9Sstevel@tonic-gate 				 * scan to next lower level
13137c478bd9Sstevel@tonic-gate 				 */
13147c478bd9Sstevel@tonic-gate 				if (timeleft == 0)
13157c478bd9Sstevel@tonic-gate 					timeleft = max(
13167c478bd9Sstevel@tonic-gate 					    1, cur_threshold(dip, i));
13177c478bd9Sstevel@tonic-gate 				else
13187c478bd9Sstevel@tonic-gate 					timeleft = min(timeleft,
13197c478bd9Sstevel@tonic-gate 					    max(1, cur_threshold(dip, i)));
13207c478bd9Sstevel@tonic-gate 				PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
13217c478bd9Sstevel@tonic-gate 				    "timeleft(%lx)\n", pmf, PM_DEVICE(dip),
13227c478bd9Sstevel@tonic-gate 				    i, timeleft))
13237c478bd9Sstevel@tonic-gate 			}
13247c478bd9Sstevel@tonic-gate 		} else {	/* comp not idle long enough */
13257c478bd9Sstevel@tonic-gate 			if (timeleft == 0)
13267c478bd9Sstevel@tonic-gate 				timeleft = thresh - idletime;
13277c478bd9Sstevel@tonic-gate 			else
13287c478bd9Sstevel@tonic-gate 				timeleft = min(timeleft, (thresh - idletime));
13297c478bd9Sstevel@tonic-gate 			PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, timeleft="
13307c478bd9Sstevel@tonic-gate 			    "%lx\n", pmf, PM_DEVICE(dip), i, timeleft))
13317c478bd9Sstevel@tonic-gate 		}
13327c478bd9Sstevel@tonic-gate 	}
13333fe80ca4SDan Cross 	ndi_devi_exit(pdip);
13347c478bd9Sstevel@tonic-gate 	kmem_free(timestamp, size);
13357c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] timeleft(%lx)\n", pmf,
13367c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip), timeleft))
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 	/*
13397c478bd9Sstevel@tonic-gate 	 * if components are already at lowest level, timeleft is left 0
13407c478bd9Sstevel@tonic-gate 	 */
13417c478bd9Sstevel@tonic-gate 	return ((timeleft == 0) ? LONG_MAX : timeleft);
13427c478bd9Sstevel@tonic-gate }
13437c478bd9Sstevel@tonic-gate 
13447c478bd9Sstevel@tonic-gate /*
13457c478bd9Sstevel@tonic-gate  * pm_scan_stop - cancel scheduled pm_rescan,
13467c478bd9Sstevel@tonic-gate  *                wait for termination of dispatched pm_scan thread
13477c478bd9Sstevel@tonic-gate  *                     and active pm_scan_dev thread.
13487c478bd9Sstevel@tonic-gate  */
13497c478bd9Sstevel@tonic-gate void
pm_scan_stop(dev_info_t * dip)13507c478bd9Sstevel@tonic-gate pm_scan_stop(dev_info_t *dip)
13517c478bd9Sstevel@tonic-gate {
13527c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "scan_stop")
13537c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp;
13547c478bd9Sstevel@tonic-gate 	timeout_id_t	scanid;
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
13577c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
13587c478bd9Sstevel@tonic-gate 	scanp = PM_GET_PM_SCAN(dip);
13597c478bd9Sstevel@tonic-gate 	if (!scanp) {
13607c478bd9Sstevel@tonic-gate 		PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] scan not initialized\n",
13617c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
13627c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
13637c478bd9Sstevel@tonic-gate 		return;
13647c478bd9Sstevel@tonic-gate 	}
13657c478bd9Sstevel@tonic-gate 	scanp->ps_scan_flags |= PM_SCAN_STOP;
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate 	/* cancel scheduled scan taskq */
13687c478bd9Sstevel@tonic-gate 	while (scanp->ps_scan_id) {
13697c478bd9Sstevel@tonic-gate 		scanid = scanp->ps_scan_id;
13707c478bd9Sstevel@tonic-gate 		scanp->ps_scan_id = 0;
13717c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
13727c478bd9Sstevel@tonic-gate 		(void) untimeout(scanid);
13737c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
13747c478bd9Sstevel@tonic-gate 	}
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate 	while (scanp->ps_scan_flags & (PM_SCANNING | PM_SCAN_DISPATCHED)) {
13777c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
13787c478bd9Sstevel@tonic-gate 		delay(1);
13797c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
13807c478bd9Sstevel@tonic-gate 	}
13817c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
13827c478bd9Sstevel@tonic-gate 	PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
13837c478bd9Sstevel@tonic-gate }
13847c478bd9Sstevel@tonic-gate 
13857c478bd9Sstevel@tonic-gate int
pm_scan_stop_walk(dev_info_t * dip,void * arg)13867c478bd9Sstevel@tonic-gate pm_scan_stop_walk(dev_info_t *dip, void *arg)
13877c478bd9Sstevel@tonic-gate {
13887c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_SCAN(dip))
13917c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
13927c478bd9Sstevel@tonic-gate 	ASSERT(!PM_ISBC(dip));
13937c478bd9Sstevel@tonic-gate 	pm_scan_stop(dip);
13947c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
13957c478bd9Sstevel@tonic-gate }
13967c478bd9Sstevel@tonic-gate 
13977c478bd9Sstevel@tonic-gate /*
13987c478bd9Sstevel@tonic-gate  * Converts a power level value to its index
13997c478bd9Sstevel@tonic-gate  */
14007c478bd9Sstevel@tonic-gate static int
power_val_to_index(pm_component_t * cp,int val)14017c478bd9Sstevel@tonic-gate power_val_to_index(pm_component_t *cp, int val)
14027c478bd9Sstevel@tonic-gate {
14037c478bd9Sstevel@tonic-gate 	int limit, i, *ip;
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	ASSERT(val != PM_LEVEL_UPONLY && val != PM_LEVEL_DOWNONLY &&
14067c478bd9Sstevel@tonic-gate 	    val != PM_LEVEL_EXACT);
14077c478bd9Sstevel@tonic-gate 	/*  convert power value into index (i) */
14087c478bd9Sstevel@tonic-gate 	limit = cp->pmc_comp.pmc_numlevels;
14097c478bd9Sstevel@tonic-gate 	ip = cp->pmc_comp.pmc_lvals;
14107c478bd9Sstevel@tonic-gate 	for (i = 0; i < limit; i++)
14117c478bd9Sstevel@tonic-gate 		if (val == *ip++)
14127c478bd9Sstevel@tonic-gate 			return (i);
14137c478bd9Sstevel@tonic-gate 	return (-1);
14147c478bd9Sstevel@tonic-gate }
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate /*
14177c478bd9Sstevel@tonic-gate  * Converts a numeric power level to a printable string
14187c478bd9Sstevel@tonic-gate  */
14197c478bd9Sstevel@tonic-gate static char *
power_val_to_string(pm_component_t * cp,int val)14207c478bd9Sstevel@tonic-gate power_val_to_string(pm_component_t *cp, int val)
14217c478bd9Sstevel@tonic-gate {
14227c478bd9Sstevel@tonic-gate 	int index;
14237c478bd9Sstevel@tonic-gate 
14247c478bd9Sstevel@tonic-gate 	if (val == PM_LEVEL_UPONLY)
14257c478bd9Sstevel@tonic-gate 		return ("<UPONLY>");
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 	if (val == PM_LEVEL_UNKNOWN ||
14287c478bd9Sstevel@tonic-gate 	    (index = power_val_to_index(cp, val)) == -1)
14297c478bd9Sstevel@tonic-gate 		return ("<LEVEL_UNKNOWN>");
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	return (cp->pmc_comp.pmc_lnames[index]);
14327c478bd9Sstevel@tonic-gate }
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate /*
14357c478bd9Sstevel@tonic-gate  * Return true if this node has been claimed by a ppm.
14367c478bd9Sstevel@tonic-gate  */
14377c478bd9Sstevel@tonic-gate static int
pm_ppm_claimed(dev_info_t * dip)14387c478bd9Sstevel@tonic-gate pm_ppm_claimed(dev_info_t *dip)
14397c478bd9Sstevel@tonic-gate {
14407c478bd9Sstevel@tonic-gate 	return (PPM(dip) != NULL);
14417c478bd9Sstevel@tonic-gate }
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate /*
14447c478bd9Sstevel@tonic-gate  * A node which was voluntarily power managed has just used up its "free cycle"
14457c478bd9Sstevel@tonic-gate  * and need is volpmd field cleared, and the same done to all its descendents
14467c478bd9Sstevel@tonic-gate  */
14477c478bd9Sstevel@tonic-gate static void
pm_clear_volpm_dip(dev_info_t * dip)14487c478bd9Sstevel@tonic-gate pm_clear_volpm_dip(dev_info_t *dip)
14497c478bd9Sstevel@tonic-gate {
14507c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "clear_volpm_dip")
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	if (dip == NULL)
14537c478bd9Sstevel@tonic-gate 		return;
14547c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: clear volpm from %s@%s(%s#%d)\n", pmf,
14557c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip)))
14567c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_volpmd = 0;
14577c478bd9Sstevel@tonic-gate 	for (dip = ddi_get_child(dip); dip; dip = ddi_get_next_sibling(dip)) {
14587c478bd9Sstevel@tonic-gate 		pm_clear_volpm_dip(dip);
14597c478bd9Sstevel@tonic-gate 	}
14607c478bd9Sstevel@tonic-gate }
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate /*
14637c478bd9Sstevel@tonic-gate  * A node which was voluntarily power managed has used up the "free cycles"
14647c478bd9Sstevel@tonic-gate  * for the subtree that it is the root of.  Scan through the list of detached
14657c478bd9Sstevel@tonic-gate  * nodes and adjust the counts of any that are descendents of the node.
14667c478bd9Sstevel@tonic-gate  */
14677c478bd9Sstevel@tonic-gate static void
pm_clear_volpm_list(dev_info_t * dip)14687c478bd9Sstevel@tonic-gate pm_clear_volpm_list(dev_info_t *dip)
14697c478bd9Sstevel@tonic-gate {
14707c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "clear_volpm_list")
14717c478bd9Sstevel@tonic-gate 	char	*pathbuf;
14727c478bd9Sstevel@tonic-gate 	size_t	len;
14737c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
14767c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
14777c478bd9Sstevel@tonic-gate 	len = strlen(pathbuf);
14787c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: clear volpm list %s\n", pmf, pathbuf))
14797c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
14807c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
14817c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: clear volpm: ni_path %s\n", pmf,
14827c478bd9Sstevel@tonic-gate 		    ip->ni_path))
14837c478bd9Sstevel@tonic-gate 		if (strncmp(pathbuf, ip->ni_path, len) == 0 &&
14847c478bd9Sstevel@tonic-gate 		    ip->ni_path[len] == '/') {
14857c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: clear volpm: %s\n", pmf,
14867c478bd9Sstevel@tonic-gate 			    ip->ni_path))
14877c478bd9Sstevel@tonic-gate 			ip->ni_volpmd = 0;
14887c478bd9Sstevel@tonic-gate 			ip->ni_wasvolpmd = 0;
14897c478bd9Sstevel@tonic-gate 		}
14907c478bd9Sstevel@tonic-gate 	}
14917c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
14927c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
14937c478bd9Sstevel@tonic-gate }
14947c478bd9Sstevel@tonic-gate 
14957c478bd9Sstevel@tonic-gate /*
14967c478bd9Sstevel@tonic-gate  * Powers a device, suspending or resuming the driver if it is a backward
14977c478bd9Sstevel@tonic-gate  * compatible device, calling into ppm to change power level.
14987c478bd9Sstevel@tonic-gate  * Called with the component's power lock held.
14997c478bd9Sstevel@tonic-gate  */
15007c478bd9Sstevel@tonic-gate static int
power_dev(dev_info_t * dip,int comp,int level,int old_level,pm_canblock_t canblock,pm_ppm_devlist_t ** devlist)15017c478bd9Sstevel@tonic-gate power_dev(dev_info_t *dip, int comp, int level, int old_level,
15027c478bd9Sstevel@tonic-gate     pm_canblock_t canblock, pm_ppm_devlist_t **devlist)
15037c478bd9Sstevel@tonic-gate {
15047c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "power_dev")
15057c478bd9Sstevel@tonic-gate 	power_req_t power_req;
15067c478bd9Sstevel@tonic-gate 	int		power_op_ret;	/* DDI_SUCCESS or DDI_FAILURE */
15077c478bd9Sstevel@tonic-gate 	int		resume_needed = 0;
15087c478bd9Sstevel@tonic-gate 	int		suspended = 0;
15097c478bd9Sstevel@tonic-gate 	int		result;
15109681b4a1Skchow #ifdef PMDDEBUG
15117c478bd9Sstevel@tonic-gate 	struct pm_component *cp = PM_CP(dip, comp);
15129681b4a1Skchow #endif
15137c478bd9Sstevel@tonic-gate 	int		bc = PM_ISBC(dip);
15147c478bd9Sstevel@tonic-gate 	int pm_all_components_off(dev_info_t *);
15157c478bd9Sstevel@tonic-gate 	int		clearvolpmd = 0;
15167c478bd9Sstevel@tonic-gate 	char		pathbuf[MAXNAMELEN];
15179681b4a1Skchow #ifdef PMDDEBUG
15187c478bd9Sstevel@tonic-gate 	char *ppmname, *ppmaddr;
15197c478bd9Sstevel@tonic-gate #endif
15207c478bd9Sstevel@tonic-gate 	/*
15217c478bd9Sstevel@tonic-gate 	 * If this is comp 0 of a backwards compat device and we are
15227c478bd9Sstevel@tonic-gate 	 * going to take the power away, we need to detach it with
15237c478bd9Sstevel@tonic-gate 	 * DDI_PM_SUSPEND command.
15247c478bd9Sstevel@tonic-gate 	 */
15257c478bd9Sstevel@tonic-gate 	if (bc && comp == 0 && POWERING_OFF(old_level, level)) {
15267c478bd9Sstevel@tonic-gate 		if (devi_detach(dip, DDI_PM_SUSPEND) != DDI_SUCCESS) {
15277c478bd9Sstevel@tonic-gate 			/* We could not suspend before turning cmpt zero off */
15287c478bd9Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: could not suspend %s@%s(%s#%d)\n",
15297c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
15307c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
15317c478bd9Sstevel@tonic-gate 		} else {
15327c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= PMC_SUSPENDED;
15337c478bd9Sstevel@tonic-gate 			suspended++;
15347c478bd9Sstevel@tonic-gate 		}
15357c478bd9Sstevel@tonic-gate 	}
15367c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_SET_POWER;
15377c478bd9Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.who = dip;
15387c478bd9Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.cmpt = comp;
15397c478bd9Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.old_level = old_level;
15407c478bd9Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.new_level = level;
15417c478bd9Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.canblock = canblock;
15427c478bd9Sstevel@tonic-gate 	power_req.req.ppm_set_power_req.cookie = NULL;
15439681b4a1Skchow #ifdef PMDDEBUG
15447c478bd9Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {
15457c478bd9Sstevel@tonic-gate 		ppmname = PM_NAME(PPM(dip));
15467c478bd9Sstevel@tonic-gate 		ppmaddr = PM_ADDR(PPM(dip));
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 	} else {
15497c478bd9Sstevel@tonic-gate 		ppmname = "noppm";
15507c478bd9Sstevel@tonic-gate 		ppmaddr = "0";
15517c478bd9Sstevel@tonic-gate 	}
15527c478bd9Sstevel@tonic-gate 	PMD(PMD_PPM, ("%s: %s@%s(%s#%d):%s[%d] %s (%d) -> %s (%d) via %s@%s\n",
15537c478bd9Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), cp->pmc_comp.pmc_name, comp,
15547c478bd9Sstevel@tonic-gate 	    power_val_to_string(cp, old_level), old_level,
15557c478bd9Sstevel@tonic-gate 	    power_val_to_string(cp, level), level, ppmname, ppmaddr))
15567c478bd9Sstevel@tonic-gate #endif
15577c478bd9Sstevel@tonic-gate 	/*
15587c478bd9Sstevel@tonic-gate 	 * If non-bc noinvolpm device is turning first comp on, or noinvolpm
15597c478bd9Sstevel@tonic-gate 	 * bc device comp 0 is powering on, then we count it as a power cycle
15607c478bd9Sstevel@tonic-gate 	 * against its voluntary count.
15617c478bd9Sstevel@tonic-gate 	 */
15627c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_volpmd &&
15637c478bd9Sstevel@tonic-gate 	    (!bc && pm_all_components_off(dip) && level != 0) ||
15647c478bd9Sstevel@tonic-gate 	    (bc && comp == 0 && POWERING_ON(old_level, level)))
15657c478bd9Sstevel@tonic-gate 		clearvolpmd = 1;
15667c478bd9Sstevel@tonic-gate 	if ((power_op_ret = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
15677c478bd9Sstevel@tonic-gate 	    &power_req, &result)) == DDI_SUCCESS) {
15687c478bd9Sstevel@tonic-gate 		/*
15697c478bd9Sstevel@tonic-gate 		 * Now do involuntary pm accounting;  If we've just cycled power
15707c478bd9Sstevel@tonic-gate 		 * on a voluntarily pm'd node, and by inference on its entire
15717c478bd9Sstevel@tonic-gate 		 * subtree, we need to set the subtree (including those nodes
15727c478bd9Sstevel@tonic-gate 		 * already detached) volpmd counts to 0, and subtract out the
15737c478bd9Sstevel@tonic-gate 		 * value of the current node's volpmd count from the ancestors
15747c478bd9Sstevel@tonic-gate 		 */
15757c478bd9Sstevel@tonic-gate 		if (clearvolpmd) {
15767c478bd9Sstevel@tonic-gate 			int volpmd = DEVI(dip)->devi_pm_volpmd;
15777c478bd9Sstevel@tonic-gate 			pm_clear_volpm_dip(dip);
15787c478bd9Sstevel@tonic-gate 			pm_clear_volpm_list(dip);
15797c478bd9Sstevel@tonic-gate 			if (volpmd) {
15807c478bd9Sstevel@tonic-gate 				(void) ddi_pathname(dip, pathbuf);
15817c478bd9Sstevel@tonic-gate 				(void) pm_noinvol_update(PM_BP_NOINVOL_POWER,
15827c478bd9Sstevel@tonic-gate 				    volpmd, 0, pathbuf, dip);
15837c478bd9Sstevel@tonic-gate 			}
15847c478bd9Sstevel@tonic-gate 		}
15857c478bd9Sstevel@tonic-gate 	} else {
15867c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: can't set comp %d (%s) of %s@%s(%s#%d) "
15877c478bd9Sstevel@tonic-gate 		    "to level %d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name,
15887c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), level, power_val_to_string(cp, level)))
15897c478bd9Sstevel@tonic-gate 	}
15907c478bd9Sstevel@tonic-gate 	/*
15917c478bd9Sstevel@tonic-gate 	 * If some other devices were also powered up (e.g. other cpus in
15927c478bd9Sstevel@tonic-gate 	 * the same domain) return a pointer to that list
15937c478bd9Sstevel@tonic-gate 	 */
15947c478bd9Sstevel@tonic-gate 	if (devlist) {
15957c478bd9Sstevel@tonic-gate 		*devlist = (pm_ppm_devlist_t *)
15967c478bd9Sstevel@tonic-gate 		    power_req.req.ppm_set_power_req.cookie;
15977c478bd9Sstevel@tonic-gate 	}
15987c478bd9Sstevel@tonic-gate 	/*
15997c478bd9Sstevel@tonic-gate 	 * We will have to resume the device if the device is backwards compat
16007c478bd9Sstevel@tonic-gate 	 * device and either of the following is true:
16017c478bd9Sstevel@tonic-gate 	 * -This is comp 0 and we have successfully powered it up
16027c478bd9Sstevel@tonic-gate 	 * -This is comp 0 and we have failed to power it down. Resume is
16037c478bd9Sstevel@tonic-gate 	 *  needed because we have suspended it above
16047c478bd9Sstevel@tonic-gate 	 */
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 	if (bc && comp == 0) {
16077c478bd9Sstevel@tonic-gate 		ASSERT(PM_ISDIRECT(dip) || DEVI_IS_DETACHING(dip));
16087c478bd9Sstevel@tonic-gate 		if (power_op_ret == DDI_SUCCESS) {
16097c478bd9Sstevel@tonic-gate 			if (POWERING_ON(old_level, level)) {
16107c478bd9Sstevel@tonic-gate 				/*
16117c478bd9Sstevel@tonic-gate 				 * It must be either suspended or resumed
16127c478bd9Sstevel@tonic-gate 				 * via pm_power_has_changed path
16137c478bd9Sstevel@tonic-gate 				 */
16147c478bd9Sstevel@tonic-gate 				ASSERT((DEVI(dip)->devi_pm_flags &
16157c478bd9Sstevel@tonic-gate 				    PMC_SUSPENDED) ||
16167c478bd9Sstevel@tonic-gate 				    (PM_CP(dip, comp)->pmc_flags &
16177c478bd9Sstevel@tonic-gate 				    PM_PHC_WHILE_SET_POWER));
16187c478bd9Sstevel@tonic-gate 
16193df2e8b2SRobert Mustacchi 				resume_needed = suspended;
16207c478bd9Sstevel@tonic-gate 			}
16217c478bd9Sstevel@tonic-gate 		} else {
16227c478bd9Sstevel@tonic-gate 			if (POWERING_OFF(old_level, level)) {
16237c478bd9Sstevel@tonic-gate 				/*
16247c478bd9Sstevel@tonic-gate 				 * It must be either suspended or resumed
16257c478bd9Sstevel@tonic-gate 				 * via pm_power_has_changed path
16267c478bd9Sstevel@tonic-gate 				 */
16277c478bd9Sstevel@tonic-gate 				ASSERT((DEVI(dip)->devi_pm_flags &
16287c478bd9Sstevel@tonic-gate 				    PMC_SUSPENDED) ||
16297c478bd9Sstevel@tonic-gate 				    (PM_CP(dip, comp)->pmc_flags &
16307c478bd9Sstevel@tonic-gate 				    PM_PHC_WHILE_SET_POWER));
16317c478bd9Sstevel@tonic-gate 
16323df2e8b2SRobert Mustacchi 				resume_needed = suspended;
16337c478bd9Sstevel@tonic-gate 			}
16347c478bd9Sstevel@tonic-gate 		}
16357c478bd9Sstevel@tonic-gate 	}
16367c478bd9Sstevel@tonic-gate 	if (resume_needed) {
16377c478bd9Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED);
16387c478bd9Sstevel@tonic-gate 		/* ppm is not interested in DDI_PM_RESUME */
16397c478bd9Sstevel@tonic-gate 		if ((power_op_ret = devi_attach(dip, DDI_PM_RESUME)) ==
16407c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
16417c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED;
16427c478bd9Sstevel@tonic-gate 		} else
16437c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "!pm: Can't resume %s@%s(%s#%d)",
16447c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip));
16457c478bd9Sstevel@tonic-gate 	}
16467c478bd9Sstevel@tonic-gate 	return (power_op_ret);
16477c478bd9Sstevel@tonic-gate }
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate /*
16507c478bd9Sstevel@tonic-gate  * Return true if we are the owner or a borrower of the devi lock.  See
16517c478bd9Sstevel@tonic-gate  * pm_lock_power_single() about borrowing the lock.
16527c478bd9Sstevel@tonic-gate  */
16537c478bd9Sstevel@tonic-gate static int
pm_devi_lock_held(dev_info_t * dip)16547c478bd9Sstevel@tonic-gate pm_devi_lock_held(dev_info_t *dip)
16557c478bd9Sstevel@tonic-gate {
16567c478bd9Sstevel@tonic-gate 	lock_loan_t *cur;
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate 	if (DEVI_BUSY_OWNED(dip))
16596152d498Smh 		return (1);
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	/* return false if no locks borrowed */
16627c478bd9Sstevel@tonic-gate 	if (lock_loan_head.pmlk_next == NULL)
16637c478bd9Sstevel@tonic-gate 		return (0);
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
16667c478bd9Sstevel@tonic-gate 	/* see if our thread is registered as a lock borrower. */
16677c478bd9Sstevel@tonic-gate 	for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
16687c478bd9Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
16697c478bd9Sstevel@tonic-gate 			break;
16707c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	return (cur != NULL && cur->pmlk_lender == DEVI(dip)->devi_busy_thread);
16737c478bd9Sstevel@tonic-gate }
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate /*
16767c478bd9Sstevel@tonic-gate  * pm_set_power: adjusts power level of device.	 Assumes device is power
16777c478bd9Sstevel@tonic-gate  * manageable & component exists.
16787c478bd9Sstevel@tonic-gate  *
16797c478bd9Sstevel@tonic-gate  * Cases which require us to bring up devices we keep up ("wekeepups") for
16807c478bd9Sstevel@tonic-gate  * backwards compatible devices:
16817c478bd9Sstevel@tonic-gate  *	component 0 is off and we're bringing it up from 0
16827c478bd9Sstevel@tonic-gate  *		bring up wekeepup first
16837c478bd9Sstevel@tonic-gate  *	and recursively when component 0 is off and we bring some other
16847c478bd9Sstevel@tonic-gate  *	component up from 0
16857c478bd9Sstevel@tonic-gate  * For devices which are not backward compatible, our dependency notion is much
16867c478bd9Sstevel@tonic-gate  * simpler.  Unless all components are off, then wekeeps must be on.
16877c478bd9Sstevel@tonic-gate  * We don't treat component 0 differently.
16887c478bd9Sstevel@tonic-gate  * Canblock tells how to deal with a direct pm'd device.
16897c478bd9Sstevel@tonic-gate  * Scan arg tells us if we were called from scan, in which case we don't need
16907c478bd9Sstevel@tonic-gate  * to go back to the root node and walk down to change power.
16917c478bd9Sstevel@tonic-gate  */
16927c478bd9Sstevel@tonic-gate int
pm_set_power(dev_info_t * dip,int comp,int level,int direction,pm_canblock_t canblock,int scan,int * retp)16937c478bd9Sstevel@tonic-gate pm_set_power(dev_info_t *dip, int comp, int level, int direction,
16947c478bd9Sstevel@tonic-gate     pm_canblock_t canblock, int scan, int *retp)
16957c478bd9Sstevel@tonic-gate {
16967c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_power")
16977c478bd9Sstevel@tonic-gate 	char		*pathbuf;
16987c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t bpc;
16997c478bd9Sstevel@tonic-gate 	pm_sp_misc_t	pspm;
17007c478bd9Sstevel@tonic-gate 	int		ret = DDI_SUCCESS;
17017c478bd9Sstevel@tonic-gate 	int		unused = DDI_SUCCESS;
17027c478bd9Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate #ifdef DEBUG
17057c478bd9Sstevel@tonic-gate 	int		diverted = 0;
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate 	/*
17087c478bd9Sstevel@tonic-gate 	 * This prevents operations on the console from calling prom_printf and
17097c478bd9Sstevel@tonic-gate 	 * either deadlocking or bringing up the console because of debug
17107c478bd9Sstevel@tonic-gate 	 * output
17117c478bd9Sstevel@tonic-gate 	 */
17127c478bd9Sstevel@tonic-gate 	if (dip == cfb_dip) {
17137c478bd9Sstevel@tonic-gate 		diverted++;
17147c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_debug_lock);
17157c478bd9Sstevel@tonic-gate 		pm_divertdebug++;
17167c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_debug_lock);
17177c478bd9Sstevel@tonic-gate 	}
17187c478bd9Sstevel@tonic-gate #endif
17197c478bd9Sstevel@tonic-gate 	ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY ||
17207c478bd9Sstevel@tonic-gate 	    direction == PM_LEVEL_EXACT);
17217c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d, dir=%s, new=%d\n",
17227c478bd9Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), comp, pm_decode_direction(direction), level))
17237c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
17247c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
17257c478bd9Sstevel@tonic-gate 	bpc.bpc_dip = dip;
17267c478bd9Sstevel@tonic-gate 	bpc.bpc_path = pathbuf;
17277c478bd9Sstevel@tonic-gate 	bpc.bpc_comp = comp;
17287c478bd9Sstevel@tonic-gate 	bpc.bpc_olevel = PM_CURPOWER(dip, comp);
17297c478bd9Sstevel@tonic-gate 	bpc.bpc_nlevel = level;
17307c478bd9Sstevel@tonic-gate 	pspm.pspm_direction = direction;
17317c478bd9Sstevel@tonic-gate 	pspm.pspm_errnop = retp;
17327c478bd9Sstevel@tonic-gate 	pspm.pspm_canblock = canblock;
17337c478bd9Sstevel@tonic-gate 	pspm.pspm_scan = scan;
17347c478bd9Sstevel@tonic-gate 	bpc.bpc_private = &pspm;
17357c478bd9Sstevel@tonic-gate 
17367c478bd9Sstevel@tonic-gate 	/*
17377c478bd9Sstevel@tonic-gate 	 * If a config operation is being done (we've locked the parent) or
17387c478bd9Sstevel@tonic-gate 	 * we already hold the power lock (we've locked the node)
17397c478bd9Sstevel@tonic-gate 	 * then we can operate directly on the node because we have already
17407c478bd9Sstevel@tonic-gate 	 * brought up all the ancestors, otherwise, we have to go back to the
17417c478bd9Sstevel@tonic-gate 	 * top of the tree.
17427c478bd9Sstevel@tonic-gate 	 */
17437c478bd9Sstevel@tonic-gate 	if (pm_devi_lock_held(pdip) || pm_devi_lock_held(dip))
17447c478bd9Sstevel@tonic-gate 		ret = pm_busop_set_power(dip, NULL, BUS_POWER_CHILD_PWRCHG,
17457c478bd9Sstevel@tonic-gate 		    (void *)&bpc, (void *)&unused);
17467c478bd9Sstevel@tonic-gate 	else
17477c478bd9Sstevel@tonic-gate 		ret = pm_busop_bus_power(ddi_root_node(), NULL,
17487c478bd9Sstevel@tonic-gate 		    BUS_POWER_CHILD_PWRCHG, (void *)&bpc, (void *)&unused);
17497c478bd9Sstevel@tonic-gate #ifdef DEBUG
17507c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS || *retp != DDI_SUCCESS) {
17517c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) can't change power, ret=%d, "
17527c478bd9Sstevel@tonic-gate 		    "errno=%d\n", pmf, PM_DEVICE(dip), ret, *retp))
17537c478bd9Sstevel@tonic-gate 	}
17547c478bd9Sstevel@tonic-gate 	if (diverted) {
17557c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_debug_lock);
17567c478bd9Sstevel@tonic-gate 		pm_divertdebug--;
17577c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_debug_lock);
17587c478bd9Sstevel@tonic-gate 	}
17597c478bd9Sstevel@tonic-gate #endif
17607c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
17617c478bd9Sstevel@tonic-gate 	return (ret);
17627c478bd9Sstevel@tonic-gate }
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate /*
17654014adcdScth  * If holddip is set, then if a dip is found we return with the node held.
17664014adcdScth  *
17674014adcdScth  * This code uses the same locking scheme as e_ddi_hold_devi_by_path
17684014adcdScth  * (resolve_pathname), but it does not drive attach.
17697c478bd9Sstevel@tonic-gate  */
17707c478bd9Sstevel@tonic-gate dev_info_t *
pm_name_to_dip(char * pathname,int holddip)17717c478bd9Sstevel@tonic-gate pm_name_to_dip(char *pathname, int holddip)
17727c478bd9Sstevel@tonic-gate {
17734014adcdScth 	struct pathname pn;
17744014adcdScth 	char		*component;
17754014adcdScth 	dev_info_t	*parent, *child;
17767c478bd9Sstevel@tonic-gate 
17774014adcdScth 	if ((pathname == NULL) || (*pathname != '/'))
17787c478bd9Sstevel@tonic-gate 		return (NULL);
17797c478bd9Sstevel@tonic-gate 
17804014adcdScth 	/* setup pathname and allocate component */
17814014adcdScth 	if (pn_get(pathname, UIO_SYSSPACE, &pn))
17824014adcdScth 		return (NULL);
17834014adcdScth 	component = kmem_alloc(MAXNAMELEN, KM_SLEEP);
17844014adcdScth 
17854014adcdScth 	/* start at top, process '/' component */
17864014adcdScth 	parent = child = ddi_root_node();
17874014adcdScth 	ndi_hold_devi(parent);
17884014adcdScth 	pn_skipslash(&pn);
17894014adcdScth 	ASSERT(i_ddi_devi_attached(parent));
17904014adcdScth 
17914014adcdScth 	/* process components of pathname */
17924014adcdScth 	while (pn_pathleft(&pn)) {
17934014adcdScth 		(void) pn_getcomponent(&pn, component);
17944014adcdScth 
17954014adcdScth 		/* enter parent and search for component child */
17963fe80ca4SDan Cross 		ndi_devi_enter(parent);
17974014adcdScth 		child = ndi_devi_findchild(parent, component);
17984014adcdScth 		if ((child == NULL) || !i_ddi_devi_attached(child)) {
17994014adcdScth 			child = NULL;
18003fe80ca4SDan Cross 			ndi_devi_exit(parent);
18014014adcdScth 			ndi_rele_devi(parent);
18024014adcdScth 			goto out;
18034014adcdScth 		}
18047c478bd9Sstevel@tonic-gate 
18054014adcdScth 		/* attached child found, hold child and release parent */
18064014adcdScth 		ndi_hold_devi(child);
18073fe80ca4SDan Cross 		ndi_devi_exit(parent);
18084014adcdScth 		ndi_rele_devi(parent);
18097c478bd9Sstevel@tonic-gate 
18104014adcdScth 		/* child becomes parent, and process next component */
18114014adcdScth 		parent = child;
18124014adcdScth 		pn_skipslash(&pn);
18137c478bd9Sstevel@tonic-gate 
18144014adcdScth 		/* loop with active ndi_devi_hold of child->parent */
18157c478bd9Sstevel@tonic-gate 	}
18167c478bd9Sstevel@tonic-gate 
18174014adcdScth out:
18184014adcdScth 	pn_free(&pn);
18194014adcdScth 	kmem_free(component, MAXNAMELEN);
18207c478bd9Sstevel@tonic-gate 
18214014adcdScth 	/* if we are not asked to return with hold, drop current hold */
18224014adcdScth 	if (child && !holddip)
18234014adcdScth 		ndi_rele_devi(child);
18244014adcdScth 	return (child);
18257c478bd9Sstevel@tonic-gate }
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate /*
18287c478bd9Sstevel@tonic-gate  * Search for a dependency and mark it unsatisfied
18297c478bd9Sstevel@tonic-gate  */
18307c478bd9Sstevel@tonic-gate static void
pm_unsatisfy(char * keeper,char * kept)18317c478bd9Sstevel@tonic-gate pm_unsatisfy(char *keeper, char *kept)
18327c478bd9Sstevel@tonic-gate {
18337c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "unsatisfy")
18347c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
18357c478bd9Sstevel@tonic-gate 
18367c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, keeper, kept))
18377c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
18387c478bd9Sstevel@tonic-gate 		if (!dp->pdr_isprop) {
18397c478bd9Sstevel@tonic-gate 			if (strcmp(dp->pdr_keeper, keeper) == 0 &&
18407c478bd9Sstevel@tonic-gate 			    (dp->pdr_kept_count > 0) &&
18417c478bd9Sstevel@tonic-gate 			    strcmp(dp->pdr_kept_paths[0], kept) == 0) {
18427c478bd9Sstevel@tonic-gate 				if (dp->pdr_satisfied) {
18437c478bd9Sstevel@tonic-gate 					dp->pdr_satisfied = 0;
18447c478bd9Sstevel@tonic-gate 					pm_unresolved_deps++;
18457c478bd9Sstevel@tonic-gate 					PMD(PMD_KEEPS, ("%s: clear satisfied, "
18467c478bd9Sstevel@tonic-gate 					    "pm_unresolved_deps now %d\n", pmf,
18477c478bd9Sstevel@tonic-gate 					    pm_unresolved_deps))
18487c478bd9Sstevel@tonic-gate 				}
18497c478bd9Sstevel@tonic-gate 			}
18507c478bd9Sstevel@tonic-gate 		}
18517c478bd9Sstevel@tonic-gate 	}
18527c478bd9Sstevel@tonic-gate }
18537c478bd9Sstevel@tonic-gate 
18547c478bd9Sstevel@tonic-gate /*
18557c478bd9Sstevel@tonic-gate  * Device dip is being un power managed, it keeps up count other devices.
18567c478bd9Sstevel@tonic-gate  * We need to release any hold we have on the kept devices, and also
18577c478bd9Sstevel@tonic-gate  * mark the dependency no longer satisfied.
18587c478bd9Sstevel@tonic-gate  */
18597c478bd9Sstevel@tonic-gate static void
pm_unkeeps(int count,char * keeper,char ** keptpaths,int pwr)18607c478bd9Sstevel@tonic-gate pm_unkeeps(int count, char *keeper, char **keptpaths, int pwr)
18617c478bd9Sstevel@tonic-gate {
18627c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "unkeeps")
18637c478bd9Sstevel@tonic-gate 	int i, j;
18647c478bd9Sstevel@tonic-gate 	dev_info_t *kept;
18657c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
18667c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
18673fe80ca4SDan Cross 	int keeper_on = 0;
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: count=%d, keeper=%s, keptpaths=%p\n", pmf, count,
18707c478bd9Sstevel@tonic-gate 	    keeper, (void *)keptpaths))
18717c478bd9Sstevel@tonic-gate 	/*
18727c478bd9Sstevel@tonic-gate 	 * Try to grab keeper. Keeper may have gone away by now,
18737c478bd9Sstevel@tonic-gate 	 * in this case, used the passed in value pwr
18747c478bd9Sstevel@tonic-gate 	 */
18757c478bd9Sstevel@tonic-gate 	dip = pm_name_to_dip(keeper, 1);
18767c478bd9Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
18777c478bd9Sstevel@tonic-gate 		/* Release power hold */
18787c478bd9Sstevel@tonic-gate 		kept = pm_name_to_dip(keptpaths[i], 1);
18797c478bd9Sstevel@tonic-gate 		if (kept) {
18807c478bd9Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)[%d]\n", pmf,
18817c478bd9Sstevel@tonic-gate 			    PM_DEVICE(kept), i))
18827c478bd9Sstevel@tonic-gate 			/*
18837c478bd9Sstevel@tonic-gate 			 * We need to check if we skipped a bringup here
18847c478bd9Sstevel@tonic-gate 			 * because we could have failed the bringup
18857c478bd9Sstevel@tonic-gate 			 * (ie DIRECT PM device) and have
18867c478bd9Sstevel@tonic-gate 			 * not increment the count.
18877c478bd9Sstevel@tonic-gate 			 */
18887c478bd9Sstevel@tonic-gate 			if ((dip != NULL) && (PM_GET_PM_INFO(dip) != NULL)) {
18897c478bd9Sstevel@tonic-gate 				keeper_on = 0;
18903fe80ca4SDan Cross 				PM_LOCK_POWER(dip);
18917c478bd9Sstevel@tonic-gate 				for (j = 0; j < PM_NUMCMPTS(dip); j++) {
18926152d498Smh 					cp = &DEVI(dip)->devi_pm_components[j];
18937c478bd9Sstevel@tonic-gate 					if (cur_power(cp)) {
18947c478bd9Sstevel@tonic-gate 						keeper_on++;
18957c478bd9Sstevel@tonic-gate 						break;
18967c478bd9Sstevel@tonic-gate 					}
18977c478bd9Sstevel@tonic-gate 				}
18987c478bd9Sstevel@tonic-gate 				if (keeper_on && (PM_SKBU(kept) == 0)) {
18997c478bd9Sstevel@tonic-gate 					pm_rele_power(kept);
19007c478bd9Sstevel@tonic-gate 					DEVI(kept)->devi_pm_flags
19016152d498Smh 					    &= ~PMC_SKIP_BRINGUP;
19027c478bd9Sstevel@tonic-gate 				}
19033fe80ca4SDan Cross 				PM_UNLOCK_POWER(dip);
19047c478bd9Sstevel@tonic-gate 			} else if (pwr) {
19057c478bd9Sstevel@tonic-gate 				if (PM_SKBU(kept) == 0) {
19067c478bd9Sstevel@tonic-gate 					pm_rele_power(kept);
19077c478bd9Sstevel@tonic-gate 					DEVI(kept)->devi_pm_flags
19087c478bd9Sstevel@tonic-gate 					    &= ~PMC_SKIP_BRINGUP;
19097c478bd9Sstevel@tonic-gate 				}
19107c478bd9Sstevel@tonic-gate 			}
19117c478bd9Sstevel@tonic-gate 			ddi_release_devi(kept);
19127c478bd9Sstevel@tonic-gate 		}
19137c478bd9Sstevel@tonic-gate 		/*
19147c478bd9Sstevel@tonic-gate 		 * mark this dependency not satisfied
19157c478bd9Sstevel@tonic-gate 		 */
19167c478bd9Sstevel@tonic-gate 		pm_unsatisfy(keeper, keptpaths[i]);
19177c478bd9Sstevel@tonic-gate 	}
19187c478bd9Sstevel@tonic-gate 	if (dip)
19197c478bd9Sstevel@tonic-gate 		ddi_release_devi(dip);
19207c478bd9Sstevel@tonic-gate }
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate /*
19237c478bd9Sstevel@tonic-gate  * Device kept is being un power managed, it is kept up by keeper.
19247c478bd9Sstevel@tonic-gate  * We need to mark the dependency no longer satisfied.
19257c478bd9Sstevel@tonic-gate  */
19267c478bd9Sstevel@tonic-gate static void
pm_unkepts(char * kept,char * keeper)19277c478bd9Sstevel@tonic-gate pm_unkepts(char *kept, char *keeper)
19287c478bd9Sstevel@tonic-gate {
19297c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "unkepts")
19307c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: kept=%s, keeper=%s\n", pmf, kept, keeper))
19317c478bd9Sstevel@tonic-gate 	ASSERT(keeper != NULL);
19327c478bd9Sstevel@tonic-gate 	/*
19337c478bd9Sstevel@tonic-gate 	 * mark this dependency not satisfied
19347c478bd9Sstevel@tonic-gate 	 */
19357c478bd9Sstevel@tonic-gate 	pm_unsatisfy(keeper, kept);
19367c478bd9Sstevel@tonic-gate }
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate /*
19397c478bd9Sstevel@tonic-gate  * Removes dependency information and hold on the kepts, if the path is a
19407c478bd9Sstevel@tonic-gate  * path of a keeper.
19417c478bd9Sstevel@tonic-gate  */
19427c478bd9Sstevel@tonic-gate static void
pm_free_keeper(char * path,int pwr)19437c478bd9Sstevel@tonic-gate pm_free_keeper(char *path, int pwr)
19447c478bd9Sstevel@tonic-gate {
19457c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
19467c478bd9Sstevel@tonic-gate 	int i;
19477c478bd9Sstevel@tonic-gate 	size_t length;
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
19507c478bd9Sstevel@tonic-gate 		if (strcmp(dp->pdr_keeper, path) != 0)
19517c478bd9Sstevel@tonic-gate 			continue;
19527c478bd9Sstevel@tonic-gate 		/*
19537c478bd9Sstevel@tonic-gate 		 * Remove all our kept holds and the dependency records,
19547c478bd9Sstevel@tonic-gate 		 * then free up the kept lists.
19557c478bd9Sstevel@tonic-gate 		 */
19567c478bd9Sstevel@tonic-gate 		pm_unkeeps(dp->pdr_kept_count, path, dp->pdr_kept_paths, pwr);
19577c478bd9Sstevel@tonic-gate 		if (dp->pdr_kept_count)  {
19587c478bd9Sstevel@tonic-gate 			for (i = 0; i < dp->pdr_kept_count; i++) {
19597c478bd9Sstevel@tonic-gate 				length = strlen(dp->pdr_kept_paths[i]);
19607c478bd9Sstevel@tonic-gate 				kmem_free(dp->pdr_kept_paths[i], length + 1);
19617c478bd9Sstevel@tonic-gate 			}
19627c478bd9Sstevel@tonic-gate 			kmem_free(dp->pdr_kept_paths,
19637c478bd9Sstevel@tonic-gate 			    dp->pdr_kept_count * sizeof (char **));
19647c478bd9Sstevel@tonic-gate 			dp->pdr_kept_paths = NULL;
19657c478bd9Sstevel@tonic-gate 			dp->pdr_kept_count = 0;
19667c478bd9Sstevel@tonic-gate 		}
19677c478bd9Sstevel@tonic-gate 	}
19687c478bd9Sstevel@tonic-gate }
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate /*
19717c478bd9Sstevel@tonic-gate  * Removes the device represented by path from the list of kepts, if the
19727c478bd9Sstevel@tonic-gate  * path is a path of a kept
19737c478bd9Sstevel@tonic-gate  */
19747c478bd9Sstevel@tonic-gate static void
pm_free_kept(char * path)19757c478bd9Sstevel@tonic-gate pm_free_kept(char *path)
19767c478bd9Sstevel@tonic-gate {
19777c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
19787c478bd9Sstevel@tonic-gate 	int i;
19797c478bd9Sstevel@tonic-gate 	int j, count;
19807c478bd9Sstevel@tonic-gate 	size_t length;
19817c478bd9Sstevel@tonic-gate 	char **paths;
19827c478bd9Sstevel@tonic-gate 
1983c6f039c7SToomas Soome 	paths = NULL;
19847c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
19857c478bd9Sstevel@tonic-gate 		if (dp->pdr_kept_count == 0)
19867c478bd9Sstevel@tonic-gate 			continue;
19877c478bd9Sstevel@tonic-gate 		count = dp->pdr_kept_count;
19887c478bd9Sstevel@tonic-gate 		/* Remove this device from the kept path lists */
19897c478bd9Sstevel@tonic-gate 		for (i = 0; i < count; i++) {
19907c478bd9Sstevel@tonic-gate 			if (strcmp(dp->pdr_kept_paths[i], path) == 0) {
19917c478bd9Sstevel@tonic-gate 				pm_unkepts(path, dp->pdr_keeper);
19927c478bd9Sstevel@tonic-gate 				length = strlen(dp->pdr_kept_paths[i]) + 1;
19937c478bd9Sstevel@tonic-gate 				kmem_free(dp->pdr_kept_paths[i], length);
19947c478bd9Sstevel@tonic-gate 				dp->pdr_kept_paths[i] = NULL;
19957c478bd9Sstevel@tonic-gate 				dp->pdr_kept_count--;
19967c478bd9Sstevel@tonic-gate 			}
19977c478bd9Sstevel@tonic-gate 		}
19987c478bd9Sstevel@tonic-gate 		/* Compact the kept paths array */
19997c478bd9Sstevel@tonic-gate 		if (dp->pdr_kept_count) {
20007c478bd9Sstevel@tonic-gate 			length = dp->pdr_kept_count * sizeof (char **);
20017c478bd9Sstevel@tonic-gate 			paths = kmem_zalloc(length, KM_SLEEP);
20027c478bd9Sstevel@tonic-gate 			j = 0;
20037c478bd9Sstevel@tonic-gate 			for (i = 0; i < count; i++) {
20047c478bd9Sstevel@tonic-gate 				if (dp->pdr_kept_paths[i] != NULL) {
20057c478bd9Sstevel@tonic-gate 					paths[j] = dp->pdr_kept_paths[i];
20067c478bd9Sstevel@tonic-gate 					j++;
20077c478bd9Sstevel@tonic-gate 				}
20087c478bd9Sstevel@tonic-gate 			}
20097c478bd9Sstevel@tonic-gate 			ASSERT(j == dp->pdr_kept_count);
20107c478bd9Sstevel@tonic-gate 		}
20117c478bd9Sstevel@tonic-gate 		/* Now free the old array and point to the new one */
20127c478bd9Sstevel@tonic-gate 		kmem_free(dp->pdr_kept_paths, count * sizeof (char **));
2013c6f039c7SToomas Soome 		dp->pdr_kept_paths = paths;
20147c478bd9Sstevel@tonic-gate 	}
20157c478bd9Sstevel@tonic-gate }
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate /*
20187c478bd9Sstevel@tonic-gate  * Free the dependency information for a device.
20197c478bd9Sstevel@tonic-gate  */
20207c478bd9Sstevel@tonic-gate void
pm_free_keeps(char * path,int pwr)20217c478bd9Sstevel@tonic-gate pm_free_keeps(char *path, int pwr)
20227c478bd9Sstevel@tonic-gate {
20237c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "free_keeps")
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate #ifdef DEBUG
20267c478bd9Sstevel@tonic-gate 	int doprdeps = 0;
20277c478bd9Sstevel@tonic-gate 	void prdeps(char *);
20287c478bd9Sstevel@tonic-gate 
20297c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s\n", pmf, path))
20307c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS) {
20317c478bd9Sstevel@tonic-gate 		doprdeps = 1;
20327c478bd9Sstevel@tonic-gate 		prdeps("pm_free_keeps before");
20337c478bd9Sstevel@tonic-gate 	}
20347c478bd9Sstevel@tonic-gate #endif
20357c478bd9Sstevel@tonic-gate 	/*
20367c478bd9Sstevel@tonic-gate 	 * First assume we are a keeper and remove all our kepts.
20377c478bd9Sstevel@tonic-gate 	 */
20387c478bd9Sstevel@tonic-gate 	pm_free_keeper(path, pwr);
20397c478bd9Sstevel@tonic-gate 	/*
20407c478bd9Sstevel@tonic-gate 	 * Now assume we a kept device, and remove all our records.
20417c478bd9Sstevel@tonic-gate 	 */
20427c478bd9Sstevel@tonic-gate 	pm_free_kept(path);
20437c478bd9Sstevel@tonic-gate #ifdef	DEBUG
20447c478bd9Sstevel@tonic-gate 	if (doprdeps) {
20457c478bd9Sstevel@tonic-gate 		prdeps("pm_free_keeps after");
20467c478bd9Sstevel@tonic-gate 	}
20477c478bd9Sstevel@tonic-gate #endif
20487c478bd9Sstevel@tonic-gate }
20497c478bd9Sstevel@tonic-gate 
20507c478bd9Sstevel@tonic-gate static int
pm_is_kept(char * path)20517c478bd9Sstevel@tonic-gate pm_is_kept(char *path)
20527c478bd9Sstevel@tonic-gate {
20537c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
20547c478bd9Sstevel@tonic-gate 	int i;
20557c478bd9Sstevel@tonic-gate 
20567c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
20577c478bd9Sstevel@tonic-gate 		if (dp->pdr_kept_count == 0)
20587c478bd9Sstevel@tonic-gate 			continue;
20597c478bd9Sstevel@tonic-gate 		for (i = 0; i < dp->pdr_kept_count; i++) {
20607c478bd9Sstevel@tonic-gate 			if (strcmp(dp->pdr_kept_paths[i], path) == 0)
20617c478bd9Sstevel@tonic-gate 				return (1);
20627c478bd9Sstevel@tonic-gate 		}
20637c478bd9Sstevel@tonic-gate 	}
20647c478bd9Sstevel@tonic-gate 	return (0);
20657c478bd9Sstevel@tonic-gate }
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate static void
e_pm_hold_rele_power(dev_info_t * dip,int cnt)20687c478bd9Sstevel@tonic-gate e_pm_hold_rele_power(dev_info_t *dip, int cnt)
20697c478bd9Sstevel@tonic-gate {
20707c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "hold_rele_power")
20717c478bd9Sstevel@tonic-gate 
2072c73a93f2Sdm 	if ((dip == NULL) ||
2073c73a93f2Sdm 	    (PM_GET_PM_INFO(dip) == NULL) || PM_ISBC(dip))
20747c478bd9Sstevel@tonic-gate 		return;
20755e3986cbScth 
20763fe80ca4SDan Cross 	PM_LOCK_POWER(dip);
20773df2e8b2SRobert Mustacchi 	ASSERT(cnt >= 0 || (cnt < 0 && PM_KUC(dip) > 0));
20787c478bd9Sstevel@tonic-gate 	PMD(PMD_KIDSUP, ("%s: kidsupcnt for %s@%s(%s#%d) %d->%d\n", pmf,
20797c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip), PM_KUC(dip), (PM_KUC(dip) + cnt)))
20805e3986cbScth 
20817c478bd9Sstevel@tonic-gate 	PM_KUC(dip) += cnt;
20825e3986cbScth 
20833fe80ca4SDan Cross 	PM_UNLOCK_POWER(dip);
20845e3986cbScth 
20857c478bd9Sstevel@tonic-gate 	if (cnt < 0 && PM_KUC(dip) == 0)
20867c478bd9Sstevel@tonic-gate 		pm_rescan(dip);
20877c478bd9Sstevel@tonic-gate }
20887c478bd9Sstevel@tonic-gate 
20897c478bd9Sstevel@tonic-gate #define	MAX_PPM_HANDLERS	4
20907c478bd9Sstevel@tonic-gate 
20917c478bd9Sstevel@tonic-gate kmutex_t ppm_lock;	/* in case we ever do multi-threaded startup */
20927c478bd9Sstevel@tonic-gate 
20937c478bd9Sstevel@tonic-gate struct	ppm_callbacks {
20947c478bd9Sstevel@tonic-gate 	int (*ppmc_func)(dev_info_t *);
20957c478bd9Sstevel@tonic-gate 	dev_info_t	*ppmc_dip;
20967c478bd9Sstevel@tonic-gate } ppm_callbacks[MAX_PPM_HANDLERS + 1];
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate /*
21007c478bd9Sstevel@tonic-gate  * This routine calls into all the registered ppms to notify them
21017c478bd9Sstevel@tonic-gate  * that either all components of power-managed devices are at their
21027c478bd9Sstevel@tonic-gate  * lowest levels or no longer all are at their lowest levels.
21037c478bd9Sstevel@tonic-gate  */
21047c478bd9Sstevel@tonic-gate static void
pm_ppm_notify_all_lowest(dev_info_t * dip,int mode)21057c478bd9Sstevel@tonic-gate pm_ppm_notify_all_lowest(dev_info_t *dip, int mode)
21067c478bd9Sstevel@tonic-gate {
21077c478bd9Sstevel@tonic-gate 	struct ppm_callbacks *ppmcp;
21087c478bd9Sstevel@tonic-gate 	power_req_t power_req;
21097c478bd9Sstevel@tonic-gate 	int result = 0;
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_ALL_LOWEST;
21127c478bd9Sstevel@tonic-gate 	power_req.req.ppm_all_lowest_req.mode = mode;
21137c478bd9Sstevel@tonic-gate 	mutex_enter(&ppm_lock);
21147c478bd9Sstevel@tonic-gate 	for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++)
21157c478bd9Sstevel@tonic-gate 		(void) pm_ctlops((dev_info_t *)ppmcp->ppmc_dip, dip,
21167c478bd9Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
21177c478bd9Sstevel@tonic-gate 	mutex_exit(&ppm_lock);
21182df1fe9cSrandyf 	if (mode == PM_ALL_LOWEST) {
21192df1fe9cSrandyf 		if (autoS3_enabled) {
21202df1fe9cSrandyf 			PMD(PMD_SX, ("pm_ppm_notify_all_lowest triggering "
21212df1fe9cSrandyf 			    "autos3\n"))
21222df1fe9cSrandyf 			mutex_enter(&srn_clone_lock);
21232df1fe9cSrandyf 			if (srn_signal) {
21242df1fe9cSrandyf 				srn_inuse++;
21252df1fe9cSrandyf 				PMD(PMD_SX, ("(*srn_signal)(AUTOSX, 3)\n"))
21262df1fe9cSrandyf 				(*srn_signal)(SRN_TYPE_AUTOSX, 3);
21272df1fe9cSrandyf 				srn_inuse--;
21282df1fe9cSrandyf 			} else {
21292df1fe9cSrandyf 				PMD(PMD_SX, ("srn_signal NULL\n"))
21302df1fe9cSrandyf 			}
21312df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
21322df1fe9cSrandyf 		} else {
21332df1fe9cSrandyf 			PMD(PMD_SX, ("pm_ppm_notify_all_lowest autos3 "
21342df1fe9cSrandyf 			    "disabled\n"));
21352df1fe9cSrandyf 		}
21362df1fe9cSrandyf 	}
21377c478bd9Sstevel@tonic-gate }
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate static void
pm_set_pm_info(dev_info_t * dip,void * value)21407c478bd9Sstevel@tonic-gate pm_set_pm_info(dev_info_t *dip, void *value)
21417c478bd9Sstevel@tonic-gate {
21427c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_info = value;
21437c478bd9Sstevel@tonic-gate }
21447c478bd9Sstevel@tonic-gate 
21457c478bd9Sstevel@tonic-gate pm_rsvp_t *pm_blocked_list;
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate /*
21487c478bd9Sstevel@tonic-gate  * Look up an entry in the blocked list by dip and component
21497c478bd9Sstevel@tonic-gate  */
21507c478bd9Sstevel@tonic-gate static pm_rsvp_t *
pm_rsvp_lookup(dev_info_t * dip,int comp)21517c478bd9Sstevel@tonic-gate pm_rsvp_lookup(dev_info_t *dip, int comp)
21527c478bd9Sstevel@tonic-gate {
21537c478bd9Sstevel@tonic-gate 	pm_rsvp_t *p;
21547c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
21557c478bd9Sstevel@tonic-gate 	for (p = pm_blocked_list; p; p = p->pr_next)
21567c478bd9Sstevel@tonic-gate 		if (p->pr_dip == dip && p->pr_comp == comp) {
21577c478bd9Sstevel@tonic-gate 			return (p);
21587c478bd9Sstevel@tonic-gate 		}
21597c478bd9Sstevel@tonic-gate 	return (NULL);
21607c478bd9Sstevel@tonic-gate }
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate /*
21637c478bd9Sstevel@tonic-gate  * Called when a device which is direct power managed (or the parent or
21647c478bd9Sstevel@tonic-gate  * dependent of such a device) changes power, or when a pm clone is closed
21657c478bd9Sstevel@tonic-gate  * that was direct power managing a device.  This call results in pm_blocked()
21667c478bd9Sstevel@tonic-gate  * (below) returning.
21677c478bd9Sstevel@tonic-gate  */
21687c478bd9Sstevel@tonic-gate void
pm_proceed(dev_info_t * dip,int cmd,int comp,int newlevel)21697c478bd9Sstevel@tonic-gate pm_proceed(dev_info_t *dip, int cmd, int comp, int newlevel)
21707c478bd9Sstevel@tonic-gate {
21717c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "proceed")
21727c478bd9Sstevel@tonic-gate 	pm_rsvp_t *found = NULL;
21737c478bd9Sstevel@tonic-gate 	pm_rsvp_t *p;
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_rsvp_lock);
21767c478bd9Sstevel@tonic-gate 	switch (cmd) {
21777c478bd9Sstevel@tonic-gate 	/*
21787c478bd9Sstevel@tonic-gate 	 * we're giving up control, let any pending op continue
21797c478bd9Sstevel@tonic-gate 	 */
21807c478bd9Sstevel@tonic-gate 	case PMP_RELEASE:
21817c478bd9Sstevel@tonic-gate 		for (p = pm_blocked_list; p; p = p->pr_next) {
21827c478bd9Sstevel@tonic-gate 			if (dip == p->pr_dip) {
21837c478bd9Sstevel@tonic-gate 				p->pr_retval = PMP_RELEASE;
21847c478bd9Sstevel@tonic-gate 				PMD(PMD_DPM, ("%s: RELEASE %s@%s(%s#%d)\n",
21857c478bd9Sstevel@tonic-gate 				    pmf, PM_DEVICE(dip)))
21867c478bd9Sstevel@tonic-gate 				cv_signal(&p->pr_cv);
21877c478bd9Sstevel@tonic-gate 			}
21887c478bd9Sstevel@tonic-gate 		}
21897c478bd9Sstevel@tonic-gate 		break;
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 	/*
21927c478bd9Sstevel@tonic-gate 	 * process has done PM_SET_CURRENT_POWER; let a matching request
21937c478bd9Sstevel@tonic-gate 	 * succeed and a non-matching request for the same device fail
21947c478bd9Sstevel@tonic-gate 	 */
21957c478bd9Sstevel@tonic-gate 	case PMP_SETPOWER:
21967c478bd9Sstevel@tonic-gate 		found = pm_rsvp_lookup(dip, comp);
21977c478bd9Sstevel@tonic-gate 		if (!found)	/* if driver not waiting */
21987c478bd9Sstevel@tonic-gate 			break;
21997c478bd9Sstevel@tonic-gate 		/*
22007c478bd9Sstevel@tonic-gate 		 * This cannot be pm_lower_power, since that can only happen
22017c478bd9Sstevel@tonic-gate 		 * during detach or probe
22027c478bd9Sstevel@tonic-gate 		 */
22037c478bd9Sstevel@tonic-gate 		if (found->pr_newlevel <= newlevel) {
22047c478bd9Sstevel@tonic-gate 			found->pr_retval = PMP_SUCCEED;
22057c478bd9Sstevel@tonic-gate 			PMD(PMD_DPM, ("%s: SUCCEED %s@%s(%s#%d)\n", pmf,
22067c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip)))
22077c478bd9Sstevel@tonic-gate 		} else {
22087c478bd9Sstevel@tonic-gate 			found->pr_retval = PMP_FAIL;
22097c478bd9Sstevel@tonic-gate 			PMD(PMD_DPM, ("%s: FAIL %s@%s(%s#%d)\n", pmf,
22107c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip)))
22117c478bd9Sstevel@tonic-gate 		}
22127c478bd9Sstevel@tonic-gate 		cv_signal(&found->pr_cv);
22137c478bd9Sstevel@tonic-gate 		break;
22147c478bd9Sstevel@tonic-gate 
22157c478bd9Sstevel@tonic-gate 	default:
22167c478bd9Sstevel@tonic-gate 		panic("pm_proceed unknown cmd %d", cmd);
22177c478bd9Sstevel@tonic-gate 	}
22187c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_rsvp_lock);
22197c478bd9Sstevel@tonic-gate }
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate /*
22227c478bd9Sstevel@tonic-gate  * This routine dispatches new work to the dependency thread. Caller must
22237c478bd9Sstevel@tonic-gate  * be prepared to block for memory if necessary.
22247c478bd9Sstevel@tonic-gate  */
22257c478bd9Sstevel@tonic-gate void
pm_dispatch_to_dep_thread(int cmd,char * keeper,char * kept,int wait,int * res,int cached_pwr)22267c478bd9Sstevel@tonic-gate pm_dispatch_to_dep_thread(int cmd, char *keeper, char *kept, int wait,
22277c478bd9Sstevel@tonic-gate     int *res, int cached_pwr)
22287c478bd9Sstevel@tonic-gate {
22297c478bd9Sstevel@tonic-gate 	pm_dep_wk_t	*new_work;
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 	new_work = kmem_zalloc(sizeof (pm_dep_wk_t), KM_SLEEP);
22327c478bd9Sstevel@tonic-gate 	new_work->pdw_type = cmd;
22337c478bd9Sstevel@tonic-gate 	new_work->pdw_wait = wait;
22347c478bd9Sstevel@tonic-gate 	new_work->pdw_done = 0;
22357c478bd9Sstevel@tonic-gate 	new_work->pdw_ret = 0;
22367c478bd9Sstevel@tonic-gate 	new_work->pdw_pwr = cached_pwr;
22377c478bd9Sstevel@tonic-gate 	cv_init(&new_work->pdw_cv, NULL, CV_DEFAULT, NULL);
22387c478bd9Sstevel@tonic-gate 	if (keeper != NULL) {
22397c478bd9Sstevel@tonic-gate 		new_work->pdw_keeper = kmem_zalloc(strlen(keeper) + 1,
22407c478bd9Sstevel@tonic-gate 		    KM_SLEEP);
22417c478bd9Sstevel@tonic-gate 		(void) strcpy(new_work->pdw_keeper, keeper);
22427c478bd9Sstevel@tonic-gate 	}
22437c478bd9Sstevel@tonic-gate 	if (kept != NULL) {
22447c478bd9Sstevel@tonic-gate 		new_work->pdw_kept = kmem_zalloc(strlen(kept) + 1, KM_SLEEP);
22457c478bd9Sstevel@tonic-gate 		(void) strcpy(new_work->pdw_kept, kept);
22467c478bd9Sstevel@tonic-gate 	}
22477c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_dep_thread_lock);
22487c478bd9Sstevel@tonic-gate 	if (pm_dep_thread_workq == NULL) {
22497c478bd9Sstevel@tonic-gate 		pm_dep_thread_workq = new_work;
22507c478bd9Sstevel@tonic-gate 		pm_dep_thread_tail = new_work;
22517c478bd9Sstevel@tonic-gate 		new_work->pdw_next = NULL;
22527c478bd9Sstevel@tonic-gate 	} else {
22537c478bd9Sstevel@tonic-gate 		pm_dep_thread_tail->pdw_next = new_work;
22547c478bd9Sstevel@tonic-gate 		pm_dep_thread_tail = new_work;
22557c478bd9Sstevel@tonic-gate 		new_work->pdw_next = NULL;
22567c478bd9Sstevel@tonic-gate 	}
22577c478bd9Sstevel@tonic-gate 	cv_signal(&pm_dep_thread_cv);
22587c478bd9Sstevel@tonic-gate 	/* If caller asked for it, wait till it is done. */
22597c478bd9Sstevel@tonic-gate 	if (wait)  {
22607c478bd9Sstevel@tonic-gate 		while (!new_work->pdw_done)
22617c478bd9Sstevel@tonic-gate 			cv_wait(&new_work->pdw_cv, &pm_dep_thread_lock);
22627c478bd9Sstevel@tonic-gate 		/*
22637c478bd9Sstevel@tonic-gate 		 * Pass return status, if any, back.
22647c478bd9Sstevel@tonic-gate 		 */
22657c478bd9Sstevel@tonic-gate 		if (res != NULL)
22667c478bd9Sstevel@tonic-gate 			*res = new_work->pdw_ret;
22677c478bd9Sstevel@tonic-gate 		/*
22687c478bd9Sstevel@tonic-gate 		 * If we asked to wait, it is our job to free the request
22697c478bd9Sstevel@tonic-gate 		 * structure.
22707c478bd9Sstevel@tonic-gate 		 */
22717c478bd9Sstevel@tonic-gate 		if (new_work->pdw_keeper)
22727c478bd9Sstevel@tonic-gate 			kmem_free(new_work->pdw_keeper,
22737c478bd9Sstevel@tonic-gate 			    strlen(new_work->pdw_keeper) + 1);
22747c478bd9Sstevel@tonic-gate 		if (new_work->pdw_kept)
22757c478bd9Sstevel@tonic-gate 			kmem_free(new_work->pdw_kept,
22767c478bd9Sstevel@tonic-gate 			    strlen(new_work->pdw_kept) + 1);
22777c478bd9Sstevel@tonic-gate 		kmem_free(new_work, sizeof (pm_dep_wk_t));
22787c478bd9Sstevel@tonic-gate 	}
22797c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_dep_thread_lock);
22807c478bd9Sstevel@tonic-gate }
22817c478bd9Sstevel@tonic-gate 
22827c478bd9Sstevel@tonic-gate /*
22837c478bd9Sstevel@tonic-gate  * Release the pm resource for this device.
22847c478bd9Sstevel@tonic-gate  */
22857c478bd9Sstevel@tonic-gate void
pm_rem_info(dev_info_t * dip)22867c478bd9Sstevel@tonic-gate pm_rem_info(dev_info_t *dip)
22877c478bd9Sstevel@tonic-gate {
22887c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "rem_info")
22897c478bd9Sstevel@tonic-gate 	int		i, count = 0;
22907c478bd9Sstevel@tonic-gate 	pm_info_t	*info = PM_GET_PM_INFO(dip);
22917c478bd9Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
22927c478bd9Sstevel@tonic-gate 	char		*pathbuf;
22937c478bd9Sstevel@tonic-gate 	int		work_type = PM_DEP_WK_DETACH;
22947c478bd9Sstevel@tonic-gate 
22957c478bd9Sstevel@tonic-gate 	ASSERT(info);
22967c478bd9Sstevel@tonic-gate 
22977c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
22987c478bd9Sstevel@tonic-gate 	if (PM_ISDIRECT(dip)) {
22997c478bd9Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DIRECT;
23007c478bd9Sstevel@tonic-gate 		ASSERT(info->pmi_clone);
23017c478bd9Sstevel@tonic-gate 		info->pmi_clone = 0;
23027c478bd9Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
23037c478bd9Sstevel@tonic-gate 	}
23047c478bd9Sstevel@tonic-gate 	ASSERT(!PM_GET_PM_SCAN(dip));
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate 	/*
23077c478bd9Sstevel@tonic-gate 	 * Now adjust parent's kidsupcnt.  BC nodes we check only comp 0,
23087c478bd9Sstevel@tonic-gate 	 * Others we check all components.  BC node that has already
23097c478bd9Sstevel@tonic-gate 	 * called pm_destroy_components() has zero component count.
23107c478bd9Sstevel@tonic-gate 	 * Parents that get notification are not adjusted because their
23117c478bd9Sstevel@tonic-gate 	 * kidsupcnt is always 0 (or 1 during configuration).
23127c478bd9Sstevel@tonic-gate 	 */
23137c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d) has %d components\n", pmf,
23147c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip), PM_NUMCMPTS(dip)))
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 	/* node is detached, so we can examine power without locking */
23177c478bd9Sstevel@tonic-gate 	if (PM_ISBC(dip)) {
23187c478bd9Sstevel@tonic-gate 		count = (PM_CURPOWER(dip, 0) != 0);
23197c478bd9Sstevel@tonic-gate 	} else {
23207c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
23217c478bd9Sstevel@tonic-gate 			count += (PM_CURPOWER(dip, i) != 0);
23227c478bd9Sstevel@tonic-gate 	}
23237c478bd9Sstevel@tonic-gate 
23247c478bd9Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) && pdip && !PM_WANTS_NOTIFICATION(pdip))
23257c478bd9Sstevel@tonic-gate 		e_pm_hold_rele_power(pdip, -count);
23267c478bd9Sstevel@tonic-gate 
23277c478bd9Sstevel@tonic-gate 	/* Schedule a request to clean up dependency records */
23287c478bd9Sstevel@tonic-gate 	pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
23297c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
23307c478bd9Sstevel@tonic-gate 	pm_dispatch_to_dep_thread(work_type, pathbuf, pathbuf,
23317c478bd9Sstevel@tonic-gate 	    PM_DEP_NOWAIT, NULL, (count > 0));
23327c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 	/*
23357c478bd9Sstevel@tonic-gate 	 * Adjust the pm_comps_notlowest count since this device is
23367c478bd9Sstevel@tonic-gate 	 * not being power-managed anymore.
23377c478bd9Sstevel@tonic-gate 	 */
23387c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
2339444f66e7SMark Haywood 		pm_component_t *cp = PM_CP(dip, i);
2340444f66e7SMark Haywood 		if (cp->pmc_cur_pwr != 0)
2341444f66e7SMark Haywood 			PM_DECR_NOTLOWEST(dip)
23427c478bd9Sstevel@tonic-gate 	}
23437c478bd9Sstevel@tonic-gate 	/*
23447c478bd9Sstevel@tonic-gate 	 * Once we clear the info pointer, it looks like it is not power
23457c478bd9Sstevel@tonic-gate 	 * managed to everybody else.
23467c478bd9Sstevel@tonic-gate 	 */
23477c478bd9Sstevel@tonic-gate 	pm_set_pm_info(dip, NULL);
23487c478bd9Sstevel@tonic-gate 	kmem_free(info, sizeof (pm_info_t));
23497c478bd9Sstevel@tonic-gate }
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate int
pm_get_norm_pwrs(dev_info_t * dip,int ** valuep,size_t * length)23527c478bd9Sstevel@tonic-gate pm_get_norm_pwrs(dev_info_t *dip, int **valuep, size_t *length)
23537c478bd9Sstevel@tonic-gate {
23547c478bd9Sstevel@tonic-gate 	int components = PM_NUMCMPTS(dip);
23557c478bd9Sstevel@tonic-gate 	int *bufp;
23567c478bd9Sstevel@tonic-gate 	size_t size;
23577c478bd9Sstevel@tonic-gate 	int i;
23587c478bd9Sstevel@tonic-gate 
23597c478bd9Sstevel@tonic-gate 	if (components <= 0) {
23607c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "!pm: %s@%s(%s#%d) has no components, "
23617c478bd9Sstevel@tonic-gate 		    "can't get normal power values\n", PM_DEVICE(dip));
23627c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
23637c478bd9Sstevel@tonic-gate 	} else {
23647c478bd9Sstevel@tonic-gate 		size = components * sizeof (int);
23657c478bd9Sstevel@tonic-gate 		bufp = kmem_alloc(size, KM_SLEEP);
23667c478bd9Sstevel@tonic-gate 		for (i = 0; i < components; i++) {
23677c478bd9Sstevel@tonic-gate 			bufp[i] = pm_get_normal_power(dip, i);
23687c478bd9Sstevel@tonic-gate 		}
23697c478bd9Sstevel@tonic-gate 	}
23707c478bd9Sstevel@tonic-gate 	*length = size;
23717c478bd9Sstevel@tonic-gate 	*valuep = bufp;
23727c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
23737c478bd9Sstevel@tonic-gate }
23747c478bd9Sstevel@tonic-gate 
23757c478bd9Sstevel@tonic-gate static int
pm_reset_timestamps(dev_info_t * dip,void * arg)23767c478bd9Sstevel@tonic-gate pm_reset_timestamps(dev_info_t *dip, void *arg)
23777c478bd9Sstevel@tonic-gate {
23787c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate 	int components;
23817c478bd9Sstevel@tonic-gate 	int	i;
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip))
23847c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
23857c478bd9Sstevel@tonic-gate 	components = PM_NUMCMPTS(dip);
23867c478bd9Sstevel@tonic-gate 	ASSERT(components > 0);
23877c478bd9Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
23887c478bd9Sstevel@tonic-gate 	for (i = 0; i < components; i++) {
23897c478bd9Sstevel@tonic-gate 		struct pm_component *cp;
23907c478bd9Sstevel@tonic-gate 		/*
23917c478bd9Sstevel@tonic-gate 		 * If the component was not marked as busy,
23927c478bd9Sstevel@tonic-gate 		 * reset its timestamp to now.
23937c478bd9Sstevel@tonic-gate 		 */
23947c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
23957c478bd9Sstevel@tonic-gate 		if (cp->pmc_timestamp)
23967c478bd9Sstevel@tonic-gate 			cp->pmc_timestamp = gethrestime_sec();
23977c478bd9Sstevel@tonic-gate 	}
23987c478bd9Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
23997c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
24007c478bd9Sstevel@tonic-gate }
24017c478bd9Sstevel@tonic-gate 
24027c478bd9Sstevel@tonic-gate /*
24037c478bd9Sstevel@tonic-gate  * Convert a power level to an index into the levels array (or
24047c478bd9Sstevel@tonic-gate  * just PM_LEVEL_UNKNOWN in that special case).
24057c478bd9Sstevel@tonic-gate  */
24067c478bd9Sstevel@tonic-gate static int
pm_level_to_index(dev_info_t * dip,pm_component_t * cp,int level)24077c478bd9Sstevel@tonic-gate pm_level_to_index(dev_info_t *dip, pm_component_t *cp, int level)
24087c478bd9Sstevel@tonic-gate {
24097c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "level_to_index")
24107c478bd9Sstevel@tonic-gate 	int i;
24117c478bd9Sstevel@tonic-gate 	int limit = cp->pmc_comp.pmc_numlevels;
24127c478bd9Sstevel@tonic-gate 	int *ip = cp->pmc_comp.pmc_lvals;
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 	if (level == PM_LEVEL_UNKNOWN)
24157c478bd9Sstevel@tonic-gate 		return (level);
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 	for (i = 0; i < limit; i++) {
24187c478bd9Sstevel@tonic-gate 		if (level == *ip++) {
24197c478bd9Sstevel@tonic-gate 			PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d)[%d] to %x\n",
24207c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip),
24217c478bd9Sstevel@tonic-gate 			    (int)(cp - DEVI(dip)->devi_pm_components), level))
24227c478bd9Sstevel@tonic-gate 			return (i);
24237c478bd9Sstevel@tonic-gate 		}
24247c478bd9Sstevel@tonic-gate 	}
24257c478bd9Sstevel@tonic-gate 	panic("pm_level_to_index: level %d not found for device "
24267c478bd9Sstevel@tonic-gate 	    "%s@%s(%s#%d)", level, PM_DEVICE(dip));
24277c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
24287c478bd9Sstevel@tonic-gate }
24297c478bd9Sstevel@tonic-gate 
24307c478bd9Sstevel@tonic-gate /*
24317c478bd9Sstevel@tonic-gate  * Internal function to set current power level
24327c478bd9Sstevel@tonic-gate  */
24337c478bd9Sstevel@tonic-gate static void
e_pm_set_cur_pwr(dev_info_t * dip,pm_component_t * cp,int level)24347c478bd9Sstevel@tonic-gate e_pm_set_cur_pwr(dev_info_t *dip, pm_component_t *cp, int level)
24357c478bd9Sstevel@tonic-gate {
24367c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_cur_pwr")
24377c478bd9Sstevel@tonic-gate 	int curpwr = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
24387c478bd9Sstevel@tonic-gate 	    cp->pmc_phc_pwr : cp->pmc_cur_pwr);
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate 	/*
24417c478bd9Sstevel@tonic-gate 	 * Nothing to adjust if current & new levels are the same.
24427c478bd9Sstevel@tonic-gate 	 */
24437c478bd9Sstevel@tonic-gate 	if (curpwr != PM_LEVEL_UNKNOWN &&
24447c478bd9Sstevel@tonic-gate 	    level == cp->pmc_comp.pmc_lvals[curpwr])
24457c478bd9Sstevel@tonic-gate 		return;
24467c478bd9Sstevel@tonic-gate 
24477c478bd9Sstevel@tonic-gate 	/*
24487c478bd9Sstevel@tonic-gate 	 * Keep the count for comps doing transition to/from lowest
24497c478bd9Sstevel@tonic-gate 	 * level.
24507c478bd9Sstevel@tonic-gate 	 */
24517c478bd9Sstevel@tonic-gate 	if (curpwr == 0) {
24527c478bd9Sstevel@tonic-gate 		PM_INCR_NOTLOWEST(dip);
24537c478bd9Sstevel@tonic-gate 	} else if (level == cp->pmc_comp.pmc_lvals[0]) {
24547c478bd9Sstevel@tonic-gate 		PM_DECR_NOTLOWEST(dip);
24557c478bd9Sstevel@tonic-gate 	}
24567c478bd9Sstevel@tonic-gate 	cp->pmc_phc_pwr = PM_LEVEL_UNKNOWN;
24577c478bd9Sstevel@tonic-gate 	cp->pmc_cur_pwr = pm_level_to_index(dip, cp, level);
24587c478bd9Sstevel@tonic-gate }
24597c478bd9Sstevel@tonic-gate 
2460d67944fbSScott Rotondo static int pm_phc_impl(dev_info_t *, int, int, int);
2461d67944fbSScott Rotondo 
24627c478bd9Sstevel@tonic-gate /*
24637c478bd9Sstevel@tonic-gate  * This is the default method of setting the power of a device if no ppm
24647c478bd9Sstevel@tonic-gate  * driver has claimed it.
24657c478bd9Sstevel@tonic-gate  */
24667c478bd9Sstevel@tonic-gate int
pm_power(dev_info_t * dip,int comp,int level)24677c478bd9Sstevel@tonic-gate pm_power(dev_info_t *dip, int comp, int level)
24687c478bd9Sstevel@tonic-gate {
24697c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "power")
24707c478bd9Sstevel@tonic-gate 	struct dev_ops	*ops;
24717c478bd9Sstevel@tonic-gate 	int		(*fn)(dev_info_t *, int, int);
24727c478bd9Sstevel@tonic-gate 	struct pm_component *cp = PM_CP(dip, comp);
24737c478bd9Sstevel@tonic-gate 	int retval;
24747c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate 	PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf,
24777c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip), comp, level))
24787c478bd9Sstevel@tonic-gate 	if (!(ops = ddi_get_driver(dip))) {
24797c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) has no ops\n", pmf,
24807c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
24817c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
24827c478bd9Sstevel@tonic-gate 	}
24837c478bd9Sstevel@tonic-gate 	if ((ops->devo_rev < 2) || !(fn = ops->devo_power)) {
24847c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s%s\n", pmf,
24857c478bd9Sstevel@tonic-gate 		    (ops->devo_rev < 2 ? " wrong devo_rev" : ""),
24867c478bd9Sstevel@tonic-gate 		    (!fn ? " devo_power NULL" : "")))
24877c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
24887c478bd9Sstevel@tonic-gate 	}
24897c478bd9Sstevel@tonic-gate 	cp->pmc_flags |= PM_POWER_OP;
24907c478bd9Sstevel@tonic-gate 	retval = (*fn)(dip, comp, level);
24917c478bd9Sstevel@tonic-gate 	cp->pmc_flags &= ~PM_POWER_OP;
24927c478bd9Sstevel@tonic-gate 	if (retval == DDI_SUCCESS) {
24937c478bd9Sstevel@tonic-gate 		e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level);
24947c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
24957c478bd9Sstevel@tonic-gate 	}
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate 	/*
24987c478bd9Sstevel@tonic-gate 	 * If pm_power_has_changed() detected a deadlock with pm_power() it
24997c478bd9Sstevel@tonic-gate 	 * updated only the power level of the component.  If our attempt to
25007c478bd9Sstevel@tonic-gate 	 * set the device new to a power level above has failed we sync the
25017c478bd9Sstevel@tonic-gate 	 * total power state via phc code now.
25027c478bd9Sstevel@tonic-gate 	 */
25037c478bd9Sstevel@tonic-gate 	if (cp->pmc_flags & PM_PHC_WHILE_SET_POWER) {
25047c478bd9Sstevel@tonic-gate 		int phc_lvl =
25057c478bd9Sstevel@tonic-gate 		    cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr];
25067c478bd9Sstevel@tonic-gate 
25077c478bd9Sstevel@tonic-gate 		ASSERT(info);
25087c478bd9Sstevel@tonic-gate 		(void) pm_phc_impl(dip, comp, phc_lvl, 0);
25097c478bd9Sstevel@tonic-gate 		PMD(PMD_PHC, ("%s: phc %s@%s(%s#%d) comp=%d level=%d\n",
25106152d498Smh 		    pmf, PM_DEVICE(dip), comp, phc_lvl))
25117c478bd9Sstevel@tonic-gate 	}
25127c478bd9Sstevel@tonic-gate 
25137c478bd9Sstevel@tonic-gate 	PMD(PMD_FAIL, ("%s: can't set comp=%d (%s) of %s@%s(%s#%d) to "
25147c478bd9Sstevel@tonic-gate 	    "level=%d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, PM_DEVICE(dip),
25157c478bd9Sstevel@tonic-gate 	    level, power_val_to_string(cp, level)));
25167c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
25177c478bd9Sstevel@tonic-gate }
25187c478bd9Sstevel@tonic-gate 
25197c478bd9Sstevel@tonic-gate int
pm_unmanage(dev_info_t * dip)25207c478bd9Sstevel@tonic-gate pm_unmanage(dev_info_t *dip)
25217c478bd9Sstevel@tonic-gate {
25227c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "unmanage")
25237c478bd9Sstevel@tonic-gate 	power_req_t power_req;
25247c478bd9Sstevel@tonic-gate 	int result, retval = 0;
25257c478bd9Sstevel@tonic-gate 
25267c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
25277c478bd9Sstevel@tonic-gate 	PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf,
25287c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip)))
25297c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_UNMANAGE;
25307c478bd9Sstevel@tonic-gate 	power_req.req.ppm_config_req.who = dip;
25317c478bd9Sstevel@tonic-gate 	if (pm_ppm_claimed(dip))
25327c478bd9Sstevel@tonic-gate 		retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
25337c478bd9Sstevel@tonic-gate 		    &power_req, &result);
25347c478bd9Sstevel@tonic-gate #ifdef DEBUG
25357c478bd9Sstevel@tonic-gate 	else
25367c478bd9Sstevel@tonic-gate 		retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
25377c478bd9Sstevel@tonic-gate 		    &power_req, &result);
25387c478bd9Sstevel@tonic-gate #endif
25397c478bd9Sstevel@tonic-gate 	ASSERT(retval == DDI_SUCCESS);
25407c478bd9Sstevel@tonic-gate 	pm_rem_info(dip);
25417c478bd9Sstevel@tonic-gate 	return (retval);
25427c478bd9Sstevel@tonic-gate }
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate int
pm_raise_power(dev_info_t * dip,int comp,int level)25457c478bd9Sstevel@tonic-gate pm_raise_power(dev_info_t *dip, int comp, int level)
25467c478bd9Sstevel@tonic-gate {
25477c478bd9Sstevel@tonic-gate 	if (level < 0)
25487c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
25497c478bd9Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
25507c478bd9Sstevel@tonic-gate 	    !e_pm_valid_power(dip, comp, level))
25517c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	return (dev_is_needed(dip, comp, level, PM_LEVEL_UPONLY));
25547c478bd9Sstevel@tonic-gate }
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate int
pm_lower_power(dev_info_t * dip,int comp,int level)25577c478bd9Sstevel@tonic-gate pm_lower_power(dev_info_t *dip, int comp, int level)
25587c478bd9Sstevel@tonic-gate {
25597c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_lower_power")
25607c478bd9Sstevel@tonic-gate 
25617c478bd9Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
25627c478bd9Sstevel@tonic-gate 	    !e_pm_valid_power(dip, comp, level)) {
25637c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) "
25647c478bd9Sstevel@tonic-gate 		    "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level))
25657c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
25667c478bd9Sstevel@tonic-gate 	}
25677c478bd9Sstevel@tonic-gate 
25687c478bd9Sstevel@tonic-gate 	if (!DEVI_IS_DETACHING(dip)) {
25697c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) not detaching\n",
25707c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
25717c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
25727c478bd9Sstevel@tonic-gate 	}
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 	/*
25757c478bd9Sstevel@tonic-gate 	 * If we don't care about saving power, or we're treating this node
25767c478bd9Sstevel@tonic-gate 	 * specially, then this is a no-op
25777c478bd9Sstevel@tonic-gate 	 */
2578c42872d4Smh 	if (!PM_SCANABLE(dip) || pm_noinvol(dip)) {
2579c42872d4Smh 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) %s%s%s%s\n",
2580c42872d4Smh 		    pmf, PM_DEVICE(dip),
25817c478bd9Sstevel@tonic-gate 		    !autopm_enabled ? "!autopm_enabled " : "",
25820e751525SEric Saxe 		    !PM_POLLING_CPUPM ? "!cpupm_polling " : "",
2583c42872d4Smh 		    PM_CPUPM_DISABLED ? "cpupm_disabled " : "",
25847c478bd9Sstevel@tonic-gate 		    pm_noinvol(dip) ? "pm_noinvol()" : ""))
25857c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
25867c478bd9Sstevel@tonic-gate 	}
25877c478bd9Sstevel@tonic-gate 
25887c478bd9Sstevel@tonic-gate 	if (dev_is_needed(dip, comp, level, PM_LEVEL_DOWNONLY) != DDI_SUCCESS) {
25897c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) dev_is_needed failed\n", pmf,
25907c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
25917c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
25927c478bd9Sstevel@tonic-gate 	}
25937c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
25947c478bd9Sstevel@tonic-gate }
25957c478bd9Sstevel@tonic-gate 
25967c478bd9Sstevel@tonic-gate /*
25977c478bd9Sstevel@tonic-gate  * Find the entries struct for a given dip in the blocked list, return it locked
25987c478bd9Sstevel@tonic-gate  */
25997c478bd9Sstevel@tonic-gate static psce_t *
pm_psc_dip_to_direct(dev_info_t * dip,pscc_t ** psccp)26007c478bd9Sstevel@tonic-gate pm_psc_dip_to_direct(dev_info_t *dip, pscc_t **psccp)
26017c478bd9Sstevel@tonic-gate {
26027c478bd9Sstevel@tonic-gate 	pscc_t *p;
26037c478bd9Sstevel@tonic-gate 	psce_t *psce;
26047c478bd9Sstevel@tonic-gate 
26057c478bd9Sstevel@tonic-gate 	rw_enter(&pm_pscc_direct_rwlock, RW_READER);
26067c478bd9Sstevel@tonic-gate 	for (p = pm_pscc_direct; p; p = p->pscc_next) {
26077c478bd9Sstevel@tonic-gate 		if (p->pscc_dip == dip) {
26087c478bd9Sstevel@tonic-gate 			*psccp = p;
26097c478bd9Sstevel@tonic-gate 			psce = p->pscc_entries;
26107c478bd9Sstevel@tonic-gate 			mutex_enter(&psce->psce_lock);
26117c478bd9Sstevel@tonic-gate 			ASSERT(psce);
26127c478bd9Sstevel@tonic-gate 			rw_exit(&pm_pscc_direct_rwlock);
26137c478bd9Sstevel@tonic-gate 			return (psce);
26147c478bd9Sstevel@tonic-gate 		}
26157c478bd9Sstevel@tonic-gate 	}
26167c478bd9Sstevel@tonic-gate 	rw_exit(&pm_pscc_direct_rwlock);
26177c478bd9Sstevel@tonic-gate 	panic("sunpm: no entry for dip %p in direct list", (void *)dip);
26187c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
26197c478bd9Sstevel@tonic-gate }
26207c478bd9Sstevel@tonic-gate 
26217c478bd9Sstevel@tonic-gate /*
26227c478bd9Sstevel@tonic-gate  * Write an entry indicating a power level change (to be passed to a process
26237c478bd9Sstevel@tonic-gate  * later) in the given psce.
26247c478bd9Sstevel@tonic-gate  * If we were called in the path that brings up the console fb in the
26257c478bd9Sstevel@tonic-gate  * case of entering the prom, we don't want to sleep.  If the alloc fails, then
26267c478bd9Sstevel@tonic-gate  * we create a record that has a size of -1, a physaddr of NULL, and that
26277c478bd9Sstevel@tonic-gate  * has the overflow flag set.
26287c478bd9Sstevel@tonic-gate  */
26297c478bd9Sstevel@tonic-gate static int
psc_entry(ushort_t event,psce_t * psce,dev_info_t * dip,int comp,int new,int old,int which,pm_canblock_t canblock)26307c478bd9Sstevel@tonic-gate psc_entry(ushort_t event, psce_t *psce, dev_info_t *dip, int comp, int new,
26317c478bd9Sstevel@tonic-gate     int old, int which, pm_canblock_t canblock)
26327c478bd9Sstevel@tonic-gate {
26337c478bd9Sstevel@tonic-gate 	char	buf[MAXNAMELEN];
26347c478bd9Sstevel@tonic-gate 	pm_state_change_t *p;
26357c478bd9Sstevel@tonic-gate 	size_t	size;
26367c478bd9Sstevel@tonic-gate 	caddr_t physpath = NULL;
26377c478bd9Sstevel@tonic-gate 	int	overrun = 0;
26387c478bd9Sstevel@tonic-gate 
26397c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&psce->psce_lock));
26407c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, buf);
26417c478bd9Sstevel@tonic-gate 	size = strlen(buf) + 1;
26427c478bd9Sstevel@tonic-gate 	p = psce->psce_in;
26437c478bd9Sstevel@tonic-gate 	if (canblock == PM_CANBLOCK_BYPASS) {
26447c478bd9Sstevel@tonic-gate 		physpath = kmem_alloc(size, KM_NOSLEEP);
26457c478bd9Sstevel@tonic-gate 		if (physpath == NULL) {
26467c478bd9Sstevel@tonic-gate 			/*
26477c478bd9Sstevel@tonic-gate 			 * mark current entry as overrun
26487c478bd9Sstevel@tonic-gate 			 */
26497c478bd9Sstevel@tonic-gate 			p->flags |= PSC_EVENT_LOST;
26507c478bd9Sstevel@tonic-gate 			size = (size_t)-1;
26517c478bd9Sstevel@tonic-gate 		}
26527c478bd9Sstevel@tonic-gate 	} else
26537c478bd9Sstevel@tonic-gate 		physpath = kmem_alloc(size, KM_SLEEP);
26547c478bd9Sstevel@tonic-gate 	if (p->size) {	/* overflow; mark the next entry */
26557c478bd9Sstevel@tonic-gate 		if (p->size != (size_t)-1)
26567c478bd9Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
26577c478bd9Sstevel@tonic-gate 		ASSERT(psce->psce_out == p);
26587c478bd9Sstevel@tonic-gate 		if (p == psce->psce_last) {
26597c478bd9Sstevel@tonic-gate 			psce->psce_first->flags |= PSC_EVENT_LOST;
26607c478bd9Sstevel@tonic-gate 			psce->psce_out = psce->psce_first;
26617c478bd9Sstevel@tonic-gate 		} else {
26627c478bd9Sstevel@tonic-gate 			(p + 1)->flags |= PSC_EVENT_LOST;
26637c478bd9Sstevel@tonic-gate 			psce->psce_out = (p + 1);
26647c478bd9Sstevel@tonic-gate 		}
26657c478bd9Sstevel@tonic-gate 		overrun++;
26667c478bd9Sstevel@tonic-gate 	} else if (physpath == NULL) {	/* alloc failed, mark this entry */
26677c478bd9Sstevel@tonic-gate 		p->flags |= PSC_EVENT_LOST;
26687c478bd9Sstevel@tonic-gate 		p->size = 0;
26697c478bd9Sstevel@tonic-gate 		p->physpath = NULL;
26707c478bd9Sstevel@tonic-gate 	}
26717c478bd9Sstevel@tonic-gate 	if (which == PSC_INTEREST) {
26727c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_compcnt_lock);
26737c478bd9Sstevel@tonic-gate 		if (pm_comps_notlowest == 0)
26747c478bd9Sstevel@tonic-gate 			p->flags |= PSC_ALL_LOWEST;
26757c478bd9Sstevel@tonic-gate 		else
26767c478bd9Sstevel@tonic-gate 			p->flags &= ~PSC_ALL_LOWEST;
26777c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_compcnt_lock);
26787c478bd9Sstevel@tonic-gate 	}
26797c478bd9Sstevel@tonic-gate 	p->event = event;
26807c478bd9Sstevel@tonic-gate 	p->timestamp = gethrestime_sec();
26817c478bd9Sstevel@tonic-gate 	p->component = comp;
26827c478bd9Sstevel@tonic-gate 	p->old_level = old;
26837c478bd9Sstevel@tonic-gate 	p->new_level = new;
26847c478bd9Sstevel@tonic-gate 	p->physpath = physpath;
26857c478bd9Sstevel@tonic-gate 	p->size = size;
26867c478bd9Sstevel@tonic-gate 	if (physpath != NULL)
26877c478bd9Sstevel@tonic-gate 		(void) strcpy(p->physpath, buf);
26887c478bd9Sstevel@tonic-gate 	if (p == psce->psce_last)
26897c478bd9Sstevel@tonic-gate 		psce->psce_in = psce->psce_first;
26907c478bd9Sstevel@tonic-gate 	else
26917c478bd9Sstevel@tonic-gate 		psce->psce_in = ++p;
26927c478bd9Sstevel@tonic-gate 	mutex_exit(&psce->psce_lock);
26937c478bd9Sstevel@tonic-gate 	return (overrun);
26947c478bd9Sstevel@tonic-gate }
26957c478bd9Sstevel@tonic-gate 
26967c478bd9Sstevel@tonic-gate /*
26977c478bd9Sstevel@tonic-gate  * Find the next entry on the interest list.  We keep a pointer to the item we
26987c478bd9Sstevel@tonic-gate  * last returned in the user's cooke.  Returns a locked entries struct.
26997c478bd9Sstevel@tonic-gate  */
27007c478bd9Sstevel@tonic-gate static psce_t *
psc_interest(void ** cookie,pscc_t ** psccp)27017c478bd9Sstevel@tonic-gate psc_interest(void **cookie, pscc_t **psccp)
27027c478bd9Sstevel@tonic-gate {
27037c478bd9Sstevel@tonic-gate 	pscc_t *pscc;
27047c478bd9Sstevel@tonic-gate 	pscc_t **cookiep = (pscc_t **)cookie;
27057c478bd9Sstevel@tonic-gate 
27067c478bd9Sstevel@tonic-gate 	if (*cookiep == NULL)
27077c478bd9Sstevel@tonic-gate 		pscc = pm_pscc_interest;
27087c478bd9Sstevel@tonic-gate 	else
27097c478bd9Sstevel@tonic-gate 		pscc = (*cookiep)->pscc_next;
27107c478bd9Sstevel@tonic-gate 	if (pscc) {
27117c478bd9Sstevel@tonic-gate 		*cookiep = pscc;
27127c478bd9Sstevel@tonic-gate 		*psccp = pscc;
27137c478bd9Sstevel@tonic-gate 		mutex_enter(&pscc->pscc_entries->psce_lock);
27147c478bd9Sstevel@tonic-gate 		return (pscc->pscc_entries);
27157c478bd9Sstevel@tonic-gate 	} else {
27167c478bd9Sstevel@tonic-gate 		return (NULL);
27177c478bd9Sstevel@tonic-gate 	}
27187c478bd9Sstevel@tonic-gate }
27197c478bd9Sstevel@tonic-gate 
27207c478bd9Sstevel@tonic-gate /*
27217c478bd9Sstevel@tonic-gate  * Create an entry for a process to pick up indicating a power level change.
27227c478bd9Sstevel@tonic-gate  */
27237c478bd9Sstevel@tonic-gate static void
pm_enqueue_notify(ushort_t cmd,dev_info_t * dip,int comp,int newlevel,int oldlevel,pm_canblock_t canblock)27247c478bd9Sstevel@tonic-gate pm_enqueue_notify(ushort_t cmd, dev_info_t *dip, int comp,
27257c478bd9Sstevel@tonic-gate     int newlevel, int oldlevel, pm_canblock_t canblock)
27267c478bd9Sstevel@tonic-gate {
27277c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "enqueue_notify")
27287c478bd9Sstevel@tonic-gate 	pscc_t	*pscc;
27297c478bd9Sstevel@tonic-gate 	psce_t	*psce;
27307c478bd9Sstevel@tonic-gate 	void		*cookie = NULL;
27317c478bd9Sstevel@tonic-gate 	int	overrun;
27327c478bd9Sstevel@tonic-gate 
27337c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
27347c478bd9Sstevel@tonic-gate 	switch (cmd) {
27357c478bd9Sstevel@tonic-gate 	case PSC_PENDING_CHANGE:	/* only for controlling process */
27367c478bd9Sstevel@tonic-gate 		PMD(PMD_DPM, ("%s: PENDING %s@%s(%s#%d), comp %d, %d -> %d\n",
27377c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), comp, oldlevel, newlevel))
27387c478bd9Sstevel@tonic-gate 		psce = pm_psc_dip_to_direct(dip, &pscc);
27397c478bd9Sstevel@tonic-gate 		ASSERT(psce);
27407c478bd9Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("%s: PENDING: %s@%s(%s#%d) pm_poll_cnt[%d] "
27417c478bd9Sstevel@tonic-gate 		    "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone,
27427c478bd9Sstevel@tonic-gate 		    pm_poll_cnt[pscc->pscc_clone]))
27437c478bd9Sstevel@tonic-gate 		overrun = psc_entry(cmd, psce, dip, comp, newlevel, oldlevel,
27447c478bd9Sstevel@tonic-gate 		    PSC_DIRECT, canblock);
27457c478bd9Sstevel@tonic-gate 		PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone))
27467c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_clone_lock);
27477c478bd9Sstevel@tonic-gate 		if (!overrun)
27487c478bd9Sstevel@tonic-gate 			pm_poll_cnt[pscc->pscc_clone]++;
27497c478bd9Sstevel@tonic-gate 		cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27507c478bd9Sstevel@tonic-gate 		pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN));
27517c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
27527c478bd9Sstevel@tonic-gate 		break;
27537c478bd9Sstevel@tonic-gate 	case PSC_HAS_CHANGED:
27547c478bd9Sstevel@tonic-gate 		PMD(PMD_DPM, ("%s: HAS %s@%s(%s#%d), comp %d, %d -> %d\n",
27557c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), comp, oldlevel, newlevel))
27567c478bd9Sstevel@tonic-gate 		if (PM_ISDIRECT(dip) && canblock != PM_CANBLOCK_BYPASS) {
27577c478bd9Sstevel@tonic-gate 			psce = pm_psc_dip_to_direct(dip, &pscc);
27587c478bd9Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("%s: HAS: %s@%s(%s#%d) pm_poll_cnt[%d] "
27597c478bd9Sstevel@tonic-gate 			    "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone,
27607c478bd9Sstevel@tonic-gate 			    pm_poll_cnt[pscc->pscc_clone]))
27617c478bd9Sstevel@tonic-gate 			overrun = psc_entry(cmd, psce, dip, comp, newlevel,
27627c478bd9Sstevel@tonic-gate 			    oldlevel, PSC_DIRECT, canblock);
27637c478bd9Sstevel@tonic-gate 			PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone))
27647c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
27657c478bd9Sstevel@tonic-gate 			if (!overrun)
27667c478bd9Sstevel@tonic-gate 				pm_poll_cnt[pscc->pscc_clone]++;
27677c478bd9Sstevel@tonic-gate 			cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27687c478bd9Sstevel@tonic-gate 			pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN));
27697c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
27707c478bd9Sstevel@tonic-gate 		}
27717c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_clone_lock);
27727c478bd9Sstevel@tonic-gate 		rw_enter(&pm_pscc_interest_rwlock, RW_READER);
27737c478bd9Sstevel@tonic-gate 		while ((psce = psc_interest(&cookie, &pscc)) != NULL) {
27747c478bd9Sstevel@tonic-gate 			(void) psc_entry(cmd, psce, dip, comp, newlevel,
27757c478bd9Sstevel@tonic-gate 			    oldlevel, PSC_INTEREST, canblock);
27767c478bd9Sstevel@tonic-gate 			cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27777c478bd9Sstevel@tonic-gate 		}
27787c478bd9Sstevel@tonic-gate 		rw_exit(&pm_pscc_interest_rwlock);
27797c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
27807c478bd9Sstevel@tonic-gate 		break;
27817c478bd9Sstevel@tonic-gate #ifdef DEBUG
27827c478bd9Sstevel@tonic-gate 	default:
27837c478bd9Sstevel@tonic-gate 		ASSERT(0);
27847c478bd9Sstevel@tonic-gate #endif
27857c478bd9Sstevel@tonic-gate 	}
27867c478bd9Sstevel@tonic-gate }
27877c478bd9Sstevel@tonic-gate 
27887c478bd9Sstevel@tonic-gate static void
pm_enqueue_notify_others(pm_ppm_devlist_t ** listp,pm_canblock_t canblock)27897c478bd9Sstevel@tonic-gate pm_enqueue_notify_others(pm_ppm_devlist_t **listp, pm_canblock_t canblock)
27907c478bd9Sstevel@tonic-gate {
27917c478bd9Sstevel@tonic-gate 	if (listp) {
27927c478bd9Sstevel@tonic-gate 		pm_ppm_devlist_t *p, *next = NULL;
27937c478bd9Sstevel@tonic-gate 
27947c478bd9Sstevel@tonic-gate 		for (p = *listp; p; p = next) {
27957c478bd9Sstevel@tonic-gate 			next = p->ppd_next;
27967c478bd9Sstevel@tonic-gate 			pm_enqueue_notify(PSC_HAS_CHANGED, p->ppd_who,
27977c478bd9Sstevel@tonic-gate 			    p->ppd_cmpt, p->ppd_new_level, p->ppd_old_level,
27987c478bd9Sstevel@tonic-gate 			    canblock);
27997c478bd9Sstevel@tonic-gate 			kmem_free(p, sizeof (pm_ppm_devlist_t));
28007c478bd9Sstevel@tonic-gate 		}
28017c478bd9Sstevel@tonic-gate 		*listp = NULL;
28027c478bd9Sstevel@tonic-gate 	}
28037c478bd9Sstevel@tonic-gate }
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate /*
28067c478bd9Sstevel@tonic-gate  * Try to get the power locks of the parent node and target (child)
28077c478bd9Sstevel@tonic-gate  * node.  Return true if successful (with both locks held) or false
28087c478bd9Sstevel@tonic-gate  * (with no locks held).
28097c478bd9Sstevel@tonic-gate  */
28107c478bd9Sstevel@tonic-gate static int
pm_try_parent_child_locks(dev_info_t * pdip,dev_info_t * dip)28113fe80ca4SDan Cross pm_try_parent_child_locks(dev_info_t *pdip, dev_info_t *dip)
28127c478bd9Sstevel@tonic-gate {
28133fe80ca4SDan Cross 	if (ndi_devi_tryenter(pdip)) {
28143fe80ca4SDan Cross 		if (PM_TRY_LOCK_POWER(dip)) {
28157c478bd9Sstevel@tonic-gate 			return (1);
28167c478bd9Sstevel@tonic-gate 		}
28173fe80ca4SDan Cross 		ndi_devi_exit(pdip);
28183fe80ca4SDan Cross 	}
28197c478bd9Sstevel@tonic-gate 	return (0);
28207c478bd9Sstevel@tonic-gate }
28217c478bd9Sstevel@tonic-gate 
28227c478bd9Sstevel@tonic-gate /*
28237c478bd9Sstevel@tonic-gate  * Determine if the power lock owner is blocked by current thread.
28247c478bd9Sstevel@tonic-gate  * returns :
2825*d5ebc493SDan Cross  *	1 - If the thread owning the effective power lock (the first lock on
28267c478bd9Sstevel@tonic-gate  *          which a thread blocks when it does PM_LOCK_POWER) is blocked by
28277c478bd9Sstevel@tonic-gate  *          a mutex held by the current thread.
28287c478bd9Sstevel@tonic-gate  *
28297c478bd9Sstevel@tonic-gate  *	0 - otherwise
28307c478bd9Sstevel@tonic-gate  *
28317c478bd9Sstevel@tonic-gate  * Note : This function is called by pm_power_has_changed to determine whether
28327c478bd9Sstevel@tonic-gate  * it is executing in parallel with pm_set_power.
28337c478bd9Sstevel@tonic-gate  */
28347c478bd9Sstevel@tonic-gate static int
pm_blocked_by_us(dev_info_t * dip)28357c478bd9Sstevel@tonic-gate pm_blocked_by_us(dev_info_t *dip)
28367c478bd9Sstevel@tonic-gate {
28377c478bd9Sstevel@tonic-gate 	power_req_t power_req;
28387c478bd9Sstevel@tonic-gate 	kthread_t *owner;
28397c478bd9Sstevel@tonic-gate 	int result;
28407c478bd9Sstevel@tonic-gate 	kmutex_t *mp;
28417c478bd9Sstevel@tonic-gate 	dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm;
28427c478bd9Sstevel@tonic-gate 
28437c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_POWER_LOCK_OWNER;
28447c478bd9Sstevel@tonic-gate 	power_req.req.ppm_power_lock_owner_req.who = dip;
28457c478bd9Sstevel@tonic-gate 	if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, &result) !=
28467c478bd9Sstevel@tonic-gate 	    DDI_SUCCESS) {
28477c478bd9Sstevel@tonic-gate 		/*
28487c478bd9Sstevel@tonic-gate 		 * It is assumed that if the device is claimed by ppm, ppm
28497c478bd9Sstevel@tonic-gate 		 * will always implement this request type and it'll always
28507c478bd9Sstevel@tonic-gate 		 * return success. We panic here, if it fails.
28517c478bd9Sstevel@tonic-gate 		 */
28527c478bd9Sstevel@tonic-gate 		panic("pm: Can't determine power lock owner of %s@%s(%s#%d)\n",
28537c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip));
28547c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
28557c478bd9Sstevel@tonic-gate 	}
28567c478bd9Sstevel@tonic-gate 
28577c478bd9Sstevel@tonic-gate 	if ((owner = power_req.req.ppm_power_lock_owner_req.owner) != NULL &&
28587c478bd9Sstevel@tonic-gate 	    owner->t_state == TS_SLEEP &&
28597c478bd9Sstevel@tonic-gate 	    owner->t_sobj_ops &&
28607c478bd9Sstevel@tonic-gate 	    SOBJ_TYPE(owner->t_sobj_ops) == SOBJ_MUTEX &&
28617c478bd9Sstevel@tonic-gate 	    (mp = (kmutex_t *)owner->t_wchan) &&
28627c478bd9Sstevel@tonic-gate 	    mutex_owner(mp) == curthread)
28637c478bd9Sstevel@tonic-gate 		return (1);
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate 	return (0);
28667c478bd9Sstevel@tonic-gate }
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate /*
28697c478bd9Sstevel@tonic-gate  * Notify parent which wants to hear about a child's power changes.
28707c478bd9Sstevel@tonic-gate  */
28717c478bd9Sstevel@tonic-gate static void
pm_notify_parent(dev_info_t * dip,dev_info_t * pdip,int comp,int old_level,int level)28727c478bd9Sstevel@tonic-gate pm_notify_parent(dev_info_t *dip,
28737c478bd9Sstevel@tonic-gate     dev_info_t *pdip, int comp, int old_level, int level)
28747c478bd9Sstevel@tonic-gate {
28757c478bd9Sstevel@tonic-gate 	pm_bp_has_changed_t bphc;
28767c478bd9Sstevel@tonic-gate 	pm_sp_misc_t pspm;
28777c478bd9Sstevel@tonic-gate 	char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
28787c478bd9Sstevel@tonic-gate 	int result = DDI_SUCCESS;
28797c478bd9Sstevel@tonic-gate 
28807c478bd9Sstevel@tonic-gate 	bphc.bphc_dip = dip;
28817c478bd9Sstevel@tonic-gate 	bphc.bphc_path = ddi_pathname(dip, pathbuf);
28827c478bd9Sstevel@tonic-gate 	bphc.bphc_comp = comp;
28837c478bd9Sstevel@tonic-gate 	bphc.bphc_olevel = old_level;
28847c478bd9Sstevel@tonic-gate 	bphc.bphc_nlevel = level;
28857c478bd9Sstevel@tonic-gate 	pspm.pspm_canblock = PM_CANBLOCK_BLOCK;
28867c478bd9Sstevel@tonic-gate 	pspm.pspm_scan = 0;
28877c478bd9Sstevel@tonic-gate 	bphc.bphc_private = &pspm;
28887c478bd9Sstevel@tonic-gate 	(void) (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
28897c478bd9Sstevel@tonic-gate 	    BUS_POWER_HAS_CHANGED, (void *)&bphc, (void *)&result);
28907c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
28917c478bd9Sstevel@tonic-gate }
28927c478bd9Sstevel@tonic-gate 
28937c478bd9Sstevel@tonic-gate /*
28947c478bd9Sstevel@tonic-gate  * Check if we need to resume a BC device, and make the attach call as required.
28957c478bd9Sstevel@tonic-gate  */
28967c478bd9Sstevel@tonic-gate static int
pm_check_and_resume(dev_info_t * dip,int comp,int old_level,int level)28977c478bd9Sstevel@tonic-gate pm_check_and_resume(dev_info_t *dip, int comp, int old_level, int level)
28987c478bd9Sstevel@tonic-gate {
28997c478bd9Sstevel@tonic-gate 	int ret = DDI_SUCCESS;
29007c478bd9Sstevel@tonic-gate 
29017c478bd9Sstevel@tonic-gate 	if (PM_ISBC(dip) && comp == 0 && old_level == 0 && level != 0) {
29027c478bd9Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED);
29037c478bd9Sstevel@tonic-gate 		/* ppm is not interested in DDI_PM_RESUME */
29047c478bd9Sstevel@tonic-gate 		if ((ret = devi_attach(dip, DDI_PM_RESUME)) != DDI_SUCCESS)
29057c478bd9Sstevel@tonic-gate 			/* XXX Should we mark it resumed, */
29067c478bd9Sstevel@tonic-gate 			/* even though it failed? */
29077c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "!pm: Can't resume %s@%s",
29087c478bd9Sstevel@tonic-gate 			    PM_NAME(dip), PM_ADDR(dip));
29097c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED;
29107c478bd9Sstevel@tonic-gate 	}
29117c478bd9Sstevel@tonic-gate 
29127c478bd9Sstevel@tonic-gate 	return (ret);
29137c478bd9Sstevel@tonic-gate }
29147c478bd9Sstevel@tonic-gate 
29157c478bd9Sstevel@tonic-gate /*
29167c478bd9Sstevel@tonic-gate  * Tests outside the lock to see if we should bother to enqueue an entry
29177c478bd9Sstevel@tonic-gate  * for any watching process.  If yes, then caller will take the lock and
29187c478bd9Sstevel@tonic-gate  * do the full protocol
29197c478bd9Sstevel@tonic-gate  */
29207c478bd9Sstevel@tonic-gate static int
pm_watchers()29217c478bd9Sstevel@tonic-gate pm_watchers()
29227c478bd9Sstevel@tonic-gate {
29237c478bd9Sstevel@tonic-gate 	if (pm_processes_stopped)
29247c478bd9Sstevel@tonic-gate 		return (0);
29257c478bd9Sstevel@tonic-gate 	return (pm_pscc_direct || pm_pscc_interest);
29267c478bd9Sstevel@tonic-gate }
29277c478bd9Sstevel@tonic-gate 
2928d67944fbSScott Rotondo static int pm_phc_impl(dev_info_t *, int, int, int);
2929d67944fbSScott Rotondo 
29307c478bd9Sstevel@tonic-gate /*
29317c478bd9Sstevel@tonic-gate  * A driver is reporting that the power of one of its device's components
29327c478bd9Sstevel@tonic-gate  * has changed.  Update the power state accordingly.
29337c478bd9Sstevel@tonic-gate  */
29347c478bd9Sstevel@tonic-gate int
pm_power_has_changed(dev_info_t * dip,int comp,int level)29357c478bd9Sstevel@tonic-gate pm_power_has_changed(dev_info_t *dip, int comp, int level)
29367c478bd9Sstevel@tonic-gate {
29377c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_power_has_changed")
29387c478bd9Sstevel@tonic-gate 	int ret;
29397c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
29407c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
29413fe80ca4SDan Cross 	int blocked, old_level;
29427c478bd9Sstevel@tonic-gate 
29437c478bd9Sstevel@tonic-gate 	if (level < 0) {
29447c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d): bad level=%d\n", pmf,
29457c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), level))
29467c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
29477c478bd9Sstevel@tonic-gate 	}
29487c478bd9Sstevel@tonic-gate 
29497c478bd9Sstevel@tonic-gate 	PMD(PMD_KIDSUP | PMD_DEP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf,
29507c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip), comp, level))
29517c478bd9Sstevel@tonic-gate 
29527c478bd9Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, &cp) ||
29537c478bd9Sstevel@tonic-gate 	    !e_pm_valid_power(dip, comp, level))
29547c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
29557c478bd9Sstevel@tonic-gate 
29567c478bd9Sstevel@tonic-gate 	/*
29577c478bd9Sstevel@tonic-gate 	 * A driver thread calling pm_power_has_changed and another thread
29587c478bd9Sstevel@tonic-gate 	 * calling pm_set_power can deadlock.  The problem is not resolvable
29597c478bd9Sstevel@tonic-gate 	 * by changing lock order, so we use pm_blocked_by_us() to detect
29607c478bd9Sstevel@tonic-gate 	 * this specific deadlock.  If we can't get the lock immediately
29617c478bd9Sstevel@tonic-gate 	 * and we are deadlocked, just update the component's level, do
29627c478bd9Sstevel@tonic-gate 	 * notifications, and return.  We intend to update the total power
29637c478bd9Sstevel@tonic-gate 	 * state later (if the other thread fails to set power to the
29647c478bd9Sstevel@tonic-gate 	 * desired level).  If we were called because of a power change on a
29657c478bd9Sstevel@tonic-gate 	 * component that isn't involved in a set_power op, update all state
29667c478bd9Sstevel@tonic-gate 	 * immediately.
29677c478bd9Sstevel@tonic-gate 	 */
29687c478bd9Sstevel@tonic-gate 	cp = PM_CP(dip, comp);
29693fe80ca4SDan Cross 	while (!pm_try_parent_child_locks(pdip, dip)) {
29707c478bd9Sstevel@tonic-gate 		if (((blocked = pm_blocked_by_us(dip)) != 0) &&
29717c478bd9Sstevel@tonic-gate 		    (cp->pmc_flags & PM_POWER_OP)) {
29727c478bd9Sstevel@tonic-gate 			if (pm_watchers()) {
29737c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_rsvp_lock);
29747c478bd9Sstevel@tonic-gate 				pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp,
29757c478bd9Sstevel@tonic-gate 				    level, cur_power(cp), PM_CANBLOCK_BLOCK);
29767c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_rsvp_lock);
29777c478bd9Sstevel@tonic-gate 			}
29787c478bd9Sstevel@tonic-gate 			if (pdip && PM_WANTS_NOTIFICATION(pdip))
29797c478bd9Sstevel@tonic-gate 				pm_notify_parent(dip,
29807c478bd9Sstevel@tonic-gate 				    pdip, comp, cur_power(cp), level);
29817c478bd9Sstevel@tonic-gate 			(void) pm_check_and_resume(dip,
29827c478bd9Sstevel@tonic-gate 			    comp, cur_power(cp), level);
29837c478bd9Sstevel@tonic-gate 
29847c478bd9Sstevel@tonic-gate 			/*
29857c478bd9Sstevel@tonic-gate 			 * Stash the old power index, update curpwr, and flag
29867c478bd9Sstevel@tonic-gate 			 * that the total power state needs to be synched.
29877c478bd9Sstevel@tonic-gate 			 */
29887c478bd9Sstevel@tonic-gate 			cp->pmc_flags |= PM_PHC_WHILE_SET_POWER;
29897c478bd9Sstevel@tonic-gate 			/*
29907c478bd9Sstevel@tonic-gate 			 * Several pm_power_has_changed calls could arrive
29917c478bd9Sstevel@tonic-gate 			 * while the set power path remains blocked.  Keep the
29927c478bd9Sstevel@tonic-gate 			 * oldest old power and the newest new power of any
29937c478bd9Sstevel@tonic-gate 			 * sequence of phc calls which arrive during deadlock.
29947c478bd9Sstevel@tonic-gate 			 */
29957c478bd9Sstevel@tonic-gate 			if (cp->pmc_phc_pwr == PM_LEVEL_UNKNOWN)
29967c478bd9Sstevel@tonic-gate 				cp->pmc_phc_pwr = cp->pmc_cur_pwr;
29977c478bd9Sstevel@tonic-gate 			cp->pmc_cur_pwr =
29987c478bd9Sstevel@tonic-gate 			    pm_level_to_index(dip, cp, level);
29997c478bd9Sstevel@tonic-gate 			PMD(PMD_PHC, ("%s: deadlock for %s@%s(%s#%d), comp=%d, "
30007c478bd9Sstevel@tonic-gate 			    "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
30017c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
30027c478bd9Sstevel@tonic-gate 		} else
30037c478bd9Sstevel@tonic-gate 			if (blocked) {	/* blocked, but different cmpt? */
30043fe80ca4SDan Cross 				if (!ndi_devi_tryenter(pdip)) {
30057c478bd9Sstevel@tonic-gate 					cmn_err(CE_NOTE,
30067c478bd9Sstevel@tonic-gate 					    "!pm: parent kuc not updated due "
30077c478bd9Sstevel@tonic-gate 					    "to possible deadlock.\n");
30087c478bd9Sstevel@tonic-gate 					return (pm_phc_impl(dip,
30096152d498Smh 					    comp, level, 1));
30107c478bd9Sstevel@tonic-gate 				}
30117c478bd9Sstevel@tonic-gate 				old_level = cur_power(cp);
30127c478bd9Sstevel@tonic-gate 				if (pdip && !PM_WANTS_NOTIFICATION(pdip) &&
30137c478bd9Sstevel@tonic-gate 				    (!PM_ISBC(dip) || comp == 0) &&
30147c478bd9Sstevel@tonic-gate 				    POWERING_ON(old_level, level))
30157c478bd9Sstevel@tonic-gate 					pm_hold_power(pdip);
30167c478bd9Sstevel@tonic-gate 				ret = pm_phc_impl(dip, comp, level, 1);
30177c478bd9Sstevel@tonic-gate 				if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
30187c478bd9Sstevel@tonic-gate 					if ((!PM_ISBC(dip) ||
30197c478bd9Sstevel@tonic-gate 					    comp == 0) && level == 0 &&
30207c478bd9Sstevel@tonic-gate 					    old_level != PM_LEVEL_UNKNOWN)
30217c478bd9Sstevel@tonic-gate 						pm_rele_power(pdip);
30227c478bd9Sstevel@tonic-gate 				}
30233fe80ca4SDan Cross 				ndi_devi_exit(pdip);
30247c478bd9Sstevel@tonic-gate 				/* child lock not held: deadlock */
30257c478bd9Sstevel@tonic-gate 				return (ret);
30267c478bd9Sstevel@tonic-gate 			}
30277c478bd9Sstevel@tonic-gate 		delay(1);
30287c478bd9Sstevel@tonic-gate 		PMD(PMD_PHC, ("%s: try lock again\n", pmf))
30297c478bd9Sstevel@tonic-gate 	}
30307c478bd9Sstevel@tonic-gate 
30317c478bd9Sstevel@tonic-gate 	/* non-deadlock case */
30327c478bd9Sstevel@tonic-gate 	old_level = cur_power(cp);
30337c478bd9Sstevel@tonic-gate 	if (pdip && !PM_WANTS_NOTIFICATION(pdip) &&
30347c478bd9Sstevel@tonic-gate 	    (!PM_ISBC(dip) || comp == 0) && POWERING_ON(old_level, level))
30357c478bd9Sstevel@tonic-gate 		pm_hold_power(pdip);
30367c478bd9Sstevel@tonic-gate 	ret = pm_phc_impl(dip, comp, level, 1);
30377c478bd9Sstevel@tonic-gate 	if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
30387c478bd9Sstevel@tonic-gate 		if ((!PM_ISBC(dip) || comp == 0) && level == 0 &&
30397c478bd9Sstevel@tonic-gate 		    old_level != PM_LEVEL_UNKNOWN)
30407c478bd9Sstevel@tonic-gate 			pm_rele_power(pdip);
30417c478bd9Sstevel@tonic-gate 	}
30423fe80ca4SDan Cross 	PM_UNLOCK_POWER(dip);
30433fe80ca4SDan Cross 	ndi_devi_exit(pdip);
30447c478bd9Sstevel@tonic-gate 	return (ret);
30457c478bd9Sstevel@tonic-gate }
30467c478bd9Sstevel@tonic-gate 
30477c478bd9Sstevel@tonic-gate /*
30487c478bd9Sstevel@tonic-gate  * Account for power changes to a component of the the console frame buffer.
30497c478bd9Sstevel@tonic-gate  * If lowering power from full (or "unkown", which is treatd as full)
30507c478bd9Sstevel@tonic-gate  * we will increment the "components off" count of the fb device.
30517c478bd9Sstevel@tonic-gate  * Subsequent lowering of the same component doesn't affect the count.  If
30527c478bd9Sstevel@tonic-gate  * raising a component back to full power, we will decrement the count.
30537c478bd9Sstevel@tonic-gate  *
30547c478bd9Sstevel@tonic-gate  * Return: the increment value for pm_cfb_comps_off (-1, 0, or 1)
30557c478bd9Sstevel@tonic-gate  */
30567c478bd9Sstevel@tonic-gate static int
calc_cfb_comps_incr(dev_info_t * dip,int cmpt,int old,int new)30577c478bd9Sstevel@tonic-gate calc_cfb_comps_incr(dev_info_t *dip, int cmpt, int old, int new)
30587c478bd9Sstevel@tonic-gate {
30597c478bd9Sstevel@tonic-gate 	struct pm_component *cp = PM_CP(dip, cmpt);
30607c478bd9Sstevel@tonic-gate 	int on = (old == PM_LEVEL_UNKNOWN || old == cp->pmc_norm_pwr);
30617c478bd9Sstevel@tonic-gate 	int want_normal = (new == cp->pmc_norm_pwr);
30627c478bd9Sstevel@tonic-gate 	int incr = 0;
30637c478bd9Sstevel@tonic-gate 
30647c478bd9Sstevel@tonic-gate 	if (on && !want_normal)
30657c478bd9Sstevel@tonic-gate 		incr = 1;
30667c478bd9Sstevel@tonic-gate 	else if (!on && want_normal)
30677c478bd9Sstevel@tonic-gate 		incr = -1;
30687c478bd9Sstevel@tonic-gate 	return (incr);
30697c478bd9Sstevel@tonic-gate }
30707c478bd9Sstevel@tonic-gate 
30717c478bd9Sstevel@tonic-gate /*
30727c478bd9Sstevel@tonic-gate  * Adjust the count of console frame buffer components < full power.
30737c478bd9Sstevel@tonic-gate  */
30747c478bd9Sstevel@tonic-gate static void
update_comps_off(int incr,dev_info_t * dip)30757c478bd9Sstevel@tonic-gate update_comps_off(int incr, dev_info_t *dip)
30767c478bd9Sstevel@tonic-gate {
30777c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_cfb_lock);
30787c478bd9Sstevel@tonic-gate 		pm_cfb_comps_off += incr;
30797c478bd9Sstevel@tonic-gate 		ASSERT(pm_cfb_comps_off <= PM_NUMCMPTS(dip));
30807c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
30817c478bd9Sstevel@tonic-gate }
30827c478bd9Sstevel@tonic-gate 
30837c478bd9Sstevel@tonic-gate /*
30847c478bd9Sstevel@tonic-gate  * Update the power state in the framework (via the ppm).  The 'notify'
30857c478bd9Sstevel@tonic-gate  * argument tells whether to notify watchers.  Power lock is already held.
30867c478bd9Sstevel@tonic-gate  */
30877c478bd9Sstevel@tonic-gate static int
pm_phc_impl(dev_info_t * dip,int comp,int level,int notify)30887c478bd9Sstevel@tonic-gate pm_phc_impl(dev_info_t *dip, int comp, int level, int notify)
30897c478bd9Sstevel@tonic-gate {
30907c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "phc_impl")
30917c478bd9Sstevel@tonic-gate 	power_req_t power_req;
30927c478bd9Sstevel@tonic-gate 	int i, dodeps = 0;
30937c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
30947c478bd9Sstevel@tonic-gate 	int result;
30957c478bd9Sstevel@tonic-gate 	int old_level;
30967c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
30977c478bd9Sstevel@tonic-gate 	int incr = 0;
30987c478bd9Sstevel@tonic-gate 	dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm;
30997c478bd9Sstevel@tonic-gate 	int work_type = 0;
31007c478bd9Sstevel@tonic-gate 	char *pathbuf;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	/* Must use "official" power level for this test. */
31037c478bd9Sstevel@tonic-gate 	cp = PM_CP(dip, comp);
31047c478bd9Sstevel@tonic-gate 	old_level = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
31057c478bd9Sstevel@tonic-gate 	    cp->pmc_phc_pwr : cp->pmc_cur_pwr);
31067c478bd9Sstevel@tonic-gate 	if (old_level != PM_LEVEL_UNKNOWN)
31077c478bd9Sstevel@tonic-gate 		old_level = cp->pmc_comp.pmc_lvals[old_level];
31087c478bd9Sstevel@tonic-gate 
31097c478bd9Sstevel@tonic-gate 	if (level == old_level) {
31107c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d is already at "
31117c478bd9Sstevel@tonic-gate 		    "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
31127c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
31137c478bd9Sstevel@tonic-gate 	}
31147c478bd9Sstevel@tonic-gate 
31157c478bd9Sstevel@tonic-gate 	/*
31167c478bd9Sstevel@tonic-gate 	 * Tell ppm about this.
31177c478bd9Sstevel@tonic-gate 	 */
31187c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY;
31197c478bd9Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.who = dip;
31207c478bd9Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.cmpt = comp;
31217c478bd9Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.new_level = level;
31227c478bd9Sstevel@tonic-gate 	power_req.req.ppm_notify_level_req.old_level = old_level;
31237c478bd9Sstevel@tonic-gate 	if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req,
31247c478bd9Sstevel@tonic-gate 	    &result) == DDI_FAILURE) {
31257c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: pm_ctlops %s@%s(%s#%d) to %d failed\n",
31267c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), level))
31277c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
31287c478bd9Sstevel@tonic-gate 	}
31297c478bd9Sstevel@tonic-gate 
31307c478bd9Sstevel@tonic-gate 	if (PM_IS_CFB(dip)) {
31317c478bd9Sstevel@tonic-gate 		incr = calc_cfb_comps_incr(dip, comp, old_level, level);
31327c478bd9Sstevel@tonic-gate 
31337c478bd9Sstevel@tonic-gate 		if (incr) {
31347c478bd9Sstevel@tonic-gate 			update_comps_off(incr, dip);
31357c478bd9Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d) comp=%d %d->%d "
31367c478bd9Sstevel@tonic-gate 			    "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
31377c478bd9Sstevel@tonic-gate 			    comp, old_level, level, pm_cfb_comps_off))
31387c478bd9Sstevel@tonic-gate 		}
31397c478bd9Sstevel@tonic-gate 	}
31407c478bd9Sstevel@tonic-gate 	e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level);
31417c478bd9Sstevel@tonic-gate 	result = DDI_SUCCESS;
31427c478bd9Sstevel@tonic-gate 
31437c478bd9Sstevel@tonic-gate 	if (notify) {
31447c478bd9Sstevel@tonic-gate 		if (pdip && PM_WANTS_NOTIFICATION(pdip))
31457c478bd9Sstevel@tonic-gate 			pm_notify_parent(dip, pdip, comp, old_level, level);
31467c478bd9Sstevel@tonic-gate 		(void) pm_check_and_resume(dip, comp, old_level, level);
31477c478bd9Sstevel@tonic-gate 	}
31487c478bd9Sstevel@tonic-gate 
31497c478bd9Sstevel@tonic-gate 	/*
31507c478bd9Sstevel@tonic-gate 	 * Decrement the dependency kidsup count if we turn a device
31517c478bd9Sstevel@tonic-gate 	 * off.
31527c478bd9Sstevel@tonic-gate 	 */
31537c478bd9Sstevel@tonic-gate 	if (POWERING_OFF(old_level, level)) {
31547c478bd9Sstevel@tonic-gate 		dodeps = 1;
31557c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++) {
31567c478bd9Sstevel@tonic-gate 			cp = PM_CP(dip, i);
31577c478bd9Sstevel@tonic-gate 			if (cur_power(cp)) {
31587c478bd9Sstevel@tonic-gate 				dodeps = 0;
31597c478bd9Sstevel@tonic-gate 				break;
31607c478bd9Sstevel@tonic-gate 			}
31617c478bd9Sstevel@tonic-gate 		}
31627c478bd9Sstevel@tonic-gate 		if (dodeps)
31637c478bd9Sstevel@tonic-gate 			work_type = PM_DEP_WK_POWER_OFF;
31647c478bd9Sstevel@tonic-gate 	}
31657c478bd9Sstevel@tonic-gate 
31667c478bd9Sstevel@tonic-gate 	/*
31677c478bd9Sstevel@tonic-gate 	 * Increment if we turn it on. Check to see
31687c478bd9Sstevel@tonic-gate 	 * if other comps are already on, if so,
31697c478bd9Sstevel@tonic-gate 	 * dont increment.
31707c478bd9Sstevel@tonic-gate 	 */
31717c478bd9Sstevel@tonic-gate 	if (POWERING_ON(old_level, level)) {
31727c478bd9Sstevel@tonic-gate 		dodeps = 1;
31737c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++) {
31747c478bd9Sstevel@tonic-gate 			cp = PM_CP(dip, i);
31757c478bd9Sstevel@tonic-gate 			if (comp == i)
31767c478bd9Sstevel@tonic-gate 				continue;
31777c478bd9Sstevel@tonic-gate 			/* -1 also treated as 0 in this case */
31787c478bd9Sstevel@tonic-gate 			if (cur_power(cp) > 0) {
31797c478bd9Sstevel@tonic-gate 				dodeps = 0;
31807c478bd9Sstevel@tonic-gate 				break;
31817c478bd9Sstevel@tonic-gate 			}
31827c478bd9Sstevel@tonic-gate 		}
31837c478bd9Sstevel@tonic-gate 		if (dodeps)
31847c478bd9Sstevel@tonic-gate 			work_type = PM_DEP_WK_POWER_ON;
31857c478bd9Sstevel@tonic-gate 	}
31867c478bd9Sstevel@tonic-gate 
31877c478bd9Sstevel@tonic-gate 	if (dodeps) {
31887c478bd9Sstevel@tonic-gate 		pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
31897c478bd9Sstevel@tonic-gate 		(void) ddi_pathname(dip, pathbuf);
31907c478bd9Sstevel@tonic-gate 		pm_dispatch_to_dep_thread(work_type, pathbuf, NULL,
31917c478bd9Sstevel@tonic-gate 		    PM_DEP_NOWAIT, NULL, 0);
31927c478bd9Sstevel@tonic-gate 		kmem_free(pathbuf, MAXPATHLEN);
31937c478bd9Sstevel@tonic-gate 	}
31947c478bd9Sstevel@tonic-gate 
31957c478bd9Sstevel@tonic-gate 	if (notify && (level != old_level) && pm_watchers()) {
31967c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_rsvp_lock);
31977c478bd9Sstevel@tonic-gate 		pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, level, old_level,
31987c478bd9Sstevel@tonic-gate 		    PM_CANBLOCK_BLOCK);
31997c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_rsvp_lock);
32007c478bd9Sstevel@tonic-gate 	}
32017c478bd9Sstevel@tonic-gate 
32027c478bd9Sstevel@tonic-gate 	PMD(PMD_RESCAN, ("%s: %s@%s(%s#%d): pm_rescan\n", pmf, PM_DEVICE(dip)))
32037c478bd9Sstevel@tonic-gate 	pm_rescan(dip);
32047c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
32057c478bd9Sstevel@tonic-gate }
32067c478bd9Sstevel@tonic-gate 
32077c478bd9Sstevel@tonic-gate /*
32087c478bd9Sstevel@tonic-gate  * This function is called at startup time to notify pm of the existence
32097c478bd9Sstevel@tonic-gate  * of any platform power managers for this platform.  As a result of
32107c478bd9Sstevel@tonic-gate  * this registration, each function provided will be called each time
32117c478bd9Sstevel@tonic-gate  * a device node is attached, until one returns true, and it must claim the
32127c478bd9Sstevel@tonic-gate  * device node (by returning non-zero) if it wants to be involved in the
32137c478bd9Sstevel@tonic-gate  * node's power management.  If it does claim the node, then it will
32147c478bd9Sstevel@tonic-gate  * subsequently be notified of attach and detach events.
32157c478bd9Sstevel@tonic-gate  *
32167c478bd9Sstevel@tonic-gate  */
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate int
pm_register_ppm(int (* func)(dev_info_t *),dev_info_t * dip)32197c478bd9Sstevel@tonic-gate pm_register_ppm(int (*func)(dev_info_t *), dev_info_t *dip)
32207c478bd9Sstevel@tonic-gate {
32217c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "register_ppm")
32227c478bd9Sstevel@tonic-gate 	struct ppm_callbacks *ppmcp;
32237c478bd9Sstevel@tonic-gate 	pm_component_t *cp;
32243fe80ca4SDan Cross 	int i, pwr, result;
32257c478bd9Sstevel@tonic-gate 	power_req_t power_req;
32267c478bd9Sstevel@tonic-gate 	struct ppm_notify_level_req *p = &power_req.req.ppm_notify_level_req;
32277c478bd9Sstevel@tonic-gate 	void pm_ppm_claim(dev_info_t *);
32287c478bd9Sstevel@tonic-gate 
32297c478bd9Sstevel@tonic-gate 	mutex_enter(&ppm_lock);
32307c478bd9Sstevel@tonic-gate 	ppmcp = ppm_callbacks;
32317c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAX_PPM_HANDLERS; i++, ppmcp++) {
32327c478bd9Sstevel@tonic-gate 		if (ppmcp->ppmc_func == NULL) {
32337c478bd9Sstevel@tonic-gate 			ppmcp->ppmc_func = func;
32347c478bd9Sstevel@tonic-gate 			ppmcp->ppmc_dip = dip;
32357c478bd9Sstevel@tonic-gate 			break;
32367c478bd9Sstevel@tonic-gate 		}
32377c478bd9Sstevel@tonic-gate 	}
32387c478bd9Sstevel@tonic-gate 	mutex_exit(&ppm_lock);
32397c478bd9Sstevel@tonic-gate 
32407c478bd9Sstevel@tonic-gate 	if (i >= MAX_PPM_HANDLERS)
32417c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
32427c478bd9Sstevel@tonic-gate 	while ((dip = ddi_get_parent(dip)) != NULL) {
32432df1fe9cSrandyf 		if (dip != ddi_root_node() && PM_GET_PM_INFO(dip) == NULL)
32447c478bd9Sstevel@tonic-gate 			continue;
32457c478bd9Sstevel@tonic-gate 		pm_ppm_claim(dip);
32462df1fe9cSrandyf 		/* don't bother with the not power-manageable nodes */
32472df1fe9cSrandyf 		if (pm_ppm_claimed(dip) && PM_GET_PM_INFO(dip)) {
32487c478bd9Sstevel@tonic-gate 			/*
32497c478bd9Sstevel@tonic-gate 			 * Tell ppm about this.
32507c478bd9Sstevel@tonic-gate 			 */
32517c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY;
32527c478bd9Sstevel@tonic-gate 			p->old_level = PM_LEVEL_UNKNOWN;
32537c478bd9Sstevel@tonic-gate 			p->who = dip;
32543fe80ca4SDan Cross 			PM_LOCK_POWER(dip);
32557c478bd9Sstevel@tonic-gate 			for (i = 0; i < PM_NUMCMPTS(dip); i++) {
32567c478bd9Sstevel@tonic-gate 				cp = PM_CP(dip, i);
32577c478bd9Sstevel@tonic-gate 				pwr = cp->pmc_cur_pwr;
32587c478bd9Sstevel@tonic-gate 				if (pwr != PM_LEVEL_UNKNOWN) {
32597c478bd9Sstevel@tonic-gate 					p->cmpt = i;
32607c478bd9Sstevel@tonic-gate 					p->new_level = cur_power(cp);
32617c478bd9Sstevel@tonic-gate 					p->old_level = PM_LEVEL_UNKNOWN;
32627c478bd9Sstevel@tonic-gate 					if (pm_ctlops(PPM(dip), dip,
32637c478bd9Sstevel@tonic-gate 					    DDI_CTLOPS_POWER, &power_req,
32647c478bd9Sstevel@tonic-gate 					    &result) == DDI_FAILURE) {
32657c478bd9Sstevel@tonic-gate 						PMD(PMD_FAIL, ("%s: pc "
32667c478bd9Sstevel@tonic-gate 						    "%s@%s(%s#%d) to %d "
32677c478bd9Sstevel@tonic-gate 						    "fails\n", pmf,
32687c478bd9Sstevel@tonic-gate 						    PM_DEVICE(dip), pwr))
32697c478bd9Sstevel@tonic-gate 					}
32707c478bd9Sstevel@tonic-gate 				}
32717c478bd9Sstevel@tonic-gate 			}
32723fe80ca4SDan Cross 			PM_UNLOCK_POWER(dip);
32737c478bd9Sstevel@tonic-gate 		}
32747c478bd9Sstevel@tonic-gate 	}
32757c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
32767c478bd9Sstevel@tonic-gate }
32777c478bd9Sstevel@tonic-gate 
32787c478bd9Sstevel@tonic-gate /*
32797c478bd9Sstevel@tonic-gate  * Call the ppm's that have registered and adjust the devinfo struct as
32807c478bd9Sstevel@tonic-gate  * appropriate.  First one to claim it gets it.  The sets of devices claimed
32817c478bd9Sstevel@tonic-gate  * by each ppm are assumed to be disjoint.
32827c478bd9Sstevel@tonic-gate  */
32837c478bd9Sstevel@tonic-gate void
pm_ppm_claim(dev_info_t * dip)32847c478bd9Sstevel@tonic-gate pm_ppm_claim(dev_info_t *dip)
32857c478bd9Sstevel@tonic-gate {
32867c478bd9Sstevel@tonic-gate 	struct ppm_callbacks *ppmcp;
32877c478bd9Sstevel@tonic-gate 
32887c478bd9Sstevel@tonic-gate 	if (PPM(dip)) {
32897c478bd9Sstevel@tonic-gate 		return;
32907c478bd9Sstevel@tonic-gate 	}
32917c478bd9Sstevel@tonic-gate 	mutex_enter(&ppm_lock);
32927c478bd9Sstevel@tonic-gate 	for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) {
32937c478bd9Sstevel@tonic-gate 		if ((*ppmcp->ppmc_func)(dip)) {
32947c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_ppm =
32957c478bd9Sstevel@tonic-gate 			    (struct dev_info *)ppmcp->ppmc_dip;
32967c478bd9Sstevel@tonic-gate 			mutex_exit(&ppm_lock);
32977c478bd9Sstevel@tonic-gate 			return;
32987c478bd9Sstevel@tonic-gate 		}
32997c478bd9Sstevel@tonic-gate 	}
33007c478bd9Sstevel@tonic-gate 	mutex_exit(&ppm_lock);
33017c478bd9Sstevel@tonic-gate }
33027c478bd9Sstevel@tonic-gate 
33037c478bd9Sstevel@tonic-gate /*
33047c478bd9Sstevel@tonic-gate  * Node is being detached so stop autopm until we see if it succeeds, in which
33057c478bd9Sstevel@tonic-gate  * case pm_stop will be called.  For backwards compatible devices we bring the
33067c478bd9Sstevel@tonic-gate  * device up to full power on the assumption the detach will succeed.
33077c478bd9Sstevel@tonic-gate  */
33087c478bd9Sstevel@tonic-gate void
pm_detaching(dev_info_t * dip)33097c478bd9Sstevel@tonic-gate pm_detaching(dev_info_t *dip)
33107c478bd9Sstevel@tonic-gate {
33117c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "detaching")
33127c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
33137c478bd9Sstevel@tonic-gate 	int iscons;
33147c478bd9Sstevel@tonic-gate 
33157c478bd9Sstevel@tonic-gate 	PMD(PMD_REMDEV, ("%s: %s@%s(%s#%d), %d comps\n", pmf, PM_DEVICE(dip),
33167c478bd9Sstevel@tonic-gate 	    PM_NUMCMPTS(dip)))
33177c478bd9Sstevel@tonic-gate 	if (info == NULL)
33187c478bd9Sstevel@tonic-gate 		return;
33197c478bd9Sstevel@tonic-gate 	ASSERT(DEVI_IS_DETACHING(dip));
33207c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
33217c478bd9Sstevel@tonic-gate 	info->pmi_dev_pm_state |= PM_DETACHING;
33227c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
33237c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip))
33247c478bd9Sstevel@tonic-gate 		pm_scan_stop(dip);
33257c478bd9Sstevel@tonic-gate 
33267c478bd9Sstevel@tonic-gate 	/*
33277c478bd9Sstevel@tonic-gate 	 * console and old-style devices get brought up when detaching.
33287c478bd9Sstevel@tonic-gate 	 */
33297c478bd9Sstevel@tonic-gate 	iscons = PM_IS_CFB(dip);
33307c478bd9Sstevel@tonic-gate 	if (iscons || PM_ISBC(dip)) {
33317c478bd9Sstevel@tonic-gate 		(void) pm_all_to_normal(dip, PM_CANBLOCK_BYPASS);
33327c478bd9Sstevel@tonic-gate 		if (iscons) {
33337c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_cfb_lock);
33347c478bd9Sstevel@tonic-gate 			while (cfb_inuse) {
33357c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
33367c478bd9Sstevel@tonic-gate 				PMD(PMD_CFB, ("%s: delay; cfb_inuse\n", pmf))
33377c478bd9Sstevel@tonic-gate 				delay(1);
33387c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
33397c478bd9Sstevel@tonic-gate 			}
33407c478bd9Sstevel@tonic-gate 			ASSERT(cfb_dip_detaching == NULL);
33417c478bd9Sstevel@tonic-gate 			ASSERT(cfb_dip);
33427c478bd9Sstevel@tonic-gate 			cfb_dip_detaching = cfb_dip;	/* case detach fails */
33437c478bd9Sstevel@tonic-gate 			cfb_dip = NULL;
33447c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_cfb_lock);
33457c478bd9Sstevel@tonic-gate 		}
33467c478bd9Sstevel@tonic-gate 	}
33477c478bd9Sstevel@tonic-gate }
33487c478bd9Sstevel@tonic-gate 
33497c478bd9Sstevel@tonic-gate /*
33507c478bd9Sstevel@tonic-gate  * Node failed to detach.  If it used to be autopm'd, make it so again.
33517c478bd9Sstevel@tonic-gate  */
33527c478bd9Sstevel@tonic-gate void
pm_detach_failed(dev_info_t * dip)33537c478bd9Sstevel@tonic-gate pm_detach_failed(dev_info_t *dip)
33547c478bd9Sstevel@tonic-gate {
33557c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "detach_failed")
33567c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
33577c478bd9Sstevel@tonic-gate 	int pm_all_at_normal(dev_info_t *);
33587c478bd9Sstevel@tonic-gate 
33597c478bd9Sstevel@tonic-gate 	if (info == NULL)
33607c478bd9Sstevel@tonic-gate 		return;
33617c478bd9Sstevel@tonic-gate 	ASSERT(DEVI_IS_DETACHING(dip));
33627c478bd9Sstevel@tonic-gate 	if (info->pmi_dev_pm_state & PM_DETACHING) {
33637c478bd9Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DETACHING;
33647c478bd9Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_ALLNORM_DEFERRED) {
33657c478bd9Sstevel@tonic-gate 			/* Make sure the operation is still needed */
33667c478bd9Sstevel@tonic-gate 			if (!pm_all_at_normal(dip)) {
33677c478bd9Sstevel@tonic-gate 				if (pm_all_to_normal(dip,
33687c478bd9Sstevel@tonic-gate 				    PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
33697c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("%s: could not bring "
33707c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d) to normal\n", pmf,
33717c478bd9Sstevel@tonic-gate 					    PM_DEVICE(dip)))
33727c478bd9Sstevel@tonic-gate 				}
33737c478bd9Sstevel@tonic-gate 			}
33747c478bd9Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_ALLNORM_DEFERRED;
33757c478bd9Sstevel@tonic-gate 		}
33767c478bd9Sstevel@tonic-gate 	}
33777c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
33787c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
3379c42872d4Smh 		if (PM_SCANABLE(dip))
33807c478bd9Sstevel@tonic-gate 			pm_scan_init(dip);
33817c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
33827c478bd9Sstevel@tonic-gate 		pm_rescan(dip);
33837c478bd9Sstevel@tonic-gate 	}
33847c478bd9Sstevel@tonic-gate }
33857c478bd9Sstevel@tonic-gate 
33867c478bd9Sstevel@tonic-gate /* generic Backwards Compatible component */
33877c478bd9Sstevel@tonic-gate static char *bc_names[] = {"off", "on"};
33887c478bd9Sstevel@tonic-gate 
33897c478bd9Sstevel@tonic-gate static pm_comp_t bc_comp = {"unknown", 2, NULL, NULL, &bc_names[0]};
33907c478bd9Sstevel@tonic-gate 
33917c478bd9Sstevel@tonic-gate static void
e_pm_default_levels(dev_info_t * dip,pm_component_t * cp,int norm)33927c478bd9Sstevel@tonic-gate e_pm_default_levels(dev_info_t *dip, pm_component_t *cp, int norm)
33937c478bd9Sstevel@tonic-gate {
33947c478bd9Sstevel@tonic-gate 	pm_comp_t *pmc;
33957c478bd9Sstevel@tonic-gate 	pmc = &cp->pmc_comp;
33967c478bd9Sstevel@tonic-gate 	pmc->pmc_numlevels = 2;
33977c478bd9Sstevel@tonic-gate 	pmc->pmc_lvals[0] = 0;
33987c478bd9Sstevel@tonic-gate 	pmc->pmc_lvals[1] = norm;
33997c478bd9Sstevel@tonic-gate 	e_pm_set_cur_pwr(dip, cp, norm);
34007c478bd9Sstevel@tonic-gate }
34017c478bd9Sstevel@tonic-gate 
34027c478bd9Sstevel@tonic-gate static void
e_pm_default_components(dev_info_t * dip,int cmpts)34037c478bd9Sstevel@tonic-gate e_pm_default_components(dev_info_t *dip, int cmpts)
34047c478bd9Sstevel@tonic-gate {
34057c478bd9Sstevel@tonic-gate 	int i;
34067c478bd9Sstevel@tonic-gate 	pm_component_t *p = DEVI(dip)->devi_pm_components;
34077c478bd9Sstevel@tonic-gate 
34087c478bd9Sstevel@tonic-gate 	p = DEVI(dip)->devi_pm_components;
34097c478bd9Sstevel@tonic-gate 	for (i = 0; i < cmpts; i++, p++) {
34107c478bd9Sstevel@tonic-gate 		p->pmc_comp = bc_comp;	/* struct assignment */
34117c478bd9Sstevel@tonic-gate 		p->pmc_comp.pmc_lvals = kmem_zalloc(2 * sizeof (int),
34127c478bd9Sstevel@tonic-gate 		    KM_SLEEP);
34137c478bd9Sstevel@tonic-gate 		p->pmc_comp.pmc_thresh = kmem_alloc(2 * sizeof (int),
34147c478bd9Sstevel@tonic-gate 		    KM_SLEEP);
34157c478bd9Sstevel@tonic-gate 		p->pmc_comp.pmc_numlevels = 2;
34167c478bd9Sstevel@tonic-gate 		p->pmc_comp.pmc_thresh[0] = INT_MAX;
34177c478bd9Sstevel@tonic-gate 		p->pmc_comp.pmc_thresh[1] = INT_MAX;
34187c478bd9Sstevel@tonic-gate 	}
34197c478bd9Sstevel@tonic-gate }
34207c478bd9Sstevel@tonic-gate 
34217c478bd9Sstevel@tonic-gate /*
34227c478bd9Sstevel@tonic-gate  * Called from functions that require components to exist already to allow
34237c478bd9Sstevel@tonic-gate  * for their creation by parsing the pm-components property.
34247c478bd9Sstevel@tonic-gate  * Device will not be power managed as a result of this call
34257c478bd9Sstevel@tonic-gate  * No locking needed because we're single threaded by the ndi_devi_enter
34267c478bd9Sstevel@tonic-gate  * done while attaching, and the device isn't visible until after it has
34277c478bd9Sstevel@tonic-gate  * attached
34287c478bd9Sstevel@tonic-gate  */
34297c478bd9Sstevel@tonic-gate int
pm_premanage(dev_info_t * dip,int style)34307c478bd9Sstevel@tonic-gate pm_premanage(dev_info_t *dip, int style)
34317c478bd9Sstevel@tonic-gate {
34327c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "premanage")
34337c478bd9Sstevel@tonic-gate 	pm_comp_t	*pcp, *compp;
34347c478bd9Sstevel@tonic-gate 	int		cmpts, i, norm, error;
34357c478bd9Sstevel@tonic-gate 	pm_component_t *p = DEVI(dip)->devi_pm_components;
34367c478bd9Sstevel@tonic-gate 	pm_comp_t *pm_autoconfig(dev_info_t *, int *);
34377c478bd9Sstevel@tonic-gate 
34387c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
34397c478bd9Sstevel@tonic-gate 	/*
34407c478bd9Sstevel@tonic-gate 	 * If this dip has already been processed, don't mess with it
34417c478bd9Sstevel@tonic-gate 	 */
34427c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE)
34437c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
34447c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_FAILED) {
34457c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
34467c478bd9Sstevel@tonic-gate 	}
34477c478bd9Sstevel@tonic-gate 	/*
34487c478bd9Sstevel@tonic-gate 	 * Look up pm-components property and create components accordingly
34497c478bd9Sstevel@tonic-gate 	 * If that fails, fall back to backwards compatibility
34507c478bd9Sstevel@tonic-gate 	 */
34517c478bd9Sstevel@tonic-gate 	if ((compp = pm_autoconfig(dip, &error)) == NULL) {
34527c478bd9Sstevel@tonic-gate 		/*
34537c478bd9Sstevel@tonic-gate 		 * If error is set, the property existed but was not well formed
34547c478bd9Sstevel@tonic-gate 		 */
34557c478bd9Sstevel@tonic-gate 		if (error || (style == PM_STYLE_NEW)) {
34567c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_FAILED;
34577c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
34587c478bd9Sstevel@tonic-gate 		}
34597c478bd9Sstevel@tonic-gate 		/*
34607c478bd9Sstevel@tonic-gate 		 * If they don't have the pm-components property, then we
34617c478bd9Sstevel@tonic-gate 		 * want the old "no pm until PM_SET_DEVICE_THRESHOLDS ioctl"
34627c478bd9Sstevel@tonic-gate 		 * behavior driver must have called pm_create_components, and
34637c478bd9Sstevel@tonic-gate 		 * we need to flesh out dummy components
34647c478bd9Sstevel@tonic-gate 		 */
34657c478bd9Sstevel@tonic-gate 		if ((cmpts = PM_NUMCMPTS(dip)) == 0) {
34667c478bd9Sstevel@tonic-gate 			/*
34677c478bd9Sstevel@tonic-gate 			 * Not really failure, but we don't want the
34687c478bd9Sstevel@tonic-gate 			 * caller to treat it as success
34697c478bd9Sstevel@tonic-gate 			 */
34707c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
34717c478bd9Sstevel@tonic-gate 		}
34727c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags |= PMC_BC;
34737c478bd9Sstevel@tonic-gate 		e_pm_default_components(dip, cmpts);
34747c478bd9Sstevel@tonic-gate 		for (i = 0; i < cmpts; i++) {
34757c478bd9Sstevel@tonic-gate 			/*
34767c478bd9Sstevel@tonic-gate 			 * if normal power not set yet, we don't really know
34777c478bd9Sstevel@tonic-gate 			 * what *ANY* of the power values are.  If normal
34787c478bd9Sstevel@tonic-gate 			 * power is set, then we assume for this backwards
34797c478bd9Sstevel@tonic-gate 			 * compatible case that the values are 0, normal power.
34807c478bd9Sstevel@tonic-gate 			 */
34817c478bd9Sstevel@tonic-gate 			norm = pm_get_normal_power(dip, i);
34827c478bd9Sstevel@tonic-gate 			if (norm == (uint_t)-1) {
34837c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: %s@%s(%s#%d)[%d]\n", pmf,
34847c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip), i))
34857c478bd9Sstevel@tonic-gate 				return (DDI_FAILURE);
34867c478bd9Sstevel@tonic-gate 			}
34877c478bd9Sstevel@tonic-gate 			/*
34887c478bd9Sstevel@tonic-gate 			 * Components of BC devices start at their normal power,
34897c478bd9Sstevel@tonic-gate 			 * so count them to be not at their lowest power.
34907c478bd9Sstevel@tonic-gate 			 */
34917c478bd9Sstevel@tonic-gate 			PM_INCR_NOTLOWEST(dip);
34927c478bd9Sstevel@tonic-gate 			e_pm_default_levels(dip, PM_CP(dip, i), norm);
34937c478bd9Sstevel@tonic-gate 		}
34947c478bd9Sstevel@tonic-gate 	} else {
34957c478bd9Sstevel@tonic-gate 		/*
34967c478bd9Sstevel@tonic-gate 		 * e_pm_create_components was called from pm_autoconfig(), it
34977c478bd9Sstevel@tonic-gate 		 * creates components with no descriptions (or known levels)
34987c478bd9Sstevel@tonic-gate 		 */
34997c478bd9Sstevel@tonic-gate 		cmpts = PM_NUMCMPTS(dip);
35007c478bd9Sstevel@tonic-gate 		ASSERT(cmpts != 0);
35017c478bd9Sstevel@tonic-gate 		pcp = compp;
35027c478bd9Sstevel@tonic-gate 		p = DEVI(dip)->devi_pm_components;
35037c478bd9Sstevel@tonic-gate 		for (i = 0; i < cmpts; i++, p++) {
35047c478bd9Sstevel@tonic-gate 			p->pmc_comp = *pcp++;   /* struct assignment */
35057c478bd9Sstevel@tonic-gate 			ASSERT(PM_CP(dip, i)->pmc_cur_pwr == 0);
35067c478bd9Sstevel@tonic-gate 			e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN);
35077c478bd9Sstevel@tonic-gate 		}
3508c42872d4Smh 		if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
3509c42872d4Smh 			pm_set_device_threshold(dip, pm_cpu_idle_threshold,
3510c42872d4Smh 			    PMC_CPU_THRESH);
3511c42872d4Smh 		else
3512c42872d4Smh 			pm_set_device_threshold(dip, pm_system_idle_threshold,
3513c42872d4Smh 			    PMC_DEF_THRESH);
35147c478bd9Sstevel@tonic-gate 		kmem_free(compp, cmpts * sizeof (pm_comp_t));
35157c478bd9Sstevel@tonic-gate 	}
35167c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
35177c478bd9Sstevel@tonic-gate }
35187c478bd9Sstevel@tonic-gate 
35197c478bd9Sstevel@tonic-gate /*
35207c478bd9Sstevel@tonic-gate  * Called from during or after the device's attach to let us know it is ready
35217c478bd9Sstevel@tonic-gate  * to play autopm.   Look up the pm model and manage the device accordingly.
35227c478bd9Sstevel@tonic-gate  * Returns system call errno value.
35237c478bd9Sstevel@tonic-gate  * If DDI_ATTACH and DDI_DETACH were in same namespace, this would be
35247c478bd9Sstevel@tonic-gate  * a little cleaner
35257c478bd9Sstevel@tonic-gate  *
35267c478bd9Sstevel@tonic-gate  * Called with dip lock held, return with dip lock unheld.
35277c478bd9Sstevel@tonic-gate  */
35287c478bd9Sstevel@tonic-gate 
35297c478bd9Sstevel@tonic-gate int
e_pm_manage(dev_info_t * dip,int style)35307c478bd9Sstevel@tonic-gate e_pm_manage(dev_info_t *dip, int style)
35317c478bd9Sstevel@tonic-gate {
35327c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "e_manage")
35337c478bd9Sstevel@tonic-gate 	pm_info_t	*info;
35347c478bd9Sstevel@tonic-gate 	dev_info_t	*pdip = ddi_get_parent(dip);
35357c478bd9Sstevel@tonic-gate 	int	pm_thresh_specd(dev_info_t *);
35367c478bd9Sstevel@tonic-gate 	int	count;
35377c478bd9Sstevel@tonic-gate 	char	*pathbuf;
35387c478bd9Sstevel@tonic-gate 
35397c478bd9Sstevel@tonic-gate 	if (pm_premanage(dip, style) != DDI_SUCCESS) {
35407c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
35417c478bd9Sstevel@tonic-gate 	}
35427c478bd9Sstevel@tonic-gate 	PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
35437c478bd9Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip) == NULL);
35447c478bd9Sstevel@tonic-gate 	info = kmem_zalloc(sizeof (pm_info_t), KM_SLEEP);
35457c478bd9Sstevel@tonic-gate 
35467c478bd9Sstevel@tonic-gate 	/*
35477c478bd9Sstevel@tonic-gate 	 * Now set up parent's kidsupcnt.  BC nodes are assumed to start
35487c478bd9Sstevel@tonic-gate 	 * out at their normal power, so they are "up", others start out
35497c478bd9Sstevel@tonic-gate 	 * unknown, which is effectively "up".  Parent which want notification
35507c478bd9Sstevel@tonic-gate 	 * get kidsupcnt of 0 always.
35517c478bd9Sstevel@tonic-gate 	 */
35527c478bd9Sstevel@tonic-gate 	count = (PM_ISBC(dip)) ? 1 : PM_NUMCMPTS(dip);
35537c478bd9Sstevel@tonic-gate 	if (count && pdip && !PM_WANTS_NOTIFICATION(pdip))
35547c478bd9Sstevel@tonic-gate 		e_pm_hold_rele_power(pdip, count);
35557c478bd9Sstevel@tonic-gate 
35567c478bd9Sstevel@tonic-gate 	pm_set_pm_info(dip, info);
35577c478bd9Sstevel@tonic-gate 	/*
35587c478bd9Sstevel@tonic-gate 	 * Apply any recorded thresholds
35597c478bd9Sstevel@tonic-gate 	 */
35607c478bd9Sstevel@tonic-gate 	(void) pm_thresh_specd(dip);
35617c478bd9Sstevel@tonic-gate 
35627c478bd9Sstevel@tonic-gate 	/*
35637c478bd9Sstevel@tonic-gate 	 * Do dependency processing.
35647c478bd9Sstevel@tonic-gate 	 */
35657c478bd9Sstevel@tonic-gate 	pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
35667c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
35677c478bd9Sstevel@tonic-gate 	pm_dispatch_to_dep_thread(PM_DEP_WK_ATTACH, pathbuf, pathbuf,
35687c478bd9Sstevel@tonic-gate 	    PM_DEP_NOWAIT, NULL, 0);
35697c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
35707c478bd9Sstevel@tonic-gate 
35717c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
35727c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
3573c42872d4Smh 		if (PM_SCANABLE(dip)) {
35747c478bd9Sstevel@tonic-gate 			pm_scan_init(dip);
35757c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
35767c478bd9Sstevel@tonic-gate 			pm_rescan(dip);
35777c478bd9Sstevel@tonic-gate 		} else {
35787c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
35797c478bd9Sstevel@tonic-gate 		}
35807c478bd9Sstevel@tonic-gate 	}
35817c478bd9Sstevel@tonic-gate 	return (0);
35827c478bd9Sstevel@tonic-gate }
35837c478bd9Sstevel@tonic-gate 
35847c478bd9Sstevel@tonic-gate /*
35857c478bd9Sstevel@tonic-gate  * This is the obsolete exported interface for a driver to find out its
35867c478bd9Sstevel@tonic-gate  * "normal" (max) power.
35877c478bd9Sstevel@tonic-gate  * We only get components destroyed while no power management is
35887c478bd9Sstevel@tonic-gate  * going on (and the device is detached), so we don't need a mutex here
35897c478bd9Sstevel@tonic-gate  */
35907c478bd9Sstevel@tonic-gate int
pm_get_normal_power(dev_info_t * dip,int comp)35917c478bd9Sstevel@tonic-gate pm_get_normal_power(dev_info_t *dip, int comp)
35927c478bd9Sstevel@tonic-gate {
35937c478bd9Sstevel@tonic-gate 
35947c478bd9Sstevel@tonic-gate 	if (comp >= 0 && comp < PM_NUMCMPTS(dip)) {
35957c478bd9Sstevel@tonic-gate 		return (PM_CP(dip, comp)->pmc_norm_pwr);
35967c478bd9Sstevel@tonic-gate 	}
35977c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
35987c478bd9Sstevel@tonic-gate }
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate /*
36017c478bd9Sstevel@tonic-gate  * Fetches the current power level.  Return DDI_SUCCESS or DDI_FAILURE.
36027c478bd9Sstevel@tonic-gate  */
36037c478bd9Sstevel@tonic-gate int
pm_get_current_power(dev_info_t * dip,int comp,int * levelp)36047c478bd9Sstevel@tonic-gate pm_get_current_power(dev_info_t *dip, int comp, int *levelp)
36057c478bd9Sstevel@tonic-gate {
36067c478bd9Sstevel@tonic-gate 	if (comp >= 0 && comp < PM_NUMCMPTS(dip)) {
36077c478bd9Sstevel@tonic-gate 		*levelp = PM_CURPOWER(dip, comp);
36087c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
36097c478bd9Sstevel@tonic-gate 	}
36107c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
36117c478bd9Sstevel@tonic-gate }
36127c478bd9Sstevel@tonic-gate 
36137c478bd9Sstevel@tonic-gate /*
36147c478bd9Sstevel@tonic-gate  * Returns current threshold of indicated component
36157c478bd9Sstevel@tonic-gate  */
36167c478bd9Sstevel@tonic-gate static int
cur_threshold(dev_info_t * dip,int comp)36177c478bd9Sstevel@tonic-gate cur_threshold(dev_info_t *dip, int comp)
36187c478bd9Sstevel@tonic-gate {
36197c478bd9Sstevel@tonic-gate 	pm_component_t *cp = PM_CP(dip, comp);
36207c478bd9Sstevel@tonic-gate 	int pwr;
36217c478bd9Sstevel@tonic-gate 
36227c478bd9Sstevel@tonic-gate 	if (PM_ISBC(dip)) {
36237c478bd9Sstevel@tonic-gate 		/*
36247c478bd9Sstevel@tonic-gate 		 * backwards compatible nodes only have one threshold
36257c478bd9Sstevel@tonic-gate 		 */
36267c478bd9Sstevel@tonic-gate 		return (cp->pmc_comp.pmc_thresh[1]);
36277c478bd9Sstevel@tonic-gate 	}
36287c478bd9Sstevel@tonic-gate 	pwr = cp->pmc_cur_pwr;
36297c478bd9Sstevel@tonic-gate 	if (pwr == PM_LEVEL_UNKNOWN) {
36307c478bd9Sstevel@tonic-gate 		int thresh;
36317c478bd9Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH)
36327c478bd9Sstevel@tonic-gate 			thresh = pm_default_nexus_threshold;
3633c42872d4Smh 		else if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
3634c42872d4Smh 			thresh = pm_cpu_idle_threshold;
36357c478bd9Sstevel@tonic-gate 		else
36367c478bd9Sstevel@tonic-gate 			thresh = pm_system_idle_threshold;
36377c478bd9Sstevel@tonic-gate 		return (thresh);
36387c478bd9Sstevel@tonic-gate 	}
36397c478bd9Sstevel@tonic-gate 	ASSERT(cp->pmc_comp.pmc_thresh);
36407c478bd9Sstevel@tonic-gate 	return (cp->pmc_comp.pmc_thresh[pwr]);
36417c478bd9Sstevel@tonic-gate }
36427c478bd9Sstevel@tonic-gate 
36437c478bd9Sstevel@tonic-gate /*
36447c478bd9Sstevel@tonic-gate  * Compute next lower component power level given power index.
36457c478bd9Sstevel@tonic-gate  */
36467c478bd9Sstevel@tonic-gate static int
pm_next_lower_power(pm_component_t * cp,int pwrndx)36477c478bd9Sstevel@tonic-gate pm_next_lower_power(pm_component_t *cp, int pwrndx)
36487c478bd9Sstevel@tonic-gate {
36497c478bd9Sstevel@tonic-gate 	int nxt_pwr;
36507c478bd9Sstevel@tonic-gate 
36517c478bd9Sstevel@tonic-gate 	if (pwrndx == PM_LEVEL_UNKNOWN) {
36527c478bd9Sstevel@tonic-gate 		nxt_pwr = cp->pmc_comp.pmc_lvals[0];
36537c478bd9Sstevel@tonic-gate 	} else {
36547c478bd9Sstevel@tonic-gate 		pwrndx--;
36557c478bd9Sstevel@tonic-gate 		ASSERT(pwrndx >= 0);
36567c478bd9Sstevel@tonic-gate 		nxt_pwr = cp->pmc_comp.pmc_lvals[pwrndx];
36577c478bd9Sstevel@tonic-gate 	}
36587c478bd9Sstevel@tonic-gate 	return (nxt_pwr);
36597c478bd9Sstevel@tonic-gate }
36607c478bd9Sstevel@tonic-gate 
36615cff7825Smh /*
36625cff7825Smh  * Update the maxpower (normal) power of a component. Note that the
36635cff7825Smh  * component's power level is only changed if it's current power level
36645cff7825Smh  * is higher than the new max power.
36655cff7825Smh  */
36665cff7825Smh int
pm_update_maxpower(dev_info_t * dip,int comp,int level)36675cff7825Smh pm_update_maxpower(dev_info_t *dip, int comp, int level)
36685cff7825Smh {
36695cff7825Smh 	PMD_FUNC(pmf, "update_maxpower")
36705cff7825Smh 	int old;
36715cff7825Smh 	int result;
36725cff7825Smh 
36735cff7825Smh 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
36745cff7825Smh 	    !e_pm_valid_power(dip, comp, level)) {
36755cff7825Smh 		PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) "
36765cff7825Smh 		    "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level))
36775cff7825Smh 		return (DDI_FAILURE);
36785cff7825Smh 	}
36795cff7825Smh 	old = e_pm_get_max_power(dip, comp);
36805cff7825Smh 	e_pm_set_max_power(dip, comp, level);
36815cff7825Smh 
36825cff7825Smh 	if (pm_set_power(dip, comp, level, PM_LEVEL_DOWNONLY,
36835cff7825Smh 	    PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) {
36845cff7825Smh 		e_pm_set_max_power(dip, comp, old);
36855cff7825Smh 		PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) pm_set_power failed\n", pmf,
36865cff7825Smh 		    PM_DEVICE(dip)))
36875cff7825Smh 		return (DDI_FAILURE);
36885cff7825Smh 	}
36895cff7825Smh 	return (DDI_SUCCESS);
36905cff7825Smh }
36915cff7825Smh 
36927c478bd9Sstevel@tonic-gate /*
36937c478bd9Sstevel@tonic-gate  * Bring all components of device to normal power
36947c478bd9Sstevel@tonic-gate  */
36957c478bd9Sstevel@tonic-gate int
pm_all_to_normal(dev_info_t * dip,pm_canblock_t canblock)36967c478bd9Sstevel@tonic-gate pm_all_to_normal(dev_info_t *dip, pm_canblock_t canblock)
36977c478bd9Sstevel@tonic-gate {
36987c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "all_to_normal")
36997c478bd9Sstevel@tonic-gate 	int		*normal;
37007c478bd9Sstevel@tonic-gate 	int		i, ncomps, result;
37017c478bd9Sstevel@tonic-gate 	size_t		size;
37027c478bd9Sstevel@tonic-gate 	int		changefailed = 0;
37037c478bd9Sstevel@tonic-gate 
37047c478bd9Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
37057c478bd9Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip));
37067c478bd9Sstevel@tonic-gate 	if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
37077c478bd9Sstevel@tonic-gate 		PMD(PMD_ALLNORM, ("%s: can't get norm pwrs for "
37087c478bd9Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
37097c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
37107c478bd9Sstevel@tonic-gate 	}
37117c478bd9Sstevel@tonic-gate 	ncomps = PM_NUMCMPTS(dip);
37127c478bd9Sstevel@tonic-gate 	for (i = 0; i < ncomps; i++) {
37137c478bd9Sstevel@tonic-gate 		if (pm_set_power(dip, i, normal[i],
37147c478bd9Sstevel@tonic-gate 		    PM_LEVEL_UPONLY, canblock, 0, &result) != DDI_SUCCESS) {
37157c478bd9Sstevel@tonic-gate 			changefailed++;
37167c478bd9Sstevel@tonic-gate 			PMD(PMD_ALLNORM | PMD_FAIL, ("%s: failed to set "
37177c478bd9Sstevel@tonic-gate 			    "%s@%s(%s#%d)[%d] to %d, errno %d\n", pmf,
37187c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip), i, normal[i], result))
37197c478bd9Sstevel@tonic-gate 		}
37207c478bd9Sstevel@tonic-gate 	}
37217c478bd9Sstevel@tonic-gate 	kmem_free(normal, size);
37227c478bd9Sstevel@tonic-gate 	if (changefailed) {
37237c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) "
37247c478bd9Sstevel@tonic-gate 		    "to full power\n", pmf, changefailed, PM_DEVICE(dip)))
37257c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
37267c478bd9Sstevel@tonic-gate 	}
37277c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
37287c478bd9Sstevel@tonic-gate }
37297c478bd9Sstevel@tonic-gate 
37307c478bd9Sstevel@tonic-gate /*
37317c478bd9Sstevel@tonic-gate  * Returns true if all components of device are at normal power
37327c478bd9Sstevel@tonic-gate  */
37337c478bd9Sstevel@tonic-gate int
pm_all_at_normal(dev_info_t * dip)37347c478bd9Sstevel@tonic-gate pm_all_at_normal(dev_info_t *dip)
37357c478bd9Sstevel@tonic-gate {
37367c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "all_at_normal")
37377c478bd9Sstevel@tonic-gate 	int		*normal;
37387c478bd9Sstevel@tonic-gate 	int		i;
37397c478bd9Sstevel@tonic-gate 	size_t		size;
37407c478bd9Sstevel@tonic-gate 
37417c478bd9Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
37427c478bd9Sstevel@tonic-gate 	if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
37437c478bd9Sstevel@tonic-gate 		PMD(PMD_ALLNORM, ("%s: can't get normal power\n", pmf))
37447c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
37457c478bd9Sstevel@tonic-gate 	}
37467c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
37477c478bd9Sstevel@tonic-gate 		int current = PM_CURPOWER(dip, i);
37487c478bd9Sstevel@tonic-gate 		if (normal[i] > current) {
37497c478bd9Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d) comp=%d, "
37507c478bd9Sstevel@tonic-gate 			    "norm=%d, cur=%d\n", pmf, PM_DEVICE(dip), i,
37517c478bd9Sstevel@tonic-gate 			    normal[i], current))
37527c478bd9Sstevel@tonic-gate 			break;
37537c478bd9Sstevel@tonic-gate 		}
37547c478bd9Sstevel@tonic-gate 	}
37557c478bd9Sstevel@tonic-gate 	kmem_free(normal, size);
37567c478bd9Sstevel@tonic-gate 	if (i != PM_NUMCMPTS(dip)) {
37577c478bd9Sstevel@tonic-gate 		return (0);
37587c478bd9Sstevel@tonic-gate 	}
37597c478bd9Sstevel@tonic-gate 	return (1);
37607c478bd9Sstevel@tonic-gate }
37617c478bd9Sstevel@tonic-gate 
3762d67944fbSScott Rotondo static void bring_pmdep_up(dev_info_t *, int);
3763d67944fbSScott Rotondo 
37647c478bd9Sstevel@tonic-gate static void
bring_wekeeps_up(char * keeper)37657c478bd9Sstevel@tonic-gate bring_wekeeps_up(char *keeper)
37667c478bd9Sstevel@tonic-gate {
37677c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "bring_wekeeps_up")
37687c478bd9Sstevel@tonic-gate 	int i;
37697c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
37707c478bd9Sstevel@tonic-gate 	pm_info_t *wku_info;
37717c478bd9Sstevel@tonic-gate 	char *kept_path;
37727c478bd9Sstevel@tonic-gate 	dev_info_t *kept;
37737c478bd9Sstevel@tonic-gate 
37747c478bd9Sstevel@tonic-gate 	if (panicstr) {
37757c478bd9Sstevel@tonic-gate 		return;
37767c478bd9Sstevel@tonic-gate 	}
37777c478bd9Sstevel@tonic-gate 	/*
37787c478bd9Sstevel@tonic-gate 	 * We process the request even if the keeper detaches because
37797c478bd9Sstevel@tonic-gate 	 * detach processing expects this to increment kidsupcnt of kept.
37807c478bd9Sstevel@tonic-gate 	 */
37817c478bd9Sstevel@tonic-gate 	PMD(PMD_BRING, ("%s: keeper= %s\n", pmf, keeper))
37827c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
37837c478bd9Sstevel@tonic-gate 		if (strcmp(dp->pdr_keeper, keeper) != 0)
37847c478bd9Sstevel@tonic-gate 			continue;
37857c478bd9Sstevel@tonic-gate 		for (i = 0; i < dp->pdr_kept_count; i++) {
37867c478bd9Sstevel@tonic-gate 			kept_path = dp->pdr_kept_paths[i];
37877c478bd9Sstevel@tonic-gate 			if (kept_path == NULL)
37887c478bd9Sstevel@tonic-gate 				continue;
37897c478bd9Sstevel@tonic-gate 			ASSERT(kept_path[0] != '\0');
37907c478bd9Sstevel@tonic-gate 			if ((kept = pm_name_to_dip(kept_path, 1)) == NULL)
37917c478bd9Sstevel@tonic-gate 				continue;
37927c478bd9Sstevel@tonic-gate 			wku_info = PM_GET_PM_INFO(kept);
37937c478bd9Sstevel@tonic-gate 			if (wku_info == NULL) {
37947c478bd9Sstevel@tonic-gate 				if (kept)
37957c478bd9Sstevel@tonic-gate 					ddi_release_devi(kept);
37967c478bd9Sstevel@tonic-gate 				continue;
37977c478bd9Sstevel@tonic-gate 			}
37987c478bd9Sstevel@tonic-gate 			/*
37997c478bd9Sstevel@tonic-gate 			 * Don't mess with it if it is being detached, it isn't
38007c478bd9Sstevel@tonic-gate 			 * safe to call its power entry point
38017c478bd9Sstevel@tonic-gate 			 */
38027c478bd9Sstevel@tonic-gate 			if (wku_info->pmi_dev_pm_state & PM_DETACHING) {
38037c478bd9Sstevel@tonic-gate 				if (kept)
38047c478bd9Sstevel@tonic-gate 					ddi_release_devi(kept);
38057c478bd9Sstevel@tonic-gate 				continue;
38067c478bd9Sstevel@tonic-gate 			}
38077c478bd9Sstevel@tonic-gate 			bring_pmdep_up(kept, 1);
38087c478bd9Sstevel@tonic-gate 			ddi_release_devi(kept);
38097c478bd9Sstevel@tonic-gate 		}
38107c478bd9Sstevel@tonic-gate 	}
38117c478bd9Sstevel@tonic-gate }
38127c478bd9Sstevel@tonic-gate 
38137c478bd9Sstevel@tonic-gate /*
38147c478bd9Sstevel@tonic-gate  * Bring up the 'kept' device passed as argument
38157c478bd9Sstevel@tonic-gate  */
38167c478bd9Sstevel@tonic-gate static void
bring_pmdep_up(dev_info_t * kept_dip,int hold)38177c478bd9Sstevel@tonic-gate bring_pmdep_up(dev_info_t *kept_dip, int hold)
38187c478bd9Sstevel@tonic-gate {
38197c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "bring_pmdep_up")
38207c478bd9Sstevel@tonic-gate 	int is_all_at_normal = 0;
38217c478bd9Sstevel@tonic-gate 
38227c478bd9Sstevel@tonic-gate 	/*
38237c478bd9Sstevel@tonic-gate 	 * If the kept device has been unmanaged, do nothing.
38247c478bd9Sstevel@tonic-gate 	 */
38257c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(kept_dip))
38267c478bd9Sstevel@tonic-gate 		return;
38277c478bd9Sstevel@tonic-gate 
38287c478bd9Sstevel@tonic-gate 	/* Just ignore DIRECT PM device till they are released. */
38297c478bd9Sstevel@tonic-gate 	if (!pm_processes_stopped && PM_ISDIRECT(kept_dip) &&
38307c478bd9Sstevel@tonic-gate 	    !(is_all_at_normal = pm_all_at_normal(kept_dip))) {
38317c478bd9Sstevel@tonic-gate 		PMD(PMD_BRING, ("%s: can't bring up PM_DIRECT %s@%s(%s#%d) "
38327c478bd9Sstevel@tonic-gate 		    "controlling process did something else\n", pmf,
38337c478bd9Sstevel@tonic-gate 		    PM_DEVICE(kept_dip)))
38347c478bd9Sstevel@tonic-gate 		DEVI(kept_dip)->devi_pm_flags |= PMC_SKIP_BRINGUP;
38357c478bd9Sstevel@tonic-gate 		return;
38367c478bd9Sstevel@tonic-gate 	}
38377c478bd9Sstevel@tonic-gate 	/* if we got here the keeper had a transition from OFF->ON */
38387c478bd9Sstevel@tonic-gate 	if (hold)
38397c478bd9Sstevel@tonic-gate 		pm_hold_power(kept_dip);
38407c478bd9Sstevel@tonic-gate 
38417c478bd9Sstevel@tonic-gate 	if (!is_all_at_normal)
38427c478bd9Sstevel@tonic-gate 		(void) pm_all_to_normal(kept_dip, PM_CANBLOCK_FAIL);
38437c478bd9Sstevel@tonic-gate }
38447c478bd9Sstevel@tonic-gate 
38457c478bd9Sstevel@tonic-gate /*
38467c478bd9Sstevel@tonic-gate  * A bunch of stuff that belongs only to the next routine (or two)
38477c478bd9Sstevel@tonic-gate  */
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate static const char namestr[] = "NAME=";
38507c478bd9Sstevel@tonic-gate static const int nameln = sizeof (namestr) - 1;
38517c478bd9Sstevel@tonic-gate static const char pmcompstr[] = "pm-components";
38527c478bd9Sstevel@tonic-gate 
38537c478bd9Sstevel@tonic-gate struct pm_comp_pkg {
38547c478bd9Sstevel@tonic-gate 	pm_comp_t		*comp;
38557c478bd9Sstevel@tonic-gate 	struct pm_comp_pkg	*next;
38567c478bd9Sstevel@tonic-gate };
38577c478bd9Sstevel@tonic-gate 
38587c478bd9Sstevel@tonic-gate #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
38597c478bd9Sstevel@tonic-gate 
38607c478bd9Sstevel@tonic-gate #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
38617c478bd9Sstevel@tonic-gate 			((ch) >= 'A' && (ch) <= 'F'))
38627c478bd9Sstevel@tonic-gate 
38637c478bd9Sstevel@tonic-gate /*
38647c478bd9Sstevel@tonic-gate  * Rather than duplicate this code ...
38657c478bd9Sstevel@tonic-gate  * (this code excerpted from the function that follows it)
38667c478bd9Sstevel@tonic-gate  */
38677c478bd9Sstevel@tonic-gate #define	FINISH_COMP { \
38687c478bd9Sstevel@tonic-gate 	ASSERT(compp); \
38697c478bd9Sstevel@tonic-gate 	compp->pmc_lnames_sz = size; \
38707c478bd9Sstevel@tonic-gate 	tp = compp->pmc_lname_buf = kmem_alloc(size, KM_SLEEP); \
38717c478bd9Sstevel@tonic-gate 	compp->pmc_numlevels = level; \
38727c478bd9Sstevel@tonic-gate 	compp->pmc_lnames = kmem_alloc(level * sizeof (char *), KM_SLEEP); \
38737c478bd9Sstevel@tonic-gate 	compp->pmc_lvals = kmem_alloc(level * sizeof (int), KM_SLEEP); \
38747c478bd9Sstevel@tonic-gate 	compp->pmc_thresh = kmem_alloc(level * sizeof (int), KM_SLEEP); \
38757c478bd9Sstevel@tonic-gate 	/* copy string out of prop array into buffer */ \
38767c478bd9Sstevel@tonic-gate 	for (j = 0; j < level; j++) { \
38777c478bd9Sstevel@tonic-gate 		compp->pmc_thresh[j] = INT_MAX;		/* only [0] sticks */ \
38787c478bd9Sstevel@tonic-gate 		compp->pmc_lvals[j] = lvals[j]; \
38797c478bd9Sstevel@tonic-gate 		(void) strcpy(tp, lnames[j]); \
38807c478bd9Sstevel@tonic-gate 		compp->pmc_lnames[j] = tp; \
38817c478bd9Sstevel@tonic-gate 		tp += lszs[j]; \
38827c478bd9Sstevel@tonic-gate 	} \
38837c478bd9Sstevel@tonic-gate 	ASSERT(tp > compp->pmc_lname_buf && tp <= \
38847c478bd9Sstevel@tonic-gate 	    compp->pmc_lname_buf + compp->pmc_lnames_sz); \
38857c478bd9Sstevel@tonic-gate 	}
38867c478bd9Sstevel@tonic-gate 
38877c478bd9Sstevel@tonic-gate /*
38887c478bd9Sstevel@tonic-gate  * Create (empty) component data structures.
38897c478bd9Sstevel@tonic-gate  */
38907c478bd9Sstevel@tonic-gate static void
e_pm_create_components(dev_info_t * dip,int num_components)38917c478bd9Sstevel@tonic-gate e_pm_create_components(dev_info_t *dip, int num_components)
38927c478bd9Sstevel@tonic-gate {
38937c478bd9Sstevel@tonic-gate 	struct pm_component *compp, *ocompp;
38947c478bd9Sstevel@tonic-gate 	int i, size = 0;
38957c478bd9Sstevel@tonic-gate 
38967c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
38977c478bd9Sstevel@tonic-gate 	ASSERT(!DEVI(dip)->devi_pm_components);
38987c478bd9Sstevel@tonic-gate 	ASSERT(!(DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE));
38997c478bd9Sstevel@tonic-gate 	size = sizeof (struct pm_component) * num_components;
39007c478bd9Sstevel@tonic-gate 
39017c478bd9Sstevel@tonic-gate 	compp = kmem_zalloc(size, KM_SLEEP);
39027c478bd9Sstevel@tonic-gate 	ocompp = compp;
39037c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_comp_size = size;
39047c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_num_components = num_components;
39057c478bd9Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
39067c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_components;  i++) {
39077c478bd9Sstevel@tonic-gate 		compp->pmc_timestamp = gethrestime_sec();
39087c478bd9Sstevel@tonic-gate 		compp->pmc_norm_pwr = (uint_t)-1;
39097c478bd9Sstevel@tonic-gate 		compp++;
39107c478bd9Sstevel@tonic-gate 	}
39117c478bd9Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
39127c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_components = ocompp;
39137c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_DONE;
39147c478bd9Sstevel@tonic-gate }
39157c478bd9Sstevel@tonic-gate 
39167c478bd9Sstevel@tonic-gate /*
39177c478bd9Sstevel@tonic-gate  * Parse hex or decimal value from char string
39187c478bd9Sstevel@tonic-gate  */
39197c478bd9Sstevel@tonic-gate static char *
pm_parsenum(char * cp,int * valp)39207c478bd9Sstevel@tonic-gate pm_parsenum(char *cp, int *valp)
39217c478bd9Sstevel@tonic-gate {
39227c478bd9Sstevel@tonic-gate 	int ch, offset;
39237c478bd9Sstevel@tonic-gate 	char numbuf[256];
39247c478bd9Sstevel@tonic-gate 	char *np = numbuf;
39257c478bd9Sstevel@tonic-gate 	int value = 0;
39267c478bd9Sstevel@tonic-gate 
39277c478bd9Sstevel@tonic-gate 	ch = *cp++;
39287c478bd9Sstevel@tonic-gate 	if (isdigit(ch)) {
39297c478bd9Sstevel@tonic-gate 		if (ch == '0') {
39307c478bd9Sstevel@tonic-gate 			if ((ch = *cp++) == 'x' || ch == 'X') {
39317c478bd9Sstevel@tonic-gate 				ch = *cp++;
39327c478bd9Sstevel@tonic-gate 				while (isxdigit(ch)) {
39337c478bd9Sstevel@tonic-gate 					*np++ = (char)ch;
39347c478bd9Sstevel@tonic-gate 					ch = *cp++;
39357c478bd9Sstevel@tonic-gate 				}
39367c478bd9Sstevel@tonic-gate 				*np = 0;
39377c478bd9Sstevel@tonic-gate 				cp--;
39387c478bd9Sstevel@tonic-gate 				goto hexval;
39397c478bd9Sstevel@tonic-gate 			} else {
39407c478bd9Sstevel@tonic-gate 				goto digit;
39417c478bd9Sstevel@tonic-gate 			}
39427c478bd9Sstevel@tonic-gate 		} else {
39437c478bd9Sstevel@tonic-gate digit:
39447c478bd9Sstevel@tonic-gate 			while (isdigit(ch)) {
39457c478bd9Sstevel@tonic-gate 				*np++ = (char)ch;
39467c478bd9Sstevel@tonic-gate 				ch = *cp++;
39477c478bd9Sstevel@tonic-gate 			}
39487c478bd9Sstevel@tonic-gate 			*np = 0;
39497c478bd9Sstevel@tonic-gate 			cp--;
39507c478bd9Sstevel@tonic-gate 			goto decval;
39517c478bd9Sstevel@tonic-gate 		}
39527c478bd9Sstevel@tonic-gate 	} else
39537c478bd9Sstevel@tonic-gate 		return (NULL);
39547c478bd9Sstevel@tonic-gate 
39557c478bd9Sstevel@tonic-gate hexval:
3956c6f039c7SToomas Soome 	offset = 0;
39577c478bd9Sstevel@tonic-gate 	for (np = numbuf; *np; np++) {
39587c478bd9Sstevel@tonic-gate 		if (*np >= 'a' && *np <= 'f')
39597c478bd9Sstevel@tonic-gate 			offset = 'a' - 10;
39607c478bd9Sstevel@tonic-gate 		else if (*np >= 'A' && *np <= 'F')
39617c478bd9Sstevel@tonic-gate 			offset = 'A' - 10;
39627c478bd9Sstevel@tonic-gate 		else if (*np >= '0' && *np <= '9')
39637c478bd9Sstevel@tonic-gate 			offset = '0';
39647c478bd9Sstevel@tonic-gate 		value *= 16;
39657c478bd9Sstevel@tonic-gate 		value += *np - offset;
39667c478bd9Sstevel@tonic-gate 	}
39677c478bd9Sstevel@tonic-gate 	*valp = value;
39687c478bd9Sstevel@tonic-gate 	return (cp);
39697c478bd9Sstevel@tonic-gate 
39707c478bd9Sstevel@tonic-gate decval:
39717c478bd9Sstevel@tonic-gate 	offset = '0';
39727c478bd9Sstevel@tonic-gate 	for (np = numbuf; *np; np++) {
39737c478bd9Sstevel@tonic-gate 		value *= 10;
39747c478bd9Sstevel@tonic-gate 		value += *np - offset;
39757c478bd9Sstevel@tonic-gate 	}
39767c478bd9Sstevel@tonic-gate 	*valp = value;
39777c478bd9Sstevel@tonic-gate 	return (cp);
39787c478bd9Sstevel@tonic-gate }
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate /*
39817c478bd9Sstevel@tonic-gate  * Set max (previously documented as "normal") power.
39827c478bd9Sstevel@tonic-gate  */
39837c478bd9Sstevel@tonic-gate static void
e_pm_set_max_power(dev_info_t * dip,int component_number,int level)39847c478bd9Sstevel@tonic-gate e_pm_set_max_power(dev_info_t *dip, int component_number, int level)
39857c478bd9Sstevel@tonic-gate {
39867c478bd9Sstevel@tonic-gate 	PM_CP(dip, component_number)->pmc_norm_pwr = level;
39877c478bd9Sstevel@tonic-gate }
39887c478bd9Sstevel@tonic-gate 
39895cff7825Smh /*
39905cff7825Smh  * Get max (previously documented as "normal") power.
39915cff7825Smh  */
39925cff7825Smh static int
e_pm_get_max_power(dev_info_t * dip,int component_number)39935cff7825Smh e_pm_get_max_power(dev_info_t *dip, int component_number)
39945cff7825Smh {
39955cff7825Smh 	return (PM_CP(dip, component_number)->pmc_norm_pwr);
39965cff7825Smh }
39975cff7825Smh 
39987c478bd9Sstevel@tonic-gate /*
39997c478bd9Sstevel@tonic-gate  * Internal routine for destroying components
40007c478bd9Sstevel@tonic-gate  * It is called even when there might not be any, so it must be forgiving.
40017c478bd9Sstevel@tonic-gate  */
40027c478bd9Sstevel@tonic-gate static void
e_pm_destroy_components(dev_info_t * dip)40037c478bd9Sstevel@tonic-gate e_pm_destroy_components(dev_info_t *dip)
40047c478bd9Sstevel@tonic-gate {
40057c478bd9Sstevel@tonic-gate 	int i;
40067c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
40077c478bd9Sstevel@tonic-gate 
40087c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
40097c478bd9Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) == 0)
40107c478bd9Sstevel@tonic-gate 		return;
40117c478bd9Sstevel@tonic-gate 	cp = DEVI(dip)->devi_pm_components;
40127c478bd9Sstevel@tonic-gate 	ASSERT(cp);
40137c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++, cp++) {
40147c478bd9Sstevel@tonic-gate 		int nlevels = cp->pmc_comp.pmc_numlevels;
40157c478bd9Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_lvals, nlevels * sizeof (int));
40167c478bd9Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_thresh, nlevels * sizeof (int));
40177c478bd9Sstevel@tonic-gate 		/*
40187c478bd9Sstevel@tonic-gate 		 * For BC nodes, the rest is static in bc_comp, so skip it
40197c478bd9Sstevel@tonic-gate 		 */
40207c478bd9Sstevel@tonic-gate 		if (PM_ISBC(dip))
40217c478bd9Sstevel@tonic-gate 			continue;
40227c478bd9Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_name, cp->pmc_comp.pmc_name_sz);
40237c478bd9Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_lnames, nlevels * sizeof (char *));
40247c478bd9Sstevel@tonic-gate 		kmem_free(cp->pmc_comp.pmc_lname_buf,
40256152d498Smh 		    cp->pmc_comp.pmc_lnames_sz);
40267c478bd9Sstevel@tonic-gate 	}
40277c478bd9Sstevel@tonic-gate 	kmem_free(DEVI(dip)->devi_pm_components, DEVI(dip)->devi_pm_comp_size);
40287c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_components = NULL;
40297c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_num_components = 0;
40307c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &=
40317c478bd9Sstevel@tonic-gate 	    ~(PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED);
40327c478bd9Sstevel@tonic-gate }
40337c478bd9Sstevel@tonic-gate 
40347c478bd9Sstevel@tonic-gate /*
40357c478bd9Sstevel@tonic-gate  * Read the pm-components property (if there is one) and use it to set up
40367c478bd9Sstevel@tonic-gate  * components.  Returns a pointer to an array of component structures if
40377c478bd9Sstevel@tonic-gate  * pm-components found and successfully parsed, else returns NULL.
40387c478bd9Sstevel@tonic-gate  * Sets error return *errp to true to indicate a failure (as opposed to no
40397c478bd9Sstevel@tonic-gate  * property being present).
40407c478bd9Sstevel@tonic-gate  */
40417c478bd9Sstevel@tonic-gate pm_comp_t *
pm_autoconfig(dev_info_t * dip,int * errp)40427c478bd9Sstevel@tonic-gate pm_autoconfig(dev_info_t *dip, int *errp)
40437c478bd9Sstevel@tonic-gate {
40447c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "autoconfig")
40457c478bd9Sstevel@tonic-gate 	uint_t nelems;
40467c478bd9Sstevel@tonic-gate 	char **pp;
40477c478bd9Sstevel@tonic-gate 	pm_comp_t *compp = NULL;
40487c478bd9Sstevel@tonic-gate 	int i, j, level, components = 0;
40497c478bd9Sstevel@tonic-gate 	size_t size = 0;
40507c478bd9Sstevel@tonic-gate 	struct pm_comp_pkg *p, *ptail;
40517c478bd9Sstevel@tonic-gate 	struct pm_comp_pkg *phead = NULL;
40527c478bd9Sstevel@tonic-gate 	int *lvals = NULL;
40537c478bd9Sstevel@tonic-gate 	int *lszs = NULL;
40547c478bd9Sstevel@tonic-gate 	int *np = NULL;
40557c478bd9Sstevel@tonic-gate 	int npi = 0;
40567c478bd9Sstevel@tonic-gate 	char **lnames = NULL;
40577c478bd9Sstevel@tonic-gate 	char *cp, *tp;
40587c478bd9Sstevel@tonic-gate 	pm_comp_t *ret = NULL;
40597c478bd9Sstevel@tonic-gate 
40607c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
40617c478bd9Sstevel@tonic-gate 	*errp = 0;	/* assume success */
40627c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
40637c478bd9Sstevel@tonic-gate 	    (char *)pmcompstr, &pp, &nelems) != DDI_PROP_SUCCESS) {
40647c478bd9Sstevel@tonic-gate 		return (NULL);
40657c478bd9Sstevel@tonic-gate 	}
40667c478bd9Sstevel@tonic-gate 
40677c478bd9Sstevel@tonic-gate 	if (nelems < 3) {	/* need at least one name and two levels */
40687c478bd9Sstevel@tonic-gate 		goto errout;
40697c478bd9Sstevel@tonic-gate 	}
40707c478bd9Sstevel@tonic-gate 
40717c478bd9Sstevel@tonic-gate 	/*
40727c478bd9Sstevel@tonic-gate 	 * pm_create_components is no longer allowed
40737c478bd9Sstevel@tonic-gate 	 */
40747c478bd9Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) != 0) {
40757c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) has %d comps\n",
40767c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), PM_NUMCMPTS(dip)))
40777c478bd9Sstevel@tonic-gate 		goto errout;
40787c478bd9Sstevel@tonic-gate 	}
40797c478bd9Sstevel@tonic-gate 
40807c478bd9Sstevel@tonic-gate 	lvals = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
40817c478bd9Sstevel@tonic-gate 	lszs = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
40827c478bd9Sstevel@tonic-gate 	lnames = kmem_alloc(nelems * sizeof (char *), KM_SLEEP);
40837c478bd9Sstevel@tonic-gate 	np = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
40847c478bd9Sstevel@tonic-gate 
40857c478bd9Sstevel@tonic-gate 	level = 0;
40867c478bd9Sstevel@tonic-gate 	phead = NULL;
40877c478bd9Sstevel@tonic-gate 	for (i = 0; i < nelems; i++) {
40887c478bd9Sstevel@tonic-gate 		cp = pp[i];
40897c478bd9Sstevel@tonic-gate 		if (!isdigit(*cp)) {	/*  must be name */
40907c478bd9Sstevel@tonic-gate 			if (strncmp(cp, namestr, nameln) != 0) {
40917c478bd9Sstevel@tonic-gate 				goto errout;
40927c478bd9Sstevel@tonic-gate 			}
40937c478bd9Sstevel@tonic-gate 			if (i != 0) {
40947c478bd9Sstevel@tonic-gate 				if (level == 0) {	/* no level spec'd */
40957c478bd9Sstevel@tonic-gate 					PMD(PMD_ERROR, ("%s: no level spec'd\n",
40967c478bd9Sstevel@tonic-gate 					    pmf))
40977c478bd9Sstevel@tonic-gate 					goto errout;
40987c478bd9Sstevel@tonic-gate 				}
40997c478bd9Sstevel@tonic-gate 				np[npi++] = lvals[level - 1];
41007c478bd9Sstevel@tonic-gate 				/* finish up previous component levels */
41017c478bd9Sstevel@tonic-gate 				FINISH_COMP;
41027c478bd9Sstevel@tonic-gate 			}
41037c478bd9Sstevel@tonic-gate 			cp += nameln;
41047c478bd9Sstevel@tonic-gate 			if (!*cp) {
41057c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: nsa\n", pmf))
41067c478bd9Sstevel@tonic-gate 				goto errout;
41077c478bd9Sstevel@tonic-gate 			}
41087c478bd9Sstevel@tonic-gate 			p = kmem_zalloc(sizeof (*phead), KM_SLEEP);
41097c478bd9Sstevel@tonic-gate 			if (phead == NULL) {
41107c478bd9Sstevel@tonic-gate 				phead = ptail = p;
41117c478bd9Sstevel@tonic-gate 			} else {
41127c478bd9Sstevel@tonic-gate 				ptail->next = p;
41137c478bd9Sstevel@tonic-gate 				ptail = p;
41147c478bd9Sstevel@tonic-gate 			}
41157c478bd9Sstevel@tonic-gate 			compp = p->comp = kmem_zalloc(sizeof (pm_comp_t),
41167c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
41177c478bd9Sstevel@tonic-gate 			compp->pmc_name_sz = strlen(cp) + 1;
41187c478bd9Sstevel@tonic-gate 			compp->pmc_name = kmem_zalloc(compp->pmc_name_sz,
41197c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
41207c478bd9Sstevel@tonic-gate 			(void) strncpy(compp->pmc_name, cp, compp->pmc_name_sz);
41217c478bd9Sstevel@tonic-gate 			components++;
41227c478bd9Sstevel@tonic-gate 			level = 0;
41237c478bd9Sstevel@tonic-gate 		} else {	/* better be power level <num>=<name> */
41247c478bd9Sstevel@tonic-gate #ifdef DEBUG
41257c478bd9Sstevel@tonic-gate 			tp = cp;
41267c478bd9Sstevel@tonic-gate #endif
41277c478bd9Sstevel@tonic-gate 			if (i == 0 ||
41287c478bd9Sstevel@tonic-gate 			    (cp = pm_parsenum(cp, &lvals[level])) == NULL) {
41297c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: parsenum(%s)\n", pmf, tp))
41307c478bd9Sstevel@tonic-gate 				goto errout;
41317c478bd9Sstevel@tonic-gate 			}
41327c478bd9Sstevel@tonic-gate #ifdef DEBUG
41337c478bd9Sstevel@tonic-gate 			tp = cp;
41347c478bd9Sstevel@tonic-gate #endif
41357c478bd9Sstevel@tonic-gate 			if (*cp++ != '=' || !*cp) {
41367c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: ex =, got %s\n", pmf, tp))
41377c478bd9Sstevel@tonic-gate 				goto errout;
41387c478bd9Sstevel@tonic-gate 			}
41397c478bd9Sstevel@tonic-gate 
41407c478bd9Sstevel@tonic-gate 			lszs[level] = strlen(cp) + 1;
41417c478bd9Sstevel@tonic-gate 			size += lszs[level];
41427c478bd9Sstevel@tonic-gate 			lnames[level] = cp;	/* points into prop string */
41437c478bd9Sstevel@tonic-gate 			level++;
41447c478bd9Sstevel@tonic-gate 		}
41457c478bd9Sstevel@tonic-gate 	}
41467c478bd9Sstevel@tonic-gate 	np[npi++] = lvals[level - 1];
41477c478bd9Sstevel@tonic-gate 	if (level == 0) {	/* ended with a name */
41487c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: ewn\n", pmf))
41497c478bd9Sstevel@tonic-gate 		goto errout;
41507c478bd9Sstevel@tonic-gate 	}
41517c478bd9Sstevel@tonic-gate 	FINISH_COMP;
41527c478bd9Sstevel@tonic-gate 
41537c478bd9Sstevel@tonic-gate 
41547c478bd9Sstevel@tonic-gate 	/*
41557c478bd9Sstevel@tonic-gate 	 * Now we have a list of components--we have to return instead an
41567c478bd9Sstevel@tonic-gate 	 * array of them, but we can just copy the top level and leave
41577c478bd9Sstevel@tonic-gate 	 * the rest as is
41587c478bd9Sstevel@tonic-gate 	 */
41597c478bd9Sstevel@tonic-gate 	(void) e_pm_create_components(dip, components);
41607c478bd9Sstevel@tonic-gate 	for (i = 0; i < components; i++)
41617c478bd9Sstevel@tonic-gate 		e_pm_set_max_power(dip, i, np[i]);
41627c478bd9Sstevel@tonic-gate 
41637c478bd9Sstevel@tonic-gate 	ret = kmem_zalloc(components * sizeof (pm_comp_t), KM_SLEEP);
41647c478bd9Sstevel@tonic-gate 	for (i = 0, p = phead; i < components; i++) {
41657c478bd9Sstevel@tonic-gate 		ASSERT(p);
41667c478bd9Sstevel@tonic-gate 		/*
41677c478bd9Sstevel@tonic-gate 		 * Now sanity-check values:  levels must be monotonically
41687c478bd9Sstevel@tonic-gate 		 * increasing
41697c478bd9Sstevel@tonic-gate 		 */
41707c478bd9Sstevel@tonic-gate 		if (p->comp->pmc_numlevels < 2) {
41717c478bd9Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) only %d "
41727c478bd9Sstevel@tonic-gate 			    "levels\n", pmf,
41737c478bd9Sstevel@tonic-gate 			    p->comp->pmc_name, PM_DEVICE(dip),
41747c478bd9Sstevel@tonic-gate 			    p->comp->pmc_numlevels))
41757c478bd9Sstevel@tonic-gate 			goto errout;
41767c478bd9Sstevel@tonic-gate 		}
41777c478bd9Sstevel@tonic-gate 		for (j = 0; j < p->comp->pmc_numlevels; j++) {
41787c478bd9Sstevel@tonic-gate 			if ((p->comp->pmc_lvals[j] < 0) || ((j > 0) &&
41797c478bd9Sstevel@tonic-gate 			    (p->comp->pmc_lvals[j] <=
41807c478bd9Sstevel@tonic-gate 			    p->comp->pmc_lvals[j - 1]))) {
41817c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) "
41827c478bd9Sstevel@tonic-gate 				    "not mono. incr, %d follows %d\n", pmf,
41837c478bd9Sstevel@tonic-gate 				    p->comp->pmc_name, PM_DEVICE(dip),
41847c478bd9Sstevel@tonic-gate 				    p->comp->pmc_lvals[j],
41857c478bd9Sstevel@tonic-gate 				    p->comp->pmc_lvals[j - 1]))
41867c478bd9Sstevel@tonic-gate 				goto errout;
41877c478bd9Sstevel@tonic-gate 			}
41887c478bd9Sstevel@tonic-gate 		}
41897c478bd9Sstevel@tonic-gate 		ret[i] = *p->comp;	/* struct assignment */
41907c478bd9Sstevel@tonic-gate 		for (j = 0; j < i; j++) {
41917c478bd9Sstevel@tonic-gate 			/*
41927c478bd9Sstevel@tonic-gate 			 * Test for unique component names
41937c478bd9Sstevel@tonic-gate 			 */
41947c478bd9Sstevel@tonic-gate 			if (strcmp(ret[j].pmc_name, ret[i].pmc_name) == 0) {
41957c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR, ("%s: %s of %s@%s(%s#%d) not "
41967c478bd9Sstevel@tonic-gate 				    "unique\n", pmf, ret[j].pmc_name,
41977c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip)))
41987c478bd9Sstevel@tonic-gate 				goto errout;
41997c478bd9Sstevel@tonic-gate 			}
42007c478bd9Sstevel@tonic-gate 		}
42017c478bd9Sstevel@tonic-gate 		ptail = p;
42027c478bd9Sstevel@tonic-gate 		p = p->next;
42037c478bd9Sstevel@tonic-gate 		phead = p;	/* errout depends on phead making sense */
42047c478bd9Sstevel@tonic-gate 		kmem_free(ptail->comp, sizeof (*ptail->comp));
42057c478bd9Sstevel@tonic-gate 		kmem_free(ptail, sizeof (*ptail));
42067c478bd9Sstevel@tonic-gate 	}
42077c478bd9Sstevel@tonic-gate out:
42087c478bd9Sstevel@tonic-gate 	ddi_prop_free(pp);
42097c478bd9Sstevel@tonic-gate 	if (lvals)
42107c478bd9Sstevel@tonic-gate 		kmem_free(lvals, nelems * sizeof (int));
42117c478bd9Sstevel@tonic-gate 	if (lszs)
42127c478bd9Sstevel@tonic-gate 		kmem_free(lszs, nelems * sizeof (int));
42137c478bd9Sstevel@tonic-gate 	if (lnames)
42147c478bd9Sstevel@tonic-gate 		kmem_free(lnames, nelems * sizeof (char *));
42157c478bd9Sstevel@tonic-gate 	if (np)
42167c478bd9Sstevel@tonic-gate 		kmem_free(np, nelems * sizeof (int));
42177c478bd9Sstevel@tonic-gate 	return (ret);
42187c478bd9Sstevel@tonic-gate 
42197c478bd9Sstevel@tonic-gate errout:
42207c478bd9Sstevel@tonic-gate 	e_pm_destroy_components(dip);
42217c478bd9Sstevel@tonic-gate 	*errp = 1;	/* signal failure */
42227c478bd9Sstevel@tonic-gate 	cmn_err(CE_CONT, "!pm: %s property ", pmcompstr);
42237c478bd9Sstevel@tonic-gate 	for (i = 0; i < nelems - 1; i++)
42247c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "!'%s', ", pp[i]);
42257c478bd9Sstevel@tonic-gate 	if (nelems != 0)
42267c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "!'%s'", pp[nelems - 1]);
42277c478bd9Sstevel@tonic-gate 	cmn_err(CE_CONT, "! for %s@%s(%s#%d) is ill-formed.\n", PM_DEVICE(dip));
42287c478bd9Sstevel@tonic-gate 	for (p = phead; p; ) {
42297c478bd9Sstevel@tonic-gate 		pm_comp_t *pp;
42307c478bd9Sstevel@tonic-gate 		int n;
42317c478bd9Sstevel@tonic-gate 
42327c478bd9Sstevel@tonic-gate 		ptail = p;
42337c478bd9Sstevel@tonic-gate 		/*
42347c478bd9Sstevel@tonic-gate 		 * Free component data structures
42357c478bd9Sstevel@tonic-gate 		 */
42367c478bd9Sstevel@tonic-gate 		pp = p->comp;
42377c478bd9Sstevel@tonic-gate 		n = pp->pmc_numlevels;
42387c478bd9Sstevel@tonic-gate 		if (pp->pmc_name_sz) {
42397c478bd9Sstevel@tonic-gate 			kmem_free(pp->pmc_name, pp->pmc_name_sz);
42407c478bd9Sstevel@tonic-gate 		}
42417c478bd9Sstevel@tonic-gate 		if (pp->pmc_lnames_sz) {
42427c478bd9Sstevel@tonic-gate 			kmem_free(pp->pmc_lname_buf, pp->pmc_lnames_sz);
42437c478bd9Sstevel@tonic-gate 		}
42447c478bd9Sstevel@tonic-gate 		if (pp->pmc_lnames) {
42457c478bd9Sstevel@tonic-gate 			kmem_free(pp->pmc_lnames, n * (sizeof (char *)));
42467c478bd9Sstevel@tonic-gate 		}
42477c478bd9Sstevel@tonic-gate 		if (pp->pmc_thresh) {
42487c478bd9Sstevel@tonic-gate 			kmem_free(pp->pmc_thresh, n * (sizeof (int)));
42497c478bd9Sstevel@tonic-gate 		}
42507c478bd9Sstevel@tonic-gate 		if (pp->pmc_lvals) {
42517c478bd9Sstevel@tonic-gate 			kmem_free(pp->pmc_lvals, n * (sizeof (int)));
42527c478bd9Sstevel@tonic-gate 		}
42537c478bd9Sstevel@tonic-gate 		p = ptail->next;
42547c478bd9Sstevel@tonic-gate 		kmem_free(ptail, sizeof (*ptail));
42557c478bd9Sstevel@tonic-gate 	}
42567c478bd9Sstevel@tonic-gate 	if (ret != NULL)
42577c478bd9Sstevel@tonic-gate 		kmem_free(ret, components * sizeof (pm_comp_t));
42587c478bd9Sstevel@tonic-gate 	ret = NULL;
42597c478bd9Sstevel@tonic-gate 	goto out;
42607c478bd9Sstevel@tonic-gate }
42617c478bd9Sstevel@tonic-gate 
42627c478bd9Sstevel@tonic-gate /*
42637c478bd9Sstevel@tonic-gate  * Set threshold values for a devices components by dividing the target
42647c478bd9Sstevel@tonic-gate  * threshold (base) by the number of transitions and assign each transition
42657c478bd9Sstevel@tonic-gate  * that threshold.  This will get the entire device down in the target time if
42667c478bd9Sstevel@tonic-gate  * all components are idle and even if there are dependencies among components.
42677c478bd9Sstevel@tonic-gate  *
42687c478bd9Sstevel@tonic-gate  * Devices may well get powered all the way down before the target time, but
42697c478bd9Sstevel@tonic-gate  * at least the EPA will be happy.
42707c478bd9Sstevel@tonic-gate  */
42717c478bd9Sstevel@tonic-gate void
pm_set_device_threshold(dev_info_t * dip,int base,int flag)42727c478bd9Sstevel@tonic-gate pm_set_device_threshold(dev_info_t *dip, int base, int flag)
42737c478bd9Sstevel@tonic-gate {
42747c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_device_threshold")
42757c478bd9Sstevel@tonic-gate 	int target_threshold = (base * 95) / 100;
42767c478bd9Sstevel@tonic-gate 	int level, comp;		/* loop counters */
42777c478bd9Sstevel@tonic-gate 	int transitions = 0;
42787c478bd9Sstevel@tonic-gate 	int ncomp = PM_NUMCMPTS(dip);
42797c478bd9Sstevel@tonic-gate 	int thresh;
42807c478bd9Sstevel@tonic-gate 	int remainder;
42817c478bd9Sstevel@tonic-gate 	pm_comp_t *pmc;
42823fe80ca4SDan Cross 	int i;
42837c478bd9Sstevel@tonic-gate 
42847c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
42857c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
42867c478bd9Sstevel@tonic-gate 	/*
42877c478bd9Sstevel@tonic-gate 	 * First we handle the easy one.  If we're setting the default
42887c478bd9Sstevel@tonic-gate 	 * threshold for a node with children, then we set it to the
42897c478bd9Sstevel@tonic-gate 	 * default nexus threshold (currently 0) and mark it as default
42907c478bd9Sstevel@tonic-gate 	 * nexus threshold instead
42917c478bd9Sstevel@tonic-gate 	 */
42927c478bd9Sstevel@tonic-gate 	if (PM_IS_NEXUS(dip)) {
42937c478bd9Sstevel@tonic-gate 		if (flag == PMC_DEF_THRESH) {
42947c478bd9Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: [%s@%s(%s#%d) NEXDEF]\n", pmf,
42957c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip)))
42967c478bd9Sstevel@tonic-gate 			thresh = pm_default_nexus_threshold;
42977c478bd9Sstevel@tonic-gate 			for (comp = 0; comp < ncomp; comp++) {
42987c478bd9Sstevel@tonic-gate 				pmc = &PM_CP(dip, comp)->pmc_comp;
42997c478bd9Sstevel@tonic-gate 				for (level = 1; level < pmc->pmc_numlevels;
43007c478bd9Sstevel@tonic-gate 				    level++) {
43017c478bd9Sstevel@tonic-gate 					pmc->pmc_thresh[level] = thresh;
43027c478bd9Sstevel@tonic-gate 				}
43037c478bd9Sstevel@tonic-gate 			}
43047c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_dev_thresh =
43057c478bd9Sstevel@tonic-gate 			    pm_default_nexus_threshold;
43067c478bd9Sstevel@tonic-gate 			/*
43077c478bd9Sstevel@tonic-gate 			 * If the nexus node is being reconfigured back to
43087c478bd9Sstevel@tonic-gate 			 * the default threshold, adjust the notlowest count.
43097c478bd9Sstevel@tonic-gate 			 */
43107c478bd9Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_flags &
43117c478bd9Sstevel@tonic-gate 			    (PMC_DEV_THRESH|PMC_COMP_THRESH)) {
43123fe80ca4SDan Cross 				PM_LOCK_POWER(dip);
43137c478bd9Sstevel@tonic-gate 				for (i = 0; i < PM_NUMCMPTS(dip); i++) {
43147c478bd9Sstevel@tonic-gate 					if (PM_CURPOWER(dip, i) == 0)
43157c478bd9Sstevel@tonic-gate 						continue;
43167c478bd9Sstevel@tonic-gate 					mutex_enter(&pm_compcnt_lock);
43177c478bd9Sstevel@tonic-gate 					ASSERT(pm_comps_notlowest);
43187c478bd9Sstevel@tonic-gate 					pm_comps_notlowest--;
43197c478bd9Sstevel@tonic-gate 					PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr "
43207c478bd9Sstevel@tonic-gate 					    "notlowest to %d\n", pmf,
43217c478bd9Sstevel@tonic-gate 					    PM_DEVICE(dip), pm_comps_notlowest))
43227c478bd9Sstevel@tonic-gate 					if (pm_comps_notlowest == 0)
43237c478bd9Sstevel@tonic-gate 						pm_ppm_notify_all_lowest(dip,
43247c478bd9Sstevel@tonic-gate 						    PM_ALL_LOWEST);
43257c478bd9Sstevel@tonic-gate 					mutex_exit(&pm_compcnt_lock);
43267c478bd9Sstevel@tonic-gate 				}
43273fe80ca4SDan Cross 				PM_UNLOCK_POWER(dip);
43287c478bd9Sstevel@tonic-gate 			}
43297c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
43307c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= PMC_NEXDEF_THRESH;
43317c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
43327c478bd9Sstevel@tonic-gate 			return;
43337c478bd9Sstevel@tonic-gate 		} else if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) {
43347c478bd9Sstevel@tonic-gate 			/*
43357c478bd9Sstevel@tonic-gate 			 * If the nexus node is being configured for a
43367c478bd9Sstevel@tonic-gate 			 * non-default threshold, include that node in
43377c478bd9Sstevel@tonic-gate 			 * the notlowest accounting.
43387c478bd9Sstevel@tonic-gate 			 */
43393fe80ca4SDan Cross 			PM_LOCK_POWER(dip);
43407c478bd9Sstevel@tonic-gate 			for (i = 0; i < PM_NUMCMPTS(dip); i++) {
43417c478bd9Sstevel@tonic-gate 				if (PM_CURPOWER(dip, i) == 0)
43427c478bd9Sstevel@tonic-gate 					continue;
43437c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_compcnt_lock);
43447c478bd9Sstevel@tonic-gate 				if (pm_comps_notlowest == 0)
43457c478bd9Sstevel@tonic-gate 					pm_ppm_notify_all_lowest(dip,
43467c478bd9Sstevel@tonic-gate 					    PM_NOT_ALL_LOWEST);
43477c478bd9Sstevel@tonic-gate 				pm_comps_notlowest++;
43487c478bd9Sstevel@tonic-gate 				PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr "
43497c478bd9Sstevel@tonic-gate 				    "notlowest to %d\n", pmf,
43507c478bd9Sstevel@tonic-gate 				    PM_DEVICE(dip), pm_comps_notlowest))
43517c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_compcnt_lock);
43527c478bd9Sstevel@tonic-gate 			}
43533fe80ca4SDan Cross 			PM_UNLOCK_POWER(dip);
43547c478bd9Sstevel@tonic-gate 		}
43557c478bd9Sstevel@tonic-gate 	}
43567c478bd9Sstevel@tonic-gate 	/*
43577c478bd9Sstevel@tonic-gate 	 * Compute the total number of transitions for all components
43587c478bd9Sstevel@tonic-gate 	 * of the device.  Distribute the threshold evenly over them
43597c478bd9Sstevel@tonic-gate 	 */
43607c478bd9Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
43617c478bd9Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
43627c478bd9Sstevel@tonic-gate 		ASSERT(pmc->pmc_numlevels > 1);
43637c478bd9Sstevel@tonic-gate 		transitions += pmc->pmc_numlevels - 1;
43647c478bd9Sstevel@tonic-gate 	}
43657c478bd9Sstevel@tonic-gate 	ASSERT(transitions);
43667c478bd9Sstevel@tonic-gate 	thresh = target_threshold / transitions;
43677c478bd9Sstevel@tonic-gate 
43687c478bd9Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
43697c478bd9Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
43707c478bd9Sstevel@tonic-gate 		for (level = 1; level < pmc->pmc_numlevels; level++) {
43717c478bd9Sstevel@tonic-gate 			pmc->pmc_thresh[level] = thresh;
43727c478bd9Sstevel@tonic-gate 		}
43737c478bd9Sstevel@tonic-gate 	}
43747c478bd9Sstevel@tonic-gate 
43757c478bd9Sstevel@tonic-gate #ifdef DEBUG
43767c478bd9Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
43777c478bd9Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
43787c478bd9Sstevel@tonic-gate 		for (level = 1; level < pmc->pmc_numlevels; level++) {
43797c478bd9Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: thresh before %s@%s(%s#%d) "
43807c478bd9Sstevel@tonic-gate 			    "comp=%d, level=%d, %d\n", pmf, PM_DEVICE(dip),
43817c478bd9Sstevel@tonic-gate 			    comp, level, pmc->pmc_thresh[level]))
43827c478bd9Sstevel@tonic-gate 		}
43837c478bd9Sstevel@tonic-gate 	}
43847c478bd9Sstevel@tonic-gate #endif
43857c478bd9Sstevel@tonic-gate 	/*
43867c478bd9Sstevel@tonic-gate 	 * Distribute any remainder till they are all gone
43877c478bd9Sstevel@tonic-gate 	 */
43887c478bd9Sstevel@tonic-gate 	remainder = target_threshold - thresh * transitions;
43897c478bd9Sstevel@tonic-gate 	level = 1;
43907c478bd9Sstevel@tonic-gate #ifdef DEBUG
43917c478bd9Sstevel@tonic-gate 	PMD(PMD_THRESH, ("%s: remainder=%d target_threshold=%d thresh=%d "
43927c478bd9Sstevel@tonic-gate 	    "trans=%d\n", pmf, remainder, target_threshold, thresh,
43937c478bd9Sstevel@tonic-gate 	    transitions))
43947c478bd9Sstevel@tonic-gate #endif
43957c478bd9Sstevel@tonic-gate 	while (remainder > 0) {
43967c478bd9Sstevel@tonic-gate 		comp = 0;
43977c478bd9Sstevel@tonic-gate 		while (remainder && (comp < ncomp)) {
43987c478bd9Sstevel@tonic-gate 			pmc = &PM_CP(dip, comp)->pmc_comp;
43997c478bd9Sstevel@tonic-gate 			if (level < pmc->pmc_numlevels) {
44007c478bd9Sstevel@tonic-gate 				pmc->pmc_thresh[level] += 1;
44017c478bd9Sstevel@tonic-gate 				remainder--;
44027c478bd9Sstevel@tonic-gate 			}
44037c478bd9Sstevel@tonic-gate 			comp++;
44047c478bd9Sstevel@tonic-gate 		}
44057c478bd9Sstevel@tonic-gate 		level++;
44067c478bd9Sstevel@tonic-gate 	}
44077c478bd9Sstevel@tonic-gate #ifdef DEBUG
44087c478bd9Sstevel@tonic-gate 	for (comp = 0; comp < ncomp; comp++) {
44097c478bd9Sstevel@tonic-gate 		pmc = &PM_CP(dip, comp)->pmc_comp;
44107c478bd9Sstevel@tonic-gate 		for (level = 1; level < pmc->pmc_numlevels; level++) {
44117c478bd9Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: thresh after %s@%s(%s#%d) "
44127c478bd9Sstevel@tonic-gate 			    "comp=%d level=%d, %d\n", pmf, PM_DEVICE(dip),
44137c478bd9Sstevel@tonic-gate 			    comp, level, pmc->pmc_thresh[level]))
44147c478bd9Sstevel@tonic-gate 		}
44157c478bd9Sstevel@tonic-gate 	}
44167c478bd9Sstevel@tonic-gate #endif
44177c478bd9Sstevel@tonic-gate 	ASSERT(PM_IAM_LOCKING_DIP(dip));
44187c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_dev_thresh = base;
44197c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
44207c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= flag;
44217c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
44227c478bd9Sstevel@tonic-gate }
44237c478bd9Sstevel@tonic-gate 
44247c478bd9Sstevel@tonic-gate /*
44257c478bd9Sstevel@tonic-gate  * Called when there is no old-style platform power management driver
44267c478bd9Sstevel@tonic-gate  */
44277c478bd9Sstevel@tonic-gate static int
ddi_no_platform_power(power_req_t * req)44287c478bd9Sstevel@tonic-gate ddi_no_platform_power(power_req_t *req)
44297c478bd9Sstevel@tonic-gate {
44307c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(req))
44317c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
44327c478bd9Sstevel@tonic-gate }
44337c478bd9Sstevel@tonic-gate 
44347c478bd9Sstevel@tonic-gate /*
44357c478bd9Sstevel@tonic-gate  * This function calls the entry point supplied by the platform-specific
44367c478bd9Sstevel@tonic-gate  * pm driver to bring the device component 'pm_cmpt' to power level 'pm_level'.
44377c478bd9Sstevel@tonic-gate  * The use of global for getting the  function name from platform-specific
44387c478bd9Sstevel@tonic-gate  * pm driver is not ideal, but it is simple and efficient.
44397c478bd9Sstevel@tonic-gate  * The previous property lookup was being done in the idle loop on swift
44407c478bd9Sstevel@tonic-gate  * systems without pmc chips and hurt deskbench performance as well as
44417c478bd9Sstevel@tonic-gate  * violating scheduler locking rules
44427c478bd9Sstevel@tonic-gate  */
44437c478bd9Sstevel@tonic-gate int	(*pm_platform_power)(power_req_t *) = ddi_no_platform_power;
44447c478bd9Sstevel@tonic-gate 
44457c478bd9Sstevel@tonic-gate /*
44467c478bd9Sstevel@tonic-gate  * Old obsolete interface for a device to request a power change (but only
44477c478bd9Sstevel@tonic-gate  * an increase in power)
44487c478bd9Sstevel@tonic-gate  */
44497c478bd9Sstevel@tonic-gate int
ddi_dev_is_needed(dev_info_t * dip,int cmpt,int level)44507c478bd9Sstevel@tonic-gate ddi_dev_is_needed(dev_info_t *dip, int cmpt, int level)
44517c478bd9Sstevel@tonic-gate {
44527c478bd9Sstevel@tonic-gate 	return (pm_raise_power(dip, cmpt, level));
44537c478bd9Sstevel@tonic-gate }
44547c478bd9Sstevel@tonic-gate 
44557c478bd9Sstevel@tonic-gate /*
44567c478bd9Sstevel@tonic-gate  * The old obsolete interface to platform power management.  Only used by
44577c478bd9Sstevel@tonic-gate  * Gypsy platform and APM on X86.
44587c478bd9Sstevel@tonic-gate  */
44597c478bd9Sstevel@tonic-gate int
ddi_power(dev_info_t * dip,int pm_cmpt,int pm_level)44607c478bd9Sstevel@tonic-gate ddi_power(dev_info_t *dip, int pm_cmpt, int pm_level)
44617c478bd9Sstevel@tonic-gate {
44627c478bd9Sstevel@tonic-gate 	power_req_t	request;
44637c478bd9Sstevel@tonic-gate 
44647c478bd9Sstevel@tonic-gate 	request.request_type = PMR_SET_POWER;
44657c478bd9Sstevel@tonic-gate 	request.req.set_power_req.who = dip;
44667c478bd9Sstevel@tonic-gate 	request.req.set_power_req.cmpt = pm_cmpt;
44677c478bd9Sstevel@tonic-gate 	request.req.set_power_req.level = pm_level;
44687c478bd9Sstevel@tonic-gate 	return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
44697c478bd9Sstevel@tonic-gate }
44707c478bd9Sstevel@tonic-gate 
44717c478bd9Sstevel@tonic-gate /*
44727c478bd9Sstevel@tonic-gate  * A driver can invoke this from its detach routine when DDI_SUSPEND is
44737c478bd9Sstevel@tonic-gate  * passed.  Returns true if subsequent processing could result in power being
44747c478bd9Sstevel@tonic-gate  * removed from the device.  The arg is not currently used because it is
44757c478bd9Sstevel@tonic-gate  * implicit in the operation of cpr/DR.
44767c478bd9Sstevel@tonic-gate  */
44777c478bd9Sstevel@tonic-gate int
ddi_removing_power(dev_info_t * dip)44787c478bd9Sstevel@tonic-gate ddi_removing_power(dev_info_t *dip)
44797c478bd9Sstevel@tonic-gate {
44807c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
44817c478bd9Sstevel@tonic-gate 	return (pm_powering_down);
44827c478bd9Sstevel@tonic-gate }
44837c478bd9Sstevel@tonic-gate 
44847c478bd9Sstevel@tonic-gate /*
44857c478bd9Sstevel@tonic-gate  * Returns true if a device indicates that its parent handles suspend/resume
44867c478bd9Sstevel@tonic-gate  * processing for it.
44877c478bd9Sstevel@tonic-gate  */
44887c478bd9Sstevel@tonic-gate int
e_ddi_parental_suspend_resume(dev_info_t * dip)44897c478bd9Sstevel@tonic-gate e_ddi_parental_suspend_resume(dev_info_t *dip)
44907c478bd9Sstevel@tonic-gate {
44917c478bd9Sstevel@tonic-gate 	return (DEVI(dip)->devi_pm_flags & PMC_PARENTAL_SR);
44927c478bd9Sstevel@tonic-gate }
44937c478bd9Sstevel@tonic-gate 
44947c478bd9Sstevel@tonic-gate /*
44957c478bd9Sstevel@tonic-gate  * Called for devices which indicate that their parent does suspend/resume
44967c478bd9Sstevel@tonic-gate  * handling for them
44977c478bd9Sstevel@tonic-gate  */
44987c478bd9Sstevel@tonic-gate int
e_ddi_suspend(dev_info_t * dip,ddi_detach_cmd_t cmd)44997c478bd9Sstevel@tonic-gate e_ddi_suspend(dev_info_t *dip, ddi_detach_cmd_t cmd)
45007c478bd9Sstevel@tonic-gate {
45017c478bd9Sstevel@tonic-gate 	power_req_t	request;
45027c478bd9Sstevel@tonic-gate 	request.request_type = PMR_SUSPEND;
45037c478bd9Sstevel@tonic-gate 	request.req.suspend_req.who = dip;
45047c478bd9Sstevel@tonic-gate 	request.req.suspend_req.cmd = cmd;
45057c478bd9Sstevel@tonic-gate 	return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
45067c478bd9Sstevel@tonic-gate }
45077c478bd9Sstevel@tonic-gate 
45087c478bd9Sstevel@tonic-gate /*
45097c478bd9Sstevel@tonic-gate  * Called for devices which indicate that their parent does suspend/resume
45107c478bd9Sstevel@tonic-gate  * handling for them
45117c478bd9Sstevel@tonic-gate  */
45127c478bd9Sstevel@tonic-gate int
e_ddi_resume(dev_info_t * dip,ddi_attach_cmd_t cmd)45137c478bd9Sstevel@tonic-gate e_ddi_resume(dev_info_t *dip, ddi_attach_cmd_t cmd)
45147c478bd9Sstevel@tonic-gate {
45157c478bd9Sstevel@tonic-gate 	power_req_t	request;
45167c478bd9Sstevel@tonic-gate 	request.request_type = PMR_RESUME;
45177c478bd9Sstevel@tonic-gate 	request.req.resume_req.who = dip;
45187c478bd9Sstevel@tonic-gate 	request.req.resume_req.cmd = cmd;
45197c478bd9Sstevel@tonic-gate 	return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
45207c478bd9Sstevel@tonic-gate }
45217c478bd9Sstevel@tonic-gate 
45227c478bd9Sstevel@tonic-gate /*
45237c478bd9Sstevel@tonic-gate  * Old obsolete exported interface for drivers to create components.
45247c478bd9Sstevel@tonic-gate  * This is now handled by exporting the pm-components property.
45257c478bd9Sstevel@tonic-gate  */
45267c478bd9Sstevel@tonic-gate int
pm_create_components(dev_info_t * dip,int num_components)45277c478bd9Sstevel@tonic-gate pm_create_components(dev_info_t *dip, int num_components)
45287c478bd9Sstevel@tonic-gate {
45297c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_create_components")
45307c478bd9Sstevel@tonic-gate 
45317c478bd9Sstevel@tonic-gate 	if (num_components < 1)
45327c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
45337c478bd9Sstevel@tonic-gate 
45347c478bd9Sstevel@tonic-gate 	if (!DEVI_IS_ATTACHING(dip)) {
45357c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
45367c478bd9Sstevel@tonic-gate 	}
45377c478bd9Sstevel@tonic-gate 
45387c478bd9Sstevel@tonic-gate 	/* don't need to lock dip because attach is single threaded */
45397c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_components) {
45407c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) already has %d\n", pmf,
45417c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), PM_NUMCMPTS(dip)))
45427c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
45437c478bd9Sstevel@tonic-gate 	}
45447c478bd9Sstevel@tonic-gate 	e_pm_create_components(dip, num_components);
45457c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= PMC_BC;
45467c478bd9Sstevel@tonic-gate 	e_pm_default_components(dip, num_components);
45477c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
45487c478bd9Sstevel@tonic-gate }
45497c478bd9Sstevel@tonic-gate 
45507c478bd9Sstevel@tonic-gate /*
45517c478bd9Sstevel@tonic-gate  * Obsolete interface previously called by drivers to destroy their components
45527c478bd9Sstevel@tonic-gate  * at detach time.  This is now done automatically.  However, we need to keep
45537c478bd9Sstevel@tonic-gate  * this for the old drivers.
45547c478bd9Sstevel@tonic-gate  */
45557c478bd9Sstevel@tonic-gate void
pm_destroy_components(dev_info_t * dip)45567c478bd9Sstevel@tonic-gate pm_destroy_components(dev_info_t *dip)
45577c478bd9Sstevel@tonic-gate {
45587c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_destroy_components")
45597c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
45607c478bd9Sstevel@tonic-gate 
45617c478bd9Sstevel@tonic-gate 	PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf,
45627c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip)))
45637c478bd9Sstevel@tonic-gate 	ASSERT(DEVI_IS_DETACHING(dip));
45647c478bd9Sstevel@tonic-gate #ifdef DEBUG
45657c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip))
45667c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "!driver exporting pm-components property "
45677c478bd9Sstevel@tonic-gate 		    "(%s@%s) calls pm_destroy_components", PM_NAME(dip),
45687c478bd9Sstevel@tonic-gate 		    PM_ADDR(dip));
45697c478bd9Sstevel@tonic-gate #endif
45707c478bd9Sstevel@tonic-gate 	/*
45717c478bd9Sstevel@tonic-gate 	 * We ignore this unless this is an old-style driver, except for
45727c478bd9Sstevel@tonic-gate 	 * printing the message above
45737c478bd9Sstevel@tonic-gate 	 */
45747c478bd9Sstevel@tonic-gate 	if (PM_NUMCMPTS(dip) == 0 || !PM_ISBC(dip)) {
45757c478bd9Sstevel@tonic-gate 		PMD(PMD_REMDEV, ("%s: ignore %s@%s(%s#%d)\n", pmf,
45767c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
45777c478bd9Sstevel@tonic-gate 		return;
45787c478bd9Sstevel@tonic-gate 	}
45797c478bd9Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip));
45807c478bd9Sstevel@tonic-gate 
45817c478bd9Sstevel@tonic-gate 	/*
45827c478bd9Sstevel@tonic-gate 	 * pm_unmanage will clear info pointer later, after dealing with
45837c478bd9Sstevel@tonic-gate 	 * dependencies
45847c478bd9Sstevel@tonic-gate 	 */
45857c478bd9Sstevel@tonic-gate 	ASSERT(!PM_GET_PM_SCAN(dip));	/* better be gone already */
45867c478bd9Sstevel@tonic-gate 	/*
45877c478bd9Sstevel@tonic-gate 	 * Now adjust parent's kidsupcnt.  We check only comp 0.
45887c478bd9Sstevel@tonic-gate 	 * Parents that get notification are not adjusted because their
45897c478bd9Sstevel@tonic-gate 	 * kidsupcnt is always 0 (or 1 during probe and attach).
45907c478bd9Sstevel@tonic-gate 	 */
45917c478bd9Sstevel@tonic-gate 	if ((PM_CURPOWER(dip, 0) != 0) && pdip && !PM_WANTS_NOTIFICATION(pdip))
45927c478bd9Sstevel@tonic-gate 		pm_rele_power(pdip);
45937c478bd9Sstevel@tonic-gate #ifdef DEBUG
45947c478bd9Sstevel@tonic-gate 	else {
45957c478bd9Sstevel@tonic-gate 		PMD(PMD_KIDSUP, ("%s: kuc stays %s@%s(%s#%d) comps gone\n",
45967c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
45977c478bd9Sstevel@tonic-gate 	}
45987c478bd9Sstevel@tonic-gate #endif
45997c478bd9Sstevel@tonic-gate 	e_pm_destroy_components(dip);
46007c478bd9Sstevel@tonic-gate 	/*
46017c478bd9Sstevel@tonic-gate 	 * Forget we ever knew anything about the components of this  device
46027c478bd9Sstevel@tonic-gate 	 */
46037c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &=
46047c478bd9Sstevel@tonic-gate 	    ~(PMC_BC | PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED);
46057c478bd9Sstevel@tonic-gate }
46067c478bd9Sstevel@tonic-gate 
46077c478bd9Sstevel@tonic-gate /*
46087c478bd9Sstevel@tonic-gate  * Exported interface for a driver to set a component busy.
46097c478bd9Sstevel@tonic-gate  */
46107c478bd9Sstevel@tonic-gate int
pm_busy_component(dev_info_t * dip,int cmpt)46117c478bd9Sstevel@tonic-gate pm_busy_component(dev_info_t *dip, int cmpt)
46127c478bd9Sstevel@tonic-gate {
46137c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
46147c478bd9Sstevel@tonic-gate 
46157c478bd9Sstevel@tonic-gate 	ASSERT(dip != NULL);
46167c478bd9Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp))
46177c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
46187c478bd9Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
46197c478bd9Sstevel@tonic-gate 	cp->pmc_busycount++;
46207c478bd9Sstevel@tonic-gate 	cp->pmc_timestamp = 0;
46217c478bd9Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
46227c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
46237c478bd9Sstevel@tonic-gate }
46247c478bd9Sstevel@tonic-gate 
46257c478bd9Sstevel@tonic-gate /*
46267c478bd9Sstevel@tonic-gate  * Exported interface for a driver to set a component idle.
46277c478bd9Sstevel@tonic-gate  */
46287c478bd9Sstevel@tonic-gate int
pm_idle_component(dev_info_t * dip,int cmpt)46297c478bd9Sstevel@tonic-gate pm_idle_component(dev_info_t *dip, int cmpt)
46307c478bd9Sstevel@tonic-gate {
46317c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_idle_component")
46327c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
46337c478bd9Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
46347c478bd9Sstevel@tonic-gate 
46357c478bd9Sstevel@tonic-gate 	if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp))
46367c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
46377c478bd9Sstevel@tonic-gate 
46387c478bd9Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
46397c478bd9Sstevel@tonic-gate 	if (cp->pmc_busycount) {
46407c478bd9Sstevel@tonic-gate 		if (--(cp->pmc_busycount) == 0)
46417c478bd9Sstevel@tonic-gate 			cp->pmc_timestamp = gethrestime_sec();
46427c478bd9Sstevel@tonic-gate 	} else {
46437c478bd9Sstevel@tonic-gate 		cp->pmc_timestamp = gethrestime_sec();
46447c478bd9Sstevel@tonic-gate 	}
46457c478bd9Sstevel@tonic-gate 
46467c478bd9Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
46477c478bd9Sstevel@tonic-gate 
46487c478bd9Sstevel@tonic-gate 	/*
46497c478bd9Sstevel@tonic-gate 	 * if device becomes idle during idle down period, try scan it down
46507c478bd9Sstevel@tonic-gate 	 */
46517c478bd9Sstevel@tonic-gate 	if (scanp && PM_IS_PID(dip)) {
46527c478bd9Sstevel@tonic-gate 		PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d) idle.\n", pmf,
46537c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
46547c478bd9Sstevel@tonic-gate 		pm_rescan(dip);
46557c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
46567c478bd9Sstevel@tonic-gate 	}
46577c478bd9Sstevel@tonic-gate 
46587c478bd9Sstevel@tonic-gate 	/*
46597c478bd9Sstevel@tonic-gate 	 * handle scan not running with nexus threshold == 0
46607c478bd9Sstevel@tonic-gate 	 */
46617c478bd9Sstevel@tonic-gate 
46627c478bd9Sstevel@tonic-gate 	if (PM_IS_NEXUS(dip) && (cp->pmc_busycount == 0)) {
46637c478bd9Sstevel@tonic-gate 		pm_rescan(dip);
46647c478bd9Sstevel@tonic-gate 	}
46657c478bd9Sstevel@tonic-gate 
46667c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
46677c478bd9Sstevel@tonic-gate }
46687c478bd9Sstevel@tonic-gate 
46697c478bd9Sstevel@tonic-gate /*
46707c478bd9Sstevel@tonic-gate  * This is the old  obsolete interface called by drivers to set their normal
46717c478bd9Sstevel@tonic-gate  * power.  Thus we can't fix its behavior or return a value.
46727c478bd9Sstevel@tonic-gate  * This functionality is replaced by the pm-component property.
46737c478bd9Sstevel@tonic-gate  * We'll only get components destroyed while no power management is
46747c478bd9Sstevel@tonic-gate  * going on (and the device is detached), so we don't need a mutex here
46757c478bd9Sstevel@tonic-gate  */
46767c478bd9Sstevel@tonic-gate void
pm_set_normal_power(dev_info_t * dip,int comp,int level)46777c478bd9Sstevel@tonic-gate pm_set_normal_power(dev_info_t *dip, int comp, int level)
46787c478bd9Sstevel@tonic-gate {
46797c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_normal_power")
46807c478bd9Sstevel@tonic-gate #ifdef DEBUG
46817c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip))
46827c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "!call to pm_set_normal_power() by %s@%s "
46837c478bd9Sstevel@tonic-gate 		    "(driver exporting pm-components property) ignored",
46847c478bd9Sstevel@tonic-gate 		    PM_NAME(dip), PM_ADDR(dip));
46857c478bd9Sstevel@tonic-gate #endif
46867c478bd9Sstevel@tonic-gate 	if (PM_ISBC(dip)) {
46877c478bd9Sstevel@tonic-gate 		PMD(PMD_NORM, ("%s: %s@%s(%s#%d) set normal power comp=%d, "
46887c478bd9Sstevel@tonic-gate 		    "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
46897c478bd9Sstevel@tonic-gate 		e_pm_set_max_power(dip, comp, level);
46907c478bd9Sstevel@tonic-gate 		e_pm_default_levels(dip, PM_CP(dip, comp), level);
46917c478bd9Sstevel@tonic-gate 	}
46927c478bd9Sstevel@tonic-gate }
46937c478bd9Sstevel@tonic-gate 
46947c478bd9Sstevel@tonic-gate /*
46957c478bd9Sstevel@tonic-gate  * Called on a successfully detached driver to free pm resources
46967c478bd9Sstevel@tonic-gate  */
46977c478bd9Sstevel@tonic-gate static void
pm_stop(dev_info_t * dip)46987c478bd9Sstevel@tonic-gate pm_stop(dev_info_t *dip)
46997c478bd9Sstevel@tonic-gate {
47007c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "stop")
47017c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
47027c478bd9Sstevel@tonic-gate 
47037c478bd9Sstevel@tonic-gate 	ASSERT(!PM_IAM_LOCKING_DIP(dip));
47047c478bd9Sstevel@tonic-gate 	/* stopping scan, destroy scan data structure */
47057c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
47067c478bd9Sstevel@tonic-gate 		pm_scan_stop(dip);
47077c478bd9Sstevel@tonic-gate 		pm_scan_fini(dip);
47087c478bd9Sstevel@tonic-gate 	}
47097c478bd9Sstevel@tonic-gate 
47107c478bd9Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) != NULL) {
47117c478bd9Sstevel@tonic-gate 		if (pm_unmanage(dip) == DDI_SUCCESS) {
47127c478bd9Sstevel@tonic-gate 			/*
47137c478bd9Sstevel@tonic-gate 			 * Old style driver may have called
47147c478bd9Sstevel@tonic-gate 			 * pm_destroy_components already, but just in case ...
47157c478bd9Sstevel@tonic-gate 			 */
47167c478bd9Sstevel@tonic-gate 			e_pm_destroy_components(dip);
47177c478bd9Sstevel@tonic-gate 		} else {
47187c478bd9Sstevel@tonic-gate 			PMD(PMD_FAIL, ("%s: can't pm_unmanage %s@%s(%s#%d)\n",
47197c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
47207c478bd9Sstevel@tonic-gate 		}
47217c478bd9Sstevel@tonic-gate 	} else {
47227c478bd9Sstevel@tonic-gate 		if (PM_NUMCMPTS(dip))
47237c478bd9Sstevel@tonic-gate 			e_pm_destroy_components(dip);
47247c478bd9Sstevel@tonic-gate 		else {
47257c478bd9Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_flags & PMC_NOPMKID) {
47267c478bd9Sstevel@tonic-gate 				DEVI(dip)->devi_pm_flags &= ~PMC_NOPMKID;
47277c478bd9Sstevel@tonic-gate 				if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
47287c478bd9Sstevel@tonic-gate 					pm_rele_power(pdip);
47295e3986cbScth 				} else if (pdip &&
47305e3986cbScth 				    MDI_VHCI(pdip) && MDI_CLIENT(dip)) {
47317c478bd9Sstevel@tonic-gate 					(void) mdi_power(pdip,
47327c478bd9Sstevel@tonic-gate 					    MDI_PM_RELE_POWER,
47337c478bd9Sstevel@tonic-gate 					    (void *)dip, NULL, 0);
47347c478bd9Sstevel@tonic-gate 				}
47357c478bd9Sstevel@tonic-gate 			}
47367c478bd9Sstevel@tonic-gate 		}
47377c478bd9Sstevel@tonic-gate 	}
47387c478bd9Sstevel@tonic-gate }
47397c478bd9Sstevel@tonic-gate 
47407c478bd9Sstevel@tonic-gate /*
47417c478bd9Sstevel@tonic-gate  * The node is the subject of a reparse pm props ioctl. Throw away the old
47427c478bd9Sstevel@tonic-gate  * info and start over.
47437c478bd9Sstevel@tonic-gate  */
47447c478bd9Sstevel@tonic-gate int
e_new_pm_props(dev_info_t * dip)47457c478bd9Sstevel@tonic-gate e_new_pm_props(dev_info_t *dip)
47467c478bd9Sstevel@tonic-gate {
47477c478bd9Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) != NULL) {
47487c478bd9Sstevel@tonic-gate 		pm_stop(dip);
47497c478bd9Sstevel@tonic-gate 
47507c478bd9Sstevel@tonic-gate 		if (e_pm_manage(dip, PM_STYLE_NEW) != DDI_SUCCESS) {
47517c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
47527c478bd9Sstevel@tonic-gate 		}
47537c478bd9Sstevel@tonic-gate 	}
47547c478bd9Sstevel@tonic-gate 	e_pm_props(dip);
47557c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
47567c478bd9Sstevel@tonic-gate }
47577c478bd9Sstevel@tonic-gate 
47587c478bd9Sstevel@tonic-gate /*
47597c478bd9Sstevel@tonic-gate  * Device has been attached, so process its pm properties
47607c478bd9Sstevel@tonic-gate  */
47617c478bd9Sstevel@tonic-gate void
e_pm_props(dev_info_t * dip)47627c478bd9Sstevel@tonic-gate e_pm_props(dev_info_t *dip)
47637c478bd9Sstevel@tonic-gate {
47647c478bd9Sstevel@tonic-gate 	char *pp;
47657c478bd9Sstevel@tonic-gate 	int len;
47667c478bd9Sstevel@tonic-gate 	int flags = 0;
47677c478bd9Sstevel@tonic-gate 	int propflag = DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP;
47687c478bd9Sstevel@tonic-gate 
47697c478bd9Sstevel@tonic-gate 	/*
47707c478bd9Sstevel@tonic-gate 	 * It doesn't matter if we do this more than once, we should always
47717c478bd9Sstevel@tonic-gate 	 * get the same answers, and if not, then the last one in is the
47727c478bd9Sstevel@tonic-gate 	 * best one.
47737c478bd9Sstevel@tonic-gate 	 */
47747c478bd9Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-hardware-state",
47757c478bd9Sstevel@tonic-gate 	    (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) {
47767c478bd9Sstevel@tonic-gate 		if (strcmp(pp, "needs-suspend-resume") == 0) {
47777c478bd9Sstevel@tonic-gate 			flags = PMC_NEEDS_SR;
47787c478bd9Sstevel@tonic-gate 		} else if (strcmp(pp, "no-suspend-resume") == 0) {
47797c478bd9Sstevel@tonic-gate 			flags = PMC_NO_SR;
47807c478bd9Sstevel@tonic-gate 		} else if (strcmp(pp, "parental-suspend-resume") == 0) {
47817c478bd9Sstevel@tonic-gate 			flags = PMC_PARENTAL_SR;
47827c478bd9Sstevel@tonic-gate 		} else {
47837c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!device %s@%s has unrecognized "
47847c478bd9Sstevel@tonic-gate 			    "%s property value '%s'", PM_NAME(dip),
47857c478bd9Sstevel@tonic-gate 			    PM_ADDR(dip), "pm-hardware-state", pp);
47867c478bd9Sstevel@tonic-gate 		}
47877c478bd9Sstevel@tonic-gate 		kmem_free(pp, len);
47887c478bd9Sstevel@tonic-gate 	}
47897c478bd9Sstevel@tonic-gate 	/*
47907c478bd9Sstevel@tonic-gate 	 * This next segment (PMC_WANTS_NOTIFY) is in
47917c478bd9Sstevel@tonic-gate 	 * support of nexus drivers which will want to be involved in
47927c478bd9Sstevel@tonic-gate 	 * (or at least notified of) their child node's power level transitions.
47937c478bd9Sstevel@tonic-gate 	 * "pm-want-child-notification?" is defined by the parent.
47947c478bd9Sstevel@tonic-gate 	 */
47957c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag,
47967c478bd9Sstevel@tonic-gate 	    "pm-want-child-notification?") && PM_HAS_BUS_POWER(dip))
47977c478bd9Sstevel@tonic-gate 		flags |= PMC_WANTS_NOTIFY;
47987c478bd9Sstevel@tonic-gate 	ASSERT(PM_HAS_BUS_POWER(dip) || !ddi_prop_exists(DDI_DEV_T_ANY,
47997c478bd9Sstevel@tonic-gate 	    dip, propflag, "pm-want-child-notification?"));
48007c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag,
48017c478bd9Sstevel@tonic-gate 	    "no-involuntary-power-cycles"))
48027c478bd9Sstevel@tonic-gate 		flags |= PMC_NO_INVOL;
4803c42872d4Smh 	/*
4804c42872d4Smh 	 * Is the device a CPU device?
4805c42872d4Smh 	 */
4806c42872d4Smh 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-class",
4807c42872d4Smh 	    (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) {
4808c42872d4Smh 		if (strcmp(pp, "CPU") == 0) {
4809c42872d4Smh 			flags |= PMC_CPU_DEVICE;
4810c42872d4Smh 		} else {
4811c42872d4Smh 			cmn_err(CE_NOTE, "!device %s@%s has unrecognized "
4812c42872d4Smh 			    "%s property value '%s'", PM_NAME(dip),
48136152d498Smh 			    PM_ADDR(dip), "pm-class", pp);
4814c42872d4Smh 		}
4815c42872d4Smh 		kmem_free(pp, len);
4816c42872d4Smh 	}
48177c478bd9Sstevel@tonic-gate 	/* devfs single threads us */
48187c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= flags;
48197c478bd9Sstevel@tonic-gate }
48207c478bd9Sstevel@tonic-gate 
48217c478bd9Sstevel@tonic-gate /*
48227c478bd9Sstevel@tonic-gate  * This is the DDI_CTLOPS_POWER handler that is used when there is no ppm
48237c478bd9Sstevel@tonic-gate  * driver which has claimed a node.
48247c478bd9Sstevel@tonic-gate  * Sets old_power in arg struct.
48257c478bd9Sstevel@tonic-gate  */
48267c478bd9Sstevel@tonic-gate static int
pm_default_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)48277c478bd9Sstevel@tonic-gate pm_default_ctlops(dev_info_t *dip, dev_info_t *rdip,
48287c478bd9Sstevel@tonic-gate     ddi_ctl_enum_t ctlop, void *arg, void *result)
48297c478bd9Sstevel@tonic-gate {
48307c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
48317c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "ctlops")
48327c478bd9Sstevel@tonic-gate 	power_req_t *reqp = (power_req_t *)arg;
48337c478bd9Sstevel@tonic-gate 	int retval;
48347c478bd9Sstevel@tonic-gate 	dev_info_t *target_dip;
48357c478bd9Sstevel@tonic-gate 	int new_level, old_level, cmpt;
48369681b4a1Skchow #ifdef PMDDEBUG
48377c478bd9Sstevel@tonic-gate 	char *format;
48387c478bd9Sstevel@tonic-gate #endif
48397c478bd9Sstevel@tonic-gate 
48407c478bd9Sstevel@tonic-gate 	/*
48417c478bd9Sstevel@tonic-gate 	 * The interface for doing the actual power level changes is now
48427c478bd9Sstevel@tonic-gate 	 * through the DDI_CTLOPS_POWER bus_ctl, so that we can plug in
48437c478bd9Sstevel@tonic-gate 	 * different platform-specific power control drivers.
48447c478bd9Sstevel@tonic-gate 	 *
48457c478bd9Sstevel@tonic-gate 	 * This driver implements the "default" version of this interface.
48467c478bd9Sstevel@tonic-gate 	 * If no ppm driver has been installed then this interface is called
48477c478bd9Sstevel@tonic-gate 	 * instead.
48487c478bd9Sstevel@tonic-gate 	 */
48497c478bd9Sstevel@tonic-gate 	ASSERT(dip == NULL);
48507c478bd9Sstevel@tonic-gate 	switch (ctlop) {
48517c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_POWER:
48527c478bd9Sstevel@tonic-gate 		switch (reqp->request_type) {
48537c478bd9Sstevel@tonic-gate 		case PMR_PPM_SET_POWER:
48547c478bd9Sstevel@tonic-gate 		{
48557c478bd9Sstevel@tonic-gate 			target_dip = reqp->req.ppm_set_power_req.who;
48567c478bd9Sstevel@tonic-gate 			ASSERT(target_dip == rdip);
48577c478bd9Sstevel@tonic-gate 			new_level = reqp->req.ppm_set_power_req.new_level;
48587c478bd9Sstevel@tonic-gate 			cmpt = reqp->req.ppm_set_power_req.cmpt;
48597c478bd9Sstevel@tonic-gate 			/* pass back old power for the PM_LEVEL_UNKNOWN case */
48607c478bd9Sstevel@tonic-gate 			old_level = PM_CURPOWER(target_dip, cmpt);
48617c478bd9Sstevel@tonic-gate 			reqp->req.ppm_set_power_req.old_level = old_level;
48627c478bd9Sstevel@tonic-gate 			retval = pm_power(target_dip, cmpt, new_level);
48637c478bd9Sstevel@tonic-gate 			PMD(PMD_PPM, ("%s: PPM_SET_POWER %s@%s(%s#%d)[%d] %d->"
48647c478bd9Sstevel@tonic-gate 			    "%d %s\n", pmf, PM_DEVICE(target_dip), cmpt,
48657c478bd9Sstevel@tonic-gate 			    old_level, new_level, (retval == DDI_SUCCESS ?
48667c478bd9Sstevel@tonic-gate 			    "chd" : "no chg")))
48677c478bd9Sstevel@tonic-gate 			return (retval);
48687c478bd9Sstevel@tonic-gate 		}
48697c478bd9Sstevel@tonic-gate 
48707c478bd9Sstevel@tonic-gate 		case PMR_PPM_PRE_DETACH:
48717c478bd9Sstevel@tonic-gate 		case PMR_PPM_POST_DETACH:
48727c478bd9Sstevel@tonic-gate 		case PMR_PPM_PRE_ATTACH:
48737c478bd9Sstevel@tonic-gate 		case PMR_PPM_POST_ATTACH:
48747c478bd9Sstevel@tonic-gate 		case PMR_PPM_PRE_PROBE:
48757c478bd9Sstevel@tonic-gate 		case PMR_PPM_POST_PROBE:
48767c478bd9Sstevel@tonic-gate 		case PMR_PPM_PRE_RESUME:
48777c478bd9Sstevel@tonic-gate 		case PMR_PPM_INIT_CHILD:
48787c478bd9Sstevel@tonic-gate 		case PMR_PPM_UNINIT_CHILD:
48799681b4a1Skchow #ifdef PMDDEBUG
48807c478bd9Sstevel@tonic-gate 			switch (reqp->request_type) {
48817c478bd9Sstevel@tonic-gate 				case PMR_PPM_PRE_DETACH:
48827c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_DETACH "
48837c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
48847c478bd9Sstevel@tonic-gate 					break;
48857c478bd9Sstevel@tonic-gate 				case PMR_PPM_POST_DETACH:
48867c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_POST_DETACH "
48877c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
48887c478bd9Sstevel@tonic-gate 					break;
48897c478bd9Sstevel@tonic-gate 				case PMR_PPM_PRE_ATTACH:
48907c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_ATTACH "
48917c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
48927c478bd9Sstevel@tonic-gate 					break;
48937c478bd9Sstevel@tonic-gate 				case PMR_PPM_POST_ATTACH:
48947c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_POST_ATTACH "
48957c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
48967c478bd9Sstevel@tonic-gate 					break;
48977c478bd9Sstevel@tonic-gate 				case PMR_PPM_PRE_PROBE:
48987c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_PROBE "
48997c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
49007c478bd9Sstevel@tonic-gate 					break;
49017c478bd9Sstevel@tonic-gate 				case PMR_PPM_POST_PROBE:
49027c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_POST_PROBE "
49037c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
49047c478bd9Sstevel@tonic-gate 					break;
49057c478bd9Sstevel@tonic-gate 				case PMR_PPM_PRE_RESUME:
49067c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_PRE_RESUME "
49077c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d) rets %d\n";
49087c478bd9Sstevel@tonic-gate 					break;
49097c478bd9Sstevel@tonic-gate 				case PMR_PPM_INIT_CHILD:
49107c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_INIT_CHILD "
49117c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
49127c478bd9Sstevel@tonic-gate 					break;
49137c478bd9Sstevel@tonic-gate 				case PMR_PPM_UNINIT_CHILD:
49147c478bd9Sstevel@tonic-gate 					format = "%s: PMR_PPM_UNINIT_CHILD "
49157c478bd9Sstevel@tonic-gate 					    "%s@%s(%s#%d)\n";
49167c478bd9Sstevel@tonic-gate 					break;
49177c478bd9Sstevel@tonic-gate 				default:
49187c478bd9Sstevel@tonic-gate 					break;
49197c478bd9Sstevel@tonic-gate 			}
49207c478bd9Sstevel@tonic-gate 			PMD(PMD_PPM, (format, pmf, PM_DEVICE(rdip),
49217c478bd9Sstevel@tonic-gate 			    reqp->req.ppm_config_req.result))
49227c478bd9Sstevel@tonic-gate #endif
49237c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49247c478bd9Sstevel@tonic-gate 
49257c478bd9Sstevel@tonic-gate 		case PMR_PPM_POWER_CHANGE_NOTIFY:
49267c478bd9Sstevel@tonic-gate 			/*
49277c478bd9Sstevel@tonic-gate 			 * Nothing for us to do
49287c478bd9Sstevel@tonic-gate 			 */
49297c478bd9Sstevel@tonic-gate 			ASSERT(reqp->req.ppm_notify_level_req.who == rdip);
49307c478bd9Sstevel@tonic-gate 			PMD(PMD_PPM, ("%s: PMR_PPM_POWER_CHANGE_NOTIFY "
49317c478bd9Sstevel@tonic-gate 			    "%s@%s(%s#%d)[%d] %d->%d\n", pmf,
49327c478bd9Sstevel@tonic-gate 			    PM_DEVICE(reqp->req.ppm_notify_level_req.who),
49337c478bd9Sstevel@tonic-gate 			    reqp->req.ppm_notify_level_req.cmpt,
49347c478bd9Sstevel@tonic-gate 			    PM_CURPOWER(reqp->req.ppm_notify_level_req.who,
49357c478bd9Sstevel@tonic-gate 			    reqp->req.ppm_notify_level_req.cmpt),
49367c478bd9Sstevel@tonic-gate 			    reqp->req.ppm_notify_level_req.new_level))
49377c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49387c478bd9Sstevel@tonic-gate 
49397c478bd9Sstevel@tonic-gate 		case PMR_PPM_UNMANAGE:
49407c478bd9Sstevel@tonic-gate 			PMD(PMD_PPM, ("%s: PMR_PPM_UNMANAGE %s@%s(%s#%d)\n",
49417c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(rdip)))
49427c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49437c478bd9Sstevel@tonic-gate 
49447c478bd9Sstevel@tonic-gate 		case PMR_PPM_LOCK_POWER:
49453fe80ca4SDan Cross 			pm_lock_power_single(reqp->req.ppm_lock_power_req.who);
49467c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49477c478bd9Sstevel@tonic-gate 
49487c478bd9Sstevel@tonic-gate 		case PMR_PPM_UNLOCK_POWER:
49497c478bd9Sstevel@tonic-gate 			pm_unlock_power_single(
49503fe80ca4SDan Cross 			    reqp->req.ppm_unlock_power_req.who);
49517c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49527c478bd9Sstevel@tonic-gate 
49537c478bd9Sstevel@tonic-gate 		case PMR_PPM_TRY_LOCK_POWER:
49547c478bd9Sstevel@tonic-gate 			*(int *)result = pm_try_locking_power_single(
49553fe80ca4SDan Cross 			    reqp->req.ppm_lock_power_req.who);
49567c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49577c478bd9Sstevel@tonic-gate 
49587c478bd9Sstevel@tonic-gate 		case PMR_PPM_POWER_LOCK_OWNER:
49597c478bd9Sstevel@tonic-gate 			target_dip = reqp->req.ppm_power_lock_owner_req.who;
49607c478bd9Sstevel@tonic-gate 			ASSERT(target_dip == rdip);
49617c478bd9Sstevel@tonic-gate 			reqp->req.ppm_power_lock_owner_req.owner =
49627c478bd9Sstevel@tonic-gate 			    DEVI(rdip)->devi_busy_thread;
49637c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
49647c478bd9Sstevel@tonic-gate 		default:
49657c478bd9Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: default!\n", pmf))
49667c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
49677c478bd9Sstevel@tonic-gate 		}
49687c478bd9Sstevel@tonic-gate 
49697c478bd9Sstevel@tonic-gate 	default:
49707c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: unknown\n", pmf))
49717c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
49727c478bd9Sstevel@tonic-gate 	}
49737c478bd9Sstevel@tonic-gate }
49747c478bd9Sstevel@tonic-gate 
49757c478bd9Sstevel@tonic-gate /*
49767c478bd9Sstevel@tonic-gate  * We overload the bus_ctl ops here--perhaps we ought to have a distinct
49777c478bd9Sstevel@tonic-gate  * power_ops struct for this functionality instead?
49787c478bd9Sstevel@tonic-gate  * However, we only ever do this on a ppm driver.
49797c478bd9Sstevel@tonic-gate  */
49807c478bd9Sstevel@tonic-gate int
pm_ctlops(dev_info_t * d,dev_info_t * r,ddi_ctl_enum_t op,void * a,void * v)49817c478bd9Sstevel@tonic-gate pm_ctlops(dev_info_t *d, dev_info_t *r, ddi_ctl_enum_t op, void *a, void *v)
49827c478bd9Sstevel@tonic-gate {
49837c478bd9Sstevel@tonic-gate 	int (*fp)();
49847c478bd9Sstevel@tonic-gate 
49857c478bd9Sstevel@tonic-gate 	/* if no ppm handler, call the default routine */
49867c478bd9Sstevel@tonic-gate 	if (d == NULL) {
49877c478bd9Sstevel@tonic-gate 		return (pm_default_ctlops(d, r, op, a, v));
49887c478bd9Sstevel@tonic-gate 	}
49897c478bd9Sstevel@tonic-gate 	if (!d || !r)
49907c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
49917c478bd9Sstevel@tonic-gate 	ASSERT(DEVI(d)->devi_ops && DEVI(d)->devi_ops->devo_bus_ops &&
49926152d498Smh 	    DEVI(d)->devi_ops->devo_bus_ops->bus_ctl);
49937c478bd9Sstevel@tonic-gate 
49947c478bd9Sstevel@tonic-gate 	fp = DEVI(d)->devi_ops->devo_bus_ops->bus_ctl;
49957c478bd9Sstevel@tonic-gate 	return ((*fp)(d, r, op, a, v));
49967c478bd9Sstevel@tonic-gate }
49977c478bd9Sstevel@tonic-gate 
49987c478bd9Sstevel@tonic-gate /*
49997c478bd9Sstevel@tonic-gate  * Called on a node when attach completes or the driver makes its first pm
50007c478bd9Sstevel@tonic-gate  * call (whichever comes first).
50017c478bd9Sstevel@tonic-gate  * In the attach case, device may not be power manageable at all.
50027c478bd9Sstevel@tonic-gate  * Don't need to lock the dip because we're single threaded by the devfs code
50037c478bd9Sstevel@tonic-gate  */
50047c478bd9Sstevel@tonic-gate static int
pm_start(dev_info_t * dip)50057c478bd9Sstevel@tonic-gate pm_start(dev_info_t *dip)
50067c478bd9Sstevel@tonic-gate {
50077c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "start")
50087c478bd9Sstevel@tonic-gate 	int ret;
50097c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
50107c478bd9Sstevel@tonic-gate 	int e_pm_manage(dev_info_t *, int);
50117c478bd9Sstevel@tonic-gate 	void pm_noinvol_specd(dev_info_t *dip);
50127c478bd9Sstevel@tonic-gate 
50137c478bd9Sstevel@tonic-gate 	e_pm_props(dip);
50147c478bd9Sstevel@tonic-gate 	pm_noinvol_specd(dip);
50157c478bd9Sstevel@tonic-gate 	/*
50167c478bd9Sstevel@tonic-gate 	 * If this dip has already been processed, don't mess with it
50177c478bd9Sstevel@tonic-gate 	 * (but decrement the speculative count we did above, as whatever
50187c478bd9Sstevel@tonic-gate 	 * code put it under pm already will have dealt with it)
50197c478bd9Sstevel@tonic-gate 	 */
50207c478bd9Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip)) {
50217c478bd9Sstevel@tonic-gate 		PMD(PMD_KIDSUP, ("%s: pm already done for %s@%s(%s#%d)\n",
50227c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip)))
50237c478bd9Sstevel@tonic-gate 		return (0);
50247c478bd9Sstevel@tonic-gate 	}
50257c478bd9Sstevel@tonic-gate 	ret = e_pm_manage(dip, PM_STYLE_UNKNOWN);
50267c478bd9Sstevel@tonic-gate 
50277c478bd9Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) == NULL) {
50287c478bd9Sstevel@tonic-gate 		/*
50297c478bd9Sstevel@tonic-gate 		 * keep the kidsupcount increment as is
50307c478bd9Sstevel@tonic-gate 		 */
50317c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags |= PMC_NOPMKID;
50327c478bd9Sstevel@tonic-gate 		if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
50337c478bd9Sstevel@tonic-gate 			pm_hold_power(pdip);
50345e3986cbScth 		} else if (pdip && MDI_VHCI(pdip) && MDI_CLIENT(dip)) {
50357c478bd9Sstevel@tonic-gate 			(void) mdi_power(pdip, MDI_PM_HOLD_POWER,
50367c478bd9Sstevel@tonic-gate 			    (void *)dip, NULL, 0);
50377c478bd9Sstevel@tonic-gate 		}
50387c478bd9Sstevel@tonic-gate 
50397c478bd9Sstevel@tonic-gate 		PMD(PMD_KIDSUP, ("%s: pm of %s@%s(%s#%d) failed, parent "
50407c478bd9Sstevel@tonic-gate 		    "left up\n", pmf, PM_DEVICE(dip)))
50417c478bd9Sstevel@tonic-gate 	}
50427c478bd9Sstevel@tonic-gate 
50437c478bd9Sstevel@tonic-gate 	return (ret);
50447c478bd9Sstevel@tonic-gate }
50457c478bd9Sstevel@tonic-gate 
50467c478bd9Sstevel@tonic-gate /*
50477c478bd9Sstevel@tonic-gate  * Keep a list of recorded thresholds.  For now we just keep a list and
50487c478bd9Sstevel@tonic-gate  * search it linearly.  We don't expect too many entries.  Can always hash it
50497c478bd9Sstevel@tonic-gate  * later if we need to.
50507c478bd9Sstevel@tonic-gate  */
50517c478bd9Sstevel@tonic-gate void
pm_record_thresh(pm_thresh_rec_t * rp)50527c478bd9Sstevel@tonic-gate pm_record_thresh(pm_thresh_rec_t *rp)
50537c478bd9Sstevel@tonic-gate {
50547c478bd9Sstevel@tonic-gate 	pm_thresh_rec_t *pptr, *ptr;
50557c478bd9Sstevel@tonic-gate 
50567c478bd9Sstevel@tonic-gate 	ASSERT(*rp->ptr_physpath);
50577c478bd9Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_WRITER);
50587c478bd9Sstevel@tonic-gate 	for (pptr = NULL, ptr = pm_thresh_head;
50597c478bd9Sstevel@tonic-gate 	    ptr; pptr = ptr,  ptr = ptr->ptr_next) {
50607c478bd9Sstevel@tonic-gate 		if (strcmp(rp->ptr_physpath, ptr->ptr_physpath) == 0) {
50617c478bd9Sstevel@tonic-gate 			/* replace this one */
50627c478bd9Sstevel@tonic-gate 			rp->ptr_next = ptr->ptr_next;
50637c478bd9Sstevel@tonic-gate 			if (pptr) {
50647c478bd9Sstevel@tonic-gate 				pptr->ptr_next = rp;
50657c478bd9Sstevel@tonic-gate 			} else {
50667c478bd9Sstevel@tonic-gate 				pm_thresh_head = rp;
50677c478bd9Sstevel@tonic-gate 			}
50687c478bd9Sstevel@tonic-gate 			rw_exit(&pm_thresh_rwlock);
50697c478bd9Sstevel@tonic-gate 			kmem_free(ptr, ptr->ptr_size);
50707c478bd9Sstevel@tonic-gate 			return;
50717c478bd9Sstevel@tonic-gate 		}
50727c478bd9Sstevel@tonic-gate 		continue;
50737c478bd9Sstevel@tonic-gate 	}
50747c478bd9Sstevel@tonic-gate 	/*
50757c478bd9Sstevel@tonic-gate 	 * There was not a match in the list, insert this one in front
50767c478bd9Sstevel@tonic-gate 	 */
50777c478bd9Sstevel@tonic-gate 	if (pm_thresh_head) {
50787c478bd9Sstevel@tonic-gate 		rp->ptr_next = pm_thresh_head;
50797c478bd9Sstevel@tonic-gate 		pm_thresh_head = rp;
50807c478bd9Sstevel@tonic-gate 	} else {
50817c478bd9Sstevel@tonic-gate 		rp->ptr_next = NULL;
50827c478bd9Sstevel@tonic-gate 		pm_thresh_head = rp;
50837c478bd9Sstevel@tonic-gate 	}
50847c478bd9Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
50857c478bd9Sstevel@tonic-gate }
50867c478bd9Sstevel@tonic-gate 
50877c478bd9Sstevel@tonic-gate /*
50887c478bd9Sstevel@tonic-gate  * Create a new dependency record and hang a new dependency entry off of it
50897c478bd9Sstevel@tonic-gate  */
50907c478bd9Sstevel@tonic-gate pm_pdr_t *
newpdr(char * kept,char * keeps,int isprop)50917c478bd9Sstevel@tonic-gate newpdr(char *kept, char *keeps, int isprop)
50927c478bd9Sstevel@tonic-gate {
50937c478bd9Sstevel@tonic-gate 	size_t size = strlen(kept) + strlen(keeps) + 2 + sizeof (pm_pdr_t);
50947c478bd9Sstevel@tonic-gate 	pm_pdr_t *p = kmem_zalloc(size, KM_SLEEP);
50957c478bd9Sstevel@tonic-gate 	p->pdr_size = size;
50967c478bd9Sstevel@tonic-gate 	p->pdr_isprop = isprop;
50977c478bd9Sstevel@tonic-gate 	p->pdr_kept_paths = NULL;
50987c478bd9Sstevel@tonic-gate 	p->pdr_kept_count = 0;
50997c478bd9Sstevel@tonic-gate 	p->pdr_kept = (char *)((intptr_t)p + sizeof (pm_pdr_t));
51007c478bd9Sstevel@tonic-gate 	(void) strcpy(p->pdr_kept, kept);
51017c478bd9Sstevel@tonic-gate 	p->pdr_keeper = (char *)((intptr_t)p->pdr_kept + strlen(kept) + 1);
51027c478bd9Sstevel@tonic-gate 	(void) strcpy(p->pdr_keeper, keeps);
51037c478bd9Sstevel@tonic-gate 	ASSERT((intptr_t)p->pdr_keeper + strlen(p->pdr_keeper) + 1 <=
51047c478bd9Sstevel@tonic-gate 	    (intptr_t)p + size);
51057c478bd9Sstevel@tonic-gate 	ASSERT((intptr_t)p->pdr_kept + strlen(p->pdr_kept) + 1 <=
51067c478bd9Sstevel@tonic-gate 	    (intptr_t)p + size);
51077c478bd9Sstevel@tonic-gate 	return (p);
51087c478bd9Sstevel@tonic-gate }
51097c478bd9Sstevel@tonic-gate 
51107c478bd9Sstevel@tonic-gate /*
51117c478bd9Sstevel@tonic-gate  * Keep a list of recorded dependencies.  We only keep the
51127c478bd9Sstevel@tonic-gate  * keeper -> kept list for simplification. At this point We do not
51137c478bd9Sstevel@tonic-gate  * care about whether the devices are attached or not yet,
51147c478bd9Sstevel@tonic-gate  * this would be done in pm_keeper() and pm_kept().
51157c478bd9Sstevel@tonic-gate  * If a PM_RESET_PM happens, then we tear down and forget the dependencies,
51167c478bd9Sstevel@tonic-gate  * and it is up to the user to issue the ioctl again if they want it
51177c478bd9Sstevel@tonic-gate  * (e.g. pmconfig)
51187c478bd9Sstevel@tonic-gate  * Returns true if dependency already exists in the list.
51197c478bd9Sstevel@tonic-gate  */
51207c478bd9Sstevel@tonic-gate int
pm_record_keeper(char * kept,char * keeper,int isprop)51217c478bd9Sstevel@tonic-gate pm_record_keeper(char *kept, char *keeper, int isprop)
51227c478bd9Sstevel@tonic-gate {
51237c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "record_keeper")
51247c478bd9Sstevel@tonic-gate 	pm_pdr_t *npdr, *ppdr, *pdr;
51257c478bd9Sstevel@tonic-gate 
51267c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s, %s\n", pmf, kept, keeper))
51277c478bd9Sstevel@tonic-gate 	ASSERT(kept && keeper);
51287c478bd9Sstevel@tonic-gate #ifdef DEBUG
51297c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
51307c478bd9Sstevel@tonic-gate 		prdeps("pm_record_keeper entry");
51317c478bd9Sstevel@tonic-gate #endif
51327c478bd9Sstevel@tonic-gate 	for (ppdr = NULL, pdr = pm_dep_head; pdr;
51337c478bd9Sstevel@tonic-gate 	    ppdr = pdr, pdr = pdr->pdr_next) {
51347c478bd9Sstevel@tonic-gate 		PMD(PMD_KEEPS, ("%s: check %s, %s\n", pmf, pdr->pdr_kept,
51357c478bd9Sstevel@tonic-gate 		    pdr->pdr_keeper))
51367c478bd9Sstevel@tonic-gate 		if (strcmp(kept, pdr->pdr_kept) == 0 &&
51377c478bd9Sstevel@tonic-gate 		    strcmp(keeper, pdr->pdr_keeper) == 0) {
51387c478bd9Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: match\n", pmf))
51397c478bd9Sstevel@tonic-gate 			return (1);
51407c478bd9Sstevel@tonic-gate 		}
51417c478bd9Sstevel@tonic-gate 	}
51427c478bd9Sstevel@tonic-gate 	/*
51437c478bd9Sstevel@tonic-gate 	 * We did not find any match, so we have to make an entry
51447c478bd9Sstevel@tonic-gate 	 */
51457c478bd9Sstevel@tonic-gate 	npdr = newpdr(kept, keeper, isprop);
51467c478bd9Sstevel@tonic-gate 	if (ppdr) {
51477c478bd9Sstevel@tonic-gate 		ASSERT(ppdr->pdr_next == NULL);
51487c478bd9Sstevel@tonic-gate 		ppdr->pdr_next = npdr;
51497c478bd9Sstevel@tonic-gate 	} else {
51507c478bd9Sstevel@tonic-gate 		ASSERT(pm_dep_head == NULL);
51517c478bd9Sstevel@tonic-gate 		pm_dep_head = npdr;
51527c478bd9Sstevel@tonic-gate 	}
51537c478bd9Sstevel@tonic-gate #ifdef DEBUG
51547c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
51557c478bd9Sstevel@tonic-gate 		prdeps("pm_record_keeper after new record");
51567c478bd9Sstevel@tonic-gate #endif
51577c478bd9Sstevel@tonic-gate 	if (!isprop)
51587c478bd9Sstevel@tonic-gate 		pm_unresolved_deps++;
51597c478bd9Sstevel@tonic-gate 	else
51607c478bd9Sstevel@tonic-gate 		pm_prop_deps++;
51617c478bd9Sstevel@tonic-gate 	return (0);
51627c478bd9Sstevel@tonic-gate }
51637c478bd9Sstevel@tonic-gate 
51647c478bd9Sstevel@tonic-gate /*
51657c478bd9Sstevel@tonic-gate  * Look up this device in the set of devices we've seen ioctls for
51667c478bd9Sstevel@tonic-gate  * to see if we are holding a threshold spec for it.  If so, make it so.
51677c478bd9Sstevel@tonic-gate  * At ioctl time, we were given the physical path of the device.
51687c478bd9Sstevel@tonic-gate  */
51697c478bd9Sstevel@tonic-gate int
pm_thresh_specd(dev_info_t * dip)51707c478bd9Sstevel@tonic-gate pm_thresh_specd(dev_info_t *dip)
51717c478bd9Sstevel@tonic-gate {
51727c478bd9Sstevel@tonic-gate 	void pm_apply_recorded_thresh(dev_info_t *, pm_thresh_rec_t *);
51737c478bd9Sstevel@tonic-gate 	char *path = 0;
51747c478bd9Sstevel@tonic-gate 	char pathbuf[MAXNAMELEN];
51757c478bd9Sstevel@tonic-gate 	pm_thresh_rec_t *rp;
51767c478bd9Sstevel@tonic-gate 
51777c478bd9Sstevel@tonic-gate 	path = ddi_pathname(dip, pathbuf);
51787c478bd9Sstevel@tonic-gate 
51797c478bd9Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_READER);
51807c478bd9Sstevel@tonic-gate 	for (rp = pm_thresh_head; rp; rp = rp->ptr_next) {
51817c478bd9Sstevel@tonic-gate 		if (strcmp(rp->ptr_physpath, path) != 0)
51827c478bd9Sstevel@tonic-gate 			continue;
51837c478bd9Sstevel@tonic-gate 		pm_apply_recorded_thresh(dip, rp);
51847c478bd9Sstevel@tonic-gate 		rw_exit(&pm_thresh_rwlock);
51857c478bd9Sstevel@tonic-gate 		return (1);
51867c478bd9Sstevel@tonic-gate 	}
51877c478bd9Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
51887c478bd9Sstevel@tonic-gate 	return (0);
51897c478bd9Sstevel@tonic-gate }
51907c478bd9Sstevel@tonic-gate 
51917c478bd9Sstevel@tonic-gate static int
pm_set_keeping(dev_info_t * keeper,dev_info_t * kept)51927c478bd9Sstevel@tonic-gate pm_set_keeping(dev_info_t *keeper, dev_info_t *kept)
51937c478bd9Sstevel@tonic-gate {
51947c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "set_keeping")
51953fe80ca4SDan Cross 	int j, up = 0;
51967c478bd9Sstevel@tonic-gate 	void prdeps(char *);
51977c478bd9Sstevel@tonic-gate 
51987c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), kept=%s@%s(%s#%d)\n", pmf,
51997c478bd9Sstevel@tonic-gate 	    PM_DEVICE(keeper), PM_DEVICE(kept)))
52007c478bd9Sstevel@tonic-gate #ifdef DEBUG
52017c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
52027c478bd9Sstevel@tonic-gate 		prdeps("Before PAD\n");
52037c478bd9Sstevel@tonic-gate #endif
52047c478bd9Sstevel@tonic-gate 	ASSERT(keeper != kept);
52057c478bd9Sstevel@tonic-gate 	if (PM_GET_PM_INFO(keeper) == NULL) {
52067c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "!device %s@%s(%s#%d) keeps up device "
520759eb1a4fSBick Torrejon 		    "%s@%s(%s#%d), but the former is not power managed",
52087c478bd9Sstevel@tonic-gate 		    PM_DEVICE(keeper), PM_DEVICE(kept));
52097c478bd9Sstevel@tonic-gate 		PMD((PMD_FAIL | PMD_KEEPS), ("%s: keeper %s@%s(%s#%d) is not"
52107c478bd9Sstevel@tonic-gate 		    "power managed\n", pmf, PM_DEVICE(keeper)))
52117c478bd9Sstevel@tonic-gate 		return (0);
52127c478bd9Sstevel@tonic-gate 	}
5213e5f4d43eSGarrett D'Amore 	if (PM_GET_PM_INFO(kept) == NULL) {
5214e5f4d43eSGarrett D'Amore 		cmn_err(CE_CONT, "!device %s@%s(%s#%d) keeps up device "
5215e5f4d43eSGarrett D'Amore 		    "%s@%s(%s#%d), but the latter is not power managed",
5216e5f4d43eSGarrett D'Amore 		    PM_DEVICE(keeper), PM_DEVICE(kept));
5217e5f4d43eSGarrett D'Amore 		PMD((PMD_FAIL | PMD_KEEPS), ("%s: kept %s@%s(%s#%d) is not"
5218e5f4d43eSGarrett D'Amore 		    "power managed\n", pmf, PM_DEVICE(kept)))
5219e5f4d43eSGarrett D'Amore 		return (0);
5220e5f4d43eSGarrett D'Amore 	}
5221e5f4d43eSGarrett D'Amore 
52223fe80ca4SDan Cross 	PM_LOCK_POWER(keeper);
52237c478bd9Sstevel@tonic-gate 	for (j = 0; j < PM_NUMCMPTS(keeper); j++) {
52247c478bd9Sstevel@tonic-gate 		if (PM_CURPOWER(keeper, j)) {
52257c478bd9Sstevel@tonic-gate 			up++;
52267c478bd9Sstevel@tonic-gate 			break;
52277c478bd9Sstevel@tonic-gate 		}
52287c478bd9Sstevel@tonic-gate 	}
52297c478bd9Sstevel@tonic-gate 	if (up) {
52307c478bd9Sstevel@tonic-gate 		/* Bringup and maintain a hold on the kept */
52317c478bd9Sstevel@tonic-gate 		PMD(PMD_KEEPS, ("%s: place a hold on kept %s@%s(%s#%d)\n", pmf,
52327c478bd9Sstevel@tonic-gate 		    PM_DEVICE(kept)))
52337c478bd9Sstevel@tonic-gate 		bring_pmdep_up(kept, 1);
52347c478bd9Sstevel@tonic-gate 	}
52353fe80ca4SDan Cross 	PM_UNLOCK_POWER(keeper);
52367c478bd9Sstevel@tonic-gate #ifdef DEBUG
52377c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_KEEPS)
52387c478bd9Sstevel@tonic-gate 		prdeps("After PAD\n");
52397c478bd9Sstevel@tonic-gate #endif
52407c478bd9Sstevel@tonic-gate 	return (1);
52417c478bd9Sstevel@tonic-gate }
52427c478bd9Sstevel@tonic-gate 
52437c478bd9Sstevel@tonic-gate /*
52447c478bd9Sstevel@tonic-gate  * Should this device keep up another device?
52457c478bd9Sstevel@tonic-gate  * Look up this device in the set of devices we've seen ioctls for
52467c478bd9Sstevel@tonic-gate  * to see if we are holding a dependency spec for it.  If so, make it so.
52477c478bd9Sstevel@tonic-gate  * Because we require the kept device to be attached already in order to
52487c478bd9Sstevel@tonic-gate  * make the list entry (and hold it), we only need to look for keepers.
52497c478bd9Sstevel@tonic-gate  * At ioctl time, we were given the physical path of the device.
52507c478bd9Sstevel@tonic-gate  */
52517c478bd9Sstevel@tonic-gate int
pm_keeper(char * keeper)52527c478bd9Sstevel@tonic-gate pm_keeper(char *keeper)
52537c478bd9Sstevel@tonic-gate {
52547c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "keeper")
52557c478bd9Sstevel@tonic-gate 	int pm_apply_recorded_dep(dev_info_t *, pm_pdr_t *);
52567c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
52577c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
52587c478bd9Sstevel@tonic-gate 	dev_info_t *kept = NULL;
52597c478bd9Sstevel@tonic-gate 	int ret = 0;
52607c478bd9Sstevel@tonic-gate 	int i;
52617c478bd9Sstevel@tonic-gate 
52627c478bd9Sstevel@tonic-gate 	if (!pm_unresolved_deps && !pm_prop_deps)
52637c478bd9Sstevel@tonic-gate 		return (0);
52647c478bd9Sstevel@tonic-gate 	ASSERT(keeper != NULL);
52657c478bd9Sstevel@tonic-gate 	dip = pm_name_to_dip(keeper, 1);
52667c478bd9Sstevel@tonic-gate 	if (dip == NULL)
52677c478bd9Sstevel@tonic-gate 		return (0);
52687c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: keeper=%s\n", pmf, keeper))
52697c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
52707c478bd9Sstevel@tonic-gate 		if (!dp->pdr_isprop) {
52717c478bd9Sstevel@tonic-gate 			if (!pm_unresolved_deps)
52727c478bd9Sstevel@tonic-gate 				continue;
52737c478bd9Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: keeper %s\n", pmf, dp->pdr_keeper))
52747c478bd9Sstevel@tonic-gate 			if (dp->pdr_satisfied) {
52757c478bd9Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: satisfied\n", pmf))
52767c478bd9Sstevel@tonic-gate 				continue;
52777c478bd9Sstevel@tonic-gate 			}
52787c478bd9Sstevel@tonic-gate 			if (strcmp(dp->pdr_keeper, keeper) == 0) {
52797c478bd9Sstevel@tonic-gate 				ret += pm_apply_recorded_dep(dip, dp);
52807c478bd9Sstevel@tonic-gate 			}
52817c478bd9Sstevel@tonic-gate 		} else {
52827c478bd9Sstevel@tonic-gate 			if (strcmp(dp->pdr_keeper, keeper) != 0)
52837c478bd9Sstevel@tonic-gate 				continue;
52847c478bd9Sstevel@tonic-gate 			for (i = 0; i < dp->pdr_kept_count; i++) {
52857c478bd9Sstevel@tonic-gate 				if (dp->pdr_kept_paths[i] == NULL)
52867c478bd9Sstevel@tonic-gate 					continue;
52877c478bd9Sstevel@tonic-gate 				kept = pm_name_to_dip(dp->pdr_kept_paths[i], 1);
52887c478bd9Sstevel@tonic-gate 				if (kept == NULL)
52897c478bd9Sstevel@tonic-gate 					continue;
52907c478bd9Sstevel@tonic-gate 				ASSERT(ddi_prop_exists(DDI_DEV_T_ANY, kept,
52917c478bd9Sstevel@tonic-gate 				    DDI_PROP_DONTPASS, dp->pdr_kept));
52927c478bd9Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), "
52937c478bd9Sstevel@tonic-gate 				    "kept=%s@%s(%s#%d) keptcnt=%d\n",
52947c478bd9Sstevel@tonic-gate 				    pmf, PM_DEVICE(dip), PM_DEVICE(kept),
52957c478bd9Sstevel@tonic-gate 				    dp->pdr_kept_count))
52967c478bd9Sstevel@tonic-gate 				if (kept != dip) {
52977c478bd9Sstevel@tonic-gate 					ret += pm_set_keeping(dip, kept);
52987c478bd9Sstevel@tonic-gate 				}
52997c478bd9Sstevel@tonic-gate 				ddi_release_devi(kept);
53007c478bd9Sstevel@tonic-gate 			}
53017c478bd9Sstevel@tonic-gate 
53027c478bd9Sstevel@tonic-gate 		}
53037c478bd9Sstevel@tonic-gate 	}
53047c478bd9Sstevel@tonic-gate 	ddi_release_devi(dip);
53057c478bd9Sstevel@tonic-gate 	return (ret);
53067c478bd9Sstevel@tonic-gate }
53077c478bd9Sstevel@tonic-gate 
53087c478bd9Sstevel@tonic-gate /*
53097c478bd9Sstevel@tonic-gate  * Should this device be kept up by another device?
53107c478bd9Sstevel@tonic-gate  * Look up all dependency recorded from PM_ADD_DEPENDENT and
53117c478bd9Sstevel@tonic-gate  * PM_ADD_DEPENDENT_PROPERTY ioctls. Record down on the keeper's
53127c478bd9Sstevel@tonic-gate  * kept device lists.
53137c478bd9Sstevel@tonic-gate  */
53147c478bd9Sstevel@tonic-gate static int
pm_kept(char * keptp)53157c478bd9Sstevel@tonic-gate pm_kept(char *keptp)
53167c478bd9Sstevel@tonic-gate {
53177c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "kept")
53187c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
53197c478bd9Sstevel@tonic-gate 	int found = 0;
53207c478bd9Sstevel@tonic-gate 	int ret = 0;
53217c478bd9Sstevel@tonic-gate 	dev_info_t *keeper;
53227c478bd9Sstevel@tonic-gate 	dev_info_t *kept;
53237c478bd9Sstevel@tonic-gate 	size_t length;
53247c478bd9Sstevel@tonic-gate 	int i;
53257c478bd9Sstevel@tonic-gate 	char **paths;
53267c478bd9Sstevel@tonic-gate 	char *path;
53277c478bd9Sstevel@tonic-gate 
53287c478bd9Sstevel@tonic-gate 	ASSERT(keptp != NULL);
53297c478bd9Sstevel@tonic-gate 	kept = pm_name_to_dip(keptp, 1);
53307c478bd9Sstevel@tonic-gate 	if (kept == NULL)
53317c478bd9Sstevel@tonic-gate 		return (0);
53327c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(kept)))
53337c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
53347c478bd9Sstevel@tonic-gate 		if (dp->pdr_isprop) {
53357c478bd9Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: property %s\n", pmf, dp->pdr_kept))
53367c478bd9Sstevel@tonic-gate 			if (ddi_prop_exists(DDI_DEV_T_ANY, kept,
53377c478bd9Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, dp->pdr_kept)) {
53387c478bd9Sstevel@tonic-gate 				/*
53397c478bd9Sstevel@tonic-gate 				 * Dont allow self dependency.
53407c478bd9Sstevel@tonic-gate 				 */
53417c478bd9Sstevel@tonic-gate 				if (strcmp(dp->pdr_keeper, keptp) == 0)
53427c478bd9Sstevel@tonic-gate 					continue;
53437c478bd9Sstevel@tonic-gate 				keeper = pm_name_to_dip(dp->pdr_keeper, 1);
53447c478bd9Sstevel@tonic-gate 				if (keeper == NULL)
53457c478bd9Sstevel@tonic-gate 					continue;
53467c478bd9Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: adding to kepts path list "
53477c478bd9Sstevel@tonic-gate 				    "%p\n", pmf, (void *)kept))
53487c478bd9Sstevel@tonic-gate #ifdef DEBUG
53497c478bd9Sstevel@tonic-gate 				if (pm_debug & PMD_DEP)
53507c478bd9Sstevel@tonic-gate 					prdeps("Before Adding from pm_kept\n");
53517c478bd9Sstevel@tonic-gate #endif
53527c478bd9Sstevel@tonic-gate 				/*
53537c478bd9Sstevel@tonic-gate 				 * Add ourselves to the dip list.
53547c478bd9Sstevel@tonic-gate 				 */
53557c478bd9Sstevel@tonic-gate 				if (dp->pdr_kept_count == 0) {
53567c478bd9Sstevel@tonic-gate 					length = strlen(keptp) + 1;
53577c478bd9Sstevel@tonic-gate 					path =
53587c478bd9Sstevel@tonic-gate 					    kmem_alloc(length, KM_SLEEP);
53597c478bd9Sstevel@tonic-gate 					paths = kmem_alloc(sizeof (char **),
53606152d498Smh 					    KM_SLEEP);
53617c478bd9Sstevel@tonic-gate 					(void) strcpy(path, keptp);
53627c478bd9Sstevel@tonic-gate 					paths[0] = path;
53637c478bd9Sstevel@tonic-gate 					dp->pdr_kept_paths = paths;
53647c478bd9Sstevel@tonic-gate 					dp->pdr_kept_count++;
53657c478bd9Sstevel@tonic-gate 				} else {
53667c478bd9Sstevel@tonic-gate 					/* Check to see if already on list */
53677c478bd9Sstevel@tonic-gate 					for (i = 0; i < dp->pdr_kept_count;
53687c478bd9Sstevel@tonic-gate 					    i++) {
53697c478bd9Sstevel@tonic-gate 						if (strcmp(keptp,
53707c478bd9Sstevel@tonic-gate 						    dp->pdr_kept_paths[i])
53717c478bd9Sstevel@tonic-gate 						    == 0) {
53727c478bd9Sstevel@tonic-gate 							found++;
53737c478bd9Sstevel@tonic-gate 							break;
53747c478bd9Sstevel@tonic-gate 						}
53757c478bd9Sstevel@tonic-gate 					}
53767c478bd9Sstevel@tonic-gate 					if (found) {
53777c478bd9Sstevel@tonic-gate 						ddi_release_devi(keeper);
53787c478bd9Sstevel@tonic-gate 						continue;
53797c478bd9Sstevel@tonic-gate 					}
53807c478bd9Sstevel@tonic-gate 					length = dp->pdr_kept_count *
53817c478bd9Sstevel@tonic-gate 					    sizeof (char **);
53827c478bd9Sstevel@tonic-gate 					paths = kmem_alloc(
53837c478bd9Sstevel@tonic-gate 					    length + sizeof (char **),
53847c478bd9Sstevel@tonic-gate 					    KM_SLEEP);
53857c478bd9Sstevel@tonic-gate 					if (dp->pdr_kept_count) {
53867c478bd9Sstevel@tonic-gate 						bcopy(dp->pdr_kept_paths,
53877c478bd9Sstevel@tonic-gate 						    paths, length);
53887c478bd9Sstevel@tonic-gate 						kmem_free(dp->pdr_kept_paths,
53896152d498Smh 						    length);
53907c478bd9Sstevel@tonic-gate 					}
53917c478bd9Sstevel@tonic-gate 					dp->pdr_kept_paths = paths;
53927c478bd9Sstevel@tonic-gate 					length = strlen(keptp) + 1;
53937c478bd9Sstevel@tonic-gate 					path =
53947c478bd9Sstevel@tonic-gate 					    kmem_alloc(length, KM_SLEEP);
53957c478bd9Sstevel@tonic-gate 					(void) strcpy(path, keptp);
53967c478bd9Sstevel@tonic-gate 					dp->pdr_kept_paths[i] = path;
53977c478bd9Sstevel@tonic-gate 					dp->pdr_kept_count++;
53987c478bd9Sstevel@tonic-gate 				}
53997c478bd9Sstevel@tonic-gate #ifdef DEBUG
54007c478bd9Sstevel@tonic-gate 				if (pm_debug & PMD_DEP)
54017c478bd9Sstevel@tonic-gate 					prdeps("After from pm_kept\n");
54027c478bd9Sstevel@tonic-gate #endif
54037c478bd9Sstevel@tonic-gate 				if (keeper) {
54047c478bd9Sstevel@tonic-gate 					ret += pm_set_keeping(keeper, kept);
54057c478bd9Sstevel@tonic-gate 					ddi_release_devi(keeper);
54067c478bd9Sstevel@tonic-gate 				}
54077c478bd9Sstevel@tonic-gate 			}
54087c478bd9Sstevel@tonic-gate 		} else {
54097c478bd9Sstevel@tonic-gate 			/*
54107c478bd9Sstevel@tonic-gate 			 * pm_keeper would be called later to do
54117c478bd9Sstevel@tonic-gate 			 * the actual pm_set_keeping.
54127c478bd9Sstevel@tonic-gate 			 */
54137c478bd9Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s: adding to kepts path list %p\n",
54147c478bd9Sstevel@tonic-gate 			    pmf, (void *)kept))
54157c478bd9Sstevel@tonic-gate #ifdef DEBUG
54167c478bd9Sstevel@tonic-gate 			if (pm_debug & PMD_DEP)
54177c478bd9Sstevel@tonic-gate 				prdeps("Before Adding from pm_kept\n");
54187c478bd9Sstevel@tonic-gate #endif
54197c478bd9Sstevel@tonic-gate 			if (strcmp(keptp, dp->pdr_kept) == 0) {
54207c478bd9Sstevel@tonic-gate 				if (dp->pdr_kept_paths == NULL) {
54217c478bd9Sstevel@tonic-gate 					length = strlen(keptp) + 1;
54227c478bd9Sstevel@tonic-gate 					path =
54237c478bd9Sstevel@tonic-gate 					    kmem_alloc(length, KM_SLEEP);
54247c478bd9Sstevel@tonic-gate 					paths = kmem_alloc(sizeof (char **),
54256152d498Smh 					    KM_SLEEP);
54267c478bd9Sstevel@tonic-gate 					(void) strcpy(path, keptp);
54277c478bd9Sstevel@tonic-gate 					paths[0] = path;
54287c478bd9Sstevel@tonic-gate 					dp->pdr_kept_paths = paths;
54297c478bd9Sstevel@tonic-gate 					dp->pdr_kept_count++;
54307c478bd9Sstevel@tonic-gate 				}
54317c478bd9Sstevel@tonic-gate 			}
54327c478bd9Sstevel@tonic-gate #ifdef DEBUG
54337c478bd9Sstevel@tonic-gate 			if (pm_debug & PMD_DEP)
54346152d498Smh 				prdeps("After from pm_kept\n");
54357c478bd9Sstevel@tonic-gate #endif
54367c478bd9Sstevel@tonic-gate 		}
54377c478bd9Sstevel@tonic-gate 	}
54387c478bd9Sstevel@tonic-gate 	ddi_release_devi(kept);
54397c478bd9Sstevel@tonic-gate 	return (ret);
54407c478bd9Sstevel@tonic-gate }
54417c478bd9Sstevel@tonic-gate 
54427c478bd9Sstevel@tonic-gate /*
54437c478bd9Sstevel@tonic-gate  * Apply a recorded dependency.  dp specifies the dependency, and
54447c478bd9Sstevel@tonic-gate  * keeper is already known to be the device that keeps up the other (kept) one.
54457c478bd9Sstevel@tonic-gate  * We have to the whole tree for the "kept" device, then apply
54467c478bd9Sstevel@tonic-gate  * the dependency (which may already be applied).
54477c478bd9Sstevel@tonic-gate  */
54487c478bd9Sstevel@tonic-gate int
pm_apply_recorded_dep(dev_info_t * keeper,pm_pdr_t * dp)54497c478bd9Sstevel@tonic-gate pm_apply_recorded_dep(dev_info_t *keeper, pm_pdr_t *dp)
54507c478bd9Sstevel@tonic-gate {
54517c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "apply_recorded_dep")
54527c478bd9Sstevel@tonic-gate 	dev_info_t *kept = NULL;
54537c478bd9Sstevel@tonic-gate 	int ret = 0;
54547c478bd9Sstevel@tonic-gate 	char *keptp = NULL;
54557c478bd9Sstevel@tonic-gate 
54567c478bd9Sstevel@tonic-gate 	/*
54577c478bd9Sstevel@tonic-gate 	 * Device to Device dependency can only be 1 to 1.
54587c478bd9Sstevel@tonic-gate 	 */
54597c478bd9Sstevel@tonic-gate 	if (dp->pdr_kept_paths == NULL)
54607c478bd9Sstevel@tonic-gate 		return (0);
54617c478bd9Sstevel@tonic-gate 	keptp = dp->pdr_kept_paths[0];
54627c478bd9Sstevel@tonic-gate 	if (keptp == NULL)
54637c478bd9Sstevel@tonic-gate 		return (0);
54647c478bd9Sstevel@tonic-gate 	ASSERT(*keptp != '\0');
54657c478bd9Sstevel@tonic-gate 	kept = pm_name_to_dip(keptp, 1);
54667c478bd9Sstevel@tonic-gate 	if (kept == NULL)
54677c478bd9Sstevel@tonic-gate 		return (0);
54687c478bd9Sstevel@tonic-gate 	if (kept) {
54697c478bd9Sstevel@tonic-gate 		PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf,
54707c478bd9Sstevel@tonic-gate 		    dp->pdr_keeper, keptp))
54717c478bd9Sstevel@tonic-gate 		if (pm_set_keeping(keeper, kept)) {
54727c478bd9Sstevel@tonic-gate 			ASSERT(dp->pdr_satisfied == 0);
54737c478bd9Sstevel@tonic-gate 			dp->pdr_satisfied = 1;
54747c478bd9Sstevel@tonic-gate 			ASSERT(pm_unresolved_deps);
54757c478bd9Sstevel@tonic-gate 			pm_unresolved_deps--;
54767c478bd9Sstevel@tonic-gate 			ret++;
54777c478bd9Sstevel@tonic-gate 		}
54787c478bd9Sstevel@tonic-gate 	}
54797c478bd9Sstevel@tonic-gate 	ddi_release_devi(kept);
54807c478bd9Sstevel@tonic-gate 
54817c478bd9Sstevel@tonic-gate 	return (ret);
54827c478bd9Sstevel@tonic-gate }
54837c478bd9Sstevel@tonic-gate 
54847c478bd9Sstevel@tonic-gate /*
54857c478bd9Sstevel@tonic-gate  * Called from common/io/pm.c
54867c478bd9Sstevel@tonic-gate  */
54877c478bd9Sstevel@tonic-gate int
pm_cur_power(pm_component_t * cp)54887c478bd9Sstevel@tonic-gate pm_cur_power(pm_component_t *cp)
54897c478bd9Sstevel@tonic-gate {
54907c478bd9Sstevel@tonic-gate 	return (cur_power(cp));
54917c478bd9Sstevel@tonic-gate }
54927c478bd9Sstevel@tonic-gate 
54937c478bd9Sstevel@tonic-gate /*
54947c478bd9Sstevel@tonic-gate  * External interface to sanity-check a power level.
54957c478bd9Sstevel@tonic-gate  */
54967c478bd9Sstevel@tonic-gate int
pm_valid_power(dev_info_t * dip,int comp,int level)54977c478bd9Sstevel@tonic-gate pm_valid_power(dev_info_t *dip, int comp, int level)
54987c478bd9Sstevel@tonic-gate {
54997c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "valid_power")
55007c478bd9Sstevel@tonic-gate 
55017c478bd9Sstevel@tonic-gate 	if (comp >= 0 && comp < PM_NUMCMPTS(dip) && level >= 0)
55027c478bd9Sstevel@tonic-gate 		return (e_pm_valid_power(dip, comp, level));
55037c478bd9Sstevel@tonic-gate 	else {
55047c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: comp=%d, ncomp=%d, level=%d\n",
55057c478bd9Sstevel@tonic-gate 		    pmf, comp, PM_NUMCMPTS(dip), level))
55067c478bd9Sstevel@tonic-gate 		return (0);
55077c478bd9Sstevel@tonic-gate 	}
55087c478bd9Sstevel@tonic-gate }
55097c478bd9Sstevel@tonic-gate 
55107c478bd9Sstevel@tonic-gate /*
55117c478bd9Sstevel@tonic-gate  * Called when a device that is direct power managed needs to change state.
55127c478bd9Sstevel@tonic-gate  * This routine arranges to block the request until the process managing
55137c478bd9Sstevel@tonic-gate  * the device makes the change (or some other incompatible change) or
55147c478bd9Sstevel@tonic-gate  * the process closes /dev/pm.
55157c478bd9Sstevel@tonic-gate  */
55167c478bd9Sstevel@tonic-gate static int
pm_block(dev_info_t * dip,int comp,int newpower,int oldpower)55177c478bd9Sstevel@tonic-gate pm_block(dev_info_t *dip, int comp, int newpower, int oldpower)
55187c478bd9Sstevel@tonic-gate {
55197c478bd9Sstevel@tonic-gate 	pm_rsvp_t *new = kmem_zalloc(sizeof (*new), KM_SLEEP);
55207c478bd9Sstevel@tonic-gate 	int ret = 0;
55217c478bd9Sstevel@tonic-gate 	void pm_dequeue_blocked(pm_rsvp_t *);
55227c478bd9Sstevel@tonic-gate 	void pm_enqueue_blocked(pm_rsvp_t *);
55237c478bd9Sstevel@tonic-gate 
55247c478bd9Sstevel@tonic-gate 	ASSERT(!pm_processes_stopped);
55257c478bd9Sstevel@tonic-gate 	ASSERT(PM_IAM_LOCKING_DIP(dip));
55267c478bd9Sstevel@tonic-gate 	new->pr_dip = dip;
55277c478bd9Sstevel@tonic-gate 	new->pr_comp = comp;
55287c478bd9Sstevel@tonic-gate 	new->pr_newlevel = newpower;
55297c478bd9Sstevel@tonic-gate 	new->pr_oldlevel = oldpower;
55307c478bd9Sstevel@tonic-gate 	cv_init(&new->pr_cv, NULL, CV_DEFAULT, NULL);
55317c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_rsvp_lock);
55327c478bd9Sstevel@tonic-gate 	pm_enqueue_blocked(new);
55337c478bd9Sstevel@tonic-gate 	pm_enqueue_notify(PSC_PENDING_CHANGE, dip, comp, newpower, oldpower,
55347c478bd9Sstevel@tonic-gate 	    PM_CANBLOCK_BLOCK);
55357c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
55367c478bd9Sstevel@tonic-gate 	/*
55377c478bd9Sstevel@tonic-gate 	 * truss may make the cv_wait_sig return prematurely
55387c478bd9Sstevel@tonic-gate 	 */
55397c478bd9Sstevel@tonic-gate 	while (ret == 0) {
55407c478bd9Sstevel@tonic-gate 		/*
55417c478bd9Sstevel@tonic-gate 		 * Normally there will be no user context involved, but if
55427c478bd9Sstevel@tonic-gate 		 * there is (e.g. we are here via an ioctl call to a driver)
55437c478bd9Sstevel@tonic-gate 		 * then we should allow the process to abort the request,
55447c478bd9Sstevel@tonic-gate 		 * or we get an unkillable process if the same thread does
55457c478bd9Sstevel@tonic-gate 		 * PM_DIRECT_PM and pm_raise_power
55467c478bd9Sstevel@tonic-gate 		 */
55477c478bd9Sstevel@tonic-gate 		if (cv_wait_sig(&new->pr_cv, &pm_rsvp_lock) == 0) {
55487c478bd9Sstevel@tonic-gate 			ret = PMP_FAIL;
55497c478bd9Sstevel@tonic-gate 		} else {
55507c478bd9Sstevel@tonic-gate 			ret = new->pr_retval;
55517c478bd9Sstevel@tonic-gate 		}
55527c478bd9Sstevel@tonic-gate 	}
55537c478bd9Sstevel@tonic-gate 	pm_dequeue_blocked(new);
55547c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_rsvp_lock);
55557c478bd9Sstevel@tonic-gate 	cv_destroy(&new->pr_cv);
55567c478bd9Sstevel@tonic-gate 	kmem_free(new, sizeof (*new));
55577c478bd9Sstevel@tonic-gate 	return (ret);
55587c478bd9Sstevel@tonic-gate }
55597c478bd9Sstevel@tonic-gate 
55607c478bd9Sstevel@tonic-gate /*
55617c478bd9Sstevel@tonic-gate  * Returns true if the process is interested in power level changes (has issued
55627c478bd9Sstevel@tonic-gate  * PM_GET_STATE_CHANGE ioctl).
55637c478bd9Sstevel@tonic-gate  */
55647c478bd9Sstevel@tonic-gate int
pm_interest_registered(int clone)55657c478bd9Sstevel@tonic-gate pm_interest_registered(int clone)
55667c478bd9Sstevel@tonic-gate {
55677c478bd9Sstevel@tonic-gate 	ASSERT(clone >= 0 && clone < PM_MAX_CLONE - 1);
55687c478bd9Sstevel@tonic-gate 	return (pm_interest[clone]);
55697c478bd9Sstevel@tonic-gate }
55707c478bd9Sstevel@tonic-gate 
5571d67944fbSScott Rotondo static void pm_enqueue_pscc(pscc_t *, pscc_t **);
5572d67944fbSScott Rotondo 
55737c478bd9Sstevel@tonic-gate /*
55747c478bd9Sstevel@tonic-gate  * Process with clone has just done PM_DIRECT_PM on dip, or has asked to
55757c478bd9Sstevel@tonic-gate  * watch all state transitions (dip == NULL).  Set up data
55767c478bd9Sstevel@tonic-gate  * structs to communicate with process about state changes.
55777c478bd9Sstevel@tonic-gate  */
55787c478bd9Sstevel@tonic-gate void
pm_register_watcher(int clone,dev_info_t * dip)55797c478bd9Sstevel@tonic-gate pm_register_watcher(int clone, dev_info_t *dip)
55807c478bd9Sstevel@tonic-gate {
55817c478bd9Sstevel@tonic-gate 	pscc_t	*p;
55827c478bd9Sstevel@tonic-gate 	psce_t	*psce;
55837c478bd9Sstevel@tonic-gate 
55847c478bd9Sstevel@tonic-gate 	/*
55857c478bd9Sstevel@tonic-gate 	 * We definitely need a control struct, then we have to search to see
55867c478bd9Sstevel@tonic-gate 	 * there is already an entries struct (in the dip != NULL case).
55877c478bd9Sstevel@tonic-gate 	 */
55887c478bd9Sstevel@tonic-gate 	pscc_t	*pscc = kmem_zalloc(sizeof (*pscc), KM_SLEEP);
55897c478bd9Sstevel@tonic-gate 	pscc->pscc_clone = clone;
55907c478bd9Sstevel@tonic-gate 	pscc->pscc_dip = dip;
55917c478bd9Sstevel@tonic-gate 
55927c478bd9Sstevel@tonic-gate 	if (dip) {
55937c478bd9Sstevel@tonic-gate 		int found = 0;
55947c478bd9Sstevel@tonic-gate 		rw_enter(&pm_pscc_direct_rwlock, RW_WRITER);
55957c478bd9Sstevel@tonic-gate 		for (p = pm_pscc_direct; p; p = p->pscc_next) {
55967c478bd9Sstevel@tonic-gate 			/*
55977c478bd9Sstevel@tonic-gate 			 * Already an entry for this clone, so just use it
55987c478bd9Sstevel@tonic-gate 			 * for the new one (for the case where a single
55997c478bd9Sstevel@tonic-gate 			 * process is watching multiple devices)
56007c478bd9Sstevel@tonic-gate 			 */
56017c478bd9Sstevel@tonic-gate 			if (p->pscc_clone == clone) {
56027c478bd9Sstevel@tonic-gate 				pscc->pscc_entries = p->pscc_entries;
56037c478bd9Sstevel@tonic-gate 				pscc->pscc_entries->psce_references++;
56047c478bd9Sstevel@tonic-gate 				found++;
56058c533f11Smh 				break;
56067c478bd9Sstevel@tonic-gate 			}
56077c478bd9Sstevel@tonic-gate 		}
56087c478bd9Sstevel@tonic-gate 		if (!found) {		/* create a new one */
56097c478bd9Sstevel@tonic-gate 			psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP);
56107c478bd9Sstevel@tonic-gate 			mutex_init(&psce->psce_lock, NULL, MUTEX_DEFAULT, NULL);
56117c478bd9Sstevel@tonic-gate 			psce->psce_first =
56127c478bd9Sstevel@tonic-gate 			    kmem_zalloc(sizeof (pm_state_change_t) * PSCCOUNT,
56137c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
56147c478bd9Sstevel@tonic-gate 			psce->psce_in = psce->psce_out = psce->psce_first;
56157c478bd9Sstevel@tonic-gate 			psce->psce_last = &psce->psce_first[PSCCOUNT - 1];
56167c478bd9Sstevel@tonic-gate 			psce->psce_references = 1;
56177c478bd9Sstevel@tonic-gate 			pscc->pscc_entries = psce;
56187c478bd9Sstevel@tonic-gate 		}
56197c478bd9Sstevel@tonic-gate 		pm_enqueue_pscc(pscc, &pm_pscc_direct);
56207c478bd9Sstevel@tonic-gate 		rw_exit(&pm_pscc_direct_rwlock);
56217c478bd9Sstevel@tonic-gate 	} else {
56227c478bd9Sstevel@tonic-gate 		ASSERT(!pm_interest_registered(clone));
56237c478bd9Sstevel@tonic-gate 		rw_enter(&pm_pscc_interest_rwlock, RW_WRITER);
56247c478bd9Sstevel@tonic-gate #ifdef DEBUG
56257c478bd9Sstevel@tonic-gate 		for (p = pm_pscc_interest; p; p = p->pscc_next) {
56267c478bd9Sstevel@tonic-gate 			/*
56277c478bd9Sstevel@tonic-gate 			 * Should not be an entry for this clone!
56287c478bd9Sstevel@tonic-gate 			 */
56297c478bd9Sstevel@tonic-gate 			ASSERT(p->pscc_clone != clone);
56307c478bd9Sstevel@tonic-gate 		}
56317c478bd9Sstevel@tonic-gate #endif
56327c478bd9Sstevel@tonic-gate 		psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP);
56337c478bd9Sstevel@tonic-gate 		psce->psce_first = kmem_zalloc(sizeof (pm_state_change_t) *
56347c478bd9Sstevel@tonic-gate 		    PSCCOUNT, KM_SLEEP);
56357c478bd9Sstevel@tonic-gate 		psce->psce_in = psce->psce_out = psce->psce_first;
56367c478bd9Sstevel@tonic-gate 		psce->psce_last = &psce->psce_first[PSCCOUNT - 1];
56377c478bd9Sstevel@tonic-gate 		psce->psce_references = 1;
56387c478bd9Sstevel@tonic-gate 		pscc->pscc_entries = psce;
56397c478bd9Sstevel@tonic-gate 		pm_enqueue_pscc(pscc, &pm_pscc_interest);
56407c478bd9Sstevel@tonic-gate 		pm_interest[clone] = 1;
56417c478bd9Sstevel@tonic-gate 		rw_exit(&pm_pscc_interest_rwlock);
56427c478bd9Sstevel@tonic-gate 	}
56437c478bd9Sstevel@tonic-gate }
56447c478bd9Sstevel@tonic-gate 
56457c478bd9Sstevel@tonic-gate /*
56467c478bd9Sstevel@tonic-gate  * Remove the given entry from the blocked list
56477c478bd9Sstevel@tonic-gate  */
56487c478bd9Sstevel@tonic-gate void
pm_dequeue_blocked(pm_rsvp_t * p)56497c478bd9Sstevel@tonic-gate pm_dequeue_blocked(pm_rsvp_t *p)
56507c478bd9Sstevel@tonic-gate {
56517c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
56527c478bd9Sstevel@tonic-gate 	if (pm_blocked_list == p) {
56537c478bd9Sstevel@tonic-gate 		ASSERT(p->pr_prev == NULL);
56547c478bd9Sstevel@tonic-gate 		if (p->pr_next != NULL)
56557c478bd9Sstevel@tonic-gate 			p->pr_next->pr_prev = NULL;
56567c478bd9Sstevel@tonic-gate 		pm_blocked_list = p->pr_next;
56577c478bd9Sstevel@tonic-gate 	} else {
56587c478bd9Sstevel@tonic-gate 		ASSERT(p->pr_prev != NULL);
56597c478bd9Sstevel@tonic-gate 		p->pr_prev->pr_next = p->pr_next;
56607c478bd9Sstevel@tonic-gate 		if (p->pr_next != NULL)
56617c478bd9Sstevel@tonic-gate 			p->pr_next->pr_prev = p->pr_prev;
56627c478bd9Sstevel@tonic-gate 	}
56637c478bd9Sstevel@tonic-gate }
56647c478bd9Sstevel@tonic-gate 
56657c478bd9Sstevel@tonic-gate /*
56667c478bd9Sstevel@tonic-gate  * Remove the given control struct from the given list
56677c478bd9Sstevel@tonic-gate  */
56687c478bd9Sstevel@tonic-gate static void
pm_dequeue_pscc(pscc_t * p,pscc_t ** list)56697c478bd9Sstevel@tonic-gate pm_dequeue_pscc(pscc_t *p, pscc_t **list)
56707c478bd9Sstevel@tonic-gate {
56717c478bd9Sstevel@tonic-gate 	if (*list == p) {
56727c478bd9Sstevel@tonic-gate 		ASSERT(p->pscc_prev == NULL);
56737c478bd9Sstevel@tonic-gate 		if (p->pscc_next != NULL)
56747c478bd9Sstevel@tonic-gate 			p->pscc_next->pscc_prev = NULL;
56757c478bd9Sstevel@tonic-gate 		*list = p->pscc_next;
56767c478bd9Sstevel@tonic-gate 	} else {
56777c478bd9Sstevel@tonic-gate 		ASSERT(p->pscc_prev != NULL);
56787c478bd9Sstevel@tonic-gate 		p->pscc_prev->pscc_next = p->pscc_next;
56797c478bd9Sstevel@tonic-gate 		if (p->pscc_next != NULL)
56807c478bd9Sstevel@tonic-gate 			p->pscc_next->pscc_prev = p->pscc_prev;
56817c478bd9Sstevel@tonic-gate 	}
56827c478bd9Sstevel@tonic-gate }
56837c478bd9Sstevel@tonic-gate 
56847c478bd9Sstevel@tonic-gate /*
56857c478bd9Sstevel@tonic-gate  * Stick the control struct specified on the front of the list
56867c478bd9Sstevel@tonic-gate  */
56877c478bd9Sstevel@tonic-gate static void
pm_enqueue_pscc(pscc_t * p,pscc_t ** list)56887c478bd9Sstevel@tonic-gate pm_enqueue_pscc(pscc_t *p, pscc_t **list)
56897c478bd9Sstevel@tonic-gate {
56907c478bd9Sstevel@tonic-gate 	pscc_t *h;	/* entry at head of list */
56917c478bd9Sstevel@tonic-gate 	if ((h = *list) == NULL) {
56927c478bd9Sstevel@tonic-gate 		*list = p;
56937c478bd9Sstevel@tonic-gate 		ASSERT(p->pscc_next == NULL);
56947c478bd9Sstevel@tonic-gate 		ASSERT(p->pscc_prev == NULL);
56957c478bd9Sstevel@tonic-gate 	} else {
56967c478bd9Sstevel@tonic-gate 		p->pscc_next = h;
56977c478bd9Sstevel@tonic-gate 		ASSERT(h->pscc_prev == NULL);
56987c478bd9Sstevel@tonic-gate 		h->pscc_prev = p;
56997c478bd9Sstevel@tonic-gate 		ASSERT(p->pscc_prev == NULL);
57007c478bd9Sstevel@tonic-gate 		*list = p;
57017c478bd9Sstevel@tonic-gate 	}
57027c478bd9Sstevel@tonic-gate }
57037c478bd9Sstevel@tonic-gate 
57047c478bd9Sstevel@tonic-gate /*
57057c478bd9Sstevel@tonic-gate  * If dip is NULL, process is closing "clone" clean up all its registrations.
57067c478bd9Sstevel@tonic-gate  * Otherwise only clean up those for dip because process is just giving up
57077c478bd9Sstevel@tonic-gate  * control of a direct device.
57087c478bd9Sstevel@tonic-gate  */
57097c478bd9Sstevel@tonic-gate void
pm_deregister_watcher(int clone,dev_info_t * dip)57107c478bd9Sstevel@tonic-gate pm_deregister_watcher(int clone, dev_info_t *dip)
57117c478bd9Sstevel@tonic-gate {
57127c478bd9Sstevel@tonic-gate 	pscc_t	*p, *pn;
57137c478bd9Sstevel@tonic-gate 	psce_t	*psce;
57147c478bd9Sstevel@tonic-gate 	int found = 0;
57157c478bd9Sstevel@tonic-gate 
57167c478bd9Sstevel@tonic-gate 	if (dip == NULL) {
57177c478bd9Sstevel@tonic-gate 		rw_enter(&pm_pscc_interest_rwlock, RW_WRITER);
57187c478bd9Sstevel@tonic-gate 		for (p = pm_pscc_interest; p; p = pn) {
57197c478bd9Sstevel@tonic-gate 			pn = p->pscc_next;
57207c478bd9Sstevel@tonic-gate 			if (p->pscc_clone == clone) {
57217c478bd9Sstevel@tonic-gate 				pm_dequeue_pscc(p, &pm_pscc_interest);
57227c478bd9Sstevel@tonic-gate 				psce = p->pscc_entries;
57237c478bd9Sstevel@tonic-gate 				ASSERT(psce->psce_references == 1);
57247c478bd9Sstevel@tonic-gate 				mutex_destroy(&psce->psce_lock);
57257c478bd9Sstevel@tonic-gate 				kmem_free(psce->psce_first,
57267c478bd9Sstevel@tonic-gate 				    sizeof (pm_state_change_t) * PSCCOUNT);
57277c478bd9Sstevel@tonic-gate 				kmem_free(psce, sizeof (*psce));
57287c478bd9Sstevel@tonic-gate 				kmem_free(p, sizeof (*p));
57297c478bd9Sstevel@tonic-gate 			}
57307c478bd9Sstevel@tonic-gate 		}
57317c478bd9Sstevel@tonic-gate 		pm_interest[clone] = 0;
57327c478bd9Sstevel@tonic-gate 		rw_exit(&pm_pscc_interest_rwlock);
57337c478bd9Sstevel@tonic-gate 	}
57347c478bd9Sstevel@tonic-gate 	found = 0;
57357c478bd9Sstevel@tonic-gate 	rw_enter(&pm_pscc_direct_rwlock, RW_WRITER);
57367c478bd9Sstevel@tonic-gate 	for (p = pm_pscc_direct; p; p = pn) {
57377c478bd9Sstevel@tonic-gate 		pn = p->pscc_next;
57387c478bd9Sstevel@tonic-gate 		if ((dip && p->pscc_dip == dip) ||
57397c478bd9Sstevel@tonic-gate 		    (dip == NULL && clone == p->pscc_clone)) {
57407c478bd9Sstevel@tonic-gate 			ASSERT(clone == p->pscc_clone);
57417c478bd9Sstevel@tonic-gate 			found++;
57427c478bd9Sstevel@tonic-gate 			/*
57437c478bd9Sstevel@tonic-gate 			 * Remove from control list
57447c478bd9Sstevel@tonic-gate 			 */
57457c478bd9Sstevel@tonic-gate 			pm_dequeue_pscc(p, &pm_pscc_direct);
57467c478bd9Sstevel@tonic-gate 			/*
57477c478bd9Sstevel@tonic-gate 			 * If we're the last reference, free the
57487c478bd9Sstevel@tonic-gate 			 * entries struct.
57497c478bd9Sstevel@tonic-gate 			 */
57507c478bd9Sstevel@tonic-gate 			psce = p->pscc_entries;
57517c478bd9Sstevel@tonic-gate 			ASSERT(psce);
57527c478bd9Sstevel@tonic-gate 			if (psce->psce_references == 1) {
57537c478bd9Sstevel@tonic-gate 				kmem_free(psce->psce_first,
57547c478bd9Sstevel@tonic-gate 				    PSCCOUNT * sizeof (pm_state_change_t));
57557c478bd9Sstevel@tonic-gate 				kmem_free(psce, sizeof (*psce));
57567c478bd9Sstevel@tonic-gate 			} else {
57577c478bd9Sstevel@tonic-gate 				psce->psce_references--;
57587c478bd9Sstevel@tonic-gate 			}
57597c478bd9Sstevel@tonic-gate 			kmem_free(p, sizeof (*p));
57607c478bd9Sstevel@tonic-gate 		}
57617c478bd9Sstevel@tonic-gate 	}
57627c478bd9Sstevel@tonic-gate 	ASSERT(dip == NULL || found);
57637c478bd9Sstevel@tonic-gate 	rw_exit(&pm_pscc_direct_rwlock);
57647c478bd9Sstevel@tonic-gate }
57657c478bd9Sstevel@tonic-gate 
57667c478bd9Sstevel@tonic-gate /*
57677c478bd9Sstevel@tonic-gate  * Search the indicated list for an entry that matches clone, and return a
57687c478bd9Sstevel@tonic-gate  * pointer to it.  To be interesting, the entry must have something ready to
57697c478bd9Sstevel@tonic-gate  * be passed up to the controlling process.
57707c478bd9Sstevel@tonic-gate  * The returned entry will be locked upon return from this call.
57717c478bd9Sstevel@tonic-gate  */
57727c478bd9Sstevel@tonic-gate static psce_t *
pm_psc_find_clone(int clone,pscc_t ** list,krwlock_t * lock)57737c478bd9Sstevel@tonic-gate pm_psc_find_clone(int clone, pscc_t **list, krwlock_t *lock)
57747c478bd9Sstevel@tonic-gate {
57757c478bd9Sstevel@tonic-gate 	pscc_t	*p;
57767c478bd9Sstevel@tonic-gate 	psce_t	*psce;
57777c478bd9Sstevel@tonic-gate 	rw_enter(lock, RW_READER);
57787c478bd9Sstevel@tonic-gate 	for (p = *list; p; p = p->pscc_next) {
57797c478bd9Sstevel@tonic-gate 		if (clone == p->pscc_clone) {
57807c478bd9Sstevel@tonic-gate 			psce = p->pscc_entries;
57817c478bd9Sstevel@tonic-gate 			mutex_enter(&psce->psce_lock);
57827c478bd9Sstevel@tonic-gate 			if (psce->psce_out->size) {
57837c478bd9Sstevel@tonic-gate 				rw_exit(lock);
57847c478bd9Sstevel@tonic-gate 				return (psce);
57857c478bd9Sstevel@tonic-gate 			} else {
57867c478bd9Sstevel@tonic-gate 				mutex_exit(&psce->psce_lock);
57877c478bd9Sstevel@tonic-gate 			}
57887c478bd9Sstevel@tonic-gate 		}
57897c478bd9Sstevel@tonic-gate 	}
57907c478bd9Sstevel@tonic-gate 	rw_exit(lock);
57917c478bd9Sstevel@tonic-gate 	return (NULL);
57927c478bd9Sstevel@tonic-gate }
57937c478bd9Sstevel@tonic-gate 
5794d67944fbSScott Rotondo static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *);
57957c478bd9Sstevel@tonic-gate /*
57967c478bd9Sstevel@tonic-gate  * Find an entry for a particular clone in the direct list.
57977c478bd9Sstevel@tonic-gate  */
57987c478bd9Sstevel@tonic-gate psce_t *
pm_psc_clone_to_direct(int clone)57997c478bd9Sstevel@tonic-gate pm_psc_clone_to_direct(int clone)
58007c478bd9Sstevel@tonic-gate {
58017c478bd9Sstevel@tonic-gate 	return (pm_psc_find_clone(clone, &pm_pscc_direct,
58027c478bd9Sstevel@tonic-gate 	    &pm_pscc_direct_rwlock));
58037c478bd9Sstevel@tonic-gate }
58047c478bd9Sstevel@tonic-gate 
58057c478bd9Sstevel@tonic-gate /*
58067c478bd9Sstevel@tonic-gate  * Find an entry for a particular clone in the interest list.
58077c478bd9Sstevel@tonic-gate  */
58087c478bd9Sstevel@tonic-gate psce_t *
pm_psc_clone_to_interest(int clone)58097c478bd9Sstevel@tonic-gate pm_psc_clone_to_interest(int clone)
58107c478bd9Sstevel@tonic-gate {
58117c478bd9Sstevel@tonic-gate 	return (pm_psc_find_clone(clone, &pm_pscc_interest,
58127c478bd9Sstevel@tonic-gate 	    &pm_pscc_interest_rwlock));
58137c478bd9Sstevel@tonic-gate }
58147c478bd9Sstevel@tonic-gate 
58157c478bd9Sstevel@tonic-gate /*
58167c478bd9Sstevel@tonic-gate  * Put the given entry at the head of the blocked list
58177c478bd9Sstevel@tonic-gate  */
58187c478bd9Sstevel@tonic-gate void
pm_enqueue_blocked(pm_rsvp_t * p)58197c478bd9Sstevel@tonic-gate pm_enqueue_blocked(pm_rsvp_t *p)
58207c478bd9Sstevel@tonic-gate {
58217c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pm_rsvp_lock));
58227c478bd9Sstevel@tonic-gate 	ASSERT(p->pr_next == NULL);
58237c478bd9Sstevel@tonic-gate 	ASSERT(p->pr_prev == NULL);
58247c478bd9Sstevel@tonic-gate 	if (pm_blocked_list != NULL) {
58257c478bd9Sstevel@tonic-gate 		p->pr_next = pm_blocked_list;
58267c478bd9Sstevel@tonic-gate 		ASSERT(pm_blocked_list->pr_prev == NULL);
58277c478bd9Sstevel@tonic-gate 		pm_blocked_list->pr_prev = p;
58287c478bd9Sstevel@tonic-gate 		pm_blocked_list = p;
58297c478bd9Sstevel@tonic-gate 	} else {
58307c478bd9Sstevel@tonic-gate 		pm_blocked_list = p;
58317c478bd9Sstevel@tonic-gate 	}
58327c478bd9Sstevel@tonic-gate }
58337c478bd9Sstevel@tonic-gate 
58347c478bd9Sstevel@tonic-gate /*
58357c478bd9Sstevel@tonic-gate  * Sets every power managed device back to its default threshold
58367c478bd9Sstevel@tonic-gate  */
58377c478bd9Sstevel@tonic-gate void
pm_all_to_default_thresholds(void)58387c478bd9Sstevel@tonic-gate pm_all_to_default_thresholds(void)
58397c478bd9Sstevel@tonic-gate {
58407c478bd9Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_set_dev_thr_walk,
58417c478bd9Sstevel@tonic-gate 	    (void *) &pm_system_idle_threshold);
58427c478bd9Sstevel@tonic-gate }
58437c478bd9Sstevel@tonic-gate 
58447c478bd9Sstevel@tonic-gate static int
pm_set_dev_thr_walk(dev_info_t * dip,void * arg)58457c478bd9Sstevel@tonic-gate pm_set_dev_thr_walk(dev_info_t *dip, void *arg)
58467c478bd9Sstevel@tonic-gate {
58477c478bd9Sstevel@tonic-gate 	int thr = (int)(*(int *)arg);
58487c478bd9Sstevel@tonic-gate 
58497c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip))
58507c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
58517c478bd9Sstevel@tonic-gate 	pm_set_device_threshold(dip, thr, PMC_DEF_THRESH);
58527c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
58537c478bd9Sstevel@tonic-gate }
58547c478bd9Sstevel@tonic-gate 
58557c478bd9Sstevel@tonic-gate /*
58567c478bd9Sstevel@tonic-gate  * Returns the current threshold value (in seconds) for the indicated component
58577c478bd9Sstevel@tonic-gate  */
58587c478bd9Sstevel@tonic-gate int
pm_current_threshold(dev_info_t * dip,int comp,int * threshp)58597c478bd9Sstevel@tonic-gate pm_current_threshold(dev_info_t *dip, int comp, int *threshp)
58607c478bd9Sstevel@tonic-gate {
58617c478bd9Sstevel@tonic-gate 	if (comp < 0 || comp >= PM_NUMCMPTS(dip)) {
58627c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
58637c478bd9Sstevel@tonic-gate 	} else {
58647c478bd9Sstevel@tonic-gate 		*threshp = cur_threshold(dip, comp);
58657c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
58667c478bd9Sstevel@tonic-gate 	}
58677c478bd9Sstevel@tonic-gate }
58687c478bd9Sstevel@tonic-gate 
58697c478bd9Sstevel@tonic-gate /*
58707c478bd9Sstevel@tonic-gate  * To be called when changing the power level of a component of a device.
58717c478bd9Sstevel@tonic-gate  * On some platforms, changing power on one device may require that power
58727c478bd9Sstevel@tonic-gate  * be changed on other, related devices in the same transaction.  Thus, we
58737c478bd9Sstevel@tonic-gate  * always pass this request to the platform power manager so that all the
58747c478bd9Sstevel@tonic-gate  * affected devices will be locked.
58757c478bd9Sstevel@tonic-gate  */
58767c478bd9Sstevel@tonic-gate void
pm_lock_power(dev_info_t * dip)58773fe80ca4SDan Cross pm_lock_power(dev_info_t *dip)
58787c478bd9Sstevel@tonic-gate {
58797c478bd9Sstevel@tonic-gate 	power_req_t power_req;
58807c478bd9Sstevel@tonic-gate 	int result;
58817c478bd9Sstevel@tonic-gate 
58827c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_LOCK_POWER;
58837c478bd9Sstevel@tonic-gate 	power_req.req.ppm_lock_power_req.who = dip;
58847c478bd9Sstevel@tonic-gate 	(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
58857c478bd9Sstevel@tonic-gate }
58867c478bd9Sstevel@tonic-gate 
58877c478bd9Sstevel@tonic-gate /*
58887c478bd9Sstevel@tonic-gate  * Release the lock (or locks) acquired to change the power of a device.
58897c478bd9Sstevel@tonic-gate  * See comments for pm_lock_power.
58907c478bd9Sstevel@tonic-gate  */
58917c478bd9Sstevel@tonic-gate void
pm_unlock_power(dev_info_t * dip)58923fe80ca4SDan Cross pm_unlock_power(dev_info_t *dip)
58937c478bd9Sstevel@tonic-gate {
58947c478bd9Sstevel@tonic-gate 	power_req_t power_req;
58957c478bd9Sstevel@tonic-gate 	int result;
58967c478bd9Sstevel@tonic-gate 
58977c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_UNLOCK_POWER;
58987c478bd9Sstevel@tonic-gate 	power_req.req.ppm_unlock_power_req.who = dip;
58997c478bd9Sstevel@tonic-gate 	(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
59007c478bd9Sstevel@tonic-gate }
59017c478bd9Sstevel@tonic-gate 
59027c478bd9Sstevel@tonic-gate 
59037c478bd9Sstevel@tonic-gate /*
59047c478bd9Sstevel@tonic-gate  * Attempt (without blocking) to acquire the lock(s) needed to change the
59057c478bd9Sstevel@tonic-gate  * power of a component of a device.  See comments for pm_lock_power.
59067c478bd9Sstevel@tonic-gate  *
59077c478bd9Sstevel@tonic-gate  * Return: 1 if lock(s) acquired, 0 if not.
59087c478bd9Sstevel@tonic-gate  */
59097c478bd9Sstevel@tonic-gate int
pm_try_locking_power(dev_info_t * dip)59103fe80ca4SDan Cross pm_try_locking_power(dev_info_t *dip)
59117c478bd9Sstevel@tonic-gate {
59127c478bd9Sstevel@tonic-gate 	power_req_t power_req;
59137c478bd9Sstevel@tonic-gate 	int result;
59147c478bd9Sstevel@tonic-gate 
59157c478bd9Sstevel@tonic-gate 	power_req.request_type = PMR_PPM_TRY_LOCK_POWER;
59167c478bd9Sstevel@tonic-gate 	power_req.req.ppm_lock_power_req.who = dip;
59177c478bd9Sstevel@tonic-gate 	(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
59187c478bd9Sstevel@tonic-gate 	return (result);
59197c478bd9Sstevel@tonic-gate }
59207c478bd9Sstevel@tonic-gate 
59217c478bd9Sstevel@tonic-gate 
59227c478bd9Sstevel@tonic-gate /*
59237c478bd9Sstevel@tonic-gate  * Lock power state of a device.
59247c478bd9Sstevel@tonic-gate  *
59257c478bd9Sstevel@tonic-gate  * The implementation handles a special case where another thread may have
59267c478bd9Sstevel@tonic-gate  * acquired the lock and created/launched this thread to do the work.  If
59277c478bd9Sstevel@tonic-gate  * the lock cannot be acquired immediately, we check to see if this thread
59287c478bd9Sstevel@tonic-gate  * is registered as a borrower of the lock.  If so, we may proceed without
59297c478bd9Sstevel@tonic-gate  * the lock.  This assumes that the lending thread blocks on the completion
59307c478bd9Sstevel@tonic-gate  * of this thread.
59317c478bd9Sstevel@tonic-gate  *
59327c478bd9Sstevel@tonic-gate  * Note 1: for use by ppm only.
59337c478bd9Sstevel@tonic-gate  *
59347c478bd9Sstevel@tonic-gate  * Note 2: On failing to get the lock immediately, we search lock_loan list
59357c478bd9Sstevel@tonic-gate  * for curthread (as borrower of the lock).  On a hit, we check that the
59367c478bd9Sstevel@tonic-gate  * lending thread already owns the lock we want.  It is safe to compare
59377c478bd9Sstevel@tonic-gate  * devi_busy_thread and thread id of the lender because in the == case (the
59387c478bd9Sstevel@tonic-gate  * only one we care about) we know that the owner is blocked.  Similarly,
59397c478bd9Sstevel@tonic-gate  * If we find that curthread isn't registered as a lock borrower, it is safe
59407c478bd9Sstevel@tonic-gate  * to use the blocking call (ndi_devi_enter) because we know that if we
59417c478bd9Sstevel@tonic-gate  * weren't already listed as a borrower (upstream on the call stack) we won't
59427c478bd9Sstevel@tonic-gate  * become one.
59437c478bd9Sstevel@tonic-gate  */
59447c478bd9Sstevel@tonic-gate void
pm_lock_power_single(dev_info_t * dip)59453fe80ca4SDan Cross pm_lock_power_single(dev_info_t *dip)
59467c478bd9Sstevel@tonic-gate {
59477c478bd9Sstevel@tonic-gate 	lock_loan_t *cur;
59487c478bd9Sstevel@tonic-gate 
59497c478bd9Sstevel@tonic-gate 	/* if the lock is available, we are done. */
59503fe80ca4SDan Cross 	if (ndi_devi_tryenter(dip))
59517c478bd9Sstevel@tonic-gate 		return;
59527c478bd9Sstevel@tonic-gate 
59537c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
59547c478bd9Sstevel@tonic-gate 	/* see if our thread is registered as a lock borrower. */
59557c478bd9Sstevel@tonic-gate 	for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
59567c478bd9Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
59577c478bd9Sstevel@tonic-gate 			break;
59587c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
59597c478bd9Sstevel@tonic-gate 
59607c478bd9Sstevel@tonic-gate 	/* if this thread not already registered, it is safe to block */
59617c478bd9Sstevel@tonic-gate 	if (cur == NULL)
59623fe80ca4SDan Cross 		ndi_devi_enter(dip);
59637c478bd9Sstevel@tonic-gate 	else {
59647c478bd9Sstevel@tonic-gate 		/* registered: does lender own the lock we want? */
59657c478bd9Sstevel@tonic-gate 		if (cur->pmlk_lender == DEVI(dip)->devi_busy_thread) {
59667c478bd9Sstevel@tonic-gate 			ASSERT(cur->pmlk_dip == NULL || cur->pmlk_dip == dip);
59677c478bd9Sstevel@tonic-gate 			cur->pmlk_dip = dip;
59687c478bd9Sstevel@tonic-gate 		} else /* no: just block for it */
59693fe80ca4SDan Cross 			ndi_devi_enter(dip);
59707c478bd9Sstevel@tonic-gate 
59717c478bd9Sstevel@tonic-gate 	}
59727c478bd9Sstevel@tonic-gate }
59737c478bd9Sstevel@tonic-gate 
59747c478bd9Sstevel@tonic-gate /*
59757c478bd9Sstevel@tonic-gate  * Drop the lock on the device's power state.  See comment for
59767c478bd9Sstevel@tonic-gate  * pm_lock_power_single() for special implementation considerations.
59777c478bd9Sstevel@tonic-gate  *
59787c478bd9Sstevel@tonic-gate  * Note: for use by ppm only.
59797c478bd9Sstevel@tonic-gate  */
59807c478bd9Sstevel@tonic-gate void
pm_unlock_power_single(dev_info_t * dip)59813fe80ca4SDan Cross pm_unlock_power_single(dev_info_t *dip)
59827c478bd9Sstevel@tonic-gate {
59837c478bd9Sstevel@tonic-gate 	lock_loan_t *cur;
59847c478bd9Sstevel@tonic-gate 
59857c478bd9Sstevel@tonic-gate 	/* optimization: mutex not needed to check empty list */
59867c478bd9Sstevel@tonic-gate 	if (lock_loan_head.pmlk_next == NULL) {
59873fe80ca4SDan Cross 		ndi_devi_exit(dip);
59887c478bd9Sstevel@tonic-gate 		return;
59897c478bd9Sstevel@tonic-gate 	}
59907c478bd9Sstevel@tonic-gate 
59917c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
59927c478bd9Sstevel@tonic-gate 	/* see if our thread is registered as a lock borrower. */
59937c478bd9Sstevel@tonic-gate 	for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
59947c478bd9Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
59957c478bd9Sstevel@tonic-gate 			break;
59967c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
59977c478bd9Sstevel@tonic-gate 
59987c478bd9Sstevel@tonic-gate 	if (cur == NULL || cur->pmlk_dip != dip)
59997c478bd9Sstevel@tonic-gate 		/* we acquired the lock directly, so return it */
60003fe80ca4SDan Cross 		ndi_devi_exit(dip);
60017c478bd9Sstevel@tonic-gate }
60027c478bd9Sstevel@tonic-gate 
60037c478bd9Sstevel@tonic-gate /*
60047c478bd9Sstevel@tonic-gate  * Try to take the lock for changing the power level of a component.
60057c478bd9Sstevel@tonic-gate  *
60067c478bd9Sstevel@tonic-gate  * Note: for use by ppm only.
60077c478bd9Sstevel@tonic-gate  */
60087c478bd9Sstevel@tonic-gate int
pm_try_locking_power_single(dev_info_t * dip)60093fe80ca4SDan Cross pm_try_locking_power_single(dev_info_t *dip)
60107c478bd9Sstevel@tonic-gate {
60113fe80ca4SDan Cross 	return (ndi_devi_tryenter(dip));
60127c478bd9Sstevel@tonic-gate }
60137c478bd9Sstevel@tonic-gate 
60147c478bd9Sstevel@tonic-gate #ifdef	DEBUG
60157c478bd9Sstevel@tonic-gate /*
60167c478bd9Sstevel@tonic-gate  * The following are used only to print out data structures for debugging
60177c478bd9Sstevel@tonic-gate  */
60187c478bd9Sstevel@tonic-gate void
prdeps(char * msg)60197c478bd9Sstevel@tonic-gate prdeps(char *msg)
60207c478bd9Sstevel@tonic-gate {
60217c478bd9Sstevel@tonic-gate 
60227c478bd9Sstevel@tonic-gate 	pm_pdr_t *rp;
60237c478bd9Sstevel@tonic-gate 	int i;
60247c478bd9Sstevel@tonic-gate 
60257c478bd9Sstevel@tonic-gate 	pm_log("pm_dep_head %s %p\n", msg, (void *)pm_dep_head);
60267c478bd9Sstevel@tonic-gate 	for (rp = pm_dep_head; rp; rp = rp->pdr_next) {
60277c478bd9Sstevel@tonic-gate 		pm_log("%p: %s keeper %s, kept %s, kept count %d, next %p\n",
60287c478bd9Sstevel@tonic-gate 		    (void *)rp, (rp->pdr_isprop ? "property" : "device"),
60297c478bd9Sstevel@tonic-gate 		    rp->pdr_keeper, rp->pdr_kept, rp->pdr_kept_count,
60307c478bd9Sstevel@tonic-gate 		    (void *)rp->pdr_next);
60317c478bd9Sstevel@tonic-gate 		if (rp->pdr_kept_count != 0) {
60327c478bd9Sstevel@tonic-gate 			pm_log("kept list = ");
60337c478bd9Sstevel@tonic-gate 			i = 0;
60347c478bd9Sstevel@tonic-gate 			while (i < rp->pdr_kept_count) {
60357c478bd9Sstevel@tonic-gate 				pm_log("%s ", rp->pdr_kept_paths[i]);
60367c478bd9Sstevel@tonic-gate 				i++;
60377c478bd9Sstevel@tonic-gate 			}
60387c478bd9Sstevel@tonic-gate 			pm_log("\n");
60397c478bd9Sstevel@tonic-gate 		}
60407c478bd9Sstevel@tonic-gate 	}
60417c478bd9Sstevel@tonic-gate }
60427c478bd9Sstevel@tonic-gate 
60437c478bd9Sstevel@tonic-gate void
pr_noinvol(char * hdr)60447c478bd9Sstevel@tonic-gate pr_noinvol(char *hdr)
60457c478bd9Sstevel@tonic-gate {
60467c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip;
60477c478bd9Sstevel@tonic-gate 
60487c478bd9Sstevel@tonic-gate 	pm_log("%s\n", hdr);
60497c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
60507c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next)
60517c478bd9Sstevel@tonic-gate 		pm_log("\tmaj %d, flags %x, noinvolpm %d %s\n",
60527c478bd9Sstevel@tonic-gate 		    ip->ni_major, ip->ni_flags, ip->ni_noinvolpm, ip->ni_path);
60537c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
60547c478bd9Sstevel@tonic-gate }
60557c478bd9Sstevel@tonic-gate #endif
60567c478bd9Sstevel@tonic-gate 
60577c478bd9Sstevel@tonic-gate /*
60587c478bd9Sstevel@tonic-gate  * Attempt to apply the thresholds indicated by rp to the node specified by
60597c478bd9Sstevel@tonic-gate  * dip.
60607c478bd9Sstevel@tonic-gate  */
60617c478bd9Sstevel@tonic-gate void
pm_apply_recorded_thresh(dev_info_t * dip,pm_thresh_rec_t * rp)60627c478bd9Sstevel@tonic-gate pm_apply_recorded_thresh(dev_info_t *dip, pm_thresh_rec_t *rp)
60637c478bd9Sstevel@tonic-gate {
60647c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "apply_recorded_thresh")
60657c478bd9Sstevel@tonic-gate 	int i, j;
60667c478bd9Sstevel@tonic-gate 	int comps = PM_NUMCMPTS(dip);
60677c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
60687c478bd9Sstevel@tonic-gate 	pm_pte_t *ep;
60697c478bd9Sstevel@tonic-gate 	int pm_valid_thresh(dev_info_t *, pm_thresh_rec_t *);
60707c478bd9Sstevel@tonic-gate 
60717c478bd9Sstevel@tonic-gate 	PMD(PMD_THRESH, ("%s: part: %s@%s(%s#%d), rp %p, %s\n", pmf,
60727c478bd9Sstevel@tonic-gate 	    PM_DEVICE(dip), (void *)rp, rp->ptr_physpath))
60737c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
60747c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip) || !pm_valid_thresh(dip, rp)) {
60757c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_GET_PM_INFO %p\n",
60767c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), (void*)PM_GET_PM_INFO(dip)))
60777c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_ISBC %d\n",
60787c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), PM_ISBC(dip)))
60797c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) pm_valid_thresh %d\n",
60807c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), pm_valid_thresh(dip, rp)))
60817c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
60827c478bd9Sstevel@tonic-gate 		return;
60837c478bd9Sstevel@tonic-gate 	}
60847c478bd9Sstevel@tonic-gate 
60857c478bd9Sstevel@tonic-gate 	ep = rp->ptr_entries;
60867c478bd9Sstevel@tonic-gate 	/*
60877c478bd9Sstevel@tonic-gate 	 * Here we do the special case of a device threshold
60887c478bd9Sstevel@tonic-gate 	 */
60897c478bd9Sstevel@tonic-gate 	if (rp->ptr_numcomps == 0) {	/* PM_SET_DEVICE_THRESHOLD product */
60907c478bd9Sstevel@tonic-gate 		ASSERT(ep && ep->pte_numthresh == 1);
60917c478bd9Sstevel@tonic-gate 		PMD(PMD_THRESH, ("%s: set dev thr %s@%s(%s#%d) to 0x%x\n",
60927c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), ep->pte_thresh[0]))
60937c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
60947c478bd9Sstevel@tonic-gate 		pm_set_device_threshold(dip, ep->pte_thresh[0], PMC_DEV_THRESH);
6095c42872d4Smh 		if (PM_SCANABLE(dip))
60967c478bd9Sstevel@tonic-gate 			pm_rescan(dip);
60977c478bd9Sstevel@tonic-gate 		return;
60987c478bd9Sstevel@tonic-gate 	}
60997c478bd9Sstevel@tonic-gate 	for (i = 0; i < comps; i++) {
61007c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
61017c478bd9Sstevel@tonic-gate 		for (j = 0; j < ep->pte_numthresh; j++) {
61027c478bd9Sstevel@tonic-gate 			PMD(PMD_THRESH, ("%s: set thr %d for %s@%s(%s#%d)[%d] "
61037c478bd9Sstevel@tonic-gate 			    "to %x\n", pmf, j, PM_DEVICE(dip),
61047c478bd9Sstevel@tonic-gate 			    i, ep->pte_thresh[j]))
61057c478bd9Sstevel@tonic-gate 			cp->pmc_comp.pmc_thresh[j + 1] = ep->pte_thresh[j];
61067c478bd9Sstevel@tonic-gate 		}
61077c478bd9Sstevel@tonic-gate 		ep++;
61087c478bd9Sstevel@tonic-gate 	}
61097c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
61107c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |= PMC_COMP_THRESH;
61117c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
61127c478bd9Sstevel@tonic-gate 
6113c42872d4Smh 	if (PM_SCANABLE(dip))
61147c478bd9Sstevel@tonic-gate 		pm_rescan(dip);
61157c478bd9Sstevel@tonic-gate }
61167c478bd9Sstevel@tonic-gate 
61177c478bd9Sstevel@tonic-gate /*
61187c478bd9Sstevel@tonic-gate  * Returns true if the threshold specified by rp could be applied to dip
61197c478bd9Sstevel@tonic-gate  * (that is, the number of components and transitions are the same)
61207c478bd9Sstevel@tonic-gate  */
61217c478bd9Sstevel@tonic-gate int
pm_valid_thresh(dev_info_t * dip,pm_thresh_rec_t * rp)61227c478bd9Sstevel@tonic-gate pm_valid_thresh(dev_info_t *dip, pm_thresh_rec_t *rp)
61237c478bd9Sstevel@tonic-gate {
61247c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "valid_thresh")
61257c478bd9Sstevel@tonic-gate 	int comps, i;
61267c478bd9Sstevel@tonic-gate 	pm_component_t *cp;
61277c478bd9Sstevel@tonic-gate 	pm_pte_t *ep;
61287c478bd9Sstevel@tonic-gate 
61297c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
61307c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: %s: no pm_info or BC\n", pmf,
61317c478bd9Sstevel@tonic-gate 		    rp->ptr_physpath))
61327c478bd9Sstevel@tonic-gate 		return (0);
61337c478bd9Sstevel@tonic-gate 	}
61347c478bd9Sstevel@tonic-gate 	/*
61357c478bd9Sstevel@tonic-gate 	 * Special case: we represent the PM_SET_DEVICE_THRESHOLD case by
61367c478bd9Sstevel@tonic-gate 	 * an entry with numcomps == 0, (since we don't know how many
61377c478bd9Sstevel@tonic-gate 	 * components there are in advance).  This is always a valid
61387c478bd9Sstevel@tonic-gate 	 * spec.
61397c478bd9Sstevel@tonic-gate 	 */
61407c478bd9Sstevel@tonic-gate 	if (rp->ptr_numcomps == 0) {
61417c478bd9Sstevel@tonic-gate 		ASSERT(rp->ptr_entries && rp->ptr_entries->pte_numthresh == 1);
61427c478bd9Sstevel@tonic-gate 		return (1);
61437c478bd9Sstevel@tonic-gate 	}
61447c478bd9Sstevel@tonic-gate 	if (rp->ptr_numcomps != (comps = PM_NUMCMPTS(dip))) {
61457c478bd9Sstevel@tonic-gate 		PMD(PMD_ERROR, ("%s: comp # mm (dip %d cmd %d) for %s\n",
61467c478bd9Sstevel@tonic-gate 		    pmf, PM_NUMCMPTS(dip), rp->ptr_numcomps, rp->ptr_physpath))
61477c478bd9Sstevel@tonic-gate 		return (0);
61487c478bd9Sstevel@tonic-gate 	}
61497c478bd9Sstevel@tonic-gate 	ep = rp->ptr_entries;
61507c478bd9Sstevel@tonic-gate 	for (i = 0; i < comps; i++) {
61517c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
61527c478bd9Sstevel@tonic-gate 		if ((ep + i)->pte_numthresh !=
61537c478bd9Sstevel@tonic-gate 		    cp->pmc_comp.pmc_numlevels - 1) {
61547c478bd9Sstevel@tonic-gate 			PMD(PMD_ERROR, ("%s: %s[%d]: thresh=%d, record=%d\n",
61557c478bd9Sstevel@tonic-gate 			    pmf, rp->ptr_physpath, i,
61567c478bd9Sstevel@tonic-gate 			    cp->pmc_comp.pmc_numlevels - 1,
61577c478bd9Sstevel@tonic-gate 			    (ep + i)->pte_numthresh))
61587c478bd9Sstevel@tonic-gate 			return (0);
61597c478bd9Sstevel@tonic-gate 		}
61607c478bd9Sstevel@tonic-gate 	}
61617c478bd9Sstevel@tonic-gate 	return (1);
61627c478bd9Sstevel@tonic-gate }
61637c478bd9Sstevel@tonic-gate 
61647c478bd9Sstevel@tonic-gate /*
61657c478bd9Sstevel@tonic-gate  * Remove any recorded threshold for device physpath
61667c478bd9Sstevel@tonic-gate  * We know there will be at most one.
61677c478bd9Sstevel@tonic-gate  */
61687c478bd9Sstevel@tonic-gate void
pm_unrecord_threshold(char * physpath)61697c478bd9Sstevel@tonic-gate pm_unrecord_threshold(char *physpath)
61707c478bd9Sstevel@tonic-gate {
61717c478bd9Sstevel@tonic-gate 	pm_thresh_rec_t *pptr, *ptr;
61727c478bd9Sstevel@tonic-gate 
61737c478bd9Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_WRITER);
61747c478bd9Sstevel@tonic-gate 	for (pptr = NULL, ptr = pm_thresh_head; ptr; ptr = ptr->ptr_next) {
61757c478bd9Sstevel@tonic-gate 		if (strcmp(physpath, ptr->ptr_physpath) == 0) {
61767c478bd9Sstevel@tonic-gate 			if (pptr) {
61777c478bd9Sstevel@tonic-gate 				pptr->ptr_next = ptr->ptr_next;
61787c478bd9Sstevel@tonic-gate 			} else {
61797c478bd9Sstevel@tonic-gate 				ASSERT(pm_thresh_head == ptr);
61807c478bd9Sstevel@tonic-gate 				pm_thresh_head = ptr->ptr_next;
61817c478bd9Sstevel@tonic-gate 			}
61827c478bd9Sstevel@tonic-gate 			kmem_free(ptr, ptr->ptr_size);
61837c478bd9Sstevel@tonic-gate 			break;
61847c478bd9Sstevel@tonic-gate 		}
61857c478bd9Sstevel@tonic-gate 		pptr = ptr;
61867c478bd9Sstevel@tonic-gate 	}
61877c478bd9Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
61887c478bd9Sstevel@tonic-gate }
61897c478bd9Sstevel@tonic-gate 
61907c478bd9Sstevel@tonic-gate /*
61917c478bd9Sstevel@tonic-gate  * Discard all recorded thresholds.  We are returning to the default pm state.
61927c478bd9Sstevel@tonic-gate  */
61937c478bd9Sstevel@tonic-gate void
pm_discard_thresholds(void)61947c478bd9Sstevel@tonic-gate pm_discard_thresholds(void)
61957c478bd9Sstevel@tonic-gate {
61967c478bd9Sstevel@tonic-gate 	pm_thresh_rec_t *rp;
61977c478bd9Sstevel@tonic-gate 	rw_enter(&pm_thresh_rwlock, RW_WRITER);
61987c478bd9Sstevel@tonic-gate 	while (pm_thresh_head) {
61997c478bd9Sstevel@tonic-gate 		rp = pm_thresh_head;
62007c478bd9Sstevel@tonic-gate 		pm_thresh_head = rp->ptr_next;
62017c478bd9Sstevel@tonic-gate 		kmem_free(rp, rp->ptr_size);
62027c478bd9Sstevel@tonic-gate 	}
62037c478bd9Sstevel@tonic-gate 	rw_exit(&pm_thresh_rwlock);
62047c478bd9Sstevel@tonic-gate }
62057c478bd9Sstevel@tonic-gate 
62067c478bd9Sstevel@tonic-gate /*
62077c478bd9Sstevel@tonic-gate  * Discard all recorded dependencies.  We are returning to the default pm state.
62087c478bd9Sstevel@tonic-gate  */
62097c478bd9Sstevel@tonic-gate void
pm_discard_dependencies(void)62107c478bd9Sstevel@tonic-gate pm_discard_dependencies(void)
62117c478bd9Sstevel@tonic-gate {
62127c478bd9Sstevel@tonic-gate 	pm_pdr_t *rp;
62137c478bd9Sstevel@tonic-gate 	int i;
62147c478bd9Sstevel@tonic-gate 	size_t length;
62157c478bd9Sstevel@tonic-gate 
62167c478bd9Sstevel@tonic-gate #ifdef DEBUG
62177c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_DEP)
62187c478bd9Sstevel@tonic-gate 		prdeps("Before discard\n");
62197c478bd9Sstevel@tonic-gate #endif
62207c478bd9Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_discard_dep_walk, NULL);
62217c478bd9Sstevel@tonic-gate 
62227c478bd9Sstevel@tonic-gate #ifdef DEBUG
62237c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_DEP)
62247c478bd9Sstevel@tonic-gate 		prdeps("After discard\n");
62257c478bd9Sstevel@tonic-gate #endif
62267c478bd9Sstevel@tonic-gate 	while (pm_dep_head) {
62277c478bd9Sstevel@tonic-gate 		rp = pm_dep_head;
62287c478bd9Sstevel@tonic-gate 		if (!rp->pdr_isprop) {
62297c478bd9Sstevel@tonic-gate 			ASSERT(rp->pdr_satisfied == 0);
62307c478bd9Sstevel@tonic-gate 			ASSERT(pm_unresolved_deps);
62317c478bd9Sstevel@tonic-gate 			pm_unresolved_deps--;
62327c478bd9Sstevel@tonic-gate 		} else {
62337c478bd9Sstevel@tonic-gate 			ASSERT(pm_prop_deps);
62347c478bd9Sstevel@tonic-gate 			pm_prop_deps--;
62357c478bd9Sstevel@tonic-gate 		}
62367c478bd9Sstevel@tonic-gate 		pm_dep_head = rp->pdr_next;
62377c478bd9Sstevel@tonic-gate 		if (rp->pdr_kept_count)  {
62387c478bd9Sstevel@tonic-gate 			for (i = 0; i < rp->pdr_kept_count; i++) {
62397c478bd9Sstevel@tonic-gate 				length = strlen(rp->pdr_kept_paths[i]) + 1;
62407c478bd9Sstevel@tonic-gate 				kmem_free(rp->pdr_kept_paths[i], length);
62417c478bd9Sstevel@tonic-gate 			}
62427c478bd9Sstevel@tonic-gate 			kmem_free(rp->pdr_kept_paths,
62436152d498Smh 			    rp->pdr_kept_count * sizeof (char **));
62447c478bd9Sstevel@tonic-gate 		}
62457c478bd9Sstevel@tonic-gate 		kmem_free(rp, rp->pdr_size);
62467c478bd9Sstevel@tonic-gate 	}
62477c478bd9Sstevel@tonic-gate }
62487c478bd9Sstevel@tonic-gate 
62497c478bd9Sstevel@tonic-gate 
62507c478bd9Sstevel@tonic-gate static int
pm_discard_dep_walk(dev_info_t * dip,void * arg)62517c478bd9Sstevel@tonic-gate pm_discard_dep_walk(dev_info_t *dip, void *arg)
62527c478bd9Sstevel@tonic-gate {
62537c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
62547c478bd9Sstevel@tonic-gate 	char *pathbuf;
62557c478bd9Sstevel@tonic-gate 
62567c478bd9Sstevel@tonic-gate 	if (PM_GET_PM_INFO(dip) == NULL)
62577c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
62587c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62597c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
62607c478bd9Sstevel@tonic-gate 	pm_free_keeper(pathbuf, 0);
62617c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
62627c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
62637c478bd9Sstevel@tonic-gate }
62647c478bd9Sstevel@tonic-gate 
62657c478bd9Sstevel@tonic-gate static int
pm_kept_walk(dev_info_t * dip,void * arg)62667c478bd9Sstevel@tonic-gate pm_kept_walk(dev_info_t *dip, void *arg)
62677c478bd9Sstevel@tonic-gate {
62687c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
62697c478bd9Sstevel@tonic-gate 	char *pathbuf;
62707c478bd9Sstevel@tonic-gate 
62717c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62727c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
62737c478bd9Sstevel@tonic-gate 	(void) pm_kept(pathbuf);
62747c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
62757c478bd9Sstevel@tonic-gate 
62767c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
62777c478bd9Sstevel@tonic-gate }
62787c478bd9Sstevel@tonic-gate 
62797c478bd9Sstevel@tonic-gate static int
pm_keeper_walk(dev_info_t * dip,void * arg)62807c478bd9Sstevel@tonic-gate pm_keeper_walk(dev_info_t *dip, void *arg)
62817c478bd9Sstevel@tonic-gate {
62827c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
62837c478bd9Sstevel@tonic-gate 	char *pathbuf;
62847c478bd9Sstevel@tonic-gate 
62857c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62867c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
62877c478bd9Sstevel@tonic-gate 	(void) pm_keeper(pathbuf);
62887c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
62897c478bd9Sstevel@tonic-gate 
62907c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
62917c478bd9Sstevel@tonic-gate }
62927c478bd9Sstevel@tonic-gate 
62937c478bd9Sstevel@tonic-gate static char *
pdw_type_decode(int type)62947c478bd9Sstevel@tonic-gate pdw_type_decode(int type)
62957c478bd9Sstevel@tonic-gate {
62967c478bd9Sstevel@tonic-gate 	switch (type) {
62977c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_POWER_ON:
62987c478bd9Sstevel@tonic-gate 		return ("power on");
62997c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_POWER_OFF:
63007c478bd9Sstevel@tonic-gate 		return ("power off");
63017c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_DETACH:
63027c478bd9Sstevel@tonic-gate 		return ("detach");
63037c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_REMOVE_DEP:
63047c478bd9Sstevel@tonic-gate 		return ("remove dep");
63057c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_BRINGUP_SELF:
63067c478bd9Sstevel@tonic-gate 		return ("bringup self");
63077c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER:
63087c478bd9Sstevel@tonic-gate 		return ("add dependent");
63097c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER_PROP:
63107c478bd9Sstevel@tonic-gate 		return ("add dependent property");
63117c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_KEPT:
63127c478bd9Sstevel@tonic-gate 		return ("kept");
63137c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_KEEPER:
63147c478bd9Sstevel@tonic-gate 		return ("keeper");
63157c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_ATTACH:
63167c478bd9Sstevel@tonic-gate 		return ("attach");
63177c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_CHECK_KEPT:
63187c478bd9Sstevel@tonic-gate 		return ("check kept");
63197c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_CPR_SUSPEND:
63207c478bd9Sstevel@tonic-gate 		return ("suspend");
63217c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_CPR_RESUME:
63227c478bd9Sstevel@tonic-gate 		return ("resume");
63237c478bd9Sstevel@tonic-gate 	default:
63247c478bd9Sstevel@tonic-gate 		return ("unknown");
63257c478bd9Sstevel@tonic-gate 	}
63267c478bd9Sstevel@tonic-gate 
63277c478bd9Sstevel@tonic-gate }
63287c478bd9Sstevel@tonic-gate 
63297c478bd9Sstevel@tonic-gate static void
pm_rele_dep(char * keeper)63307c478bd9Sstevel@tonic-gate pm_rele_dep(char *keeper)
63317c478bd9Sstevel@tonic-gate {
63327c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "rele_dep")
63337c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
63347c478bd9Sstevel@tonic-gate 	char *kept_path = NULL;
63357c478bd9Sstevel@tonic-gate 	dev_info_t *kept = NULL;
63367c478bd9Sstevel@tonic-gate 	int count = 0;
63377c478bd9Sstevel@tonic-gate 
63387c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
63397c478bd9Sstevel@tonic-gate 		if (strcmp(dp->pdr_keeper, keeper) != 0)
63407c478bd9Sstevel@tonic-gate 			continue;
63417c478bd9Sstevel@tonic-gate 		for (count = 0; count < dp->pdr_kept_count; count++) {
63427c478bd9Sstevel@tonic-gate 			kept_path = dp->pdr_kept_paths[count];
63437c478bd9Sstevel@tonic-gate 			if (kept_path == NULL)
63447c478bd9Sstevel@tonic-gate 				continue;
63457c478bd9Sstevel@tonic-gate 			kept = pm_name_to_dip(kept_path, 1);
63467c478bd9Sstevel@tonic-gate 			if (kept) {
63477c478bd9Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: release kept=%s@%s(%s#%d) "
63487c478bd9Sstevel@tonic-gate 				    "of keeper=%s\n", pmf, PM_DEVICE(kept),
63497c478bd9Sstevel@tonic-gate 				    keeper))
63507c478bd9Sstevel@tonic-gate 				ASSERT(DEVI(kept)->devi_pm_kidsupcnt > 0);
63517c478bd9Sstevel@tonic-gate 				pm_rele_power(kept);
63527c478bd9Sstevel@tonic-gate 				ddi_release_devi(kept);
63537c478bd9Sstevel@tonic-gate 			}
63547c478bd9Sstevel@tonic-gate 		}
63557c478bd9Sstevel@tonic-gate 	}
63567c478bd9Sstevel@tonic-gate }
63577c478bd9Sstevel@tonic-gate 
63587c478bd9Sstevel@tonic-gate /*
63597c478bd9Sstevel@tonic-gate  * Called when we are just released from direct PM.  Bring ourself up
63607c478bd9Sstevel@tonic-gate  * if our keeper is up since dependency is not honored while a kept
63617c478bd9Sstevel@tonic-gate  * device is under direct PM.
63627c478bd9Sstevel@tonic-gate  */
63637c478bd9Sstevel@tonic-gate static void
pm_bring_self_up(char * keptpath)63647c478bd9Sstevel@tonic-gate pm_bring_self_up(char *keptpath)
63657c478bd9Sstevel@tonic-gate {
63667c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "bring_self_up")
63677c478bd9Sstevel@tonic-gate 	dev_info_t *kept;
63687c478bd9Sstevel@tonic-gate 	dev_info_t *keeper;
63697c478bd9Sstevel@tonic-gate 	pm_pdr_t *dp;
63707c478bd9Sstevel@tonic-gate 	int i, j;
63713fe80ca4SDan Cross 	int up = 0;
63727c478bd9Sstevel@tonic-gate 
63737c478bd9Sstevel@tonic-gate 	kept = pm_name_to_dip(keptpath, 1);
63747c478bd9Sstevel@tonic-gate 	if (kept == NULL)
63757c478bd9Sstevel@tonic-gate 		return;
63767c478bd9Sstevel@tonic-gate 	PMD(PMD_KEEPS, ("%s: kept=%s@%s(%s#%d)\n", pmf, PM_DEVICE(kept)))
63777c478bd9Sstevel@tonic-gate 	for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
63787c478bd9Sstevel@tonic-gate 		if (dp->pdr_kept_count == 0)
63797c478bd9Sstevel@tonic-gate 			continue;
63807c478bd9Sstevel@tonic-gate 		for (i = 0; i < dp->pdr_kept_count; i++) {
63817c478bd9Sstevel@tonic-gate 			if (strcmp(dp->pdr_kept_paths[i], keptpath) != 0)
63827c478bd9Sstevel@tonic-gate 				continue;
63837c478bd9Sstevel@tonic-gate 			keeper = pm_name_to_dip(dp->pdr_keeper, 1);
63847c478bd9Sstevel@tonic-gate 			if (keeper) {
63857c478bd9Sstevel@tonic-gate 				PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d)\n",
63867c478bd9Sstevel@tonic-gate 				    pmf, PM_DEVICE(keeper)))
63873fe80ca4SDan Cross 				PM_LOCK_POWER(keeper);
63887c478bd9Sstevel@tonic-gate 				for (j = 0; j < PM_NUMCMPTS(keeper);
63897c478bd9Sstevel@tonic-gate 				    j++) {
63907c478bd9Sstevel@tonic-gate 					if (PM_CURPOWER(keeper, j)) {
63917c478bd9Sstevel@tonic-gate 						PMD(PMD_KEEPS, ("%s: comp="
63927c478bd9Sstevel@tonic-gate 						    "%d is up\n", pmf, j))
63937c478bd9Sstevel@tonic-gate 						up++;
63947c478bd9Sstevel@tonic-gate 					}
63957c478bd9Sstevel@tonic-gate 				}
63967c478bd9Sstevel@tonic-gate 				if (up) {
63977c478bd9Sstevel@tonic-gate 					if (PM_SKBU(kept))
63987c478bd9Sstevel@tonic-gate 						DEVI(kept)->devi_pm_flags &=
63997c478bd9Sstevel@tonic-gate 						    ~PMC_SKIP_BRINGUP;
64007c478bd9Sstevel@tonic-gate 					bring_pmdep_up(kept, 1);
64017c478bd9Sstevel@tonic-gate 				}
64023fe80ca4SDan Cross 				PM_UNLOCK_POWER(keeper);
64037c478bd9Sstevel@tonic-gate 				ddi_release_devi(keeper);
64047c478bd9Sstevel@tonic-gate 			}
64057c478bd9Sstevel@tonic-gate 		}
64067c478bd9Sstevel@tonic-gate 	}
64077c478bd9Sstevel@tonic-gate 	ddi_release_devi(kept);
64087c478bd9Sstevel@tonic-gate }
64097c478bd9Sstevel@tonic-gate 
64107c478bd9Sstevel@tonic-gate static void
pm_process_dep_request(pm_dep_wk_t * work)64117c478bd9Sstevel@tonic-gate pm_process_dep_request(pm_dep_wk_t *work)
64127c478bd9Sstevel@tonic-gate {
64137c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "dep_req")
64147c478bd9Sstevel@tonic-gate 	int ret;
64157c478bd9Sstevel@tonic-gate 
64167c478bd9Sstevel@tonic-gate 	PMD(PMD_DEP, ("%s: work=%s\n", pmf,
64177c478bd9Sstevel@tonic-gate 	    pdw_type_decode(work->pdw_type)))
64187c478bd9Sstevel@tonic-gate 	PMD(PMD_DEP, ("%s: keeper=%s, kept=%s\n", pmf,
64197c478bd9Sstevel@tonic-gate 	    (work->pdw_keeper ? work->pdw_keeper : "NULL"),
64207c478bd9Sstevel@tonic-gate 	    (work->pdw_kept ? work->pdw_kept : "NULL")))
64217c478bd9Sstevel@tonic-gate 
6422c6f039c7SToomas Soome 	ret = 0;
64237c478bd9Sstevel@tonic-gate 	switch (work->pdw_type) {
64247c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_POWER_ON:
64257c478bd9Sstevel@tonic-gate 		/* Bring up the kept devices and put a hold on them */
64267c478bd9Sstevel@tonic-gate 		bring_wekeeps_up(work->pdw_keeper);
64277c478bd9Sstevel@tonic-gate 		break;
64287c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_POWER_OFF:
64297c478bd9Sstevel@tonic-gate 		/* Release the kept devices */
64307c478bd9Sstevel@tonic-gate 		pm_rele_dep(work->pdw_keeper);
64317c478bd9Sstevel@tonic-gate 		break;
64327c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_DETACH:
64337c478bd9Sstevel@tonic-gate 		pm_free_keeps(work->pdw_keeper, work->pdw_pwr);
64347c478bd9Sstevel@tonic-gate 		break;
64357c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_REMOVE_DEP:
64367c478bd9Sstevel@tonic-gate 		pm_discard_dependencies();
64377c478bd9Sstevel@tonic-gate 		break;
64387c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_BRINGUP_SELF:
64397c478bd9Sstevel@tonic-gate 		/*
64407c478bd9Sstevel@tonic-gate 		 * We deferred satisfying our dependency till now, so satisfy
64417c478bd9Sstevel@tonic-gate 		 * it again and bring ourselves up.
64427c478bd9Sstevel@tonic-gate 		 */
64437c478bd9Sstevel@tonic-gate 		pm_bring_self_up(work->pdw_kept);
64447c478bd9Sstevel@tonic-gate 		break;
64457c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER:
64467c478bd9Sstevel@tonic-gate 		(void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 0);
64477c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
64487c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
64497c478bd9Sstevel@tonic-gate 		break;
64507c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_RECORD_KEEPER_PROP:
64517c478bd9Sstevel@tonic-gate 		(void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 1);
64527c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
64537c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
64547c478bd9Sstevel@tonic-gate 		break;
64557c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_KEPT:
64567c478bd9Sstevel@tonic-gate 		ret = pm_kept(work->pdw_kept);
64577c478bd9Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_KEPT: pm_kept returns %d\n", pmf,
64587c478bd9Sstevel@tonic-gate 		    ret))
64597c478bd9Sstevel@tonic-gate 		break;
64607c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_KEEPER:
64617c478bd9Sstevel@tonic-gate 		ret = pm_keeper(work->pdw_keeper);
64627c478bd9Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_KEEPER: pm_keeper returns %d\n",
64637c478bd9Sstevel@tonic-gate 		    pmf, ret))
64647c478bd9Sstevel@tonic-gate 		break;
64657c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_ATTACH:
64667c478bd9Sstevel@tonic-gate 		ret = pm_keeper(work->pdw_keeper);
64677c478bd9Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_keeper returns %d\n",
64687c478bd9Sstevel@tonic-gate 		    pmf, ret))
64697c478bd9Sstevel@tonic-gate 		ret = pm_kept(work->pdw_kept);
64707c478bd9Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_kept returns %d\n",
64717c478bd9Sstevel@tonic-gate 		    pmf, ret))
64727c478bd9Sstevel@tonic-gate 		break;
64737c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_CHECK_KEPT:
64747c478bd9Sstevel@tonic-gate 		ret = pm_is_kept(work->pdw_kept);
64757c478bd9Sstevel@tonic-gate 		PMD(PMD_DEP, ("%s: PM_DEP_WK_CHECK_KEPT: kept=%s, ret=%d\n",
64767c478bd9Sstevel@tonic-gate 		    pmf, work->pdw_kept, ret))
64777c478bd9Sstevel@tonic-gate 		break;
64787c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_CPR_SUSPEND:
64797c478bd9Sstevel@tonic-gate 		pm_discard_dependencies();
64807c478bd9Sstevel@tonic-gate 		break;
64817c478bd9Sstevel@tonic-gate 	case PM_DEP_WK_CPR_RESUME:
64827c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
64837c478bd9Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
64847c478bd9Sstevel@tonic-gate 		break;
64857c478bd9Sstevel@tonic-gate 	default:
64867c478bd9Sstevel@tonic-gate 		ASSERT(0);
64877c478bd9Sstevel@tonic-gate 		break;
64887c478bd9Sstevel@tonic-gate 	}
64897c478bd9Sstevel@tonic-gate 	/*
64907c478bd9Sstevel@tonic-gate 	 * Free the work structure if the requester is not waiting
64917c478bd9Sstevel@tonic-gate 	 * Otherwise it is the requester's responsiblity to free it.
64927c478bd9Sstevel@tonic-gate 	 */
64937c478bd9Sstevel@tonic-gate 	if (!work->pdw_wait) {
64947c478bd9Sstevel@tonic-gate 		if (work->pdw_keeper)
64957c478bd9Sstevel@tonic-gate 			kmem_free(work->pdw_keeper,
64967c478bd9Sstevel@tonic-gate 			    strlen(work->pdw_keeper) + 1);
64977c478bd9Sstevel@tonic-gate 		if (work->pdw_kept)
64987c478bd9Sstevel@tonic-gate 			kmem_free(work->pdw_kept, strlen(work->pdw_kept) + 1);
64997c478bd9Sstevel@tonic-gate 		kmem_free(work, sizeof (pm_dep_wk_t));
65007c478bd9Sstevel@tonic-gate 	} else {
65017c478bd9Sstevel@tonic-gate 		/*
65027c478bd9Sstevel@tonic-gate 		 * Notify requester if it is waiting for it.
65037c478bd9Sstevel@tonic-gate 		 */
65047c478bd9Sstevel@tonic-gate 		work->pdw_ret = ret;
65057c478bd9Sstevel@tonic-gate 		work->pdw_done = 1;
65067c478bd9Sstevel@tonic-gate 		cv_signal(&work->pdw_cv);
65077c478bd9Sstevel@tonic-gate 	}
65087c478bd9Sstevel@tonic-gate }
65097c478bd9Sstevel@tonic-gate 
65107c478bd9Sstevel@tonic-gate /*
65117c478bd9Sstevel@tonic-gate  * Process PM dependency requests.
65127c478bd9Sstevel@tonic-gate  */
65137c478bd9Sstevel@tonic-gate static void
pm_dep_thread(void)65147c478bd9Sstevel@tonic-gate pm_dep_thread(void)
65157c478bd9Sstevel@tonic-gate {
65167c478bd9Sstevel@tonic-gate 	pm_dep_wk_t *work;
65177c478bd9Sstevel@tonic-gate 	callb_cpr_t cprinfo;
65187c478bd9Sstevel@tonic-gate 
65197c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &pm_dep_thread_lock, callb_generic_cpr,
65207c478bd9Sstevel@tonic-gate 	    "pm_dep_thread");
65217c478bd9Sstevel@tonic-gate 	for (;;) {
65227c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_dep_thread_lock);
65237c478bd9Sstevel@tonic-gate 		if (pm_dep_thread_workq == NULL) {
65247c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
65257c478bd9Sstevel@tonic-gate 			cv_wait(&pm_dep_thread_cv, &pm_dep_thread_lock);
65267c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo, &pm_dep_thread_lock);
65277c478bd9Sstevel@tonic-gate 		}
65287c478bd9Sstevel@tonic-gate 		work = pm_dep_thread_workq;
65297c478bd9Sstevel@tonic-gate 		pm_dep_thread_workq = work->pdw_next;
65307c478bd9Sstevel@tonic-gate 		if (pm_dep_thread_tail == work)
65317c478bd9Sstevel@tonic-gate 			pm_dep_thread_tail = work->pdw_next;
65327c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_dep_thread_lock);
65337c478bd9Sstevel@tonic-gate 		pm_process_dep_request(work);
65347c478bd9Sstevel@tonic-gate 
65357c478bd9Sstevel@tonic-gate 	}
65367c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
65377c478bd9Sstevel@tonic-gate }
65387c478bd9Sstevel@tonic-gate 
65397c478bd9Sstevel@tonic-gate /*
65407c478bd9Sstevel@tonic-gate  * Set the power level of the indicated device to unknown (if it is not a
65417c478bd9Sstevel@tonic-gate  * backwards compatible device), as it has just been resumed, and it won't
65427c478bd9Sstevel@tonic-gate  * know if the power was removed or not. Adjust parent's kidsupcnt if necessary.
65437c478bd9Sstevel@tonic-gate  */
65447c478bd9Sstevel@tonic-gate void
pm_forget_power_level(dev_info_t * dip)65457c478bd9Sstevel@tonic-gate pm_forget_power_level(dev_info_t *dip)
65467c478bd9Sstevel@tonic-gate {
65477c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
65487c478bd9Sstevel@tonic-gate 	int i, count = 0;
65497c478bd9Sstevel@tonic-gate 
65507c478bd9Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
65517c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
65527c478bd9Sstevel@tonic-gate 			count += (PM_CURPOWER(dip, i) == 0);
65537c478bd9Sstevel@tonic-gate 
65547c478bd9Sstevel@tonic-gate 		if (count && pdip && !PM_WANTS_NOTIFICATION(pdip))
65557c478bd9Sstevel@tonic-gate 			e_pm_hold_rele_power(pdip, count);
65567c478bd9Sstevel@tonic-gate 
65577c478bd9Sstevel@tonic-gate 		/*
65587c478bd9Sstevel@tonic-gate 		 * Count this as a power cycle if we care
65597c478bd9Sstevel@tonic-gate 		 */
65607c478bd9Sstevel@tonic-gate 		if (DEVI(dip)->devi_pm_volpmd &&
65617c478bd9Sstevel@tonic-gate 		    PM_CP(dip, 0)->pmc_cur_pwr == 0)
65627c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_volpmd = 0;
65637c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
65647c478bd9Sstevel@tonic-gate 			e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN);
65657c478bd9Sstevel@tonic-gate 	}
65667c478bd9Sstevel@tonic-gate }
65677c478bd9Sstevel@tonic-gate 
65687c478bd9Sstevel@tonic-gate /*
65697c478bd9Sstevel@tonic-gate  * This function advises the caller whether it should make a power-off
65707c478bd9Sstevel@tonic-gate  * transition at this time or not.  If the transition is not advised
65717c478bd9Sstevel@tonic-gate  * at this time, the time that the next power-off transition can
65727c478bd9Sstevel@tonic-gate  * be made from now is returned through "intervalp" pointer.
65737c478bd9Sstevel@tonic-gate  * This function returns:
65747c478bd9Sstevel@tonic-gate  *
65757c478bd9Sstevel@tonic-gate  *  1  power-off advised
65767c478bd9Sstevel@tonic-gate  *  0  power-off not advised, intervalp will point to seconds from
65777c478bd9Sstevel@tonic-gate  *	  now that a power-off is advised.  If it is passed the number
65787c478bd9Sstevel@tonic-gate  *	  of years that policy specifies the device should last,
65797c478bd9Sstevel@tonic-gate  *	  a large number is returned as the time interval.
65807c478bd9Sstevel@tonic-gate  *  -1  error
65817c478bd9Sstevel@tonic-gate  */
65827c478bd9Sstevel@tonic-gate int
pm_trans_check(struct pm_trans_data * datap,time_t * intervalp)65837c478bd9Sstevel@tonic-gate pm_trans_check(struct pm_trans_data *datap, time_t *intervalp)
65847c478bd9Sstevel@tonic-gate {
65857c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_trans_check")
65867c478bd9Sstevel@tonic-gate 	char dbuf[DC_SCSI_MFR_LEN];
65877c478bd9Sstevel@tonic-gate 	struct pm_scsi_cycles *scp;
65887c478bd9Sstevel@tonic-gate 	int service_years, service_weeks, full_years;
65897c478bd9Sstevel@tonic-gate 	time_t now, service_seconds, tdiff;
65907c478bd9Sstevel@tonic-gate 	time_t within_year, when_allowed;
65917c478bd9Sstevel@tonic-gate 	char *ptr;
65927c478bd9Sstevel@tonic-gate 	int lower_bound_cycles, upper_bound_cycles, cycles_allowed;
65937c478bd9Sstevel@tonic-gate 	int cycles_diff, cycles_over;
65942ee4dfc7SJane Chu 	struct pm_smart_count *smart_p;
65957c478bd9Sstevel@tonic-gate 
65967c478bd9Sstevel@tonic-gate 	if (datap == NULL) {
65977c478bd9Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: NULL data pointer!\n", pmf))
65987c478bd9Sstevel@tonic-gate 		return (-1);
65997c478bd9Sstevel@tonic-gate 	}
66007c478bd9Sstevel@tonic-gate 
66017c478bd9Sstevel@tonic-gate 	if (datap->format == DC_SCSI_FORMAT) {
66027c478bd9Sstevel@tonic-gate 		/*
66037c478bd9Sstevel@tonic-gate 		 * Power cycles of the scsi drives are distributed
66047c478bd9Sstevel@tonic-gate 		 * over 5 years with the following percentage ratio:
66057c478bd9Sstevel@tonic-gate 		 *
66067c478bd9Sstevel@tonic-gate 		 *	30%, 25%, 20%, 15%, and 10%
66077c478bd9Sstevel@tonic-gate 		 *
66087c478bd9Sstevel@tonic-gate 		 * The power cycle quota for each year is distributed
66097c478bd9Sstevel@tonic-gate 		 * linearly through out the year.  The equation for
66107c478bd9Sstevel@tonic-gate 		 * determining the expected cycles is:
66117c478bd9Sstevel@tonic-gate 		 *
66127c478bd9Sstevel@tonic-gate 		 *	e = a * (n / y)
66137c478bd9Sstevel@tonic-gate 		 *
66147c478bd9Sstevel@tonic-gate 		 * e = expected cycles
66157c478bd9Sstevel@tonic-gate 		 * a = allocated cycles for this year
66167c478bd9Sstevel@tonic-gate 		 * n = number of seconds since beginning of this year
66177c478bd9Sstevel@tonic-gate 		 * y = number of seconds in a year
66187c478bd9Sstevel@tonic-gate 		 *
66197c478bd9Sstevel@tonic-gate 		 * Note that beginning of the year starts the day that
66207c478bd9Sstevel@tonic-gate 		 * the drive has been put on service.
66217c478bd9Sstevel@tonic-gate 		 *
66227c478bd9Sstevel@tonic-gate 		 * If the drive has passed its expected cycles, we
66237c478bd9Sstevel@tonic-gate 		 * can determine when it can start to power cycle
66247c478bd9Sstevel@tonic-gate 		 * again to keep it on track to meet the 5-year
66257c478bd9Sstevel@tonic-gate 		 * life expectancy.  The equation for determining
66267c478bd9Sstevel@tonic-gate 		 * when to power cycle is:
66277c478bd9Sstevel@tonic-gate 		 *
66287c478bd9Sstevel@tonic-gate 		 *	w = y * (c / a)
66297c478bd9Sstevel@tonic-gate 		 *
66307c478bd9Sstevel@tonic-gate 		 * w = when it can power cycle again
66317c478bd9Sstevel@tonic-gate 		 * y = number of seconds in a year
66327c478bd9Sstevel@tonic-gate 		 * c = current number of cycles
66337c478bd9Sstevel@tonic-gate 		 * a = allocated cycles for the year
66347c478bd9Sstevel@tonic-gate 		 *
66357c478bd9Sstevel@tonic-gate 		 */
66367c478bd9Sstevel@tonic-gate 		char pcnt[DC_SCSI_NPY] = { 30, 55, 75, 90, 100 };
66377c478bd9Sstevel@tonic-gate 
66387c478bd9Sstevel@tonic-gate 		scp = &datap->un.scsi_cycles;
66397c478bd9Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: format=%d, lifemax=%d, ncycles=%d, "
66407c478bd9Sstevel@tonic-gate 		    "svc_date=%s, svc_flag=%d\n", pmf, datap->format,
66417c478bd9Sstevel@tonic-gate 		    scp->lifemax, scp->ncycles, scp->svc_date, scp->flag))
66427c478bd9Sstevel@tonic-gate 		if (scp->ncycles < 0 || scp->flag != 0) {
66437c478bd9Sstevel@tonic-gate 			PMD(PMD_TCHECK, ("%s: ncycles < 0 || flag != 0\n", pmf))
66447c478bd9Sstevel@tonic-gate 			return (-1);
66457c478bd9Sstevel@tonic-gate 		}
66467c478bd9Sstevel@tonic-gate 
66477c478bd9Sstevel@tonic-gate 		if (scp->ncycles > scp->lifemax) {
66487c478bd9Sstevel@tonic-gate 			*intervalp = (LONG_MAX / hz);
66497c478bd9Sstevel@tonic-gate 			return (0);
66507c478bd9Sstevel@tonic-gate 		}
66517c478bd9Sstevel@tonic-gate 
66527c478bd9Sstevel@tonic-gate 		/*
66537c478bd9Sstevel@tonic-gate 		 * convert service date to time_t
66547c478bd9Sstevel@tonic-gate 		 */
66557c478bd9Sstevel@tonic-gate 		bcopy(scp->svc_date, dbuf, DC_SCSI_YEAR_LEN);
66567c478bd9Sstevel@tonic-gate 		dbuf[DC_SCSI_YEAR_LEN] = '\0';
66577c478bd9Sstevel@tonic-gate 		ptr = dbuf;
66587c478bd9Sstevel@tonic-gate 		service_years = stoi(&ptr) - EPOCH_YEAR;
66597c478bd9Sstevel@tonic-gate 		bcopy(&scp->svc_date[DC_SCSI_YEAR_LEN], dbuf,
66607c478bd9Sstevel@tonic-gate 		    DC_SCSI_WEEK_LEN);
66617c478bd9Sstevel@tonic-gate 		dbuf[DC_SCSI_WEEK_LEN] = '\0';
66627c478bd9Sstevel@tonic-gate 
66637c478bd9Sstevel@tonic-gate 		/*
66647c478bd9Sstevel@tonic-gate 		 * scsi standard does not specify WW data,
66657c478bd9Sstevel@tonic-gate 		 * could be (00-51) or (01-52)
66667c478bd9Sstevel@tonic-gate 		 */
66677c478bd9Sstevel@tonic-gate 		ptr = dbuf;
66687c478bd9Sstevel@tonic-gate 		service_weeks = stoi(&ptr);
66697c478bd9Sstevel@tonic-gate 		if (service_years < 0 ||
66707c478bd9Sstevel@tonic-gate 		    service_weeks < 0 || service_weeks > 52) {
66717c478bd9Sstevel@tonic-gate 			PMD(PMD_TCHECK, ("%s: service year %d and week %d\n",
66727c478bd9Sstevel@tonic-gate 			    pmf, service_years, service_weeks))
66737c478bd9Sstevel@tonic-gate 			return (-1);
66747c478bd9Sstevel@tonic-gate 		}
66757c478bd9Sstevel@tonic-gate 
66767c478bd9Sstevel@tonic-gate 		/*
66777c478bd9Sstevel@tonic-gate 		 * calculate service date in seconds-since-epoch,
66787c478bd9Sstevel@tonic-gate 		 * adding one day for each leap-year.
66797c478bd9Sstevel@tonic-gate 		 *
66807c478bd9Sstevel@tonic-gate 		 * (years-since-epoch + 2) fixes integer truncation,
66817c478bd9Sstevel@tonic-gate 		 * example: (8) leap-years during [1972, 2000]
66827c478bd9Sstevel@tonic-gate 		 * (2000 - 1970) = 30;  and  (30 + 2) / 4 = 8;
66837c478bd9Sstevel@tonic-gate 		 */
66847c478bd9Sstevel@tonic-gate 		service_seconds = (service_years * DC_SPY) +
66857c478bd9Sstevel@tonic-gate 		    (service_weeks * DC_SPW) +
66867c478bd9Sstevel@tonic-gate 		    (((service_years + 2) / 4) * DC_SPD);
66877c478bd9Sstevel@tonic-gate 
66887c478bd9Sstevel@tonic-gate 		now = gethrestime_sec();
66897c478bd9Sstevel@tonic-gate 		/*
66907c478bd9Sstevel@tonic-gate 		 * since the granularity of 'svc_date' is day not second,
66917c478bd9Sstevel@tonic-gate 		 * 'now' should be rounded up to full day.
66927c478bd9Sstevel@tonic-gate 		 */
66937c478bd9Sstevel@tonic-gate 		now = ((now + DC_SPD -1) / DC_SPD) * DC_SPD;
66947c478bd9Sstevel@tonic-gate 		if (service_seconds > now) {
66957c478bd9Sstevel@tonic-gate 			PMD(PMD_TCHECK, ("%s: service date (%ld) later "
66967c478bd9Sstevel@tonic-gate 			    "than now (%ld)!\n", pmf, service_seconds, now))
66977c478bd9Sstevel@tonic-gate 			return (-1);
66987c478bd9Sstevel@tonic-gate 		}
66997c478bd9Sstevel@tonic-gate 
67007c478bd9Sstevel@tonic-gate 		tdiff = now - service_seconds;
67017c478bd9Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: age is %ld sec\n", pmf, tdiff))
67027c478bd9Sstevel@tonic-gate 
67037c478bd9Sstevel@tonic-gate 		/*
67047c478bd9Sstevel@tonic-gate 		 * NOTE - Leap years are not considered in the calculations
67057c478bd9Sstevel@tonic-gate 		 * below.
67067c478bd9Sstevel@tonic-gate 		 */
67077c478bd9Sstevel@tonic-gate 		full_years = (tdiff / DC_SPY);
67087c478bd9Sstevel@tonic-gate 		if ((full_years >= DC_SCSI_NPY) &&
67097c478bd9Sstevel@tonic-gate 		    (scp->ncycles <= scp->lifemax))
67107c478bd9Sstevel@tonic-gate 			return (1);
67117c478bd9Sstevel@tonic-gate 
67127c478bd9Sstevel@tonic-gate 		/*
67137c478bd9Sstevel@tonic-gate 		 * Determine what is the normal cycle usage for the
67147c478bd9Sstevel@tonic-gate 		 * device at the beginning and the end of this year.
67157c478bd9Sstevel@tonic-gate 		 */
67167c478bd9Sstevel@tonic-gate 		lower_bound_cycles = (!full_years) ? 0 :
67177c478bd9Sstevel@tonic-gate 		    ((scp->lifemax * pcnt[full_years - 1]) / 100);
67187c478bd9Sstevel@tonic-gate 		upper_bound_cycles = (scp->lifemax * pcnt[full_years]) / 100;
67197c478bd9Sstevel@tonic-gate 
67207c478bd9Sstevel@tonic-gate 		if (scp->ncycles <= lower_bound_cycles)
67217c478bd9Sstevel@tonic-gate 			return (1);
67227c478bd9Sstevel@tonic-gate 
67237c478bd9Sstevel@tonic-gate 		/*
67247c478bd9Sstevel@tonic-gate 		 * The linear slope that determines how many cycles
67257c478bd9Sstevel@tonic-gate 		 * are allowed this year is number of seconds
67267c478bd9Sstevel@tonic-gate 		 * passed this year over total number of seconds in a year.
67277c478bd9Sstevel@tonic-gate 		 */
67287c478bd9Sstevel@tonic-gate 		cycles_diff = (upper_bound_cycles - lower_bound_cycles);
67297c478bd9Sstevel@tonic-gate 		within_year = (tdiff % DC_SPY);
67307c478bd9Sstevel@tonic-gate 		cycles_allowed = lower_bound_cycles +
67317c478bd9Sstevel@tonic-gate 		    (((uint64_t)cycles_diff * (uint64_t)within_year) / DC_SPY);
67327c478bd9Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: lived %d yrs and %ld secs\n", pmf,
67337c478bd9Sstevel@tonic-gate 		    full_years, within_year))
67347c478bd9Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: # of cycles allowed %d\n", pmf,
67357c478bd9Sstevel@tonic-gate 		    cycles_allowed))
67367c478bd9Sstevel@tonic-gate 
67377c478bd9Sstevel@tonic-gate 		if (scp->ncycles <= cycles_allowed)
67387c478bd9Sstevel@tonic-gate 			return (1);
67397c478bd9Sstevel@tonic-gate 
67407c478bd9Sstevel@tonic-gate 		/*
67417c478bd9Sstevel@tonic-gate 		 * The transition is not advised now but we can
67427c478bd9Sstevel@tonic-gate 		 * determine when the next transition can be made.
67437c478bd9Sstevel@tonic-gate 		 *
67447c478bd9Sstevel@tonic-gate 		 * Depending on how many cycles the device has been
67457c478bd9Sstevel@tonic-gate 		 * over-used, we may need to skip years with
67467c478bd9Sstevel@tonic-gate 		 * different percentage quota in order to determine
67477c478bd9Sstevel@tonic-gate 		 * when the next transition can be made.
67487c478bd9Sstevel@tonic-gate 		 */
67497c478bd9Sstevel@tonic-gate 		cycles_over = (scp->ncycles - lower_bound_cycles);
67507c478bd9Sstevel@tonic-gate 		while (cycles_over > cycles_diff) {
67517c478bd9Sstevel@tonic-gate 			full_years++;
67527c478bd9Sstevel@tonic-gate 			if (full_years >= DC_SCSI_NPY) {
67537c478bd9Sstevel@tonic-gate 				*intervalp = (LONG_MAX / hz);
67547c478bd9Sstevel@tonic-gate 				return (0);
67557c478bd9Sstevel@tonic-gate 			}
67567c478bd9Sstevel@tonic-gate 			cycles_over -= cycles_diff;
67577c478bd9Sstevel@tonic-gate 			lower_bound_cycles = upper_bound_cycles;
67587c478bd9Sstevel@tonic-gate 			upper_bound_cycles =
67597c478bd9Sstevel@tonic-gate 			    (scp->lifemax * pcnt[full_years]) / 100;
67607c478bd9Sstevel@tonic-gate 			cycles_diff = (upper_bound_cycles - lower_bound_cycles);
67617c478bd9Sstevel@tonic-gate 		}
67627c478bd9Sstevel@tonic-gate 
67637c478bd9Sstevel@tonic-gate 		/*
67647c478bd9Sstevel@tonic-gate 		 * The linear slope that determines when the next transition
67657c478bd9Sstevel@tonic-gate 		 * can be made is the relative position of used cycles within a
67667c478bd9Sstevel@tonic-gate 		 * year over total number of cycles within that year.
67677c478bd9Sstevel@tonic-gate 		 */
67687c478bd9Sstevel@tonic-gate 		when_allowed = service_seconds + (full_years * DC_SPY) +
67697c478bd9Sstevel@tonic-gate 		    (((uint64_t)DC_SPY * (uint64_t)cycles_over) / cycles_diff);
67707c478bd9Sstevel@tonic-gate 		*intervalp = (when_allowed - now);
67717c478bd9Sstevel@tonic-gate 		if (*intervalp > (LONG_MAX / hz))
67727c478bd9Sstevel@tonic-gate 			*intervalp = (LONG_MAX / hz);
67737c478bd9Sstevel@tonic-gate 		PMD(PMD_TCHECK, ("%s: no cycle is allowed in %ld secs\n", pmf,
67747c478bd9Sstevel@tonic-gate 		    *intervalp))
67757c478bd9Sstevel@tonic-gate 		return (0);
67762ee4dfc7SJane Chu 	} else if (datap->format == DC_SMART_FORMAT) {
67772ee4dfc7SJane Chu 		/*
67782ee4dfc7SJane Chu 		 * power cycles of SATA disks are reported from SMART
67792ee4dfc7SJane Chu 		 * attributes.
67802ee4dfc7SJane Chu 		 */
67812ee4dfc7SJane Chu 		smart_p = &datap->un.smart_count;
67822ee4dfc7SJane Chu 		if (smart_p->consumed >= smart_p->allowed) {
67832ee4dfc7SJane Chu 			*intervalp = (LONG_MAX / hz);
67842ee4dfc7SJane Chu 			PMD(PMD_TCHECK, ("%s: exceeded lifemax cycles.\n", pmf))
67852ee4dfc7SJane Chu 			return (0);
67862ee4dfc7SJane Chu 		} else
67872ee4dfc7SJane Chu 			return (1);
67887c478bd9Sstevel@tonic-gate 	}
67897c478bd9Sstevel@tonic-gate 
67907c478bd9Sstevel@tonic-gate 	PMD(PMD_TCHECK, ("%s: unknown format!\n", pmf))
67917c478bd9Sstevel@tonic-gate 	return (-1);
67927c478bd9Sstevel@tonic-gate }
67937c478bd9Sstevel@tonic-gate 
67947c478bd9Sstevel@tonic-gate /*
67957c478bd9Sstevel@tonic-gate  * Nexus drivers call into pm framework to indicate which child driver is about
67967c478bd9Sstevel@tonic-gate  * to be installed.  In some platforms, ppm may need to configure the hardware
67977c478bd9Sstevel@tonic-gate  * for successful installation of a driver.
67987c478bd9Sstevel@tonic-gate  */
67997c478bd9Sstevel@tonic-gate int
pm_init_child(dev_info_t * dip)68007c478bd9Sstevel@tonic-gate pm_init_child(dev_info_t *dip)
68017c478bd9Sstevel@tonic-gate {
68027c478bd9Sstevel@tonic-gate 	power_req_t power_req;
68037c478bd9Sstevel@tonic-gate 
68047c478bd9Sstevel@tonic-gate 	ASSERT(ddi_binding_name(dip));
68057c478bd9Sstevel@tonic-gate 	ASSERT(ddi_get_name_addr(dip));
68067c478bd9Sstevel@tonic-gate 	pm_ppm_claim(dip);
68077c478bd9Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {	/* if ppm driver claims the node */
68087c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_INIT_CHILD;
68097c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
68107c478bd9Sstevel@tonic-gate 		ASSERT(PPM(dip) != NULL);
68117c478bd9Sstevel@tonic-gate 		return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req,
68127c478bd9Sstevel@tonic-gate 		    NULL));
68137c478bd9Sstevel@tonic-gate 	} else {
68147c478bd9Sstevel@tonic-gate #ifdef DEBUG
68157c478bd9Sstevel@tonic-gate 		/* pass it to the default handler so we can debug things */
68167c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_INIT_CHILD;
68177c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
68187c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(NULL, dip,
68197c478bd9Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, NULL);
68207c478bd9Sstevel@tonic-gate #endif
68217c478bd9Sstevel@tonic-gate 	}
68227c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
68237c478bd9Sstevel@tonic-gate }
68247c478bd9Sstevel@tonic-gate 
68257c478bd9Sstevel@tonic-gate /*
68267c478bd9Sstevel@tonic-gate  * Bring parent of a node that is about to be probed up to full power, and
68277c478bd9Sstevel@tonic-gate  * arrange for it to stay up until pm_post_probe() or pm_post_attach() decide
68287c478bd9Sstevel@tonic-gate  * it is time to let it go down again
68297c478bd9Sstevel@tonic-gate  */
68307c478bd9Sstevel@tonic-gate void
pm_pre_probe(dev_info_t * dip,pm_ppm_cookie_t * cp)68317c478bd9Sstevel@tonic-gate pm_pre_probe(dev_info_t *dip, pm_ppm_cookie_t *cp)
68327c478bd9Sstevel@tonic-gate {
68337c478bd9Sstevel@tonic-gate 	int result;
68347c478bd9Sstevel@tonic-gate 	power_req_t power_req;
68357c478bd9Sstevel@tonic-gate 
68367c478bd9Sstevel@tonic-gate 	bzero(cp, sizeof (*cp));
68377c478bd9Sstevel@tonic-gate 	cp->ppc_dip = dip;
68387c478bd9Sstevel@tonic-gate 
68397c478bd9Sstevel@tonic-gate 	pm_ppm_claim(dip);
68407c478bd9Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {	/* if ppm driver claims the node */
68417c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_PRE_PROBE;
68427c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
68437c478bd9Sstevel@tonic-gate 		ASSERT(PPM(dip) != NULL);
68447c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(PPM(dip), dip,
68457c478bd9Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
68467c478bd9Sstevel@tonic-gate 		cp->ppc_ppm = PPM(dip);
68477c478bd9Sstevel@tonic-gate 	} else {
68487c478bd9Sstevel@tonic-gate #ifdef DEBUG
68497c478bd9Sstevel@tonic-gate 		/* pass it to the default handler so we can debug things */
68507c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_PRE_PROBE;
68517c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
68527c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(NULL, dip,
68537c478bd9Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
68547c478bd9Sstevel@tonic-gate #endif
68557c478bd9Sstevel@tonic-gate 		cp->ppc_ppm = NULL;
68567c478bd9Sstevel@tonic-gate 	}
68577c478bd9Sstevel@tonic-gate }
68587c478bd9Sstevel@tonic-gate 
68597c478bd9Sstevel@tonic-gate int
pm_pre_config(dev_info_t * dip,char * devnm)68607c478bd9Sstevel@tonic-gate pm_pre_config(dev_info_t *dip, char *devnm)
68617c478bd9Sstevel@tonic-gate {
68627c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pre_config")
68637c478bd9Sstevel@tonic-gate 	int ret;
68647c478bd9Sstevel@tonic-gate 
68657c478bd9Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
68667c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
68677c478bd9Sstevel@tonic-gate 		ret = mdi_power(dip, MDI_PM_PRE_CONFIG, NULL, devnm, 0);
68687c478bd9Sstevel@tonic-gate 		return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
68697c478bd9Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
68707c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
68717c478bd9Sstevel@tonic-gate 
68727c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
68737c478bd9Sstevel@tonic-gate 	pm_hold_power(dip);
68747c478bd9Sstevel@tonic-gate 	ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK);
68757c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS)
68767c478bd9Sstevel@tonic-gate 		pm_rele_power(dip);
68777c478bd9Sstevel@tonic-gate 	return (ret);
68787c478bd9Sstevel@tonic-gate }
68797c478bd9Sstevel@tonic-gate 
68807c478bd9Sstevel@tonic-gate /*
68817c478bd9Sstevel@tonic-gate  * This routine is called by devfs during its walk to unconfigue a node.
68827c478bd9Sstevel@tonic-gate  * If the call is due to auto mod_unloads and the dip is not at its
68837c478bd9Sstevel@tonic-gate  * full power, we return DDI_FAILURE to terminate the walk, otherwise
68847c478bd9Sstevel@tonic-gate  * return DDI_SUCCESS.
68857c478bd9Sstevel@tonic-gate  */
68867c478bd9Sstevel@tonic-gate int
pm_pre_unconfig(dev_info_t * dip,int flags,int * held,char * devnm)68877c478bd9Sstevel@tonic-gate pm_pre_unconfig(dev_info_t *dip, int flags, int *held, char *devnm)
68887c478bd9Sstevel@tonic-gate {
68897c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pre_unconfig")
68907c478bd9Sstevel@tonic-gate 	int ret;
68917c478bd9Sstevel@tonic-gate 
68927c478bd9Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
68937c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf,
68947c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), flags))
68957c478bd9Sstevel@tonic-gate 		ret = mdi_power(dip, MDI_PM_PRE_UNCONFIG, held, devnm, flags);
68967c478bd9Sstevel@tonic-gate 		return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
68977c478bd9Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
68987c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
68997c478bd9Sstevel@tonic-gate 
69007c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, PM_DEVICE(dip),
69017c478bd9Sstevel@tonic-gate 	    flags))
69027c478bd9Sstevel@tonic-gate 	*held = 0;
69037c478bd9Sstevel@tonic-gate 
69047c478bd9Sstevel@tonic-gate 	/*
69057c478bd9Sstevel@tonic-gate 	 * If the dip is a leaf node, don't power it up.
69067c478bd9Sstevel@tonic-gate 	 */
69077c478bd9Sstevel@tonic-gate 	if (!ddi_get_child(dip))
69087c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
69097c478bd9Sstevel@tonic-gate 
69107c478bd9Sstevel@tonic-gate 	/*
69117c478bd9Sstevel@tonic-gate 	 * Do not power up the node if it is called due to auto-modunload.
69127c478bd9Sstevel@tonic-gate 	 */
69137c478bd9Sstevel@tonic-gate 	if ((flags & NDI_AUTODETACH) && !pm_all_at_normal(dip))
69147c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
69157c478bd9Sstevel@tonic-gate 
69167c478bd9Sstevel@tonic-gate 	pm_hold_power(dip);
69177c478bd9Sstevel@tonic-gate 	*held = 1;
69187c478bd9Sstevel@tonic-gate 	ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK);
69197c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS) {
69207c478bd9Sstevel@tonic-gate 		pm_rele_power(dip);
69217c478bd9Sstevel@tonic-gate 		*held = 0;
69227c478bd9Sstevel@tonic-gate 	}
69237c478bd9Sstevel@tonic-gate 	return (ret);
69247c478bd9Sstevel@tonic-gate }
69257c478bd9Sstevel@tonic-gate 
69267c478bd9Sstevel@tonic-gate /*
69277c478bd9Sstevel@tonic-gate  * Notify ppm of attach action.  Parent is already held at full power by
69287c478bd9Sstevel@tonic-gate  * probe action.
69297c478bd9Sstevel@tonic-gate  */
69307c478bd9Sstevel@tonic-gate void
pm_pre_attach(dev_info_t * dip,pm_ppm_cookie_t * cp,ddi_attach_cmd_t cmd)69317c478bd9Sstevel@tonic-gate pm_pre_attach(dev_info_t *dip, pm_ppm_cookie_t *cp, ddi_attach_cmd_t cmd)
69327c478bd9Sstevel@tonic-gate {
69337c478bd9Sstevel@tonic-gate 	static char *me = "pm_pre_attach";
69347c478bd9Sstevel@tonic-gate 	power_req_t power_req;
69357c478bd9Sstevel@tonic-gate 	int result;
69367c478bd9Sstevel@tonic-gate 
69377c478bd9Sstevel@tonic-gate 	/*
69387c478bd9Sstevel@tonic-gate 	 * Initialize and fill in the PPM cookie
69397c478bd9Sstevel@tonic-gate 	 */
69407c478bd9Sstevel@tonic-gate 	bzero(cp, sizeof (*cp));
69417c478bd9Sstevel@tonic-gate 	cp->ppc_cmd = (int)cmd;
69427c478bd9Sstevel@tonic-gate 	cp->ppc_ppm = PPM(dip);
69437c478bd9Sstevel@tonic-gate 	cp->ppc_dip = dip;
69447c478bd9Sstevel@tonic-gate 
69457c478bd9Sstevel@tonic-gate 	/*
69467c478bd9Sstevel@tonic-gate 	 * DDI_ATTACH and DDI_RESUME cmds need to call platform specific
69477c478bd9Sstevel@tonic-gate 	 * Power Management stuff. DDI_RESUME also has to purge it's
69487c478bd9Sstevel@tonic-gate 	 * powerlevel information.
69497c478bd9Sstevel@tonic-gate 	 */
69507c478bd9Sstevel@tonic-gate 	switch (cmd) {
69517c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
69527c478bd9Sstevel@tonic-gate 		if (cp->ppc_ppm) {	/* if ppm driver claims the node */
69537c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_ATTACH;
69547c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
69557c478bd9Sstevel@tonic-gate 			ASSERT(PPM(dip));
69567c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(cp->ppc_ppm, dip, DDI_CTLOPS_POWER,
69577c478bd9Sstevel@tonic-gate 			    &power_req, &result);
69587c478bd9Sstevel@tonic-gate 		}
69597c478bd9Sstevel@tonic-gate #ifdef DEBUG
69607c478bd9Sstevel@tonic-gate 		else {
69617c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_ATTACH;
69627c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
69637c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(NULL, dip,
69647c478bd9Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
69657c478bd9Sstevel@tonic-gate 		}
69667c478bd9Sstevel@tonic-gate #endif
69677c478bd9Sstevel@tonic-gate 		break;
69687c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
69697c478bd9Sstevel@tonic-gate 		pm_forget_power_level(dip);
69707c478bd9Sstevel@tonic-gate 
69717c478bd9Sstevel@tonic-gate 		if (cp->ppc_ppm) {	/* if ppm driver claims the node */
69727c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_RESUME;
69737c478bd9Sstevel@tonic-gate 			power_req.req.resume_req.who = cp->ppc_dip;
69747c478bd9Sstevel@tonic-gate 			power_req.req.resume_req.cmd =
69757c478bd9Sstevel@tonic-gate 			    (ddi_attach_cmd_t)cp->ppc_cmd;
69767c478bd9Sstevel@tonic-gate 			ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
69777c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
69787c478bd9Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
69797c478bd9Sstevel@tonic-gate 		}
69807c478bd9Sstevel@tonic-gate #ifdef DEBUG
69817c478bd9Sstevel@tonic-gate 		else {
69827c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_RESUME;
69837c478bd9Sstevel@tonic-gate 			power_req.req.resume_req.who = cp->ppc_dip;
69847c478bd9Sstevel@tonic-gate 			power_req.req.resume_req.cmd =
69857c478bd9Sstevel@tonic-gate 			    (ddi_attach_cmd_t)cp->ppc_cmd;
69867c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(NULL, cp->ppc_dip,
69877c478bd9Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
69887c478bd9Sstevel@tonic-gate 		}
69897c478bd9Sstevel@tonic-gate #endif
69907c478bd9Sstevel@tonic-gate 		break;
69917c478bd9Sstevel@tonic-gate 
69927c478bd9Sstevel@tonic-gate 	case DDI_PM_RESUME:
69937c478bd9Sstevel@tonic-gate 		break;
69947c478bd9Sstevel@tonic-gate 
69957c478bd9Sstevel@tonic-gate 	default:
69967c478bd9Sstevel@tonic-gate 		panic(me);
69977c478bd9Sstevel@tonic-gate 	}
69987c478bd9Sstevel@tonic-gate }
69997c478bd9Sstevel@tonic-gate 
70007c478bd9Sstevel@tonic-gate /*
70017c478bd9Sstevel@tonic-gate  * Nexus drivers call into pm framework to indicate which child driver is
70027c478bd9Sstevel@tonic-gate  * being uninstalled.  In some platforms, ppm may need to reconfigure the
70037c478bd9Sstevel@tonic-gate  * hardware since the device driver is no longer installed.
70047c478bd9Sstevel@tonic-gate  */
70057c478bd9Sstevel@tonic-gate int
pm_uninit_child(dev_info_t * dip)70067c478bd9Sstevel@tonic-gate pm_uninit_child(dev_info_t *dip)
70077c478bd9Sstevel@tonic-gate {
70087c478bd9Sstevel@tonic-gate 	power_req_t power_req;
70097c478bd9Sstevel@tonic-gate 
70107c478bd9Sstevel@tonic-gate 	ASSERT(ddi_binding_name(dip));
70117c478bd9Sstevel@tonic-gate 	ASSERT(ddi_get_name_addr(dip));
70127c478bd9Sstevel@tonic-gate 	pm_ppm_claim(dip);
70137c478bd9Sstevel@tonic-gate 	if (pm_ppm_claimed(dip)) {	/* if ppm driver claims the node */
70147c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_UNINIT_CHILD;
70157c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
70167c478bd9Sstevel@tonic-gate 		ASSERT(PPM(dip));
70177c478bd9Sstevel@tonic-gate 		return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req,
70187c478bd9Sstevel@tonic-gate 		    NULL));
70197c478bd9Sstevel@tonic-gate 	} else {
70207c478bd9Sstevel@tonic-gate #ifdef DEBUG
70217c478bd9Sstevel@tonic-gate 		/* pass it to the default handler so we can debug things */
70227c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_UNINIT_CHILD;
70237c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = dip;
70247c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(NULL, dip, DDI_CTLOPS_POWER, &power_req, NULL);
70257c478bd9Sstevel@tonic-gate #endif
70267c478bd9Sstevel@tonic-gate 	}
70277c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
70287c478bd9Sstevel@tonic-gate }
70297c478bd9Sstevel@tonic-gate /*
70307c478bd9Sstevel@tonic-gate  * Decrement kidsupcnt so scan can turn the parent back off if it is idle
70317c478bd9Sstevel@tonic-gate  * Also notify ppm of result of probe if there is a ppm that cares
70327c478bd9Sstevel@tonic-gate  */
70337c478bd9Sstevel@tonic-gate void
pm_post_probe(pm_ppm_cookie_t * cp,int ret,int probe_failed)70347c478bd9Sstevel@tonic-gate pm_post_probe(pm_ppm_cookie_t *cp, int ret, int probe_failed)
70357c478bd9Sstevel@tonic-gate {
70367c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(probe_failed))
70377c478bd9Sstevel@tonic-gate 	int result;
70387c478bd9Sstevel@tonic-gate 	power_req_t power_req;
70397c478bd9Sstevel@tonic-gate 
70407c478bd9Sstevel@tonic-gate 	if (cp->ppc_ppm) {	/* if ppm driver claims the node */
70417c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_PROBE;
70427c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
70437c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
70447c478bd9Sstevel@tonic-gate 		ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
70457c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, DDI_CTLOPS_POWER,
70467c478bd9Sstevel@tonic-gate 		    &power_req, &result);
70477c478bd9Sstevel@tonic-gate 	}
70487c478bd9Sstevel@tonic-gate #ifdef DEBUG
70497c478bd9Sstevel@tonic-gate 	else {
70507c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_PROBE;
70517c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
70527c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
70537c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(NULL, cp->ppc_dip, DDI_CTLOPS_POWER,
70547c478bd9Sstevel@tonic-gate 		    &power_req, &result);
70557c478bd9Sstevel@tonic-gate 	}
70567c478bd9Sstevel@tonic-gate #endif
70577c478bd9Sstevel@tonic-gate }
70587c478bd9Sstevel@tonic-gate 
70597c478bd9Sstevel@tonic-gate void
pm_post_config(dev_info_t * dip,char * devnm)70607c478bd9Sstevel@tonic-gate pm_post_config(dev_info_t *dip, char *devnm)
70617c478bd9Sstevel@tonic-gate {
70627c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "post_config")
70637c478bd9Sstevel@tonic-gate 
70647c478bd9Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
70657c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
70667c478bd9Sstevel@tonic-gate 		(void) mdi_power(dip, MDI_PM_POST_CONFIG, NULL, devnm, 0);
70677c478bd9Sstevel@tonic-gate 		return;
70687c478bd9Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
70697c478bd9Sstevel@tonic-gate 		return;
70707c478bd9Sstevel@tonic-gate 
70717c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
70727c478bd9Sstevel@tonic-gate 	pm_rele_power(dip);
70737c478bd9Sstevel@tonic-gate }
70747c478bd9Sstevel@tonic-gate 
70757c478bd9Sstevel@tonic-gate void
pm_post_unconfig(dev_info_t * dip,int held,char * devnm)70767c478bd9Sstevel@tonic-gate pm_post_unconfig(dev_info_t *dip, int held, char *devnm)
70777c478bd9Sstevel@tonic-gate {
70787c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "post_unconfig")
70797c478bd9Sstevel@tonic-gate 
70807c478bd9Sstevel@tonic-gate 	if (MDI_VHCI(dip)) {
70817c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf,
70827c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), held))
70837c478bd9Sstevel@tonic-gate 		(void) mdi_power(dip, MDI_PM_POST_UNCONFIG, &held, devnm, 0);
70847c478bd9Sstevel@tonic-gate 		return;
70857c478bd9Sstevel@tonic-gate 	} else if (!PM_GET_PM_INFO(dip))
70867c478bd9Sstevel@tonic-gate 		return;
70877c478bd9Sstevel@tonic-gate 
70887c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, PM_DEVICE(dip),
70897c478bd9Sstevel@tonic-gate 	    held))
70907c478bd9Sstevel@tonic-gate 	if (!held)
70917c478bd9Sstevel@tonic-gate 		return;
70927c478bd9Sstevel@tonic-gate 	/*
70937c478bd9Sstevel@tonic-gate 	 * We have held power in pre_unconfig, release it here.
70947c478bd9Sstevel@tonic-gate 	 */
70957c478bd9Sstevel@tonic-gate 	pm_rele_power(dip);
70967c478bd9Sstevel@tonic-gate }
70977c478bd9Sstevel@tonic-gate 
70987c478bd9Sstevel@tonic-gate /*
70997c478bd9Sstevel@tonic-gate  * Notify ppm of result of attach if there is a ppm that cares
71007c478bd9Sstevel@tonic-gate  */
71017c478bd9Sstevel@tonic-gate void
pm_post_attach(pm_ppm_cookie_t * cp,int ret)71027c478bd9Sstevel@tonic-gate pm_post_attach(pm_ppm_cookie_t *cp, int ret)
71037c478bd9Sstevel@tonic-gate {
71047c478bd9Sstevel@tonic-gate 	int result;
71057c478bd9Sstevel@tonic-gate 	power_req_t power_req;
71067c478bd9Sstevel@tonic-gate 	dev_info_t	*dip;
71077c478bd9Sstevel@tonic-gate 
71087c478bd9Sstevel@tonic-gate 	if (cp->ppc_cmd != DDI_ATTACH)
71097c478bd9Sstevel@tonic-gate 		return;
71107c478bd9Sstevel@tonic-gate 
71117c478bd9Sstevel@tonic-gate 	dip = cp->ppc_dip;
71127c478bd9Sstevel@tonic-gate 
71137c478bd9Sstevel@tonic-gate 	if (ret == DDI_SUCCESS) {
71147c478bd9Sstevel@tonic-gate 		/*
71157c478bd9Sstevel@tonic-gate 		 * Attach succeeded, so proceed to doing post-attach pm tasks
71167c478bd9Sstevel@tonic-gate 		 */
71177c478bd9Sstevel@tonic-gate 		if (PM_GET_PM_INFO(dip) == NULL)
71187c478bd9Sstevel@tonic-gate 			(void) pm_start(dip);
71197c478bd9Sstevel@tonic-gate 	} else {
71207c478bd9Sstevel@tonic-gate 		/*
71217c478bd9Sstevel@tonic-gate 		 * Attach may have got pm started before failing
71227c478bd9Sstevel@tonic-gate 		 */
71237c478bd9Sstevel@tonic-gate 		pm_stop(dip);
71247c478bd9Sstevel@tonic-gate 	}
71257c478bd9Sstevel@tonic-gate 
71267c478bd9Sstevel@tonic-gate 	if (cp->ppc_ppm) {	/* if ppm driver claims the node */
71277c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_ATTACH;
71287c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
71297c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
71307c478bd9Sstevel@tonic-gate 		ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
71317c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
71327c478bd9Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
71337c478bd9Sstevel@tonic-gate 	}
71347c478bd9Sstevel@tonic-gate #ifdef DEBUG
71357c478bd9Sstevel@tonic-gate 	else {
71367c478bd9Sstevel@tonic-gate 		power_req.request_type = PMR_PPM_POST_ATTACH;
71377c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.who = cp->ppc_dip;
71387c478bd9Sstevel@tonic-gate 		power_req.req.ppm_config_req.result = ret;
71397c478bd9Sstevel@tonic-gate 		(void) pm_ctlops(NULL, cp->ppc_dip,
71407c478bd9Sstevel@tonic-gate 		    DDI_CTLOPS_POWER, &power_req, &result);
71417c478bd9Sstevel@tonic-gate 	}
71427c478bd9Sstevel@tonic-gate #endif
71437c478bd9Sstevel@tonic-gate }
71447c478bd9Sstevel@tonic-gate 
71457c478bd9Sstevel@tonic-gate /*
71467c478bd9Sstevel@tonic-gate  * Notify ppm of attach action.  Parent is already held at full power by
71477c478bd9Sstevel@tonic-gate  * probe action.
71487c478bd9Sstevel@tonic-gate  */
71497c478bd9Sstevel@tonic-gate void
pm_pre_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,pm_ppm_cookie_t * cp)71507c478bd9Sstevel@tonic-gate pm_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, pm_ppm_cookie_t *cp)
71517c478bd9Sstevel@tonic-gate {
71527c478bd9Sstevel@tonic-gate 	int result;
71537c478bd9Sstevel@tonic-gate 	power_req_t power_req;
71547c478bd9Sstevel@tonic-gate 
71557c478bd9Sstevel@tonic-gate 	bzero(cp, sizeof (*cp));
71567c478bd9Sstevel@tonic-gate 	cp->ppc_dip = dip;
71577c478bd9Sstevel@tonic-gate 	cp->ppc_cmd = (int)cmd;
71587c478bd9Sstevel@tonic-gate 
71597c478bd9Sstevel@tonic-gate 	switch (cmd) {
71607c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
71617c478bd9Sstevel@tonic-gate 		pm_detaching(dip);		/* suspend pm while detaching */
71627c478bd9Sstevel@tonic-gate 		if (pm_ppm_claimed(dip)) {	/* if ppm driver claims node */
71637c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_DETACH;
71647c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
71657c478bd9Sstevel@tonic-gate 			ASSERT(PPM(dip));
71667c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
71677c478bd9Sstevel@tonic-gate 			    &power_req, &result);
71687c478bd9Sstevel@tonic-gate 			cp->ppc_ppm = PPM(dip);
71697c478bd9Sstevel@tonic-gate 		} else {
71707c478bd9Sstevel@tonic-gate #ifdef DEBUG
71717c478bd9Sstevel@tonic-gate 			/* pass to the default handler so we can debug things */
71727c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_PRE_DETACH;
71737c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = dip;
71747c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(NULL, dip,
71757c478bd9Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
71767c478bd9Sstevel@tonic-gate #endif
71777c478bd9Sstevel@tonic-gate 			cp->ppc_ppm = NULL;
71787c478bd9Sstevel@tonic-gate 		}
71797c478bd9Sstevel@tonic-gate 		break;
71807c478bd9Sstevel@tonic-gate 
71817c478bd9Sstevel@tonic-gate 	default:
71827c478bd9Sstevel@tonic-gate 		break;
71837c478bd9Sstevel@tonic-gate 	}
71847c478bd9Sstevel@tonic-gate }
71857c478bd9Sstevel@tonic-gate 
71867c478bd9Sstevel@tonic-gate /*
71877c478bd9Sstevel@tonic-gate  * Dip is either a leaf node that exported "no-involuntary-power-cycles" prop.,
71887c478bd9Sstevel@tonic-gate  * (if devi_pm_noinvol count is 0) or an ancestor of such a node.  We need to
71897c478bd9Sstevel@tonic-gate  * make an entry to record the details, which includes certain flag settings.
71907c478bd9Sstevel@tonic-gate  */
71917c478bd9Sstevel@tonic-gate static void
pm_record_invol_path(char * path,int flags,int noinvolpm,int volpmd,int wasvolpmd,major_t major)71927c478bd9Sstevel@tonic-gate pm_record_invol_path(char *path, int flags, int noinvolpm, int volpmd,
71937c478bd9Sstevel@tonic-gate     int wasvolpmd, major_t major)
71947c478bd9Sstevel@tonic-gate {
71957c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "record_invol_path")
71967c478bd9Sstevel@tonic-gate 	major_t pm_path_to_major(char *);
71977c478bd9Sstevel@tonic-gate 	size_t plen;
71987c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip, *np, *pp;
71997c478bd9Sstevel@tonic-gate 	pp = NULL;
72007c478bd9Sstevel@tonic-gate 
72017c478bd9Sstevel@tonic-gate 	plen = strlen(path) + 1;
72027c478bd9Sstevel@tonic-gate 	np = kmem_zalloc(sizeof (*np), KM_SLEEP);
72037c478bd9Sstevel@tonic-gate 	np->ni_size = plen;
72047c478bd9Sstevel@tonic-gate 	np->ni_path = kmem_alloc(plen, KM_SLEEP);
72057c478bd9Sstevel@tonic-gate 	np->ni_noinvolpm = noinvolpm;
72067c478bd9Sstevel@tonic-gate 	np->ni_volpmd = volpmd;
72077c478bd9Sstevel@tonic-gate 	np->ni_wasvolpmd = wasvolpmd;
72087c478bd9Sstevel@tonic-gate 	np->ni_flags = flags;
72097c478bd9Sstevel@tonic-gate 	(void) strcpy(np->ni_path, path);
72107c478bd9Sstevel@tonic-gate 	/*
72117c478bd9Sstevel@tonic-gate 	 * If we haven't actually seen the node attached, it is hard to figure
72127c478bd9Sstevel@tonic-gate 	 * out its major.  If we could hold the node by path, we would be much
72137c478bd9Sstevel@tonic-gate 	 * happier here.
72147c478bd9Sstevel@tonic-gate 	 */
7215a204de77Scth 	if (major == DDI_MAJOR_T_NONE) {
72167c478bd9Sstevel@tonic-gate 		np->ni_major = pm_path_to_major(path);
72177c478bd9Sstevel@tonic-gate 	} else {
72187c478bd9Sstevel@tonic-gate 		np->ni_major = major;
72197c478bd9Sstevel@tonic-gate 	}
72207c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
72217c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
72227c478bd9Sstevel@tonic-gate 		int comp = strcmp(path, ip->ni_path);
72237c478bd9Sstevel@tonic-gate 		if (comp < 0) {
72247c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: %s insert before %s\n",
72257c478bd9Sstevel@tonic-gate 			    pmf, path, ip->ni_path))
72267c478bd9Sstevel@tonic-gate 			/* insert before current entry */
72277c478bd9Sstevel@tonic-gate 			np->ni_next = ip;
72287c478bd9Sstevel@tonic-gate 			if (pp) {
72297c478bd9Sstevel@tonic-gate 				pp->ni_next = np;
72307c478bd9Sstevel@tonic-gate 			} else {
72317c478bd9Sstevel@tonic-gate 				pm_noinvol_head = np;
72327c478bd9Sstevel@tonic-gate 			}
72337c478bd9Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
72347c478bd9Sstevel@tonic-gate #ifdef DEBUG
72357c478bd9Sstevel@tonic-gate 			if (pm_debug & PMD_NOINVOL)
72367c478bd9Sstevel@tonic-gate 				pr_noinvol("record_invol_path exit0");
72377c478bd9Sstevel@tonic-gate #endif
72387c478bd9Sstevel@tonic-gate 			return;
72397c478bd9Sstevel@tonic-gate 		} else if (comp == 0) {
72407c478bd9Sstevel@tonic-gate 			panic("%s already in pm_noinvol list", path);
72417c478bd9Sstevel@tonic-gate 		}
72427c478bd9Sstevel@tonic-gate 	}
72437c478bd9Sstevel@tonic-gate 	/*
72447c478bd9Sstevel@tonic-gate 	 * If we did not find an entry in the list that this should go before,
72457c478bd9Sstevel@tonic-gate 	 * then it must go at the end
72467c478bd9Sstevel@tonic-gate 	 */
72477c478bd9Sstevel@tonic-gate 	if (pp) {
72487c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: %s append after %s\n", pmf, path,
72497c478bd9Sstevel@tonic-gate 		    pp->ni_path))
72507c478bd9Sstevel@tonic-gate 		ASSERT(pp->ni_next == 0);
72517c478bd9Sstevel@tonic-gate 		pp->ni_next = np;
72527c478bd9Sstevel@tonic-gate 	} else {
72537c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: %s added to end-of-list\n", pmf, path))
72547c478bd9Sstevel@tonic-gate 		ASSERT(!pm_noinvol_head);
72557c478bd9Sstevel@tonic-gate 		pm_noinvol_head = np;
72567c478bd9Sstevel@tonic-gate 	}
72577c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
72587c478bd9Sstevel@tonic-gate #ifdef DEBUG
72597c478bd9Sstevel@tonic-gate 	if (pm_debug & PMD_NOINVOL)
72607c478bd9Sstevel@tonic-gate 		pr_noinvol("record_invol_path exit");
72617c478bd9Sstevel@tonic-gate #endif
72627c478bd9Sstevel@tonic-gate }
72637c478bd9Sstevel@tonic-gate 
72647c478bd9Sstevel@tonic-gate void
pm_record_invol(dev_info_t * dip)72657c478bd9Sstevel@tonic-gate pm_record_invol(dev_info_t *dip)
72667c478bd9Sstevel@tonic-gate {
72677c478bd9Sstevel@tonic-gate 	char *pathbuf;
72687c478bd9Sstevel@tonic-gate 	int pm_all_components_off(dev_info_t *);
72697c478bd9Sstevel@tonic-gate 	int volpmd = (PM_NUMCMPTS(dip) > 0) && pm_all_components_off(dip);
72707c478bd9Sstevel@tonic-gate 
72717c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
72727c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
72737c478bd9Sstevel@tonic-gate 
72747c478bd9Sstevel@tonic-gate 	pm_record_invol_path(pathbuf, (DEVI(dip)->devi_pm_flags &
72757c478bd9Sstevel@tonic-gate 	    (PMC_NO_INVOL | PMC_CONSOLE_FB)), DEVI(dip)->devi_pm_noinvolpm,
72767c478bd9Sstevel@tonic-gate 	    DEVI(dip)->devi_pm_volpmd, volpmd, PM_MAJOR(dip));
72777c478bd9Sstevel@tonic-gate 
72787c478bd9Sstevel@tonic-gate 	/*
72797c478bd9Sstevel@tonic-gate 	 * If this child's detach will be holding up its ancestors, then we
72807c478bd9Sstevel@tonic-gate 	 * allow for an exception to that if all children of this type have
72817c478bd9Sstevel@tonic-gate 	 * gone down voluntarily.
72827c478bd9Sstevel@tonic-gate 	 * Now walk down the tree incrementing devi_pm_noinvolpm
72837c478bd9Sstevel@tonic-gate 	 */
72847c478bd9Sstevel@tonic-gate 	(void) pm_noinvol_update(PM_BP_NOINVOL_DETACH, 0, volpmd, pathbuf,
72857c478bd9Sstevel@tonic-gate 	    dip);
72867c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
72877c478bd9Sstevel@tonic-gate }
72887c478bd9Sstevel@tonic-gate 
72897c478bd9Sstevel@tonic-gate void
pm_post_detach(pm_ppm_cookie_t * cp,int ret)72907c478bd9Sstevel@tonic-gate pm_post_detach(pm_ppm_cookie_t *cp, int ret)
72917c478bd9Sstevel@tonic-gate {
72927c478bd9Sstevel@tonic-gate 	dev_info_t *dip = cp->ppc_dip;
72937c478bd9Sstevel@tonic-gate 	int result;
72947c478bd9Sstevel@tonic-gate 	power_req_t power_req;
72957c478bd9Sstevel@tonic-gate 
72967c478bd9Sstevel@tonic-gate 	switch (cp->ppc_cmd) {
72977c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
72987c478bd9Sstevel@tonic-gate 		if (cp->ppc_ppm) {	/* if ppm driver claims the node */
72997c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_POST_DETACH;
73007c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = cp->ppc_dip;
73017c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.result = ret;
73027c478bd9Sstevel@tonic-gate 			ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
73037c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
73047c478bd9Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
73057c478bd9Sstevel@tonic-gate 		}
73067c478bd9Sstevel@tonic-gate #ifdef DEBUG
73077c478bd9Sstevel@tonic-gate 		else {
73087c478bd9Sstevel@tonic-gate 			power_req.request_type = PMR_PPM_POST_DETACH;
73097c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.who = cp->ppc_dip;
73107c478bd9Sstevel@tonic-gate 			power_req.req.ppm_config_req.result = ret;
73117c478bd9Sstevel@tonic-gate 			(void) pm_ctlops(NULL, cp->ppc_dip,
73127c478bd9Sstevel@tonic-gate 			    DDI_CTLOPS_POWER, &power_req, &result);
73137c478bd9Sstevel@tonic-gate 		}
73147c478bd9Sstevel@tonic-gate #endif
73157c478bd9Sstevel@tonic-gate 		if (ret == DDI_SUCCESS) {
73167c478bd9Sstevel@tonic-gate 			/*
73177c478bd9Sstevel@tonic-gate 			 * For hotplug detach we assume it is *really* gone
73187c478bd9Sstevel@tonic-gate 			 */
73197c478bd9Sstevel@tonic-gate 			if (cp->ppc_cmd == DDI_DETACH &&
73207c478bd9Sstevel@tonic-gate 			    ((DEVI(dip)->devi_pm_flags &
73217c478bd9Sstevel@tonic-gate 			    (PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
73227c478bd9Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_noinvolpm))
73237c478bd9Sstevel@tonic-gate 				pm_record_invol(dip);
73247c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags &=
73257c478bd9Sstevel@tonic-gate 			    ~(PMC_NO_INVOL | PMC_NOINVOL_DONE);
73267c478bd9Sstevel@tonic-gate 
73277c478bd9Sstevel@tonic-gate 			/*
73287c478bd9Sstevel@tonic-gate 			 * If console fb is detaching, then we don't need to
73297c478bd9Sstevel@tonic-gate 			 * worry any more about it going off (pm_detaching has
73307c478bd9Sstevel@tonic-gate 			 * brought up all components)
73317c478bd9Sstevel@tonic-gate 			 */
73327c478bd9Sstevel@tonic-gate 			if (PM_IS_CFB(dip)) {
73337c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
73347c478bd9Sstevel@tonic-gate 				ASSERT(cfb_dip_detaching);
73357c478bd9Sstevel@tonic-gate 				ASSERT(cfb_dip == NULL);
73367c478bd9Sstevel@tonic-gate 				ASSERT(pm_cfb_comps_off == 0);
73377c478bd9Sstevel@tonic-gate 				cfb_dip_detaching = NULL;
73387c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
73397c478bd9Sstevel@tonic-gate 			}
73407c478bd9Sstevel@tonic-gate 			pm_stop(dip);	/* make it permanent */
73417c478bd9Sstevel@tonic-gate 		} else {
73427c478bd9Sstevel@tonic-gate 			if (PM_IS_CFB(dip)) {
73437c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
73447c478bd9Sstevel@tonic-gate 				ASSERT(cfb_dip_detaching);
73457c478bd9Sstevel@tonic-gate 				ASSERT(cfb_dip == NULL);
73467c478bd9Sstevel@tonic-gate 				ASSERT(pm_cfb_comps_off == 0);
73477c478bd9Sstevel@tonic-gate 				cfb_dip = cfb_dip_detaching;
73487c478bd9Sstevel@tonic-gate 				cfb_dip_detaching = NULL;
73497c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
73507c478bd9Sstevel@tonic-gate 			}
73517c478bd9Sstevel@tonic-gate 			pm_detach_failed(dip);	/* resume power management */
73527c478bd9Sstevel@tonic-gate 		}
73537c478bd9Sstevel@tonic-gate 		break;
73547c478bd9Sstevel@tonic-gate 	case DDI_PM_SUSPEND:
73557c478bd9Sstevel@tonic-gate 		break;
73567c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
73577c478bd9Sstevel@tonic-gate 		break;				/* legal, but nothing to do */
73587c478bd9Sstevel@tonic-gate 	default:
73597c478bd9Sstevel@tonic-gate #ifdef DEBUG
73607c478bd9Sstevel@tonic-gate 		panic("pm_post_detach: unrecognized cmd %d for detach",
73617c478bd9Sstevel@tonic-gate 		    cp->ppc_cmd);
73627c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
73637c478bd9Sstevel@tonic-gate #else
73647c478bd9Sstevel@tonic-gate 		break;
73657c478bd9Sstevel@tonic-gate #endif
73667c478bd9Sstevel@tonic-gate 	}
73677c478bd9Sstevel@tonic-gate }
73687c478bd9Sstevel@tonic-gate 
73697c478bd9Sstevel@tonic-gate /*
73707c478bd9Sstevel@tonic-gate  * Called after vfs_mountroot has got the clock started to fix up timestamps
73717c478bd9Sstevel@tonic-gate  * that were set when root bush drivers attached.  hresttime was 0 then, so the
73727c478bd9Sstevel@tonic-gate  * devices look busy but have a 0 busycnt
73737c478bd9Sstevel@tonic-gate  */
73747c478bd9Sstevel@tonic-gate int
pm_adjust_timestamps(dev_info_t * dip,void * arg)73757c478bd9Sstevel@tonic-gate pm_adjust_timestamps(dev_info_t *dip, void *arg)
73767c478bd9Sstevel@tonic-gate {
73777c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
73787c478bd9Sstevel@tonic-gate 
73797c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
73807c478bd9Sstevel@tonic-gate 	struct pm_component *cp;
73817c478bd9Sstevel@tonic-gate 	int i;
73827c478bd9Sstevel@tonic-gate 
73837c478bd9Sstevel@tonic-gate 	if (!info)
73847c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
73857c478bd9Sstevel@tonic-gate 	PM_LOCK_BUSY(dip);
73867c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
73877c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
73887c478bd9Sstevel@tonic-gate 		if (cp->pmc_timestamp == 0 && cp->pmc_busycount == 0)
73897c478bd9Sstevel@tonic-gate 			cp->pmc_timestamp = gethrestime_sec();
73907c478bd9Sstevel@tonic-gate 	}
73917c478bd9Sstevel@tonic-gate 	PM_UNLOCK_BUSY(dip);
73927c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
73937c478bd9Sstevel@tonic-gate }
73947c478bd9Sstevel@tonic-gate 
73957c478bd9Sstevel@tonic-gate /*
73967c478bd9Sstevel@tonic-gate  * Called at attach time to see if the device being attached has a record in
73977c478bd9Sstevel@tonic-gate  * the no involuntary power cycles list.  If so, we do some bookkeeping on the
73987c478bd9Sstevel@tonic-gate  * parents and set a flag in the dip
73997c478bd9Sstevel@tonic-gate  */
74007c478bd9Sstevel@tonic-gate void
pm_noinvol_specd(dev_info_t * dip)74017c478bd9Sstevel@tonic-gate pm_noinvol_specd(dev_info_t *dip)
74027c478bd9Sstevel@tonic-gate {
74037c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_specd")
74047c478bd9Sstevel@tonic-gate 	char *pathbuf;
74057c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip, *pp = NULL;
74067c478bd9Sstevel@tonic-gate 	int wasvolpmd;
74077c478bd9Sstevel@tonic-gate 	int found = 0;
74087c478bd9Sstevel@tonic-gate 
74097c478bd9Sstevel@tonic-gate 	if (DEVI(dip)->devi_pm_flags & PMC_NOINVOL_DONE)
74107c478bd9Sstevel@tonic-gate 		return;
74117c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_flags |=  PMC_NOINVOL_DONE;
74127c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
74137c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(dip, pathbuf);
74147c478bd9Sstevel@tonic-gate 
74157c478bd9Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
74167c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_volpmd = 0;
74177c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_pm_noinvolpm = 0;
74187c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
74197c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
74207c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n",
74217c478bd9Sstevel@tonic-gate 		    pmf, pathbuf, ip->ni_path))
74227c478bd9Sstevel@tonic-gate 		if (strcmp(pathbuf, ip->ni_path) == 0) {
74237c478bd9Sstevel@tonic-gate 			found++;
74247c478bd9Sstevel@tonic-gate 			break;
74257c478bd9Sstevel@tonic-gate 		}
74267c478bd9Sstevel@tonic-gate 	}
74277c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
74287c478bd9Sstevel@tonic-gate 	if (!found) {
74297c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
74307c478bd9Sstevel@tonic-gate 		kmem_free(pathbuf, MAXPATHLEN);
74317c478bd9Sstevel@tonic-gate 		return;
74327c478bd9Sstevel@tonic-gate 	}
74337c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
74347c478bd9Sstevel@tonic-gate 	pp = NULL;
74357c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
74367c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n",
74377c478bd9Sstevel@tonic-gate 		    pmf, pathbuf, ip->ni_path))
74387c478bd9Sstevel@tonic-gate 		if (strcmp(pathbuf, ip->ni_path) == 0) {
74397c478bd9Sstevel@tonic-gate 			ip->ni_flags &= ~PMC_DRIVER_REMOVED;
74407c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_flags |= ip->ni_flags;
74417c478bd9Sstevel@tonic-gate 			/*
74427c478bd9Sstevel@tonic-gate 			 * Handle special case of console fb
74437c478bd9Sstevel@tonic-gate 			 */
74447c478bd9Sstevel@tonic-gate 			if (PM_IS_CFB(dip)) {
74457c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
74467c478bd9Sstevel@tonic-gate 				cfb_dip = dip;
74477c478bd9Sstevel@tonic-gate 				PMD(PMD_CFB, ("%s: %s@%s(%s#%d) setting "
74487c478bd9Sstevel@tonic-gate 				    "cfb_dip\n", pmf, PM_DEVICE(dip)))
74497c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
74507c478bd9Sstevel@tonic-gate 			}
74517c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_noinvolpm = ip->ni_noinvolpm;
74527c478bd9Sstevel@tonic-gate 			ASSERT((DEVI(dip)->devi_pm_flags &
74537c478bd9Sstevel@tonic-gate 			    (PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
74547c478bd9Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_noinvolpm);
74557c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_volpmd = ip->ni_volpmd;
74567c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: noinvol=%d, volpmd=%d, "
74577c478bd9Sstevel@tonic-gate 			    "wasvolpmd=%d, flags=%x, path=%s\n", pmf,
74587c478bd9Sstevel@tonic-gate 			    ip->ni_noinvolpm, ip->ni_volpmd,
74597c478bd9Sstevel@tonic-gate 			    ip->ni_wasvolpmd, ip->ni_flags, ip->ni_path))
74607c478bd9Sstevel@tonic-gate 			/*
74617c478bd9Sstevel@tonic-gate 			 * free the entry in hopes the list will now be empty
74627c478bd9Sstevel@tonic-gate 			 * and we won't have to search it any more until the
74637c478bd9Sstevel@tonic-gate 			 * device detaches
74647c478bd9Sstevel@tonic-gate 			 */
74657c478bd9Sstevel@tonic-gate 			if (pp) {
74667c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: free %s, prev %s\n",
74677c478bd9Sstevel@tonic-gate 				    pmf, ip->ni_path, pp->ni_path))
74687c478bd9Sstevel@tonic-gate 				pp->ni_next = ip->ni_next;
74697c478bd9Sstevel@tonic-gate 			} else {
74707c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: free %s head\n",
74717c478bd9Sstevel@tonic-gate 				    pmf, ip->ni_path))
74727c478bd9Sstevel@tonic-gate 				ASSERT(pm_noinvol_head == ip);
74737c478bd9Sstevel@tonic-gate 				pm_noinvol_head = ip->ni_next;
74747c478bd9Sstevel@tonic-gate 			}
74757c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
74767c478bd9Sstevel@tonic-gate 			wasvolpmd = ip->ni_wasvolpmd;
74777c478bd9Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
74787c478bd9Sstevel@tonic-gate 			kmem_free(ip->ni_path, ip->ni_size);
74797c478bd9Sstevel@tonic-gate 			kmem_free(ip, sizeof (*ip));
74807c478bd9Sstevel@tonic-gate 			/*
74817c478bd9Sstevel@tonic-gate 			 * Now walk up the tree decrementing devi_pm_noinvolpm
74827c478bd9Sstevel@tonic-gate 			 * (and volpmd if appropriate)
74837c478bd9Sstevel@tonic-gate 			 */
74847c478bd9Sstevel@tonic-gate 			(void) pm_noinvol_update(PM_BP_NOINVOL_ATTACH, 0,
74857c478bd9Sstevel@tonic-gate 			    wasvolpmd, pathbuf, dip);
74867c478bd9Sstevel@tonic-gate #ifdef DEBUG
74877c478bd9Sstevel@tonic-gate 			if (pm_debug & PMD_NOINVOL)
74887c478bd9Sstevel@tonic-gate 				pr_noinvol("noinvol_specd exit");
74897c478bd9Sstevel@tonic-gate #endif
74907c478bd9Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
74917c478bd9Sstevel@tonic-gate 			return;
74927c478bd9Sstevel@tonic-gate 		}
74937c478bd9Sstevel@tonic-gate 	}
74947c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
74957c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
74967c478bd9Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
74977c478bd9Sstevel@tonic-gate }
74987c478bd9Sstevel@tonic-gate 
74997c478bd9Sstevel@tonic-gate int
pm_all_components_off(dev_info_t * dip)75007c478bd9Sstevel@tonic-gate pm_all_components_off(dev_info_t *dip)
75017c478bd9Sstevel@tonic-gate {
75027c478bd9Sstevel@tonic-gate 	int i;
75037c478bd9Sstevel@tonic-gate 	pm_component_t *cp;
75047c478bd9Sstevel@tonic-gate 
75057c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
75067c478bd9Sstevel@tonic-gate 		cp = PM_CP(dip, i);
75077c478bd9Sstevel@tonic-gate 		if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN ||
75087c478bd9Sstevel@tonic-gate 		    cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr])
75097c478bd9Sstevel@tonic-gate 			return (0);
75107c478bd9Sstevel@tonic-gate 	}
75117c478bd9Sstevel@tonic-gate 	return (1);	/* all off */
75127c478bd9Sstevel@tonic-gate }
75137c478bd9Sstevel@tonic-gate 
75147c478bd9Sstevel@tonic-gate /*
75157c478bd9Sstevel@tonic-gate  * Make sure that all "no involuntary power cycles" devices are attached.
75167c478bd9Sstevel@tonic-gate  * Called before doing a cpr suspend to make sure the driver has a say about
75177c478bd9Sstevel@tonic-gate  * the power cycle
75187c478bd9Sstevel@tonic-gate  */
75197c478bd9Sstevel@tonic-gate int
pm_reattach_noinvol(void)75207c478bd9Sstevel@tonic-gate pm_reattach_noinvol(void)
75217c478bd9Sstevel@tonic-gate {
75227c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "reattach_noinvol")
75237c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip;
75247c478bd9Sstevel@tonic-gate 	char *path;
75257c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
75267c478bd9Sstevel@tonic-gate 
75277c478bd9Sstevel@tonic-gate 	/*
75287c478bd9Sstevel@tonic-gate 	 * Prevent the modunload thread from unloading any modules until we
75297c478bd9Sstevel@tonic-gate 	 * have completely stopped all kernel threads.
75307c478bd9Sstevel@tonic-gate 	 */
75317c478bd9Sstevel@tonic-gate 	modunload_disable();
75327c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
75337c478bd9Sstevel@tonic-gate 		/*
75347c478bd9Sstevel@tonic-gate 		 * Forget we'v ever seen any entry
75357c478bd9Sstevel@tonic-gate 		 */
75367c478bd9Sstevel@tonic-gate 		ip->ni_persistent = 0;
75377c478bd9Sstevel@tonic-gate 	}
75387c478bd9Sstevel@tonic-gate restart:
75397c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
75407c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
75419681b4a1Skchow #ifdef PMDDEBUG
75427c478bd9Sstevel@tonic-gate 		major_t maj;
75437c478bd9Sstevel@tonic-gate 		maj = ip->ni_major;
75449681b4a1Skchow #endif
75457c478bd9Sstevel@tonic-gate 		path = ip->ni_path;
75467c478bd9Sstevel@tonic-gate 		if (path != NULL && !(ip->ni_flags & PMC_DRIVER_REMOVED)) {
75477c478bd9Sstevel@tonic-gate 			if (ip->ni_persistent) {
75487c478bd9Sstevel@tonic-gate 				/*
75497c478bd9Sstevel@tonic-gate 				 * If we weren't able to make this entry
75507c478bd9Sstevel@tonic-gate 				 * go away, then we give up, as
75517c478bd9Sstevel@tonic-gate 				 * holding/attaching the driver ought to have
75527c478bd9Sstevel@tonic-gate 				 * resulted in this entry being deleted
75537c478bd9Sstevel@tonic-gate 				 */
75547c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: can't reattach %s "
75557c478bd9Sstevel@tonic-gate 				    "(%s|%d)\n", pmf, ip->ni_path,
75567c478bd9Sstevel@tonic-gate 				    ddi_major_to_name(maj), (int)maj))
75577c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "cpr: unable to reattach %s ",
75587c478bd9Sstevel@tonic-gate 				    ip->ni_path);
75597c478bd9Sstevel@tonic-gate 				modunload_enable();
75607c478bd9Sstevel@tonic-gate 				rw_exit(&pm_noinvol_rwlock);
75617c478bd9Sstevel@tonic-gate 				return (0);
75627c478bd9Sstevel@tonic-gate 			}
75637c478bd9Sstevel@tonic-gate 			ip->ni_persistent++;
75647c478bd9Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
75657c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: holding %s\n", pmf, path))
75667c478bd9Sstevel@tonic-gate 			dip = e_ddi_hold_devi_by_path(path, 0);
75677c478bd9Sstevel@tonic-gate 			if (dip == NULL) {
75687c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: can't hold (%s|%d)\n",
75697c478bd9Sstevel@tonic-gate 				    pmf, path, (int)maj))
75707c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "cpr: unable to hold %s "
75717c478bd9Sstevel@tonic-gate 				    "driver", path);
75727c478bd9Sstevel@tonic-gate 				modunload_enable();
75737c478bd9Sstevel@tonic-gate 				return (0);
75747c478bd9Sstevel@tonic-gate 			} else {
75757c478bd9Sstevel@tonic-gate 				PMD(PMD_DHR, ("%s: release %s\n", pmf, path))
75767c478bd9Sstevel@tonic-gate 				/*
75777c478bd9Sstevel@tonic-gate 				 * Since the modunload thread is stopped, we
75787c478bd9Sstevel@tonic-gate 				 * don't have to keep the driver held, which
75797c478bd9Sstevel@tonic-gate 				 * saves a ton of bookkeeping
75807c478bd9Sstevel@tonic-gate 				 */
75817c478bd9Sstevel@tonic-gate 				ddi_release_devi(dip);
75827c478bd9Sstevel@tonic-gate 				goto restart;
75837c478bd9Sstevel@tonic-gate 			}
75847c478bd9Sstevel@tonic-gate 		} else {
75857c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: skip %s; unknown major\n",
75867c478bd9Sstevel@tonic-gate 			    pmf, ip->ni_path))
75877c478bd9Sstevel@tonic-gate 			continue;
75887c478bd9Sstevel@tonic-gate 		}
75897c478bd9Sstevel@tonic-gate 	}
75907c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
75917c478bd9Sstevel@tonic-gate 	return (1);
75927c478bd9Sstevel@tonic-gate }
75937c478bd9Sstevel@tonic-gate 
75947c478bd9Sstevel@tonic-gate void
pm_reattach_noinvol_fini(void)75957c478bd9Sstevel@tonic-gate pm_reattach_noinvol_fini(void)
75967c478bd9Sstevel@tonic-gate {
75977c478bd9Sstevel@tonic-gate 	modunload_enable();
75987c478bd9Sstevel@tonic-gate }
75997c478bd9Sstevel@tonic-gate 
76007c478bd9Sstevel@tonic-gate /*
76017c478bd9Sstevel@tonic-gate  * Display pm support code
76027c478bd9Sstevel@tonic-gate  */
76037c478bd9Sstevel@tonic-gate 
76047c478bd9Sstevel@tonic-gate 
76057c478bd9Sstevel@tonic-gate /*
76067c478bd9Sstevel@tonic-gate  * console frame-buffer power-mgmt gets enabled when debugging
76077c478bd9Sstevel@tonic-gate  * services are not present or console fbpm override is set
76087c478bd9Sstevel@tonic-gate  */
76097c478bd9Sstevel@tonic-gate void
pm_cfb_setup(const char * stdout_path)76107c478bd9Sstevel@tonic-gate pm_cfb_setup(const char *stdout_path)
76117c478bd9Sstevel@tonic-gate {
76127c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "cfb_setup")
76137c478bd9Sstevel@tonic-gate 	extern int obpdebug;
76147c478bd9Sstevel@tonic-gate 	char *devname;
76157c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
76167c478bd9Sstevel@tonic-gate 	int devname_len;
76177c478bd9Sstevel@tonic-gate 	extern dev_info_t *fbdip;
76187c478bd9Sstevel@tonic-gate 
76197c478bd9Sstevel@tonic-gate 	/*
76207c478bd9Sstevel@tonic-gate 	 * By virtue of this function being called (from consconfig),
76217c478bd9Sstevel@tonic-gate 	 * we know stdout is a framebuffer.
76227c478bd9Sstevel@tonic-gate 	 */
76237c478bd9Sstevel@tonic-gate 	stdout_is_framebuffer = 1;
76247c478bd9Sstevel@tonic-gate 
76257c478bd9Sstevel@tonic-gate 	if (obpdebug || (boothowto & RB_DEBUG)) {
76267c478bd9Sstevel@tonic-gate 		if (pm_cfb_override == 0) {
76277c478bd9Sstevel@tonic-gate 			/*
76287c478bd9Sstevel@tonic-gate 			 * Console is frame buffer, but we want to suppress
76297c478bd9Sstevel@tonic-gate 			 * pm on it because of debugging setup
76307c478bd9Sstevel@tonic-gate 			 */
76317c478bd9Sstevel@tonic-gate 			pm_cfb_enabled = 0;
76327c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE, "Kernel debugger present: disabling "
76337c478bd9Sstevel@tonic-gate 			    "console power management.");
76347c478bd9Sstevel@tonic-gate 			/*
76357c478bd9Sstevel@tonic-gate 			 * however, we still need to know which is the console
76367c478bd9Sstevel@tonic-gate 			 * fb in order to suppress pm on it
76377c478bd9Sstevel@tonic-gate 			 */
76387c478bd9Sstevel@tonic-gate 		} else {
76397c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "Kernel debugger present: see "
7640bbf21555SRichard Lowe 			    "kmdb(1) for interaction with power management.");
76417c478bd9Sstevel@tonic-gate 		}
76427c478bd9Sstevel@tonic-gate 	}
76437c478bd9Sstevel@tonic-gate #ifdef DEBUG
76447c478bd9Sstevel@tonic-gate 	/*
76457c478bd9Sstevel@tonic-gate 	 * IF console is fb and is power managed, don't do prom_printfs from
76467c478bd9Sstevel@tonic-gate 	 * pm debug macro
76477c478bd9Sstevel@tonic-gate 	 */
76482df1fe9cSrandyf 	if (pm_cfb_enabled && !pm_debug_to_console) {
76497c478bd9Sstevel@tonic-gate 		if (pm_debug)
76507c478bd9Sstevel@tonic-gate 			prom_printf("pm debug output will be to log only\n");
76517c478bd9Sstevel@tonic-gate 		pm_divertdebug++;
76527c478bd9Sstevel@tonic-gate 	}
76537c478bd9Sstevel@tonic-gate #endif
76547c478bd9Sstevel@tonic-gate 	devname = i_ddi_strdup((char *)stdout_path, KM_SLEEP);
76557c478bd9Sstevel@tonic-gate 	devname_len = strlen(devname) + 1;
76567c478bd9Sstevel@tonic-gate 	PMD(PMD_CFB, ("%s: stripped %s\n", pmf, devname))
76577c478bd9Sstevel@tonic-gate 	/* if the driver is attached */
76587c478bd9Sstevel@tonic-gate 	if ((dip = fbdip) != NULL) {
76597c478bd9Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: attached: %s@%s(%s#%d)\n", pmf,
76607c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
76617c478bd9Sstevel@tonic-gate 		/*
76627c478bd9Sstevel@tonic-gate 		 * We set up here as if the driver were power manageable in case
76637c478bd9Sstevel@tonic-gate 		 * we get a later attach of a pm'able driver (which would result
76647c478bd9Sstevel@tonic-gate 		 * in a panic later)
76657c478bd9Sstevel@tonic-gate 		 */
76667c478bd9Sstevel@tonic-gate 		cfb_dip = dip;
76677c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_flags |= (PMC_CONSOLE_FB | PMC_NO_INVOL);
76687c478bd9Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: cfb_dip -> %s@%s(%s#%d)\n", pmf,
76697c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
76707c478bd9Sstevel@tonic-gate #ifdef DEBUG
76717c478bd9Sstevel@tonic-gate 		if (!(PM_GET_PM_INFO(dip) != NULL && PM_NUMCMPTS(dip))) {
76727c478bd9Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d) not power-managed\n",
76737c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
76747c478bd9Sstevel@tonic-gate 		}
76757c478bd9Sstevel@tonic-gate #endif
76767c478bd9Sstevel@tonic-gate 	} else {
76777c478bd9Sstevel@tonic-gate 		char *ep;
76787c478bd9Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: pntd %s failed\n", pmf, devname))
76797c478bd9Sstevel@tonic-gate 		pm_record_invol_path(devname,
76807c478bd9Sstevel@tonic-gate 		    (PMC_CONSOLE_FB | PMC_NO_INVOL), 1, 0, 0,
7681a204de77Scth 		    DDI_MAJOR_T_NONE);
76827c478bd9Sstevel@tonic-gate 		for (ep = strrchr(devname, '/'); ep != devname;
76837c478bd9Sstevel@tonic-gate 		    ep = strrchr(devname, '/')) {
76847c478bd9Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: devname %s\n", pmf, devname))
76857c478bd9Sstevel@tonic-gate 			*ep = '\0';
76867c478bd9Sstevel@tonic-gate 			dip = pm_name_to_dip(devname, 0);
76877c478bd9Sstevel@tonic-gate 			if (dip != NULL) {
76887c478bd9Sstevel@tonic-gate 				/*
76897c478bd9Sstevel@tonic-gate 				 * Walk up the tree incrementing
76907c478bd9Sstevel@tonic-gate 				 * devi_pm_noinvolpm
76917c478bd9Sstevel@tonic-gate 				 */
76927c478bd9Sstevel@tonic-gate 				(void) pm_noinvol_update(PM_BP_NOINVOL_CFB,
76937c478bd9Sstevel@tonic-gate 				    0, 0, devname, dip);
76947c478bd9Sstevel@tonic-gate 				break;
76957c478bd9Sstevel@tonic-gate 			} else {
76967c478bd9Sstevel@tonic-gate 				pm_record_invol_path(devname,
7697a204de77Scth 				    PMC_NO_INVOL, 1, 0, 0, DDI_MAJOR_T_NONE);
76987c478bd9Sstevel@tonic-gate 			}
76997c478bd9Sstevel@tonic-gate 		}
77007c478bd9Sstevel@tonic-gate 	}
77017c478bd9Sstevel@tonic-gate 	kmem_free(devname, devname_len);
77027c478bd9Sstevel@tonic-gate }
77037c478bd9Sstevel@tonic-gate 
77047c478bd9Sstevel@tonic-gate void
pm_cfb_rele(void)77057c478bd9Sstevel@tonic-gate pm_cfb_rele(void)
77067c478bd9Sstevel@tonic-gate {
77077c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
77087c478bd9Sstevel@tonic-gate 	/*
77097c478bd9Sstevel@tonic-gate 	 * this call isn't using the console any  more, it is ok to take it
77107c478bd9Sstevel@tonic-gate 	 * down if the count goes to 0
77117c478bd9Sstevel@tonic-gate 	 */
77127c478bd9Sstevel@tonic-gate 	cfb_inuse--;
77137c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_cfb_lock);
77147c478bd9Sstevel@tonic-gate }
77157c478bd9Sstevel@tonic-gate 
77167c478bd9Sstevel@tonic-gate /*
77177c478bd9Sstevel@tonic-gate  * software interrupt handler for fbpm; this function exists because we can't
77187c478bd9Sstevel@tonic-gate  * bring up the frame buffer power from above lock level.  So if we need to,
77197c478bd9Sstevel@tonic-gate  * we instead schedule a softint that runs this routine and takes us into
77207c478bd9Sstevel@tonic-gate  * debug_enter (a bit delayed from the original request, but avoiding a panic).
77217c478bd9Sstevel@tonic-gate  */
77227c478bd9Sstevel@tonic-gate static uint_t
pm_cfb_softint(caddr_t int_handler_arg)77237c478bd9Sstevel@tonic-gate pm_cfb_softint(caddr_t int_handler_arg)
77247c478bd9Sstevel@tonic-gate {
77257c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(int_handler_arg))
77267c478bd9Sstevel@tonic-gate 	int rval = DDI_INTR_UNCLAIMED;
77277c478bd9Sstevel@tonic-gate 
77287c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
77297c478bd9Sstevel@tonic-gate 	if (pm_soft_pending) {
77307c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
77317c478bd9Sstevel@tonic-gate 		debug_enter((char *)NULL);
77327c478bd9Sstevel@tonic-gate 		/* acquired in debug_enter before calling pm_cfb_trigger */
77337c478bd9Sstevel@tonic-gate 		pm_cfb_rele();
77347c478bd9Sstevel@tonic-gate 		mutex_enter(&pm_cfb_lock);
7735d3d50737SRafael Vanoni 		pm_soft_pending = B_FALSE;
77367c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
77377c478bd9Sstevel@tonic-gate 		rval = DDI_INTR_CLAIMED;
77387c478bd9Sstevel@tonic-gate 	} else
77397c478bd9Sstevel@tonic-gate 		mutex_exit(&pm_cfb_lock);
77407c478bd9Sstevel@tonic-gate 
77417c478bd9Sstevel@tonic-gate 	return (rval);
77427c478bd9Sstevel@tonic-gate }
77437c478bd9Sstevel@tonic-gate 
77447c478bd9Sstevel@tonic-gate void
pm_cfb_setup_intr(void)77457c478bd9Sstevel@tonic-gate pm_cfb_setup_intr(void)
77467c478bd9Sstevel@tonic-gate {
77477c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "cfb_setup_intr")
77487c478bd9Sstevel@tonic-gate 	extern void prom_set_outfuncs(void (*)(void), void (*)(void));
77497c478bd9Sstevel@tonic-gate 	void pm_cfb_check_and_powerup(void);
77507c478bd9Sstevel@tonic-gate 
77512df1fe9cSrandyf 	mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
77522df1fe9cSrandyf #ifdef PMDDEBUG
77532df1fe9cSrandyf 	mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
77542df1fe9cSrandyf #endif
77552df1fe9cSrandyf 
77567c478bd9Sstevel@tonic-gate 	if (!stdout_is_framebuffer) {
77577c478bd9Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: console not fb\n", pmf))
77587c478bd9Sstevel@tonic-gate 		return;
77597c478bd9Sstevel@tonic-gate 	}
77602df1fe9cSrandyf 
77617c478bd9Sstevel@tonic-gate 	/*
77627c478bd9Sstevel@tonic-gate 	 * setup software interrupt handler
77637c478bd9Sstevel@tonic-gate 	 */
77647c478bd9Sstevel@tonic-gate 	if (ddi_add_softintr(ddi_root_node(), DDI_SOFTINT_HIGH, &pm_soft_id,
77657c478bd9Sstevel@tonic-gate 	    NULL, NULL, pm_cfb_softint, NULL) != DDI_SUCCESS)
77667c478bd9Sstevel@tonic-gate 		panic("pm: unable to register soft intr.");
77677c478bd9Sstevel@tonic-gate 
77687c478bd9Sstevel@tonic-gate 	prom_set_outfuncs(pm_cfb_check_and_powerup, pm_cfb_rele);
77697c478bd9Sstevel@tonic-gate }
77707c478bd9Sstevel@tonic-gate 
77717c478bd9Sstevel@tonic-gate /*
77727c478bd9Sstevel@tonic-gate  * Checks to see if it is safe to write to the console wrt power management
77737c478bd9Sstevel@tonic-gate  * (i.e. if the console is a framebuffer, then it must be at full power)
77747c478bd9Sstevel@tonic-gate  * returns 1 when power is off (power-up is needed)
77757c478bd9Sstevel@tonic-gate  * returns 0 when power is on (power-up not needed)
77767c478bd9Sstevel@tonic-gate  */
77777c478bd9Sstevel@tonic-gate int
pm_cfb_check_and_hold(void)77787c478bd9Sstevel@tonic-gate pm_cfb_check_and_hold(void)
77797c478bd9Sstevel@tonic-gate {
77807c478bd9Sstevel@tonic-gate 	/*
77817c478bd9Sstevel@tonic-gate 	 * cfb_dip is set iff console is a power manageable frame buffer
77827c478bd9Sstevel@tonic-gate 	 * device
77837c478bd9Sstevel@tonic-gate 	 */
77847c478bd9Sstevel@tonic-gate 	extern int modrootloaded;
77857c478bd9Sstevel@tonic-gate 
77867c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
77877c478bd9Sstevel@tonic-gate 	cfb_inuse++;
77887c478bd9Sstevel@tonic-gate 	ASSERT(cfb_inuse);	/* wrap? */
77897c478bd9Sstevel@tonic-gate 	if (modrootloaded && cfb_dip) {
77907c478bd9Sstevel@tonic-gate 		/*
77917c478bd9Sstevel@tonic-gate 		 * don't power down the frame buffer, the prom is using it
77927c478bd9Sstevel@tonic-gate 		 */
77937c478bd9Sstevel@tonic-gate 		if (pm_cfb_comps_off) {
77947c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_cfb_lock);
77957c478bd9Sstevel@tonic-gate 			return (1);
77967c478bd9Sstevel@tonic-gate 		}
77977c478bd9Sstevel@tonic-gate 	}
77987c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_cfb_lock);
77997c478bd9Sstevel@tonic-gate 	return (0);
78007c478bd9Sstevel@tonic-gate }
78017c478bd9Sstevel@tonic-gate 
78027c478bd9Sstevel@tonic-gate /*
78037c478bd9Sstevel@tonic-gate  * turn on cfb power (which is known to be off).
78047c478bd9Sstevel@tonic-gate  * Must be called below lock level!
78057c478bd9Sstevel@tonic-gate  */
78067c478bd9Sstevel@tonic-gate void
pm_cfb_powerup(void)78077c478bd9Sstevel@tonic-gate pm_cfb_powerup(void)
78087c478bd9Sstevel@tonic-gate {
78097c478bd9Sstevel@tonic-gate 	pm_info_t *info;
78107c478bd9Sstevel@tonic-gate 	int norm;
78117c478bd9Sstevel@tonic-gate 	int ccount, ci;
78127c478bd9Sstevel@tonic-gate 	int unused;
78137c478bd9Sstevel@tonic-gate #ifdef DEBUG
78147c478bd9Sstevel@tonic-gate 	/*
78157c478bd9Sstevel@tonic-gate 	 * Can't reenter prom_prekern, so suppress pm debug messages
78167c478bd9Sstevel@tonic-gate 	 * (still go to circular buffer).
78177c478bd9Sstevel@tonic-gate 	 */
78187c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_debug_lock);
78197c478bd9Sstevel@tonic-gate 	pm_divertdebug++;
78207c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_debug_lock);
78217c478bd9Sstevel@tonic-gate #endif
78227c478bd9Sstevel@tonic-gate 	info = PM_GET_PM_INFO(cfb_dip);
78237c478bd9Sstevel@tonic-gate 	ASSERT(info);
78247c478bd9Sstevel@tonic-gate 
78257c478bd9Sstevel@tonic-gate 	ccount = PM_NUMCMPTS(cfb_dip);
78267c478bd9Sstevel@tonic-gate 	for (ci = 0; ci < ccount; ci++) {
78277c478bd9Sstevel@tonic-gate 		norm = pm_get_normal_power(cfb_dip, ci);
78287c478bd9Sstevel@tonic-gate 		(void) pm_set_power(cfb_dip, ci, norm, PM_LEVEL_UPONLY,
78297c478bd9Sstevel@tonic-gate 		    PM_CANBLOCK_BYPASS, 0, &unused);
78307c478bd9Sstevel@tonic-gate 	}
78317c478bd9Sstevel@tonic-gate #ifdef DEBUG
78327c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_debug_lock);
78337c478bd9Sstevel@tonic-gate 	pm_divertdebug--;
78347c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_debug_lock);
78357c478bd9Sstevel@tonic-gate #endif
78367c478bd9Sstevel@tonic-gate }
78377c478bd9Sstevel@tonic-gate 
78387c478bd9Sstevel@tonic-gate /*
78397c478bd9Sstevel@tonic-gate  * Check if the console framebuffer is powered up.  If not power it up.
78407c478bd9Sstevel@tonic-gate  * Note: Calling pm_cfb_check_and_hold has put a hold on the power state which
78417c478bd9Sstevel@tonic-gate  * must be released by calling pm_cfb_rele when the console fb operation
78427c478bd9Sstevel@tonic-gate  * is completed.
78437c478bd9Sstevel@tonic-gate  */
78447c478bd9Sstevel@tonic-gate void
pm_cfb_check_and_powerup(void)78457c478bd9Sstevel@tonic-gate pm_cfb_check_and_powerup(void)
78467c478bd9Sstevel@tonic-gate {
78477c478bd9Sstevel@tonic-gate 	if (pm_cfb_check_and_hold())
78487c478bd9Sstevel@tonic-gate 		pm_cfb_powerup();
78497c478bd9Sstevel@tonic-gate }
78507c478bd9Sstevel@tonic-gate 
78517c478bd9Sstevel@tonic-gate /*
78527c478bd9Sstevel@tonic-gate  * Trigger a low level interrupt to power up console frame buffer.
78537c478bd9Sstevel@tonic-gate  */
78547c478bd9Sstevel@tonic-gate void
pm_cfb_trigger(void)78557c478bd9Sstevel@tonic-gate pm_cfb_trigger(void)
78567c478bd9Sstevel@tonic-gate {
78577c478bd9Sstevel@tonic-gate 	if (cfb_dip == NULL)
78587c478bd9Sstevel@tonic-gate 		return;
78597c478bd9Sstevel@tonic-gate 
78607c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_cfb_lock);
78617c478bd9Sstevel@tonic-gate 	/*
7862d3d50737SRafael Vanoni 	 * If the machine appears to be hung, pulling the keyboard connector of
78637c478bd9Sstevel@tonic-gate 	 * the console will cause a high level interrupt and go to debug_enter.
78647c478bd9Sstevel@tonic-gate 	 * But, if the fb is powered down, this routine will be called to bring
7865d3d50737SRafael Vanoni 	 * it up (by generating a softint to do the work). If a second attempt
7866d3d50737SRafael Vanoni 	 * at triggering this softint happens before the first one completes,
7867d3d50737SRafael Vanoni 	 * we panic as softints are most likely not being handled.
78687c478bd9Sstevel@tonic-gate 	 */
7869d3d50737SRafael Vanoni 	if (pm_soft_pending) {
7870d3d50737SRafael Vanoni 		panicstr = "pm_cfb_trigger: failed to enter the debugger";
78717c478bd9Sstevel@tonic-gate 		panic(panicstr);	/* does a power up at any intr level */
78727c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
78737c478bd9Sstevel@tonic-gate 	}
7874d3d50737SRafael Vanoni 	pm_soft_pending = B_TRUE;
78757c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_cfb_lock);
78767c478bd9Sstevel@tonic-gate 	ddi_trigger_softintr(pm_soft_id);
78777c478bd9Sstevel@tonic-gate }
78787c478bd9Sstevel@tonic-gate 
7879d67944fbSScott Rotondo static major_t i_path_to_major(char *, char *);
7880d67944fbSScott Rotondo 
78817c478bd9Sstevel@tonic-gate major_t
pm_path_to_major(char * path)78827c478bd9Sstevel@tonic-gate pm_path_to_major(char *path)
78837c478bd9Sstevel@tonic-gate {
78847c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "path_to_major")
78857c478bd9Sstevel@tonic-gate 	char *np, *ap, *bp;
78867c478bd9Sstevel@tonic-gate 	major_t ret;
78877c478bd9Sstevel@tonic-gate 	size_t len;
78887c478bd9Sstevel@tonic-gate 
78897c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s\n", pmf, path))
78907c478bd9Sstevel@tonic-gate 
78917c478bd9Sstevel@tonic-gate 	np = strrchr(path, '/');
78927c478bd9Sstevel@tonic-gate 	if (np != NULL)
78937c478bd9Sstevel@tonic-gate 		np++;
78947c478bd9Sstevel@tonic-gate 	else
78957c478bd9Sstevel@tonic-gate 		np = path;
78967c478bd9Sstevel@tonic-gate 	len = strlen(np) + 1;
78977c478bd9Sstevel@tonic-gate 	bp = kmem_alloc(len, KM_SLEEP);
78987c478bd9Sstevel@tonic-gate 	(void) strcpy(bp, np);
78997c478bd9Sstevel@tonic-gate 	if ((ap = strchr(bp, '@')) != NULL) {
79007c478bd9Sstevel@tonic-gate 		*ap = '\0';
79017c478bd9Sstevel@tonic-gate 	}
79027c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %d\n", pmf, ddi_name_to_major(np)))
79037c478bd9Sstevel@tonic-gate 	ret = i_path_to_major(path, np);
79047c478bd9Sstevel@tonic-gate 	kmem_free(bp, len);
79057c478bd9Sstevel@tonic-gate 	return (ret);
79067c478bd9Sstevel@tonic-gate }
79077c478bd9Sstevel@tonic-gate 
79087c478bd9Sstevel@tonic-gate #ifdef DEBUG
79092df1fe9cSrandyf #ifndef sparc
79102df1fe9cSrandyf clock_t pt_sleep = 1;
79112df1fe9cSrandyf #endif
79127c478bd9Sstevel@tonic-gate 
79132df1fe9cSrandyf char	*pm_msgp;
79142df1fe9cSrandyf char	*pm_bufend;
79152df1fe9cSrandyf char	*pm_msgbuf = NULL;
79162df1fe9cSrandyf int	pm_logpages = 0x100;
79172df1fe9cSrandyf #include <sys/sunldi.h>
79182df1fe9cSrandyf #include <sys/uio.h>
79192df1fe9cSrandyf clock_t	pm_log_sleep = 1000;
79202df1fe9cSrandyf int	pm_extra_cr = 1;
79212df1fe9cSrandyf volatile int pm_tty = 1;
79227c478bd9Sstevel@tonic-gate 
79237c478bd9Sstevel@tonic-gate #define	PMLOGPGS	pm_logpages
79247c478bd9Sstevel@tonic-gate 
79252df1fe9cSrandyf #if defined(__x86)
79262df1fe9cSrandyf void pm_printf(char *s);
79272df1fe9cSrandyf #endif
79282df1fe9cSrandyf 
79297c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
79307c478bd9Sstevel@tonic-gate void
pm_log(const char * fmt,...)79317c478bd9Sstevel@tonic-gate pm_log(const char *fmt, ...)
79327c478bd9Sstevel@tonic-gate {
79337c478bd9Sstevel@tonic-gate 	va_list adx;
79347c478bd9Sstevel@tonic-gate 	size_t size;
79357c478bd9Sstevel@tonic-gate 
79367c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_debug_lock);
79377c478bd9Sstevel@tonic-gate 	if (pm_msgbuf == NULL) {
79387c478bd9Sstevel@tonic-gate 		pm_msgbuf = kmem_zalloc(mmu_ptob(PMLOGPGS), KM_SLEEP);
79397c478bd9Sstevel@tonic-gate 		pm_bufend = pm_msgbuf + mmu_ptob(PMLOGPGS) - 1;
79407c478bd9Sstevel@tonic-gate 		pm_msgp = pm_msgbuf;
79417c478bd9Sstevel@tonic-gate 	}
79427c478bd9Sstevel@tonic-gate 	va_start(adx, fmt);
79437c478bd9Sstevel@tonic-gate 	size = vsnprintf(NULL, 0, fmt, adx) + 1;
79447c478bd9Sstevel@tonic-gate 	va_end(adx);
79457c478bd9Sstevel@tonic-gate 	va_start(adx, fmt);
79467c478bd9Sstevel@tonic-gate 	if (size > (pm_bufend - pm_msgp)) {		/* wraps */
79477c478bd9Sstevel@tonic-gate 		bzero(pm_msgp, pm_bufend - pm_msgp);
79487c478bd9Sstevel@tonic-gate 		(void) vsnprintf(pm_msgbuf, size, fmt, adx);
79497c478bd9Sstevel@tonic-gate 		if (!pm_divertdebug)
79507c478bd9Sstevel@tonic-gate 			prom_printf("%s", pm_msgp);
79512df1fe9cSrandyf #if defined(__x86)
79522df1fe9cSrandyf 		if (pm_tty) {
79532df1fe9cSrandyf 			pm_printf(pm_msgp);
79542df1fe9cSrandyf 			if (pm_extra_cr)
79552df1fe9cSrandyf 				pm_printf("\r");
79562df1fe9cSrandyf 		}
79572df1fe9cSrandyf #endif
79587c478bd9Sstevel@tonic-gate 		pm_msgp = pm_msgbuf + size;
79597c478bd9Sstevel@tonic-gate 	} else {
79607c478bd9Sstevel@tonic-gate 		(void) vsnprintf(pm_msgp, size, fmt, adx);
79612df1fe9cSrandyf #if defined(__x86)
79622df1fe9cSrandyf 		if (pm_tty) {
79632df1fe9cSrandyf 			pm_printf(pm_msgp);
79642df1fe9cSrandyf 			if (pm_extra_cr)
79652df1fe9cSrandyf 				pm_printf("\r");
79662df1fe9cSrandyf 		}
79672df1fe9cSrandyf #endif
79687c478bd9Sstevel@tonic-gate 		if (!pm_divertdebug)
79697c478bd9Sstevel@tonic-gate 			prom_printf("%s", pm_msgp);
79707c478bd9Sstevel@tonic-gate 		pm_msgp += size;
79717c478bd9Sstevel@tonic-gate 	}
79727c478bd9Sstevel@tonic-gate 	va_end(adx);
79737c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_debug_lock);
79742df1fe9cSrandyf 	drv_usecwait((clock_t)pm_log_sleep);
79757c478bd9Sstevel@tonic-gate }
79767c478bd9Sstevel@tonic-gate #endif	/* DEBUG */
79777c478bd9Sstevel@tonic-gate 
79787c478bd9Sstevel@tonic-gate /*
79797c478bd9Sstevel@tonic-gate  * We want to save the state of any directly pm'd devices over the suspend/
79807c478bd9Sstevel@tonic-gate  * resume process so that we can put them back the way the controlling
79817c478bd9Sstevel@tonic-gate  * process left them.
79827c478bd9Sstevel@tonic-gate  */
79837c478bd9Sstevel@tonic-gate void
pm_save_direct_levels(void)79847c478bd9Sstevel@tonic-gate pm_save_direct_levels(void)
79857c478bd9Sstevel@tonic-gate {
79867c478bd9Sstevel@tonic-gate 	pm_processes_stopped = 1;
79877c478bd9Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_save_direct_lvl_walk, 0);
79887c478bd9Sstevel@tonic-gate }
79897c478bd9Sstevel@tonic-gate 
79907c478bd9Sstevel@tonic-gate static int
pm_save_direct_lvl_walk(dev_info_t * dip,void * arg)79917c478bd9Sstevel@tonic-gate pm_save_direct_lvl_walk(dev_info_t *dip, void *arg)
79927c478bd9Sstevel@tonic-gate {
79937c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
79947c478bd9Sstevel@tonic-gate 	int i;
79957c478bd9Sstevel@tonic-gate 	int *ip;
79967c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
79977c478bd9Sstevel@tonic-gate 
79987c478bd9Sstevel@tonic-gate 	if (!info)
79997c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
80007c478bd9Sstevel@tonic-gate 
80017c478bd9Sstevel@tonic-gate 	if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) {
80027c478bd9Sstevel@tonic-gate 		if (PM_NUMCMPTS(dip) > 2) {
80037c478bd9Sstevel@tonic-gate 			info->pmi_lp = kmem_alloc(PM_NUMCMPTS(dip) *
80047c478bd9Sstevel@tonic-gate 			    sizeof (int), KM_SLEEP);
80057c478bd9Sstevel@tonic-gate 			ip = info->pmi_lp;
80067c478bd9Sstevel@tonic-gate 		} else {
80077c478bd9Sstevel@tonic-gate 			ip = info->pmi_levels;
80087c478bd9Sstevel@tonic-gate 		}
80097c478bd9Sstevel@tonic-gate 		/* autopm and processes are stopped, ok not to lock power */
80107c478bd9Sstevel@tonic-gate 		for (i = 0; i < PM_NUMCMPTS(dip); i++)
80117c478bd9Sstevel@tonic-gate 			*ip++ = PM_CURPOWER(dip, i);
80127c478bd9Sstevel@tonic-gate 		/*
80137c478bd9Sstevel@tonic-gate 		 * There is a small window between stopping the
80147c478bd9Sstevel@tonic-gate 		 * processes and setting pm_processes_stopped where
80157c478bd9Sstevel@tonic-gate 		 * a driver could get hung up in a pm_raise_power()
80167c478bd9Sstevel@tonic-gate 		 * call.  Free any such driver now.
80177c478bd9Sstevel@tonic-gate 		 */
80187c478bd9Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
80197c478bd9Sstevel@tonic-gate 	}
80207c478bd9Sstevel@tonic-gate 
80217c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
80227c478bd9Sstevel@tonic-gate }
80237c478bd9Sstevel@tonic-gate 
80247c478bd9Sstevel@tonic-gate void
pm_restore_direct_levels(void)80257c478bd9Sstevel@tonic-gate pm_restore_direct_levels(void)
80267c478bd9Sstevel@tonic-gate {
80277c478bd9Sstevel@tonic-gate 	/*
80287c478bd9Sstevel@tonic-gate 	 * If cpr didn't call pm_save_direct_levels, (because stopping user
80297c478bd9Sstevel@tonic-gate 	 * threads failed) then we don't want to try to restore them
80307c478bd9Sstevel@tonic-gate 	 */
80317c478bd9Sstevel@tonic-gate 	if (!pm_processes_stopped)
80327c478bd9Sstevel@tonic-gate 		return;
80337c478bd9Sstevel@tonic-gate 
80347c478bd9Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_restore_direct_lvl_walk, 0);
80357c478bd9Sstevel@tonic-gate 	pm_processes_stopped = 0;
80367c478bd9Sstevel@tonic-gate }
80377c478bd9Sstevel@tonic-gate 
80387c478bd9Sstevel@tonic-gate static int
pm_restore_direct_lvl_walk(dev_info_t * dip,void * arg)80397c478bd9Sstevel@tonic-gate pm_restore_direct_lvl_walk(dev_info_t *dip, void *arg)
80407c478bd9Sstevel@tonic-gate {
80417c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
80427c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "restore_direct_lvl_walk")
80437c478bd9Sstevel@tonic-gate 	int i, nc, result;
80447c478bd9Sstevel@tonic-gate 	int *ip;
80457c478bd9Sstevel@tonic-gate 
80467c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
80477c478bd9Sstevel@tonic-gate 	if (!info)
80487c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
80497c478bd9Sstevel@tonic-gate 
80507c478bd9Sstevel@tonic-gate 	if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) {
80517c478bd9Sstevel@tonic-gate 		if ((nc = PM_NUMCMPTS(dip)) > 2) {
80527c478bd9Sstevel@tonic-gate 			ip = &info->pmi_lp[nc - 1];
80537c478bd9Sstevel@tonic-gate 		} else {
80547c478bd9Sstevel@tonic-gate 			ip = &info->pmi_levels[nc - 1];
80557c478bd9Sstevel@tonic-gate 		}
80567c478bd9Sstevel@tonic-gate 		/*
80577c478bd9Sstevel@tonic-gate 		 * Because fb drivers fail attempts to turn off the
80587c478bd9Sstevel@tonic-gate 		 * fb when the monitor is on, but treat a request to
80597c478bd9Sstevel@tonic-gate 		 * turn on the monitor as a request to turn on the
80607c478bd9Sstevel@tonic-gate 		 * fb too, we process components in descending order
80617c478bd9Sstevel@tonic-gate 		 * Because autopm is disabled and processes aren't
80627c478bd9Sstevel@tonic-gate 		 * running, it is ok to examine current power outside
80637c478bd9Sstevel@tonic-gate 		 * of the power lock
80647c478bd9Sstevel@tonic-gate 		 */
80657c478bd9Sstevel@tonic-gate 		for (i = nc - 1; i >= 0; i--, ip--) {
80667c478bd9Sstevel@tonic-gate 			if (PM_CURPOWER(dip, i) == *ip)
80677c478bd9Sstevel@tonic-gate 				continue;
80687c478bd9Sstevel@tonic-gate 			if (pm_set_power(dip, i, *ip, PM_LEVEL_EXACT,
80696152d498Smh 			    PM_CANBLOCK_BYPASS, 0, &result) != DDI_SUCCESS) {
80707c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "cpr: unable "
80717c478bd9Sstevel@tonic-gate 				    "to restore power level of "
80727c478bd9Sstevel@tonic-gate 				    "component %d of directly "
80737c478bd9Sstevel@tonic-gate 				    "power manged device %s@%s"
80747c478bd9Sstevel@tonic-gate 				    " to %d",
80757c478bd9Sstevel@tonic-gate 				    i, PM_NAME(dip),
80767c478bd9Sstevel@tonic-gate 				    PM_ADDR(dip), *ip);
80777c478bd9Sstevel@tonic-gate 				PMD(PMD_FAIL, ("%s: failed to restore "
80787c478bd9Sstevel@tonic-gate 				    "%s@%s(%s#%d)[%d] exact(%d)->%d, "
80797c478bd9Sstevel@tonic-gate 				    "errno %d\n", pmf, PM_DEVICE(dip), i,
80807c478bd9Sstevel@tonic-gate 				    PM_CURPOWER(dip, i), *ip, result))
80817c478bd9Sstevel@tonic-gate 			}
80827c478bd9Sstevel@tonic-gate 		}
80837c478bd9Sstevel@tonic-gate 		if (nc > 2) {
80847c478bd9Sstevel@tonic-gate 			kmem_free(info->pmi_lp, nc * sizeof (int));
80857c478bd9Sstevel@tonic-gate 			info->pmi_lp = NULL;
80867c478bd9Sstevel@tonic-gate 		}
80877c478bd9Sstevel@tonic-gate 	}
80887c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
80897c478bd9Sstevel@tonic-gate }
80907c478bd9Sstevel@tonic-gate 
80917c478bd9Sstevel@tonic-gate /*
80927c478bd9Sstevel@tonic-gate  * Stolen from the bootdev module
80937c478bd9Sstevel@tonic-gate  * attempt to convert a path to a major number
80947c478bd9Sstevel@tonic-gate  */
80957c478bd9Sstevel@tonic-gate static major_t
i_path_to_major(char * path,char * leaf_name)80967c478bd9Sstevel@tonic-gate i_path_to_major(char *path, char *leaf_name)
80977c478bd9Sstevel@tonic-gate {
80987c478bd9Sstevel@tonic-gate 	extern major_t path_to_major(char *pathname);
80997c478bd9Sstevel@tonic-gate 	major_t maj;
81007c478bd9Sstevel@tonic-gate 
8101a204de77Scth 	if ((maj = path_to_major(path)) == DDI_MAJOR_T_NONE) {
81027c478bd9Sstevel@tonic-gate 		maj = ddi_name_to_major(leaf_name);
81037c478bd9Sstevel@tonic-gate 	}
81047c478bd9Sstevel@tonic-gate 
81057c478bd9Sstevel@tonic-gate 	return (maj);
81067c478bd9Sstevel@tonic-gate }
81077c478bd9Sstevel@tonic-gate 
8108d67944fbSScott Rotondo static void i_pm_driver_removed(major_t major);
8109d67944fbSScott Rotondo 
81107c478bd9Sstevel@tonic-gate /*
81117c478bd9Sstevel@tonic-gate  * When user calls rem_drv, we need to forget no-involuntary-power-cycles state
81127c478bd9Sstevel@tonic-gate  * An entry in the list means that the device is detached, so we need to
81137c478bd9Sstevel@tonic-gate  * adjust its ancestors as if they had just seen this attach, and any detached
81147c478bd9Sstevel@tonic-gate  * ancestors need to have their list entries adjusted.
81157c478bd9Sstevel@tonic-gate  */
81167c478bd9Sstevel@tonic-gate void
pm_driver_removed(major_t major)81177c478bd9Sstevel@tonic-gate pm_driver_removed(major_t major)
81187c478bd9Sstevel@tonic-gate {
81197c478bd9Sstevel@tonic-gate 
81207c478bd9Sstevel@tonic-gate 	/*
81217c478bd9Sstevel@tonic-gate 	 * Serialize removal of drivers. This is to keep ancestors of
81227c478bd9Sstevel@tonic-gate 	 * a node that is being deleted from getting deleted and added back
81237c478bd9Sstevel@tonic-gate 	 * with different counters.
81247c478bd9Sstevel@tonic-gate 	 */
81257c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_remdrv_lock);
81267c478bd9Sstevel@tonic-gate 	i_pm_driver_removed(major);
81277c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_remdrv_lock);
81287c478bd9Sstevel@tonic-gate }
81297c478bd9Sstevel@tonic-gate 
8130d67944fbSScott Rotondo static void adjust_ancestors(char *, int);
8131d67944fbSScott Rotondo static int pm_is_noinvol_ancestor(pm_noinvol_t *);
8132d67944fbSScott Rotondo static void pm_noinvol_process_ancestors(char *);
8133d67944fbSScott Rotondo 
81347c478bd9Sstevel@tonic-gate /*
81357c478bd9Sstevel@tonic-gate  * This routine is called recursively by pm_noinvol_process_ancestors()
81367c478bd9Sstevel@tonic-gate  */
81377c478bd9Sstevel@tonic-gate static void
i_pm_driver_removed(major_t major)81387c478bd9Sstevel@tonic-gate i_pm_driver_removed(major_t major)
81397c478bd9Sstevel@tonic-gate {
81407c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "driver_removed")
81417c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip, *pp = NULL;
81427c478bd9Sstevel@tonic-gate 	int wasvolpmd;
8143a204de77Scth 	ASSERT(major != DDI_MAJOR_T_NONE);
81447c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s\n", pmf, ddi_major_to_name(major)))
81457c478bd9Sstevel@tonic-gate again:
81467c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_WRITER);
81477c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
81487c478bd9Sstevel@tonic-gate 		if (major != ip->ni_major)
81497c478bd9Sstevel@tonic-gate 			continue;
81507c478bd9Sstevel@tonic-gate 		/*
81517c478bd9Sstevel@tonic-gate 		 * If it is an ancestor of no-invol node, which is
81527c478bd9Sstevel@tonic-gate 		 * not removed, skip it. This is to cover the case of
81537c478bd9Sstevel@tonic-gate 		 * ancestor removed without removing its descendants.
81547c478bd9Sstevel@tonic-gate 		 */
81557c478bd9Sstevel@tonic-gate 		if (pm_is_noinvol_ancestor(ip)) {
81567c478bd9Sstevel@tonic-gate 			ip->ni_flags |= PMC_DRIVER_REMOVED;
81577c478bd9Sstevel@tonic-gate 			continue;
81587c478bd9Sstevel@tonic-gate 		}
81597c478bd9Sstevel@tonic-gate 		wasvolpmd = ip->ni_wasvolpmd;
81607c478bd9Sstevel@tonic-gate 		/*
81617c478bd9Sstevel@tonic-gate 		 * remove the entry from the list
81627c478bd9Sstevel@tonic-gate 		 */
81637c478bd9Sstevel@tonic-gate 		if (pp) {
81647c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: freeing %s, prev is %s\n",
81657c478bd9Sstevel@tonic-gate 			    pmf, ip->ni_path, pp->ni_path))
81667c478bd9Sstevel@tonic-gate 			pp->ni_next = ip->ni_next;
81677c478bd9Sstevel@tonic-gate 		} else {
81687c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: free %s head\n", pmf,
81697c478bd9Sstevel@tonic-gate 			    ip->ni_path))
81707c478bd9Sstevel@tonic-gate 			ASSERT(pm_noinvol_head == ip);
81717c478bd9Sstevel@tonic-gate 			pm_noinvol_head = ip->ni_next;
81727c478bd9Sstevel@tonic-gate 		}
81737c478bd9Sstevel@tonic-gate 		rw_exit(&pm_noinvol_rwlock);
81747c478bd9Sstevel@tonic-gate 		adjust_ancestors(ip->ni_path, wasvolpmd);
81757c478bd9Sstevel@tonic-gate 		/*
81767c478bd9Sstevel@tonic-gate 		 * Had an ancestor been removed before this node, it would have
81777c478bd9Sstevel@tonic-gate 		 * been skipped. Adjust the no-invol counters for such skipped
81787c478bd9Sstevel@tonic-gate 		 * ancestors.
81797c478bd9Sstevel@tonic-gate 		 */
81807c478bd9Sstevel@tonic-gate 		pm_noinvol_process_ancestors(ip->ni_path);
81817c478bd9Sstevel@tonic-gate 		kmem_free(ip->ni_path, ip->ni_size);
81827c478bd9Sstevel@tonic-gate 		kmem_free(ip, sizeof (*ip));
81837c478bd9Sstevel@tonic-gate 		goto again;
81847c478bd9Sstevel@tonic-gate 	}
81857c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
81867c478bd9Sstevel@tonic-gate }
81877c478bd9Sstevel@tonic-gate 
81887c478bd9Sstevel@tonic-gate /*
81897c478bd9Sstevel@tonic-gate  * returns 1, if *aip is a ancestor of a no-invol node
81907c478bd9Sstevel@tonic-gate  *	   0, otherwise
81917c478bd9Sstevel@tonic-gate  */
81927c478bd9Sstevel@tonic-gate static int
pm_is_noinvol_ancestor(pm_noinvol_t * aip)81937c478bd9Sstevel@tonic-gate pm_is_noinvol_ancestor(pm_noinvol_t *aip)
81947c478bd9Sstevel@tonic-gate {
81957c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip;
81967c478bd9Sstevel@tonic-gate 
81977c478bd9Sstevel@tonic-gate 	ASSERT(strlen(aip->ni_path) != 0);
81987c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
81997c478bd9Sstevel@tonic-gate 		if (ip == aip)
82007c478bd9Sstevel@tonic-gate 			continue;
82017c478bd9Sstevel@tonic-gate 		/*
82027c478bd9Sstevel@tonic-gate 		 * To be an ancestor, the path must be an initial substring of
82037c478bd9Sstevel@tonic-gate 		 * the descendent, and end just before a '/' in the
82047c478bd9Sstevel@tonic-gate 		 * descendent's path.
82057c478bd9Sstevel@tonic-gate 		 */
82067c478bd9Sstevel@tonic-gate 		if ((strstr(ip->ni_path, aip->ni_path) == ip->ni_path) &&
82077c478bd9Sstevel@tonic-gate 		    (ip->ni_path[strlen(aip->ni_path)] == '/'))
82087c478bd9Sstevel@tonic-gate 			return (1);
82097c478bd9Sstevel@tonic-gate 	}
82107c478bd9Sstevel@tonic-gate 	return (0);
82117c478bd9Sstevel@tonic-gate }
82127c478bd9Sstevel@tonic-gate 
82137c478bd9Sstevel@tonic-gate /*
82147c478bd9Sstevel@tonic-gate  * scan through the pm_noinvolpm list adjusting ancestors of the current
82157c478bd9Sstevel@tonic-gate  * node;  Modifies string *path.
82167c478bd9Sstevel@tonic-gate  */
82177c478bd9Sstevel@tonic-gate static void
adjust_ancestors(char * path,int wasvolpmd)82187c478bd9Sstevel@tonic-gate adjust_ancestors(char *path, int wasvolpmd)
82197c478bd9Sstevel@tonic-gate {
82207c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "adjust_ancestors")
82217c478bd9Sstevel@tonic-gate 	char *cp;
82227c478bd9Sstevel@tonic-gate 	pm_noinvol_t *lp;
82237c478bd9Sstevel@tonic-gate 	pm_noinvol_t *pp = NULL;
8224a204de77Scth 	major_t locked = DDI_MAJOR_T_NONE;
82257c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
82267c478bd9Sstevel@tonic-gate 	char	*pathbuf;
82277c478bd9Sstevel@tonic-gate 	size_t pathbuflen = strlen(path) + 1;
82287c478bd9Sstevel@tonic-gate 
82297c478bd9Sstevel@tonic-gate 	/*
82307c478bd9Sstevel@tonic-gate 	 * First we look up the ancestor's dip.  If we find it, then we
82317c478bd9Sstevel@tonic-gate 	 * adjust counts up the tree
82327c478bd9Sstevel@tonic-gate 	 */
82337c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s wasvolpmd %d\n", pmf, path, wasvolpmd))
82347c478bd9Sstevel@tonic-gate 	pathbuf = kmem_alloc(pathbuflen, KM_SLEEP);
82357c478bd9Sstevel@tonic-gate 	(void) strcpy(pathbuf, path);
82367c478bd9Sstevel@tonic-gate 	cp = strrchr(pathbuf, '/');
82377c478bd9Sstevel@tonic-gate 	if (cp == NULL)	{
82387c478bd9Sstevel@tonic-gate 		/* if no ancestors, then nothing to do */
82397c478bd9Sstevel@tonic-gate 		kmem_free(pathbuf, pathbuflen);
82407c478bd9Sstevel@tonic-gate 		return;
82417c478bd9Sstevel@tonic-gate 	}
82427c478bd9Sstevel@tonic-gate 	*cp = '\0';
82437c478bd9Sstevel@tonic-gate 	dip = pm_name_to_dip(pathbuf, 1);
82447c478bd9Sstevel@tonic-gate 	if (dip != NULL) {
82457c478bd9Sstevel@tonic-gate 		locked = PM_MAJOR(dip);
82467c478bd9Sstevel@tonic-gate 
82477c478bd9Sstevel@tonic-gate 		(void) pm_noinvol_update(PM_BP_NOINVOL_REMDRV, 0, wasvolpmd,
82487c478bd9Sstevel@tonic-gate 		    path, dip);
82497c478bd9Sstevel@tonic-gate 
8250a204de77Scth 		if (locked != DDI_MAJOR_T_NONE)
82517c478bd9Sstevel@tonic-gate 			ddi_release_devi(dip);
82527c478bd9Sstevel@tonic-gate 	} else {
82537c478bd9Sstevel@tonic-gate 		char *apath;
82547c478bd9Sstevel@tonic-gate 		size_t len = strlen(pathbuf) + 1;
82557c478bd9Sstevel@tonic-gate 		int  lock_held = 1;
82567c478bd9Sstevel@tonic-gate 
82577c478bd9Sstevel@tonic-gate 		/*
82587c478bd9Sstevel@tonic-gate 		 * Now check for ancestors that exist only in the list
82597c478bd9Sstevel@tonic-gate 		 */
82607c478bd9Sstevel@tonic-gate 		apath = kmem_alloc(len, KM_SLEEP);
82617c478bd9Sstevel@tonic-gate 		(void) strcpy(apath, pathbuf);
82627c478bd9Sstevel@tonic-gate 		rw_enter(&pm_noinvol_rwlock, RW_WRITER);
82637c478bd9Sstevel@tonic-gate 		for (lp = pm_noinvol_head; lp; pp = lp, lp = lp->ni_next) {
82647c478bd9Sstevel@tonic-gate 			/*
82657c478bd9Sstevel@tonic-gate 			 * This can only happen once.  Since we have to drop
82667c478bd9Sstevel@tonic-gate 			 * the lock, we need to extract the relevant info.
82677c478bd9Sstevel@tonic-gate 			 */
82687c478bd9Sstevel@tonic-gate 			if (strcmp(pathbuf, lp->ni_path) == 0) {
82697c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: %s no %d -> %d\n", pmf,
82707c478bd9Sstevel@tonic-gate 				    lp->ni_path, lp->ni_noinvolpm,
82717c478bd9Sstevel@tonic-gate 				    lp->ni_noinvolpm - 1))
82727c478bd9Sstevel@tonic-gate 				lp->ni_noinvolpm--;
82737c478bd9Sstevel@tonic-gate 				if (wasvolpmd && lp->ni_volpmd) {
82747c478bd9Sstevel@tonic-gate 					PMD(PMD_NOINVOL, ("%s: %s vol %d -> "
82757c478bd9Sstevel@tonic-gate 					    "%d\n", pmf, lp->ni_path,
82767c478bd9Sstevel@tonic-gate 					    lp->ni_volpmd, lp->ni_volpmd - 1))
82777c478bd9Sstevel@tonic-gate 					lp->ni_volpmd--;
82787c478bd9Sstevel@tonic-gate 				}
82797c478bd9Sstevel@tonic-gate 				/*
82807c478bd9Sstevel@tonic-gate 				 * remove the entry from the list, if there
82817c478bd9Sstevel@tonic-gate 				 * are no more no-invol descendants and node
82827c478bd9Sstevel@tonic-gate 				 * itself is not a no-invol node.
82837c478bd9Sstevel@tonic-gate 				 */
82847c478bd9Sstevel@tonic-gate 				if (!(lp->ni_noinvolpm ||
82857c478bd9Sstevel@tonic-gate 				    (lp->ni_flags & PMC_NO_INVOL))) {
82867c478bd9Sstevel@tonic-gate 					ASSERT(lp->ni_volpmd == 0);
82877c478bd9Sstevel@tonic-gate 					if (pp) {
82887c478bd9Sstevel@tonic-gate 						PMD(PMD_NOINVOL, ("%s: freeing "
82897c478bd9Sstevel@tonic-gate 						    "%s, prev is %s\n", pmf,
82907c478bd9Sstevel@tonic-gate 						    lp->ni_path, pp->ni_path))
82917c478bd9Sstevel@tonic-gate 						pp->ni_next = lp->ni_next;
82927c478bd9Sstevel@tonic-gate 					} else {
82937c478bd9Sstevel@tonic-gate 						PMD(PMD_NOINVOL, ("%s: free %s "
82947c478bd9Sstevel@tonic-gate 						    "head\n", pmf, lp->ni_path))
82957c478bd9Sstevel@tonic-gate 						ASSERT(pm_noinvol_head == lp);
82967c478bd9Sstevel@tonic-gate 						pm_noinvol_head = lp->ni_next;
82977c478bd9Sstevel@tonic-gate 					}
82987c478bd9Sstevel@tonic-gate 					lock_held = 0;
82997c478bd9Sstevel@tonic-gate 					rw_exit(&pm_noinvol_rwlock);
83007c478bd9Sstevel@tonic-gate 					adjust_ancestors(apath, wasvolpmd);
83017c478bd9Sstevel@tonic-gate 					/* restore apath */
83027c478bd9Sstevel@tonic-gate 					(void) strcpy(apath, pathbuf);
83037c478bd9Sstevel@tonic-gate 					kmem_free(lp->ni_path, lp->ni_size);
83047c478bd9Sstevel@tonic-gate 					kmem_free(lp, sizeof (*lp));
83057c478bd9Sstevel@tonic-gate 				}
83067c478bd9Sstevel@tonic-gate 				break;
83077c478bd9Sstevel@tonic-gate 			}
83087c478bd9Sstevel@tonic-gate 		}
83097c478bd9Sstevel@tonic-gate 		if (lock_held)
83107c478bd9Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
83117c478bd9Sstevel@tonic-gate 		adjust_ancestors(apath, wasvolpmd);
83127c478bd9Sstevel@tonic-gate 		kmem_free(apath, len);
83137c478bd9Sstevel@tonic-gate 	}
83147c478bd9Sstevel@tonic-gate 	kmem_free(pathbuf, pathbuflen);
83157c478bd9Sstevel@tonic-gate }
83167c478bd9Sstevel@tonic-gate 
83177c478bd9Sstevel@tonic-gate /*
83187c478bd9Sstevel@tonic-gate  * Do no-invol processing for any ancestors i.e. adjust counters of ancestors,
83197c478bd9Sstevel@tonic-gate  * which were skipped even though their drivers were removed.
83207c478bd9Sstevel@tonic-gate  */
83217c478bd9Sstevel@tonic-gate static void
pm_noinvol_process_ancestors(char * path)83227c478bd9Sstevel@tonic-gate pm_noinvol_process_ancestors(char *path)
83237c478bd9Sstevel@tonic-gate {
83247c478bd9Sstevel@tonic-gate 	pm_noinvol_t *lp;
83257c478bd9Sstevel@tonic-gate 
83267c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
83277c478bd9Sstevel@tonic-gate 	for (lp = pm_noinvol_head; lp; lp = lp->ni_next) {
83287c478bd9Sstevel@tonic-gate 		if (strstr(path, lp->ni_path) &&
83297c478bd9Sstevel@tonic-gate 		    (lp->ni_flags & PMC_DRIVER_REMOVED)) {
83307c478bd9Sstevel@tonic-gate 			rw_exit(&pm_noinvol_rwlock);
83317c478bd9Sstevel@tonic-gate 			i_pm_driver_removed(lp->ni_major);
83327c478bd9Sstevel@tonic-gate 			return;
83337c478bd9Sstevel@tonic-gate 		}
83347c478bd9Sstevel@tonic-gate 	}
83357c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
83367c478bd9Sstevel@tonic-gate }
83377c478bd9Sstevel@tonic-gate 
83387c478bd9Sstevel@tonic-gate /*
83397c478bd9Sstevel@tonic-gate  * Returns true if (detached) device needs to be kept up because it exported the
83407c478bd9Sstevel@tonic-gate  * "no-involuntary-power-cycles" property or we're pretending it did (console
83417c478bd9Sstevel@tonic-gate  * fb case) or it is an ancestor of such a device and has used up the "one
83427c478bd9Sstevel@tonic-gate  * free cycle" allowed when all such leaf nodes have voluntarily powered down
83437c478bd9Sstevel@tonic-gate  * upon detach.  In any event, we need an exact hit on the path or we return
83447c478bd9Sstevel@tonic-gate  * false.
83457c478bd9Sstevel@tonic-gate  */
83467c478bd9Sstevel@tonic-gate int
pm_noinvol_detached(char * path)83477c478bd9Sstevel@tonic-gate pm_noinvol_detached(char *path)
83487c478bd9Sstevel@tonic-gate {
83497c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_detached")
83507c478bd9Sstevel@tonic-gate 	pm_noinvol_t *ip;
83517c478bd9Sstevel@tonic-gate 	int ret = 0;
83527c478bd9Sstevel@tonic-gate 
83537c478bd9Sstevel@tonic-gate 	rw_enter(&pm_noinvol_rwlock, RW_READER);
83547c478bd9Sstevel@tonic-gate 	for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
83557c478bd9Sstevel@tonic-gate 		if (strcmp(path, ip->ni_path) == 0) {
83567c478bd9Sstevel@tonic-gate 			if (ip->ni_flags & PMC_CONSOLE_FB) {
83577c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB "
83587c478bd9Sstevel@tonic-gate 				    "%s\n", pmf, path))
83597c478bd9Sstevel@tonic-gate 				ret = 1;
83607c478bd9Sstevel@tonic-gate 				break;
83617c478bd9Sstevel@tonic-gate 			}
83627c478bd9Sstevel@tonic-gate #ifdef	DEBUG
83637c478bd9Sstevel@tonic-gate 			if (ip->ni_noinvolpm != ip->ni_volpmd)
83647c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s"
83657c478bd9Sstevel@tonic-gate 				    "\n", pmf, ip->ni_noinvolpm, ip->ni_volpmd,
83667c478bd9Sstevel@tonic-gate 				    path))
83677c478bd9Sstevel@tonic-gate #endif
83687c478bd9Sstevel@tonic-gate 			ret = (ip->ni_noinvolpm != ip->ni_volpmd);
83697c478bd9Sstevel@tonic-gate 			break;
83707c478bd9Sstevel@tonic-gate 		}
83717c478bd9Sstevel@tonic-gate 	}
83727c478bd9Sstevel@tonic-gate 	rw_exit(&pm_noinvol_rwlock);
83737c478bd9Sstevel@tonic-gate 	return (ret);
83747c478bd9Sstevel@tonic-gate }
83757c478bd9Sstevel@tonic-gate 
83767c478bd9Sstevel@tonic-gate int
pm_is_cfb(dev_info_t * dip)83777c478bd9Sstevel@tonic-gate pm_is_cfb(dev_info_t *dip)
83787c478bd9Sstevel@tonic-gate {
83797c478bd9Sstevel@tonic-gate 	return (dip == cfb_dip);
83807c478bd9Sstevel@tonic-gate }
83817c478bd9Sstevel@tonic-gate 
83827c478bd9Sstevel@tonic-gate #ifdef	DEBUG
83837c478bd9Sstevel@tonic-gate /*
83847c478bd9Sstevel@tonic-gate  * Return true if all components of the console frame buffer are at
83857c478bd9Sstevel@tonic-gate  * "normal" power, i.e., fully on.  For the case where the console is not
83867c478bd9Sstevel@tonic-gate  * a framebuffer, we also return true
83877c478bd9Sstevel@tonic-gate  */
83887c478bd9Sstevel@tonic-gate int
pm_cfb_is_up(void)83897c478bd9Sstevel@tonic-gate pm_cfb_is_up(void)
83907c478bd9Sstevel@tonic-gate {
83917c478bd9Sstevel@tonic-gate 	return (pm_cfb_comps_off == 0);
83927c478bd9Sstevel@tonic-gate }
83937c478bd9Sstevel@tonic-gate #endif
83947c478bd9Sstevel@tonic-gate 
83957c478bd9Sstevel@tonic-gate /*
83967c478bd9Sstevel@tonic-gate  * Preventing scan from powering down the node by incrementing the
83977c478bd9Sstevel@tonic-gate  * kidsupcnt.
83987c478bd9Sstevel@tonic-gate  */
83997c478bd9Sstevel@tonic-gate void
pm_hold_power(dev_info_t * dip)84007c478bd9Sstevel@tonic-gate pm_hold_power(dev_info_t *dip)
84017c478bd9Sstevel@tonic-gate {
84027c478bd9Sstevel@tonic-gate 	e_pm_hold_rele_power(dip, 1);
84037c478bd9Sstevel@tonic-gate }
84047c478bd9Sstevel@tonic-gate 
84057c478bd9Sstevel@tonic-gate /*
84067c478bd9Sstevel@tonic-gate  * Releasing the hold by decrementing the kidsupcnt allowing scan
84077c478bd9Sstevel@tonic-gate  * to power down the node if all conditions are met.
84087c478bd9Sstevel@tonic-gate  */
84097c478bd9Sstevel@tonic-gate void
pm_rele_power(dev_info_t * dip)84107c478bd9Sstevel@tonic-gate pm_rele_power(dev_info_t *dip)
84117c478bd9Sstevel@tonic-gate {
84127c478bd9Sstevel@tonic-gate 	e_pm_hold_rele_power(dip, -1);
84137c478bd9Sstevel@tonic-gate }
84147c478bd9Sstevel@tonic-gate 
84157c478bd9Sstevel@tonic-gate /*
84167c478bd9Sstevel@tonic-gate  * A wrapper of pm_all_to_normal() to power up a dip
84177c478bd9Sstevel@tonic-gate  * to its normal level
84187c478bd9Sstevel@tonic-gate  */
84197c478bd9Sstevel@tonic-gate int
pm_powerup(dev_info_t * dip)84207c478bd9Sstevel@tonic-gate pm_powerup(dev_info_t *dip)
84217c478bd9Sstevel@tonic-gate {
84227c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "pm_powerup")
84237c478bd9Sstevel@tonic-gate 
84247c478bd9Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
84257c478bd9Sstevel@tonic-gate 	ASSERT(!(servicing_interrupt()));
84267c478bd9Sstevel@tonic-gate 
84277c478bd9Sstevel@tonic-gate 	/*
84287c478bd9Sstevel@tonic-gate 	 * in case this node is not already participating pm
84297c478bd9Sstevel@tonic-gate 	 */
84307c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip)) {
84317c478bd9Sstevel@tonic-gate 		if (!DEVI_IS_ATTACHING(dip))
84327c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
84337c478bd9Sstevel@tonic-gate 		if (pm_start(dip) != DDI_SUCCESS)
84347c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
84357c478bd9Sstevel@tonic-gate 		if (!PM_GET_PM_INFO(dip))
84367c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
84377c478bd9Sstevel@tonic-gate 	}
84387c478bd9Sstevel@tonic-gate 
84397c478bd9Sstevel@tonic-gate 	return (pm_all_to_normal(dip, PM_CANBLOCK_BLOCK));
84407c478bd9Sstevel@tonic-gate }
84417c478bd9Sstevel@tonic-gate 
84427c478bd9Sstevel@tonic-gate int
pm_rescan_walk(dev_info_t * dip,void * arg)84437c478bd9Sstevel@tonic-gate pm_rescan_walk(dev_info_t *dip, void *arg)
84447c478bd9Sstevel@tonic-gate {
84457c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
84467c478bd9Sstevel@tonic-gate 
84477c478bd9Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
84487c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
84497c478bd9Sstevel@tonic-gate 
84507c478bd9Sstevel@tonic-gate 	/*
84517c478bd9Sstevel@tonic-gate 	 * Currently pm_cpr_callb/resume code is the only caller
84527c478bd9Sstevel@tonic-gate 	 * and it needs to make sure that stopped scan get
84537c478bd9Sstevel@tonic-gate 	 * reactivated. Otherwise, rescan walk needn't reactive
84547c478bd9Sstevel@tonic-gate 	 * stopped scan.
84557c478bd9Sstevel@tonic-gate 	 */
84567c478bd9Sstevel@tonic-gate 	pm_scan_init(dip);
84577c478bd9Sstevel@tonic-gate 
84587c478bd9Sstevel@tonic-gate 	(void) pm_rescan(dip);
84597c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
84607c478bd9Sstevel@tonic-gate }
84617c478bd9Sstevel@tonic-gate 
84627c478bd9Sstevel@tonic-gate static dev_info_t *
pm_get_next_descendent(dev_info_t * dip,dev_info_t * tdip)84637c478bd9Sstevel@tonic-gate pm_get_next_descendent(dev_info_t *dip, dev_info_t *tdip)
84647c478bd9Sstevel@tonic-gate {
84657c478bd9Sstevel@tonic-gate 	dev_info_t *wdip, *pdip;
84667c478bd9Sstevel@tonic-gate 
84677c478bd9Sstevel@tonic-gate 	for (wdip = tdip; wdip != dip; wdip = pdip) {
84687c478bd9Sstevel@tonic-gate 		pdip = ddi_get_parent(wdip);
84697c478bd9Sstevel@tonic-gate 		if (pdip == dip)
84707c478bd9Sstevel@tonic-gate 			return (wdip);
84717c478bd9Sstevel@tonic-gate 	}
84727c478bd9Sstevel@tonic-gate 	return (NULL);
84737c478bd9Sstevel@tonic-gate }
84747c478bd9Sstevel@tonic-gate 
84757c478bd9Sstevel@tonic-gate int
pm_busop_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)84767c478bd9Sstevel@tonic-gate pm_busop_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
84777c478bd9Sstevel@tonic-gate     void *arg, void *result)
84787c478bd9Sstevel@tonic-gate {
84797c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "bp_bus_power")
84807c478bd9Sstevel@tonic-gate 	dev_info_t	*cdip;
84817c478bd9Sstevel@tonic-gate 	pm_info_t	*cinfo;
84827c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t	*bpc;
84837c478bd9Sstevel@tonic-gate 	pm_sp_misc_t		*pspm;
84847c478bd9Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t *bpn;
84857c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t new_bpc;
84867c478bd9Sstevel@tonic-gate 	pm_bp_noinvol_t *bpi;
84877c478bd9Sstevel@tonic-gate 	dev_info_t *tdip;
84887c478bd9Sstevel@tonic-gate 	char *pathbuf;
84897c478bd9Sstevel@tonic-gate 	int		ret = DDI_SUCCESS;
84907c478bd9Sstevel@tonic-gate 	int		errno = 0;
84917c478bd9Sstevel@tonic-gate 	pm_component_t *cp;
84927c478bd9Sstevel@tonic-gate 
84937c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip),
84947c478bd9Sstevel@tonic-gate 	    pm_decode_op(op)))
84957c478bd9Sstevel@tonic-gate 	switch (op) {
84967c478bd9Sstevel@tonic-gate 	case BUS_POWER_CHILD_PWRCHG:
84977c478bd9Sstevel@tonic-gate 		bpc = (pm_bp_child_pwrchg_t *)arg;
84987c478bd9Sstevel@tonic-gate 		pspm = (pm_sp_misc_t *)bpc->bpc_private;
84997c478bd9Sstevel@tonic-gate 		tdip = bpc->bpc_dip;
85007c478bd9Sstevel@tonic-gate 		cdip = pm_get_next_descendent(dip, tdip);
85017c478bd9Sstevel@tonic-gate 		cinfo = PM_GET_PM_INFO(cdip);
85027c478bd9Sstevel@tonic-gate 		if (cdip != tdip) {
85037c478bd9Sstevel@tonic-gate 			/*
85047c478bd9Sstevel@tonic-gate 			 * If the node is an involved parent, it needs to
85057c478bd9Sstevel@tonic-gate 			 * power up the node as it is needed.  There is nothing
85067c478bd9Sstevel@tonic-gate 			 * else the framework can do here.
85077c478bd9Sstevel@tonic-gate 			 */
85087c478bd9Sstevel@tonic-gate 			if (PM_WANTS_NOTIFICATION(cdip)) {
85097c478bd9Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: call bus_power for "
85107c478bd9Sstevel@tonic-gate 				    "%s@%s(%s#%d)\n", pmf, PM_DEVICE(cdip)))
85117c478bd9Sstevel@tonic-gate 				return ((*PM_BUS_POWER_FUNC(cdip))(cdip,
85127c478bd9Sstevel@tonic-gate 				    impl_arg, op, arg, result));
85137c478bd9Sstevel@tonic-gate 			}
85147c478bd9Sstevel@tonic-gate 			ASSERT(pspm->pspm_direction == PM_LEVEL_UPONLY ||
85157c478bd9Sstevel@tonic-gate 			    pspm->pspm_direction == PM_LEVEL_DOWNONLY ||
85167c478bd9Sstevel@tonic-gate 			    pspm->pspm_direction == PM_LEVEL_EXACT);
85177c478bd9Sstevel@tonic-gate 			/*
85187c478bd9Sstevel@tonic-gate 			 * we presume that the parent needs to be up in
85197c478bd9Sstevel@tonic-gate 			 * order for the child to change state (either
85207c478bd9Sstevel@tonic-gate 			 * because it must already be on if the child is on
85217c478bd9Sstevel@tonic-gate 			 * (and the pm_all_to_normal_nexus() will be a nop)
85227c478bd9Sstevel@tonic-gate 			 * or because it will need to be on for the child
85237c478bd9Sstevel@tonic-gate 			 * to come on; so we make the call regardless
85247c478bd9Sstevel@tonic-gate 			 */
85257c478bd9Sstevel@tonic-gate 			pm_hold_power(cdip);
85267c478bd9Sstevel@tonic-gate 			if (cinfo) {
85277c478bd9Sstevel@tonic-gate 				pm_canblock_t canblock = pspm->pspm_canblock;
85287c478bd9Sstevel@tonic-gate 				ret = pm_all_to_normal_nexus(cdip, canblock);
85297c478bd9Sstevel@tonic-gate 				if (ret != DDI_SUCCESS) {
85307c478bd9Sstevel@tonic-gate 					pm_rele_power(cdip);
85317c478bd9Sstevel@tonic-gate 					return (ret);
85327c478bd9Sstevel@tonic-gate 				}
85337c478bd9Sstevel@tonic-gate 			}
85347c478bd9Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: walk down to %s@%s(%s#%d)\n", pmf,
85357c478bd9Sstevel@tonic-gate 			    PM_DEVICE(cdip)))
85367c478bd9Sstevel@tonic-gate 			ret = pm_busop_bus_power(cdip, impl_arg, op, arg,
85377c478bd9Sstevel@tonic-gate 			    result);
85387c478bd9Sstevel@tonic-gate 			pm_rele_power(cdip);
85397c478bd9Sstevel@tonic-gate 		} else {
85407c478bd9Sstevel@tonic-gate 			ret = pm_busop_set_power(cdip, impl_arg, op, arg,
85417c478bd9Sstevel@tonic-gate 			    result);
85427c478bd9Sstevel@tonic-gate 		}
85437c478bd9Sstevel@tonic-gate 		return (ret);
85447c478bd9Sstevel@tonic-gate 
85457c478bd9Sstevel@tonic-gate 	case BUS_POWER_NEXUS_PWRUP:
85467c478bd9Sstevel@tonic-gate 		bpn = (pm_bp_nexus_pwrup_t *)arg;
85477c478bd9Sstevel@tonic-gate 		pspm = (pm_sp_misc_t *)bpn->bpn_private;
85487c478bd9Sstevel@tonic-gate 
85497c478bd9Sstevel@tonic-gate 		if (!e_pm_valid_info(dip, NULL) ||
85507c478bd9Sstevel@tonic-gate 		    !e_pm_valid_comp(dip, bpn->bpn_comp, &cp) ||
85517c478bd9Sstevel@tonic-gate 		    !e_pm_valid_power(dip, bpn->bpn_comp, bpn->bpn_level)) {
85527c478bd9Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: %s@%s(%s#%d) has no pm info; EIO\n",
85537c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(dip)))
85547c478bd9Sstevel@tonic-gate 			*pspm->pspm_errnop = EIO;
85557c478bd9Sstevel@tonic-gate 			*(int *)result = DDI_FAILURE;
85567c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
85577c478bd9Sstevel@tonic-gate 		}
85587c478bd9Sstevel@tonic-gate 
85597c478bd9Sstevel@tonic-gate 		ASSERT(bpn->bpn_dip == dip);
85607c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: nexus powerup for %s@%s(%s#%d)\n", pmf,
85617c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip)))
85627c478bd9Sstevel@tonic-gate 		new_bpc.bpc_dip = dip;
85637c478bd9Sstevel@tonic-gate 		pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
85647c478bd9Sstevel@tonic-gate 		new_bpc.bpc_path = ddi_pathname(dip, pathbuf);
85657c478bd9Sstevel@tonic-gate 		new_bpc.bpc_comp = bpn->bpn_comp;
85667c478bd9Sstevel@tonic-gate 		new_bpc.bpc_olevel = PM_CURPOWER(dip, bpn->bpn_comp);
85677c478bd9Sstevel@tonic-gate 		new_bpc.bpc_nlevel = bpn->bpn_level;
85687c478bd9Sstevel@tonic-gate 		new_bpc.bpc_private = bpn->bpn_private;
85697c478bd9Sstevel@tonic-gate 		((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_direction =
85707c478bd9Sstevel@tonic-gate 		    PM_LEVEL_UPONLY;
85717c478bd9Sstevel@tonic-gate 		((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_errnop =
85727c478bd9Sstevel@tonic-gate 		    &errno;
85737c478bd9Sstevel@tonic-gate 		ret = pm_busop_set_power(dip, impl_arg, BUS_POWER_CHILD_PWRCHG,
85747c478bd9Sstevel@tonic-gate 		    (void *)&new_bpc, result);
85757c478bd9Sstevel@tonic-gate 		kmem_free(pathbuf, MAXPATHLEN);
85767c478bd9Sstevel@tonic-gate 		return (ret);
85777c478bd9Sstevel@tonic-gate 
85787c478bd9Sstevel@tonic-gate 	case BUS_POWER_NOINVOL:
85797c478bd9Sstevel@tonic-gate 		bpi = (pm_bp_noinvol_t *)arg;
85807c478bd9Sstevel@tonic-gate 		tdip = bpi->bpni_dip;
85817c478bd9Sstevel@tonic-gate 		cdip = pm_get_next_descendent(dip, tdip);
85827c478bd9Sstevel@tonic-gate 
85837c478bd9Sstevel@tonic-gate 		/* In case of rem_drv, the leaf node has been removed */
85847c478bd9Sstevel@tonic-gate 		if (cdip == NULL)
85857c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
85867c478bd9Sstevel@tonic-gate 
85877c478bd9Sstevel@tonic-gate 		cinfo = PM_GET_PM_INFO(cdip);
85887c478bd9Sstevel@tonic-gate 		if (cdip != tdip) {
85897c478bd9Sstevel@tonic-gate 			if (PM_WANTS_NOTIFICATION(cdip)) {
85907c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL,
85917c478bd9Sstevel@tonic-gate 				    ("%s: call bus_power for %s@%s(%s#%d)\n",
85927c478bd9Sstevel@tonic-gate 				    pmf, PM_DEVICE(cdip)))
85937c478bd9Sstevel@tonic-gate 				ret = (*PM_BUS_POWER_FUNC(cdip))
85947c478bd9Sstevel@tonic-gate 				    (cdip, NULL, op, arg, result);
85957c478bd9Sstevel@tonic-gate 				if ((cinfo) && (ret == DDI_SUCCESS))
85967c478bd9Sstevel@tonic-gate 					(void) pm_noinvol_update_node(cdip,
85977c478bd9Sstevel@tonic-gate 					    bpi);
85987c478bd9Sstevel@tonic-gate 				return (ret);
85997c478bd9Sstevel@tonic-gate 			} else {
86007c478bd9Sstevel@tonic-gate 				PMD(PMD_NOINVOL,
86017c478bd9Sstevel@tonic-gate 				    ("%s: walk down to %s@%s(%s#%d)\n", pmf,
86027c478bd9Sstevel@tonic-gate 				    PM_DEVICE(cdip)))
86037c478bd9Sstevel@tonic-gate 				ret = pm_busop_bus_power(cdip, NULL, op,
86047c478bd9Sstevel@tonic-gate 				    arg, result);
86057c478bd9Sstevel@tonic-gate 				/*
86067c478bd9Sstevel@tonic-gate 				 * Update the current node.
86077c478bd9Sstevel@tonic-gate 				 */
86087c478bd9Sstevel@tonic-gate 				if ((cinfo) && (ret == DDI_SUCCESS))
86097c478bd9Sstevel@tonic-gate 					(void) pm_noinvol_update_node(cdip,
86107c478bd9Sstevel@tonic-gate 					    bpi);
86117c478bd9Sstevel@tonic-gate 				return (ret);
86127c478bd9Sstevel@tonic-gate 			}
86137c478bd9Sstevel@tonic-gate 		} else {
86147c478bd9Sstevel@tonic-gate 			/*
86157c478bd9Sstevel@tonic-gate 			 * For attach, detach, power up:
86167c478bd9Sstevel@tonic-gate 			 * Do nothing for leaf node since its
86177c478bd9Sstevel@tonic-gate 			 * counts are already updated.
86187c478bd9Sstevel@tonic-gate 			 * For CFB and driver removal, since the
86197c478bd9Sstevel@tonic-gate 			 * path and the target dip passed in is up to and incl.
86207c478bd9Sstevel@tonic-gate 			 * the immediate ancestor, need to do the update.
86217c478bd9Sstevel@tonic-gate 			 */
86227c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: target %s@%s(%s#%d) is "
86237c478bd9Sstevel@tonic-gate 			    "reached\n", pmf, PM_DEVICE(cdip)))
86247c478bd9Sstevel@tonic-gate 			if (cinfo && ((bpi->bpni_cmd == PM_BP_NOINVOL_REMDRV) ||
86257c478bd9Sstevel@tonic-gate 			    (bpi->bpni_cmd == PM_BP_NOINVOL_CFB)))
86267c478bd9Sstevel@tonic-gate 				(void) pm_noinvol_update_node(cdip, bpi);
86277c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
86287c478bd9Sstevel@tonic-gate 		}
86297c478bd9Sstevel@tonic-gate 
86307c478bd9Sstevel@tonic-gate 	default:
86317c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: operation %d is not supported!\n", pmf, op))
86327c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
86337c478bd9Sstevel@tonic-gate 	}
86347c478bd9Sstevel@tonic-gate }
86357c478bd9Sstevel@tonic-gate 
86367c478bd9Sstevel@tonic-gate static int
pm_busop_set_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * resultp)86377c478bd9Sstevel@tonic-gate pm_busop_set_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
86387c478bd9Sstevel@tonic-gate     void *arg, void *resultp)
86397c478bd9Sstevel@tonic-gate {
86407c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(impl_arg))
86417c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "bp_set_power")
86428c533f11Smh 	pm_ppm_devlist_t *devl = NULL;
86433fe80ca4SDan Cross 	int clevel;
86447c478bd9Sstevel@tonic-gate 	int ret = DDI_SUCCESS;
86457c478bd9Sstevel@tonic-gate 	dev_info_t *cdip;
86467c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
86477c478bd9Sstevel@tonic-gate 	pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private;
86487c478bd9Sstevel@tonic-gate 	pm_canblock_t canblock = pspm->pspm_canblock;
86497c478bd9Sstevel@tonic-gate 	int scan = pspm->pspm_scan;
86507c478bd9Sstevel@tonic-gate 	int comp = bpc->bpc_comp;
86517c478bd9Sstevel@tonic-gate 	int olevel = bpc->bpc_olevel;
86527c478bd9Sstevel@tonic-gate 	int nlevel = bpc->bpc_nlevel;
86537c478bd9Sstevel@tonic-gate 	int comps_off_incr = 0;
86547c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = ddi_get_parent(dip);
86557c478bd9Sstevel@tonic-gate 	int dodeps;
86567c478bd9Sstevel@tonic-gate 	int direction = pspm->pspm_direction;
86577c478bd9Sstevel@tonic-gate 	int *errnop = pspm->pspm_errnop;
86589681b4a1Skchow #ifdef PMDDEBUG
86597c478bd9Sstevel@tonic-gate 	char *dir = pm_decode_direction(direction);
86609681b4a1Skchow #endif
86617c478bd9Sstevel@tonic-gate 	int *iresp = (int *)resultp;
86627c478bd9Sstevel@tonic-gate 	time_t	idletime, thresh;
86637c478bd9Sstevel@tonic-gate 	pm_component_t *cp = PM_CP(dip, comp);
86647c478bd9Sstevel@tonic-gate 	int work_type;
86657c478bd9Sstevel@tonic-gate 
86667c478bd9Sstevel@tonic-gate 	*iresp = DDI_SUCCESS;
86677c478bd9Sstevel@tonic-gate 	*errnop = 0;
86687c478bd9Sstevel@tonic-gate 	ASSERT(op == BUS_POWER_CHILD_PWRCHG);
86697c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip),
86707c478bd9Sstevel@tonic-gate 	    pm_decode_op(op)))
86717c478bd9Sstevel@tonic-gate 
86727c478bd9Sstevel@tonic-gate 	/*
86737c478bd9Sstevel@tonic-gate 	 * The following set of conditions indicate we are here to handle a
86747c478bd9Sstevel@tonic-gate 	 * driver's pm_[raise|lower]_power request, but the device is being
86757c478bd9Sstevel@tonic-gate 	 * power managed (PM_DIRECT_PM) by a user process.  For that case
86767c478bd9Sstevel@tonic-gate 	 * we want to pm_block and pass a status back to the caller based
86777c478bd9Sstevel@tonic-gate 	 * on whether the controlling process's next activity on the device
86787c478bd9Sstevel@tonic-gate 	 * matches the current request or not.  This distinction tells
86797c478bd9Sstevel@tonic-gate 	 * downstream functions to avoid calling into a driver or changing
86807c478bd9Sstevel@tonic-gate 	 * the framework's power state.  To actually block, we need:
86817c478bd9Sstevel@tonic-gate 	 *
86827c478bd9Sstevel@tonic-gate 	 * PM_ISDIRECT(dip)
86837c478bd9Sstevel@tonic-gate 	 *	no reason to block unless a process is directly controlling dev
86847c478bd9Sstevel@tonic-gate 	 * direction != PM_LEVEL_EXACT
86857c478bd9Sstevel@tonic-gate 	 *	EXACT is used by controlling proc's PM_SET_CURRENT_POWER ioctl
86867c478bd9Sstevel@tonic-gate 	 * !pm_processes_stopped
86877c478bd9Sstevel@tonic-gate 	 *	don't block if controlling proc already be stopped for cpr
86887c478bd9Sstevel@tonic-gate 	 * canblock != PM_CANBLOCK_BYPASS
86897c478bd9Sstevel@tonic-gate 	 *	our caller must not have explicitly prevented blocking
86907c478bd9Sstevel@tonic-gate 	 */
86917c478bd9Sstevel@tonic-gate 	if (direction != PM_LEVEL_EXACT && canblock != PM_CANBLOCK_BYPASS) {
86927c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
86937c478bd9Sstevel@tonic-gate 		while (PM_ISDIRECT(dip) && !pm_processes_stopped) {
86947c478bd9Sstevel@tonic-gate 			/* releases dip lock */
86957c478bd9Sstevel@tonic-gate 			ret = pm_busop_match_request(dip, bpc);
86967c478bd9Sstevel@tonic-gate 			if (ret == EAGAIN) {
86977c478bd9Sstevel@tonic-gate 				PM_LOCK_DIP(dip);
86987c478bd9Sstevel@tonic-gate 				continue;
86997c478bd9Sstevel@tonic-gate 			}
87007c478bd9Sstevel@tonic-gate 			return (*iresp = ret);
87017c478bd9Sstevel@tonic-gate 		}
87027c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
87037c478bd9Sstevel@tonic-gate 	}
87047c478bd9Sstevel@tonic-gate 	/* BC device is never scanned, so power will stick until we are done */
87057c478bd9Sstevel@tonic-gate 	if (PM_ISBC(dip) && comp != 0 && nlevel != 0 &&
87067c478bd9Sstevel@tonic-gate 	    direction != PM_LEVEL_DOWNONLY) {
87077c478bd9Sstevel@tonic-gate 		int nrmpwr0 = pm_get_normal_power(dip, 0);
87087c478bd9Sstevel@tonic-gate 		if (pm_set_power(dip, 0, nrmpwr0, direction,
87097c478bd9Sstevel@tonic-gate 		    canblock, 0, resultp) != DDI_SUCCESS) {
87107c478bd9Sstevel@tonic-gate 			/* *resultp set by pm_set_power */
87117c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
87127c478bd9Sstevel@tonic-gate 		}
87137c478bd9Sstevel@tonic-gate 	}
87147c478bd9Sstevel@tonic-gate 	if (PM_WANTS_NOTIFICATION(pdip)) {
87157c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: pre_notify %s@%s(%s#%d) for child "
87167c478bd9Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", pmf, PM_DEVICE(pdip), PM_DEVICE(dip)))
87177c478bd9Sstevel@tonic-gate 		ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
87187c478bd9Sstevel@tonic-gate 		    BUS_POWER_PRE_NOTIFICATION, bpc, resultp);
87197c478bd9Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
87207c478bd9Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: failed to pre_notify %s@%s(%s#%d)\n",
87217c478bd9Sstevel@tonic-gate 			    pmf, PM_DEVICE(pdip)))
87227c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
87237c478bd9Sstevel@tonic-gate 		}
87247c478bd9Sstevel@tonic-gate 	} else {
87257c478bd9Sstevel@tonic-gate 		/*
87267c478bd9Sstevel@tonic-gate 		 * Since we don't know what the actual power level is,
87277c478bd9Sstevel@tonic-gate 		 * we place a power hold on the parent no matter what
87287c478bd9Sstevel@tonic-gate 		 * component and level is changing.
87297c478bd9Sstevel@tonic-gate 		 */
87307c478bd9Sstevel@tonic-gate 		pm_hold_power(pdip);
87317c478bd9Sstevel@tonic-gate 	}
87323fe80ca4SDan Cross 	PM_LOCK_POWER(dip);
87336152d498Smh 	clevel = PM_CURPOWER(dip, comp);
87345cff7825Smh 	/*
87355cff7825Smh 	 * It's possible that a call was made to pm_update_maxpower()
87365cff7825Smh 	 * on another thread before we took the lock above. So, we need to
87375cff7825Smh 	 * make sure that this request isn't processed after the
87385cff7825Smh 	 * change of power executed on behalf of pm_update_maxpower().
87395cff7825Smh 	 */
87405cff7825Smh 	if (nlevel > pm_get_normal_power(dip, comp)) {
87415cff7825Smh 		PMD(PMD_SET, ("%s: requested level is higher than normal.\n",
87425cff7825Smh 		    pmf))
87435cff7825Smh 		ret = DDI_FAILURE;
87445cff7825Smh 		*iresp = DDI_FAILURE;
87455cff7825Smh 		goto post_notify;
87465cff7825Smh 	}
87477c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, olvl=%d, nlvl=%d, clvl=%d, "
87487c478bd9Sstevel@tonic-gate 	    "dir=%s\n", pmf, PM_DEVICE(dip), comp, bpc->bpc_olevel, nlevel,
87497c478bd9Sstevel@tonic-gate 	    clevel, dir))
87507c478bd9Sstevel@tonic-gate 	switch (direction) {
87517c478bd9Sstevel@tonic-gate 	case PM_LEVEL_UPONLY:
87527c478bd9Sstevel@tonic-gate 		/* Powering up */
87537c478bd9Sstevel@tonic-gate 		if (clevel >= nlevel) {
87547c478bd9Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: current level is already "
87557c478bd9Sstevel@tonic-gate 			    "at or above the requested level.\n", pmf))
87567c478bd9Sstevel@tonic-gate 			*iresp = DDI_SUCCESS;
87577c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
87587c478bd9Sstevel@tonic-gate 			goto post_notify;
87597c478bd9Sstevel@tonic-gate 		}
87607c478bd9Sstevel@tonic-gate 		break;
87617c478bd9Sstevel@tonic-gate 	case PM_LEVEL_EXACT:
87627c478bd9Sstevel@tonic-gate 		/* specific level request */
87637c478bd9Sstevel@tonic-gate 		if (clevel == nlevel && !PM_ISBC(dip)) {
87647c478bd9Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: current level is already "
87657c478bd9Sstevel@tonic-gate 			    "at the requested level.\n", pmf))
87667c478bd9Sstevel@tonic-gate 			*iresp = DDI_SUCCESS;
87677c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
87687c478bd9Sstevel@tonic-gate 			goto post_notify;
87697c478bd9Sstevel@tonic-gate 		} else if (PM_IS_CFB(dip) && (nlevel < clevel)) {
87707c478bd9Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: powerdown of console\n", pmf))
87717c478bd9Sstevel@tonic-gate 			if (!pm_cfb_enabled) {
87727c478bd9Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_CFB,
87737c478bd9Sstevel@tonic-gate 				    ("%s: !pm_cfb_enabled, fails\n", pmf))
87747c478bd9Sstevel@tonic-gate 				*errnop = EINVAL;
87757c478bd9Sstevel@tonic-gate 				*iresp = DDI_FAILURE;
87767c478bd9Sstevel@tonic-gate 				ret = DDI_FAILURE;
87777c478bd9Sstevel@tonic-gate 				goto post_notify;
87787c478bd9Sstevel@tonic-gate 			}
87797c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_cfb_lock);
87807c478bd9Sstevel@tonic-gate 			while (cfb_inuse) {
87817c478bd9Sstevel@tonic-gate 				mutex_exit(&pm_cfb_lock);
87827c478bd9Sstevel@tonic-gate 				if (delay_sig(1) == EINTR) {
87837c478bd9Sstevel@tonic-gate 					ret = DDI_FAILURE;
87847c478bd9Sstevel@tonic-gate 					*iresp = DDI_FAILURE;
87857c478bd9Sstevel@tonic-gate 					*errnop = EINTR;
87867c478bd9Sstevel@tonic-gate 					goto post_notify;
87877c478bd9Sstevel@tonic-gate 				}
87887c478bd9Sstevel@tonic-gate 				mutex_enter(&pm_cfb_lock);
87897c478bd9Sstevel@tonic-gate 			}
87907c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_cfb_lock);
87917c478bd9Sstevel@tonic-gate 		}
87927c478bd9Sstevel@tonic-gate 		break;
87937c478bd9Sstevel@tonic-gate 	case PM_LEVEL_DOWNONLY:
87947c478bd9Sstevel@tonic-gate 		/* Powering down */
87957c478bd9Sstevel@tonic-gate 		thresh = cur_threshold(dip, comp);
87967c478bd9Sstevel@tonic-gate 		idletime = gethrestime_sec() - cp->pmc_timestamp;
87977c478bd9Sstevel@tonic-gate 		if (scan && ((PM_KUC(dip) != 0) ||
8798a99367ecSosaeed 		    (cp->pmc_busycount > 0) ||
8799a99367ecSosaeed 		    ((idletime < thresh) && !PM_IS_PID(dip)))) {
88007c478bd9Sstevel@tonic-gate #ifdef	DEBUG
88017c478bd9Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_kidsupcnt != 0)
88027c478bd9Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: scan failed: "
88037c478bd9Sstevel@tonic-gate 				    "kidsupcnt != 0\n", pmf))
88047c478bd9Sstevel@tonic-gate 			if (cp->pmc_busycount > 0)
88057c478bd9Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: scan failed: "
88067c478bd9Sstevel@tonic-gate 				    "device become busy\n", pmf))
88077c478bd9Sstevel@tonic-gate 			if (idletime < thresh)
88087c478bd9Sstevel@tonic-gate 				PMD(PMD_SET, ("%s: scan failed: device "
88097c478bd9Sstevel@tonic-gate 				    "hasn't been idle long enough\n", pmf))
88107c478bd9Sstevel@tonic-gate #endif
88117c478bd9Sstevel@tonic-gate 			*iresp = DDI_FAILURE;
88127c478bd9Sstevel@tonic-gate 			*errnop = EBUSY;
88137c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
88147c478bd9Sstevel@tonic-gate 			goto post_notify;
88157c478bd9Sstevel@tonic-gate 		} else if (clevel != PM_LEVEL_UNKNOWN && clevel <= nlevel) {
88167c478bd9Sstevel@tonic-gate 			PMD(PMD_SET, ("%s: current level is already at "
88177c478bd9Sstevel@tonic-gate 			    "or below the requested level.\n", pmf))
88187c478bd9Sstevel@tonic-gate 			*iresp = DDI_SUCCESS;
88197c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
88207c478bd9Sstevel@tonic-gate 			goto post_notify;
88217c478bd9Sstevel@tonic-gate 		}
88227c478bd9Sstevel@tonic-gate 		break;
88237c478bd9Sstevel@tonic-gate 	}
88247c478bd9Sstevel@tonic-gate 
88257c478bd9Sstevel@tonic-gate 	if (PM_IS_CFB(dip) && (comps_off_incr =
88267c478bd9Sstevel@tonic-gate 	    calc_cfb_comps_incr(dip, comp, clevel, nlevel)) > 0) {
88277c478bd9Sstevel@tonic-gate 		/*
88287c478bd9Sstevel@tonic-gate 		 * Pre-adjust pm_cfb_comps_off if lowering a console fb
88297c478bd9Sstevel@tonic-gate 		 * component from full power.  Remember that we tried to
88307c478bd9Sstevel@tonic-gate 		 * lower power in case it fails and we need to back out
88317c478bd9Sstevel@tonic-gate 		 * the adjustment.
88327c478bd9Sstevel@tonic-gate 		 */
88337c478bd9Sstevel@tonic-gate 		update_comps_off(comps_off_incr, dip);
88347c478bd9Sstevel@tonic-gate 		PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d cfb_comps_off->%d\n",
88357c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), comp, clevel, nlevel,
88367c478bd9Sstevel@tonic-gate 		    pm_cfb_comps_off))
88377c478bd9Sstevel@tonic-gate 	}
88387c478bd9Sstevel@tonic-gate 
88397c478bd9Sstevel@tonic-gate 	if ((*iresp = power_dev(dip,
88407c478bd9Sstevel@tonic-gate 	    comp, nlevel, clevel, canblock, &devl)) == DDI_SUCCESS) {
88417c478bd9Sstevel@tonic-gate #ifdef DEBUG
88427c478bd9Sstevel@tonic-gate 		/*
88437c478bd9Sstevel@tonic-gate 		 * All descendents of this node should already be powered off.
88447c478bd9Sstevel@tonic-gate 		 */
88457c478bd9Sstevel@tonic-gate 		if (PM_CURPOWER(dip, comp) == 0) {
88467c478bd9Sstevel@tonic-gate 			pm_desc_pwrchk_t pdpchk;
88477c478bd9Sstevel@tonic-gate 			pdpchk.pdpc_dip = dip;
88487c478bd9Sstevel@tonic-gate 			pdpchk.pdpc_par_involved = PM_WANTS_NOTIFICATION(dip);
88493fe80ca4SDan Cross 			ndi_devi_enter(dip);
88507c478bd9Sstevel@tonic-gate 			for (cdip = ddi_get_child(dip); cdip != NULL;
88517c478bd9Sstevel@tonic-gate 			    cdip = ddi_get_next_sibling(cdip)) {
88523fe80ca4SDan Cross 				ndi_devi_enter(cdip);
88537c478bd9Sstevel@tonic-gate 				ddi_walk_devs(cdip, pm_desc_pwrchk_walk,
88547c478bd9Sstevel@tonic-gate 				    (void *)&pdpchk);
88553fe80ca4SDan Cross 				ndi_devi_exit(cdip);
88567c478bd9Sstevel@tonic-gate 			}
88573fe80ca4SDan Cross 			ndi_devi_exit(dip);
88587c478bd9Sstevel@tonic-gate 		}
88597c478bd9Sstevel@tonic-gate #endif
88607c478bd9Sstevel@tonic-gate 		/*
88617c478bd9Sstevel@tonic-gate 		 * Post-adjust pm_cfb_comps_off if we brought an fb component
88627c478bd9Sstevel@tonic-gate 		 * back up to full power.
88637c478bd9Sstevel@tonic-gate 		 */
88647c478bd9Sstevel@tonic-gate 		if (PM_IS_CFB(dip) && comps_off_incr < 0) {
88657c478bd9Sstevel@tonic-gate 			update_comps_off(comps_off_incr, dip);
88667c478bd9Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d "
88677c478bd9Sstevel@tonic-gate 			    "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
88687c478bd9Sstevel@tonic-gate 			    comp, clevel, nlevel, pm_cfb_comps_off))
88697c478bd9Sstevel@tonic-gate 		}
88707c478bd9Sstevel@tonic-gate 		dodeps = 0;
88717c478bd9Sstevel@tonic-gate 		if (POWERING_OFF(clevel, nlevel)) {
88727c478bd9Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
88737c478bd9Sstevel@tonic-gate 				dodeps = (comp == 0);
88747c478bd9Sstevel@tonic-gate 			} else {
88757c478bd9Sstevel@tonic-gate 				int i;
88767c478bd9Sstevel@tonic-gate 				dodeps = 1;
88777c478bd9Sstevel@tonic-gate 				for (i = 0; i < PM_NUMCMPTS(dip); i++) {
88787c478bd9Sstevel@tonic-gate 					/* if some component still on */
88797c478bd9Sstevel@tonic-gate 					if (PM_CURPOWER(dip, i)) {
88807c478bd9Sstevel@tonic-gate 						dodeps = 0;
88817c478bd9Sstevel@tonic-gate 						break;
88827c478bd9Sstevel@tonic-gate 					}
88837c478bd9Sstevel@tonic-gate 				}
88847c478bd9Sstevel@tonic-gate 			}
88857c478bd9Sstevel@tonic-gate 			if (dodeps)
88867c478bd9Sstevel@tonic-gate 				work_type = PM_DEP_WK_POWER_OFF;
88877c478bd9Sstevel@tonic-gate 		} else if (POWERING_ON(clevel, nlevel)) {
88887c478bd9Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
88897c478bd9Sstevel@tonic-gate 				dodeps = (comp == 0);
88907c478bd9Sstevel@tonic-gate 			} else {
88917c478bd9Sstevel@tonic-gate 				int i;
88927c478bd9Sstevel@tonic-gate 				dodeps = 1;
88937c478bd9Sstevel@tonic-gate 				for (i = 0; i < PM_NUMCMPTS(dip); i++) {
88947c478bd9Sstevel@tonic-gate 					if (i == comp)
88957c478bd9Sstevel@tonic-gate 						continue;
88967c478bd9Sstevel@tonic-gate 					if (PM_CURPOWER(dip, i) > 0) {
88977c478bd9Sstevel@tonic-gate 						dodeps = 0;
88987c478bd9Sstevel@tonic-gate 						break;
88997c478bd9Sstevel@tonic-gate 					}
89007c478bd9Sstevel@tonic-gate 				}
89017c478bd9Sstevel@tonic-gate 			}
89027c478bd9Sstevel@tonic-gate 			if (dodeps)
89037c478bd9Sstevel@tonic-gate 				work_type = PM_DEP_WK_POWER_ON;
89047c478bd9Sstevel@tonic-gate 		}
89057c478bd9Sstevel@tonic-gate 
89067c478bd9Sstevel@tonic-gate 		if (dodeps) {
89077c478bd9Sstevel@tonic-gate 			char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
89087c478bd9Sstevel@tonic-gate 
89097c478bd9Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
89107c478bd9Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(work_type, pathbuf, NULL,
89117c478bd9Sstevel@tonic-gate 			    PM_DEP_NOWAIT, NULL, 0);
89127c478bd9Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
89137c478bd9Sstevel@tonic-gate 		}
89147c478bd9Sstevel@tonic-gate 		if ((PM_CURPOWER(dip, comp) == nlevel) && pm_watchers()) {
89157c478bd9Sstevel@tonic-gate 			int old;
89167c478bd9Sstevel@tonic-gate 
89177c478bd9Sstevel@tonic-gate 			/* If old power cached during deadlock, use it. */
89187c478bd9Sstevel@tonic-gate 			old = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
89197c478bd9Sstevel@tonic-gate 			    cp->pmc_phc_pwr : olevel);
89207c478bd9Sstevel@tonic-gate 			mutex_enter(&pm_rsvp_lock);
89217c478bd9Sstevel@tonic-gate 			pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, nlevel,
89227c478bd9Sstevel@tonic-gate 			    old, canblock);
89237c478bd9Sstevel@tonic-gate 			pm_enqueue_notify_others(&devl, canblock);
89247c478bd9Sstevel@tonic-gate 			mutex_exit(&pm_rsvp_lock);
89258c533f11Smh 		} else {
89268c533f11Smh 			pm_ppm_devlist_t *p;
89278c533f11Smh 			pm_ppm_devlist_t *next;
89288c533f11Smh 			for (p = devl; p != NULL; p = next) {
89298c533f11Smh 				next = p->ppd_next;
89308c533f11Smh 				kmem_free(p, sizeof (pm_ppm_devlist_t));
89318c533f11Smh 			}
89328c533f11Smh 			devl = NULL;
89337c478bd9Sstevel@tonic-gate 		}
89347c478bd9Sstevel@tonic-gate 
89357c478bd9Sstevel@tonic-gate 		/*
89367c478bd9Sstevel@tonic-gate 		 * If we are coming from a scan, don't do it again,
89377c478bd9Sstevel@tonic-gate 		 * else we can have infinite loops.
89387c478bd9Sstevel@tonic-gate 		 */
89397c478bd9Sstevel@tonic-gate 		if (!scan)
89407c478bd9Sstevel@tonic-gate 			pm_rescan(dip);
89417c478bd9Sstevel@tonic-gate 	} else {
89427c478bd9Sstevel@tonic-gate 		/* if we incremented pm_comps_off_count, but failed */
89437c478bd9Sstevel@tonic-gate 		if (comps_off_incr > 0) {
89447c478bd9Sstevel@tonic-gate 			update_comps_off(-comps_off_incr, dip);
89457c478bd9Sstevel@tonic-gate 			PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d "
89467c478bd9Sstevel@tonic-gate 			    "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
89477c478bd9Sstevel@tonic-gate 			    comp, clevel, nlevel, pm_cfb_comps_off))
89487c478bd9Sstevel@tonic-gate 		}
89497c478bd9Sstevel@tonic-gate 		*errnop = EIO;
89507c478bd9Sstevel@tonic-gate 	}
89517c478bd9Sstevel@tonic-gate 
89527c478bd9Sstevel@tonic-gate post_notify:
89537c478bd9Sstevel@tonic-gate 	/*
89547c478bd9Sstevel@tonic-gate 	 * This thread may have been in deadlock with pm_power_has_changed.
89557c478bd9Sstevel@tonic-gate 	 * Before releasing power lock, clear the flag which marks this
89567c478bd9Sstevel@tonic-gate 	 * condition.
89577c478bd9Sstevel@tonic-gate 	 */
89587c478bd9Sstevel@tonic-gate 	cp->pmc_flags &= ~PM_PHC_WHILE_SET_POWER;
89597c478bd9Sstevel@tonic-gate 
89607c478bd9Sstevel@tonic-gate 	/*
89617c478bd9Sstevel@tonic-gate 	 * Update the old power level in the bus power structure with the
89627c478bd9Sstevel@tonic-gate 	 * actual power level before the transition was made to the new level.
89637c478bd9Sstevel@tonic-gate 	 * Some involved parents depend on this information to keep track of
89647c478bd9Sstevel@tonic-gate 	 * their children's power transition.
89657c478bd9Sstevel@tonic-gate 	 */
89667c478bd9Sstevel@tonic-gate 	if (*iresp != DDI_FAILURE)
89677c478bd9Sstevel@tonic-gate 		bpc->bpc_olevel = clevel;
89687c478bd9Sstevel@tonic-gate 
89697c478bd9Sstevel@tonic-gate 	if (PM_WANTS_NOTIFICATION(pdip)) {
89707c478bd9Sstevel@tonic-gate 		ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
89717c478bd9Sstevel@tonic-gate 		    BUS_POWER_POST_NOTIFICATION, bpc, resultp);
89723fe80ca4SDan Cross 		PM_UNLOCK_POWER(dip);
89737c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: post_notify %s@%s(%s#%d) for "
89747c478bd9Sstevel@tonic-gate 		    "child %s@%s(%s#%d), ret=%d\n", pmf, PM_DEVICE(pdip),
89757c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), ret))
89767c478bd9Sstevel@tonic-gate 	} else {
89777c478bd9Sstevel@tonic-gate 		nlevel = cur_power(cp); /* in case phc deadlock updated pwr */
89783fe80ca4SDan Cross 		PM_UNLOCK_POWER(dip);
89797c478bd9Sstevel@tonic-gate 		/*
89807c478bd9Sstevel@tonic-gate 		 * Now that we know what power transition has occurred
89817c478bd9Sstevel@tonic-gate 		 * (if any), release the power hold.  Leave the hold
89827c478bd9Sstevel@tonic-gate 		 * in effect in the case of OFF->ON transition.
89837c478bd9Sstevel@tonic-gate 		 */
89847c478bd9Sstevel@tonic-gate 		if (!(clevel == 0 && nlevel > 0 &&
89857c478bd9Sstevel@tonic-gate 		    (!PM_ISBC(dip) || comp == 0)))
89867c478bd9Sstevel@tonic-gate 			pm_rele_power(pdip);
89877c478bd9Sstevel@tonic-gate 		/*
89887c478bd9Sstevel@tonic-gate 		 * If the power transition was an ON->OFF transition,
89897c478bd9Sstevel@tonic-gate 		 * remove the power hold from the parent.
89907c478bd9Sstevel@tonic-gate 		 */
89917c478bd9Sstevel@tonic-gate 		if ((clevel > 0 || clevel == PM_LEVEL_UNKNOWN) &&
89927c478bd9Sstevel@tonic-gate 		    nlevel == 0 && (!PM_ISBC(dip) || comp == 0))
89937c478bd9Sstevel@tonic-gate 			pm_rele_power(pdip);
89947c478bd9Sstevel@tonic-gate 	}
89957c478bd9Sstevel@tonic-gate 	if (*iresp != DDI_SUCCESS || ret != DDI_SUCCESS)
89967c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
89977c478bd9Sstevel@tonic-gate 	else
89987c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
89997c478bd9Sstevel@tonic-gate }
90007c478bd9Sstevel@tonic-gate 
90017c478bd9Sstevel@tonic-gate /*
90027c478bd9Sstevel@tonic-gate  * If an app (SunVTS or Xsun) has taken control, then block until it
90037c478bd9Sstevel@tonic-gate  * gives it up or makes the requested power level change, unless
90047c478bd9Sstevel@tonic-gate  * we have other instructions about blocking.  Returns DDI_SUCCESS,
90057c478bd9Sstevel@tonic-gate  * DDI_FAILURE or EAGAIN (owner released device from directpm).
90067c478bd9Sstevel@tonic-gate  */
90077c478bd9Sstevel@tonic-gate static int
pm_busop_match_request(dev_info_t * dip,void * arg)90087c478bd9Sstevel@tonic-gate pm_busop_match_request(dev_info_t *dip, void *arg)
90097c478bd9Sstevel@tonic-gate {
90107c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "bp_match_request")
90117c478bd9Sstevel@tonic-gate 	pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
90127c478bd9Sstevel@tonic-gate 	pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private;
90137c478bd9Sstevel@tonic-gate 	int comp = bpc->bpc_comp;
90147c478bd9Sstevel@tonic-gate 	int nlevel = bpc->bpc_nlevel;
90157c478bd9Sstevel@tonic-gate 	pm_canblock_t canblock = pspm->pspm_canblock;
90167c478bd9Sstevel@tonic-gate 	int direction = pspm->pspm_direction;
90173fe80ca4SDan Cross 	int clevel;
90187c478bd9Sstevel@tonic-gate 
90197c478bd9Sstevel@tonic-gate 	ASSERT(PM_IAM_LOCKING_DIP(dip));
90203fe80ca4SDan Cross 	PM_LOCK_POWER(dip);
90217c478bd9Sstevel@tonic-gate 	clevel = PM_CURPOWER(dip, comp);
90227c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, nlvl=%d, clvl=%d\n",
90237c478bd9Sstevel@tonic-gate 	    pmf, PM_DEVICE(dip), comp, nlevel, clevel))
90247c478bd9Sstevel@tonic-gate 	if (direction == PM_LEVEL_UPONLY) {
90257c478bd9Sstevel@tonic-gate 		if (clevel >= nlevel) {
90263fe80ca4SDan Cross 			PM_UNLOCK_POWER(dip);
90277c478bd9Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
90287c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
90297c478bd9Sstevel@tonic-gate 		}
90307c478bd9Sstevel@tonic-gate 	} else if (clevel == nlevel) {
90313fe80ca4SDan Cross 		PM_UNLOCK_POWER(dip);
90327c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
90337c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
90347c478bd9Sstevel@tonic-gate 	}
90357c478bd9Sstevel@tonic-gate 	if (canblock == PM_CANBLOCK_FAIL) {
90363fe80ca4SDan Cross 		PM_UNLOCK_POWER(dip);
90377c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
90387c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
90397c478bd9Sstevel@tonic-gate 	}
90407c478bd9Sstevel@tonic-gate 	if (canblock == PM_CANBLOCK_BLOCK) {
90417c478bd9Sstevel@tonic-gate 		/*
90427c478bd9Sstevel@tonic-gate 		 * To avoid a deadlock, we must not hold the
90437c478bd9Sstevel@tonic-gate 		 * power lock when we pm_block.
90447c478bd9Sstevel@tonic-gate 		 */
90453fe80ca4SDan Cross 		PM_UNLOCK_POWER(dip);
90467c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: blocking\n", pmf))
90476152d498Smh 		/* pm_block releases dip lock */
90486152d498Smh 		switch (pm_block(dip, comp, nlevel, clevel)) {
90496152d498Smh 		case PMP_RELEASE:
90506152d498Smh 			return (EAGAIN);
90516152d498Smh 		case PMP_SUCCEED:
90526152d498Smh 			return (DDI_SUCCESS);
90536152d498Smh 		case PMP_FAIL:
90546152d498Smh 			return (DDI_FAILURE);
90556152d498Smh 		}
90567c478bd9Sstevel@tonic-gate 	} else {
90577c478bd9Sstevel@tonic-gate 		ASSERT(0);
90587c478bd9Sstevel@tonic-gate 	}
90597c478bd9Sstevel@tonic-gate 	_NOTE(NOTREACHED);
90607c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);	/* keep gcc happy */
90617c478bd9Sstevel@tonic-gate }
90627c478bd9Sstevel@tonic-gate 
90637c478bd9Sstevel@tonic-gate static int
pm_all_to_normal_nexus(dev_info_t * dip,pm_canblock_t canblock)90647c478bd9Sstevel@tonic-gate pm_all_to_normal_nexus(dev_info_t *dip, pm_canblock_t canblock)
90657c478bd9Sstevel@tonic-gate {
90667c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "all_to_normal_nexus")
90677c478bd9Sstevel@tonic-gate 	int		*normal;
90687c478bd9Sstevel@tonic-gate 	int		i, ncomps;
90697c478bd9Sstevel@tonic-gate 	size_t		size;
90707c478bd9Sstevel@tonic-gate 	int		changefailed = 0;
90717c478bd9Sstevel@tonic-gate 	int		ret, result = DDI_SUCCESS;
90727c478bd9Sstevel@tonic-gate 	pm_bp_nexus_pwrup_t	bpn;
90737c478bd9Sstevel@tonic-gate 	pm_sp_misc_t	pspm;
90747c478bd9Sstevel@tonic-gate 
90757c478bd9Sstevel@tonic-gate 	ASSERT(PM_GET_PM_INFO(dip));
90767c478bd9Sstevel@tonic-gate 	PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
90777c478bd9Sstevel@tonic-gate 	if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
90787c478bd9Sstevel@tonic-gate 		PMD(PMD_ALLNORM, ("%s: can't get norm pwrs\n", pmf))
90797c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
90807c478bd9Sstevel@tonic-gate 	}
90817c478bd9Sstevel@tonic-gate 	ncomps = PM_NUMCMPTS(dip);
90827c478bd9Sstevel@tonic-gate 	for (i = 0; i < ncomps; i++) {
90837c478bd9Sstevel@tonic-gate 		bpn.bpn_dip = dip;
90847c478bd9Sstevel@tonic-gate 		bpn.bpn_comp = i;
90857c478bd9Sstevel@tonic-gate 		bpn.bpn_level = normal[i];
90867c478bd9Sstevel@tonic-gate 		pspm.pspm_canblock = canblock;
90877c478bd9Sstevel@tonic-gate 		pspm.pspm_scan = 0;
90887c478bd9Sstevel@tonic-gate 		bpn.bpn_private = &pspm;
90897c478bd9Sstevel@tonic-gate 		ret = pm_busop_bus_power(dip, NULL, BUS_POWER_NEXUS_PWRUP,
90907c478bd9Sstevel@tonic-gate 		    (void *)&bpn, (void *)&result);
90917c478bd9Sstevel@tonic-gate 		if (ret != DDI_SUCCESS || result != DDI_SUCCESS) {
90927c478bd9Sstevel@tonic-gate 			PMD(PMD_FAIL | PMD_ALLNORM, ("%s: %s@%s(%s#%d)[%d] "
90937c478bd9Sstevel@tonic-gate 			    "->%d failure result %d\n", pmf, PM_DEVICE(dip),
90947c478bd9Sstevel@tonic-gate 			    i, normal[i], result))
90957c478bd9Sstevel@tonic-gate 			changefailed++;
90967c478bd9Sstevel@tonic-gate 		}
90977c478bd9Sstevel@tonic-gate 	}
90987c478bd9Sstevel@tonic-gate 	kmem_free(normal, size);
90997c478bd9Sstevel@tonic-gate 	if (changefailed) {
91007c478bd9Sstevel@tonic-gate 		PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) "
91017c478bd9Sstevel@tonic-gate 		    "full power\n", pmf, changefailed, PM_DEVICE(dip)))
91027c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
91037c478bd9Sstevel@tonic-gate 	}
91047c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
91057c478bd9Sstevel@tonic-gate }
91067c478bd9Sstevel@tonic-gate 
91077c478bd9Sstevel@tonic-gate int
pm_noinvol_update(int subcmd,int volpmd,int wasvolpmd,char * path,dev_info_t * tdip)91087c478bd9Sstevel@tonic-gate pm_noinvol_update(int subcmd, int volpmd, int wasvolpmd, char *path,
91097c478bd9Sstevel@tonic-gate     dev_info_t *tdip)
91107c478bd9Sstevel@tonic-gate {
91117c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_update")
91127c478bd9Sstevel@tonic-gate 	pm_bp_noinvol_t args;
91137c478bd9Sstevel@tonic-gate 	int ret;
91147c478bd9Sstevel@tonic-gate 	int result = DDI_SUCCESS;
91157c478bd9Sstevel@tonic-gate 
91167c478bd9Sstevel@tonic-gate 	args.bpni_path = path;
91177c478bd9Sstevel@tonic-gate 	args.bpni_dip = tdip;
91187c478bd9Sstevel@tonic-gate 	args.bpni_cmd = subcmd;
91197c478bd9Sstevel@tonic-gate 	args.bpni_wasvolpmd = wasvolpmd;
91207c478bd9Sstevel@tonic-gate 	args.bpni_volpmd = volpmd;
91217c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: update for path %s tdip %p subcmd %d "
91227c478bd9Sstevel@tonic-gate 	    "volpmd %d wasvolpmd %d\n", pmf,
91237c478bd9Sstevel@tonic-gate 	    path, (void *)tdip, subcmd, wasvolpmd, volpmd))
91247c478bd9Sstevel@tonic-gate 	ret = pm_busop_bus_power(ddi_root_node(), NULL, BUS_POWER_NOINVOL,
91257c478bd9Sstevel@tonic-gate 	    &args, &result);
91267c478bd9Sstevel@tonic-gate 	return (ret);
91277c478bd9Sstevel@tonic-gate }
91287c478bd9Sstevel@tonic-gate 
91297c478bd9Sstevel@tonic-gate void
pm_noinvol_update_node(dev_info_t * dip,pm_bp_noinvol_t * req)91307c478bd9Sstevel@tonic-gate pm_noinvol_update_node(dev_info_t *dip, pm_bp_noinvol_t *req)
91317c478bd9Sstevel@tonic-gate {
91327c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "noinvol_update_node")
91337c478bd9Sstevel@tonic-gate 
91347c478bd9Sstevel@tonic-gate 	PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
91357c478bd9Sstevel@tonic-gate 	switch (req->bpni_cmd) {
91367c478bd9Sstevel@tonic-gate 	case PM_BP_NOINVOL_ATTACH:
91377c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: PM_PB_NOINVOL_ATTACH %s@%s(%s#%d) "
91387c478bd9Sstevel@tonic-gate 		    "noinvol %d->%d\n", pmf, PM_DEVICE(dip),
91397c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm,
91407c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm - 1))
91417c478bd9Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_noinvolpm);
91427c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
91437c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm--;
91447c478bd9Sstevel@tonic-gate 		if (req->bpni_wasvolpmd) {
91457c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_ATTACH "
91467c478bd9Sstevel@tonic-gate 			    "%s@%s(%s#%d) volpmd %d->%d\n", pmf,
91477c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd,
91487c478bd9Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd - 1))
91497c478bd9Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_volpmd)
91507c478bd9Sstevel@tonic-gate 				DEVI(dip)->devi_pm_volpmd--;
91517c478bd9Sstevel@tonic-gate 		}
91527c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
91537c478bd9Sstevel@tonic-gate 		break;
91547c478bd9Sstevel@tonic-gate 
91557c478bd9Sstevel@tonic-gate 	case PM_BP_NOINVOL_DETACH:
91567c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH %s@%s(%s#%d) "
91577c478bd9Sstevel@tonic-gate 		    "noinvolpm %d->%d\n", pmf, PM_DEVICE(dip),
91587c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm,
91597c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm + 1))
91607c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
91617c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm++;
91627c478bd9Sstevel@tonic-gate 		if (req->bpni_wasvolpmd) {
91637c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH "
91647c478bd9Sstevel@tonic-gate 			    "%s@%s(%s#%d) volpmd %d->%d\n", pmf,
91657c478bd9Sstevel@tonic-gate 			    PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd,
91667c478bd9Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd + 1))
91677c478bd9Sstevel@tonic-gate 			DEVI(dip)->devi_pm_volpmd++;
91687c478bd9Sstevel@tonic-gate 		}
91697c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
91707c478bd9Sstevel@tonic-gate 		break;
91717c478bd9Sstevel@tonic-gate 
91727c478bd9Sstevel@tonic-gate 	case PM_BP_NOINVOL_REMDRV:
91737c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
91747c478bd9Sstevel@tonic-gate 		    "noinvol %d->%d\n", pmf, PM_DEVICE(dip),
91757c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm,
91767c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm - 1))
91777c478bd9Sstevel@tonic-gate 		ASSERT(DEVI(dip)->devi_pm_noinvolpm);
91787c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
91797c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm--;
91807c478bd9Sstevel@tonic-gate 		if (req->bpni_wasvolpmd) {
91817c478bd9Sstevel@tonic-gate 			PMD(PMD_NOINVOL,
91827c478bd9Sstevel@tonic-gate 			    ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
91837c478bd9Sstevel@tonic-gate 			    "volpmd %d->%d\n", pmf, PM_DEVICE(dip),
91847c478bd9Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd,
91857c478bd9Sstevel@tonic-gate 			    DEVI(dip)->devi_pm_volpmd - 1))
91867c478bd9Sstevel@tonic-gate 			/*
91877c478bd9Sstevel@tonic-gate 			 * A power up could come in between and
91887c478bd9Sstevel@tonic-gate 			 * clear the volpmd, if that's the case,
91897c478bd9Sstevel@tonic-gate 			 * volpmd would be clear.
91907c478bd9Sstevel@tonic-gate 			 */
91917c478bd9Sstevel@tonic-gate 			if (DEVI(dip)->devi_pm_volpmd)
91927c478bd9Sstevel@tonic-gate 				DEVI(dip)->devi_pm_volpmd--;
91937c478bd9Sstevel@tonic-gate 		}
91947c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
91957c478bd9Sstevel@tonic-gate 		break;
91967c478bd9Sstevel@tonic-gate 
91977c478bd9Sstevel@tonic-gate 	case PM_BP_NOINVOL_CFB:
91987c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL,
91997c478bd9Sstevel@tonic-gate 		    ("%s: PM_BP_NOIVOL_CFB %s@%s(%s#%d) noinvol %d->%d\n",
92007c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip), DEVI(dip)->devi_pm_noinvolpm,
92017c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_noinvolpm + 1))
92027c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
92037c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_noinvolpm++;
92047c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
92057c478bd9Sstevel@tonic-gate 		break;
92067c478bd9Sstevel@tonic-gate 
92077c478bd9Sstevel@tonic-gate 	case PM_BP_NOINVOL_POWER:
92087c478bd9Sstevel@tonic-gate 		PMD(PMD_NOINVOL,
92097c478bd9Sstevel@tonic-gate 		    ("%s: PM_BP_NOIVOL_PWR %s@%s(%s#%d) volpmd %d->%d\n",
92107c478bd9Sstevel@tonic-gate 		    pmf, PM_DEVICE(dip),
92117c478bd9Sstevel@tonic-gate 		    DEVI(dip)->devi_pm_volpmd, DEVI(dip)->devi_pm_volpmd -
92127c478bd9Sstevel@tonic-gate 		    req->bpni_volpmd))
92137c478bd9Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
92147c478bd9Sstevel@tonic-gate 		DEVI(dip)->devi_pm_volpmd -= req->bpni_volpmd;
92157c478bd9Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
92167c478bd9Sstevel@tonic-gate 		break;
92177c478bd9Sstevel@tonic-gate 
92187c478bd9Sstevel@tonic-gate 	default:
92197c478bd9Sstevel@tonic-gate 		break;
92207c478bd9Sstevel@tonic-gate 	}
92217c478bd9Sstevel@tonic-gate 
92227c478bd9Sstevel@tonic-gate }
92237c478bd9Sstevel@tonic-gate 
92247c478bd9Sstevel@tonic-gate #ifdef DEBUG
92257c478bd9Sstevel@tonic-gate static int
pm_desc_pwrchk_walk(dev_info_t * dip,void * arg)92267c478bd9Sstevel@tonic-gate pm_desc_pwrchk_walk(dev_info_t *dip, void *arg)
92277c478bd9Sstevel@tonic-gate {
92287c478bd9Sstevel@tonic-gate 	PMD_FUNC(pmf, "desc_pwrchk")
92297c478bd9Sstevel@tonic-gate 	pm_desc_pwrchk_t *pdpchk = (pm_desc_pwrchk_t *)arg;
92307c478bd9Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
92312df1fe9cSrandyf 	int i;
92322df1fe9cSrandyf 	/* LINTED */
92332df1fe9cSrandyf 	int curpwr, ce_level;
92347c478bd9Sstevel@tonic-gate 
92357c478bd9Sstevel@tonic-gate 	if (!info)
92367c478bd9Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
92377c478bd9Sstevel@tonic-gate 
92387c478bd9Sstevel@tonic-gate 	PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
92397c478bd9Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
92402df1fe9cSrandyf 		/* LINTED */
92412df1fe9cSrandyf 		if ((curpwr = PM_CURPOWER(dip, i)) == 0)
92427c478bd9Sstevel@tonic-gate 			continue;
92432df1fe9cSrandyf 		/* E_FUNC_SET_NOT_USED */
92447c478bd9Sstevel@tonic-gate 		ce_level = (pdpchk->pdpc_par_involved == 0) ? CE_PANIC :
92457c478bd9Sstevel@tonic-gate 		    CE_WARN;
92467c478bd9Sstevel@tonic-gate 		PMD(PMD_SET, ("%s: %s@%s(%s#%d) is powered off while desc "
92477c478bd9Sstevel@tonic-gate 		    "%s@%s(%s#%d)[%d] is at %d\n", pmf,
92487c478bd9Sstevel@tonic-gate 		    PM_DEVICE(pdpchk->pdpc_dip), PM_DEVICE(dip), i, curpwr))
92497c478bd9Sstevel@tonic-gate 		cmn_err(ce_level, "!device %s@%s(%s#%d) is powered on, "
92507c478bd9Sstevel@tonic-gate 		    "while its ancestor, %s@%s(%s#%d), is powering off!",
92517c478bd9Sstevel@tonic-gate 		    PM_DEVICE(dip), PM_DEVICE(pdpchk->pdpc_dip));
92527c478bd9Sstevel@tonic-gate 	}
92537c478bd9Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
92547c478bd9Sstevel@tonic-gate }
92557c478bd9Sstevel@tonic-gate #endif
92567c478bd9Sstevel@tonic-gate 
92577c478bd9Sstevel@tonic-gate /*
92587c478bd9Sstevel@tonic-gate  * Record the fact that one thread is borrowing the lock on a device node.
92597c478bd9Sstevel@tonic-gate  * Use is restricted to the case where the lending thread will block until
92607c478bd9Sstevel@tonic-gate  * the borrowing thread (always curthread) completes.
92617c478bd9Sstevel@tonic-gate  */
92627c478bd9Sstevel@tonic-gate void
pm_borrow_lock(kthread_t * lender)92637c478bd9Sstevel@tonic-gate pm_borrow_lock(kthread_t *lender)
92647c478bd9Sstevel@tonic-gate {
92657c478bd9Sstevel@tonic-gate 	lock_loan_t *prev = &lock_loan_head;
92667c478bd9Sstevel@tonic-gate 	lock_loan_t *cur = (lock_loan_t *)kmem_zalloc(sizeof (*cur), KM_SLEEP);
92677c478bd9Sstevel@tonic-gate 
92687c478bd9Sstevel@tonic-gate 	cur->pmlk_borrower = curthread;
92697c478bd9Sstevel@tonic-gate 	cur->pmlk_lender = lender;
92707c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
92717c478bd9Sstevel@tonic-gate 	cur->pmlk_next = prev->pmlk_next;
92727c478bd9Sstevel@tonic-gate 	prev->pmlk_next = cur;
92737c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
92747c478bd9Sstevel@tonic-gate }
92757c478bd9Sstevel@tonic-gate 
92767c478bd9Sstevel@tonic-gate /*
92777c478bd9Sstevel@tonic-gate  * Return the borrowed lock.  A thread can borrow only one.
92787c478bd9Sstevel@tonic-gate  */
92797c478bd9Sstevel@tonic-gate void
pm_return_lock(void)92807c478bd9Sstevel@tonic-gate pm_return_lock(void)
92817c478bd9Sstevel@tonic-gate {
92827c478bd9Sstevel@tonic-gate 	lock_loan_t *cur;
92837c478bd9Sstevel@tonic-gate 	lock_loan_t *prev = &lock_loan_head;
92847c478bd9Sstevel@tonic-gate 
92857c478bd9Sstevel@tonic-gate 	mutex_enter(&pm_loan_lock);
92867c478bd9Sstevel@tonic-gate 	ASSERT(prev->pmlk_next != NULL);
92877c478bd9Sstevel@tonic-gate 	for (cur = prev->pmlk_next; cur; prev = cur, cur = cur->pmlk_next)
92887c478bd9Sstevel@tonic-gate 		if (cur->pmlk_borrower == curthread)
92897c478bd9Sstevel@tonic-gate 			break;
92907c478bd9Sstevel@tonic-gate 
92917c478bd9Sstevel@tonic-gate 	ASSERT(cur != NULL);
92927c478bd9Sstevel@tonic-gate 	prev->pmlk_next = cur->pmlk_next;
92937c478bd9Sstevel@tonic-gate 	mutex_exit(&pm_loan_lock);
92947c478bd9Sstevel@tonic-gate 	kmem_free(cur, sizeof (*cur));
92957c478bd9Sstevel@tonic-gate }
92962df1fe9cSrandyf 
92972df1fe9cSrandyf #if defined(__x86)
92982df1fe9cSrandyf 
92992df1fe9cSrandyf #define	CPR_RXR	0x1
93002df1fe9cSrandyf #define	CPR_TXR	0x20
93012df1fe9cSrandyf #define	CPR_DATAREG	0x3f8
93022df1fe9cSrandyf #define	CPR_LSTAT	0x3fd
93032df1fe9cSrandyf #define	CPR_INTRCTL	0x3f9
93042df1fe9cSrandyf 
93052df1fe9cSrandyf char
pm_getchar(void)93062df1fe9cSrandyf pm_getchar(void)
93072df1fe9cSrandyf {
93082df1fe9cSrandyf 	while ((inb(CPR_LSTAT) & CPR_RXR) != CPR_RXR)
93092df1fe9cSrandyf 		drv_usecwait(10);
93102df1fe9cSrandyf 
93112df1fe9cSrandyf 	return (inb(CPR_DATAREG));
93122df1fe9cSrandyf 
93132df1fe9cSrandyf }
93142df1fe9cSrandyf 
93152df1fe9cSrandyf void
pm_putchar(char c)93162df1fe9cSrandyf pm_putchar(char c)
93172df1fe9cSrandyf {
93182df1fe9cSrandyf 	while ((inb(CPR_LSTAT) & CPR_TXR) == 0)
93192df1fe9cSrandyf 		drv_usecwait(10);
93202df1fe9cSrandyf 
93212df1fe9cSrandyf 	outb(CPR_DATAREG, c);
93222df1fe9cSrandyf }
93232df1fe9cSrandyf 
93242df1fe9cSrandyf void
pm_printf(char * s)93252df1fe9cSrandyf pm_printf(char *s)
93262df1fe9cSrandyf {
93272df1fe9cSrandyf 	while (*s) {
93282df1fe9cSrandyf 		pm_putchar(*s++);
93292df1fe9cSrandyf 	}
93302df1fe9cSrandyf }
93312df1fe9cSrandyf 
93322df1fe9cSrandyf #endif
93332df1fe9cSrandyf 
93342df1fe9cSrandyf int
pm_ppm_searchlist(pm_searchargs_t * sp)93352df1fe9cSrandyf pm_ppm_searchlist(pm_searchargs_t *sp)
93362df1fe9cSrandyf {
93372df1fe9cSrandyf 	power_req_t power_req;
93382df1fe9cSrandyf 	int result = 0;
93392df1fe9cSrandyf 	/* LINTED */
93402df1fe9cSrandyf 	int ret;
93412df1fe9cSrandyf 
93422df1fe9cSrandyf 	power_req.request_type = PMR_PPM_SEARCH_LIST;
93432df1fe9cSrandyf 	power_req.req.ppm_search_list_req.searchlist = sp;
93442df1fe9cSrandyf 	ASSERT(DEVI(ddi_root_node())->devi_pm_ppm);
93452df1fe9cSrandyf 	ret = pm_ctlops((dev_info_t *)DEVI(ddi_root_node())->devi_pm_ppm,
93462df1fe9cSrandyf 	    ddi_root_node(), DDI_CTLOPS_POWER, &power_req, &result);
93472df1fe9cSrandyf 	PMD(PMD_SX, ("pm_ppm_searchlist returns %d, result %d\n",
93482df1fe9cSrandyf 	    ret, result))
93492df1fe9cSrandyf 	return (result);
93502df1fe9cSrandyf }
9351