10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51961Scth * Common Development and Distribution License (the "License").
61961Scth * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
228906SEric.Saxe@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * sunpm.c builds sunpm.o "power management framework"
280Sstevel@tonic-gate * kernel-resident power management code. Implements power management
290Sstevel@tonic-gate * policy
300Sstevel@tonic-gate * Assumes: all backwards compat. device components wake up on &
310Sstevel@tonic-gate * the pm_info pointer in dev_info is initially NULL
320Sstevel@tonic-gate *
330Sstevel@tonic-gate * PM - (device) Power Management
340Sstevel@tonic-gate *
350Sstevel@tonic-gate * Each device may have 0 or more components. If a device has no components,
360Sstevel@tonic-gate * then it can't be power managed. Each component has 2 or more
370Sstevel@tonic-gate * power states.
380Sstevel@tonic-gate *
390Sstevel@tonic-gate * "Backwards Compatible" (bc) devices:
400Sstevel@tonic-gate * There are two different types of devices from the point of view of this
410Sstevel@tonic-gate * code. The original type, left over from the original PM implementation on
420Sstevel@tonic-gate * the voyager platform are known in this code as "backwards compatible"
430Sstevel@tonic-gate * devices (PM_ISBC(dip) returns true).
440Sstevel@tonic-gate * They are recognized by the pm code by the lack of a pm-components property
450Sstevel@tonic-gate * and a call made by the driver to pm_create_components(9F).
460Sstevel@tonic-gate * For these devices, component 0 is special, and represents the power state
470Sstevel@tonic-gate * of the device. If component 0 is to be set to power level 0 (off), then
480Sstevel@tonic-gate * the framework must first call into the driver's detach(9E) routine with
490Sstevel@tonic-gate * DDI_PM_SUSPEND, to get the driver to save the hardware state of the device.
500Sstevel@tonic-gate * After setting component 0 from 0 to a non-zero power level, a call must be
510Sstevel@tonic-gate * made into the driver's attach(9E) routine with DDI_PM_RESUME.
520Sstevel@tonic-gate *
530Sstevel@tonic-gate * Currently, the only way to get a bc device power managed is via a set of
540Sstevel@tonic-gate * ioctls (PM_DIRECT_PM, PM_SET_CURRENT_POWER) issued to /dev/pm.
550Sstevel@tonic-gate *
560Sstevel@tonic-gate * For non-bc devices, the driver describes the components by exporting a
570Sstevel@tonic-gate * pm-components(9P) property that tells how many components there are,
580Sstevel@tonic-gate * tells what each component's power state values are, and provides human
590Sstevel@tonic-gate * readable strings (currently unused) for each component name and power state.
600Sstevel@tonic-gate * Devices which export pm-components(9P) are automatically power managed
610Sstevel@tonic-gate * whenever autopm is enabled (via PM_START_PM ioctl issued by pmconfig(1M)
623028Smh27603 * after parsing power.conf(4)). The exception to this rule is that power
633028Smh27603 * manageable CPU devices may be automatically managed independently of autopm
643028Smh27603 * by either enabling or disabling (via PM_START_CPUPM and PM_STOP_CPUPM
653028Smh27603 * ioctls) cpupm. If the CPU devices are not managed independently, then they
663028Smh27603 * are managed by autopm. In either case, for automatically power managed
673028Smh27603 * devices, all components are considered independent of each other, and it is
683028Smh27603 * up to the driver to decide when a transition requires saving or restoring
693028Smh27603 * hardware state.
700Sstevel@tonic-gate *
710Sstevel@tonic-gate * Each device component also has a threshold time associated with each power
720Sstevel@tonic-gate * transition (see power.conf(4)), and a busy/idle state maintained by the
730Sstevel@tonic-gate * driver calling pm_idle_component(9F) and pm_busy_component(9F).
740Sstevel@tonic-gate * Components are created idle.
750Sstevel@tonic-gate *
760Sstevel@tonic-gate * The PM framework provides several functions:
770Sstevel@tonic-gate * -implement PM policy as described in power.conf(4)
780Sstevel@tonic-gate * Policy is set by pmconfig(1M) issuing pm ioctls based on power.conf(4).
790Sstevel@tonic-gate * Policies consist of:
800Sstevel@tonic-gate * -set threshold values (defaults if none provided by pmconfig)
810Sstevel@tonic-gate * -set dependencies among devices
820Sstevel@tonic-gate * -enable/disable autopm
833028Smh27603 * -enable/disable cpupm
843028Smh27603 * -turn down idle components based on thresholds (if autopm or cpupm is
853028Smh27603 * enabled) (aka scanning)
860Sstevel@tonic-gate * -maintain power states based on dependencies among devices
870Sstevel@tonic-gate * -upon request, or when the frame buffer powers off, attempt to turn off
880Sstevel@tonic-gate * all components that are idle or become idle over the next (10 sec)
890Sstevel@tonic-gate * period in an attempt to get down to an EnergyStar compliant state
900Sstevel@tonic-gate * -prevent powering off of a device which exported the
910Sstevel@tonic-gate * pm-no-involuntary-power-cycles property without active involvement of
920Sstevel@tonic-gate * the device's driver (so no removing power when the device driver is
930Sstevel@tonic-gate * not attached)
940Sstevel@tonic-gate * -provide a mechanism for a device driver to request that a device's component
950Sstevel@tonic-gate * be brought back to the power level necessary for the use of the device
960Sstevel@tonic-gate * -allow a process to directly control the power levels of device components
970Sstevel@tonic-gate * (via ioctls issued to /dev/pm--see usr/src/uts/common/io/pm.c)
980Sstevel@tonic-gate * -ensure that the console frame buffer is powered up before being referenced
990Sstevel@tonic-gate * via prom_printf() or other prom calls that might generate console output
1000Sstevel@tonic-gate * -maintain implicit dependencies (e.g. parent must be powered up if child is)
1010Sstevel@tonic-gate * -provide "backwards compatible" behavior for devices without pm-components
1020Sstevel@tonic-gate * property
1030Sstevel@tonic-gate *
1040Sstevel@tonic-gate * Scanning:
1053028Smh27603 * Whenever autopm or cpupm is enabled, the framework attempts to bring each
1063028Smh27603 * component of each managed device to its lowest power based on the threshold
1073028Smh27603 * of idleness associated with each transition and the busy/idle state of the
1083028Smh27603 * component.
1090Sstevel@tonic-gate *
1100Sstevel@tonic-gate * The actual work of this is done by pm_scan_dev(), which cycles through each
1110Sstevel@tonic-gate * component of a device, checking its idleness against its current threshold,
1120Sstevel@tonic-gate * and calling pm_set_power() as appropriate to change the power level.
1130Sstevel@tonic-gate * This function also indicates when it would next be profitable to scan the
1140Sstevel@tonic-gate * device again, and a new scan is scheduled after that time.
1150Sstevel@tonic-gate *
1160Sstevel@tonic-gate * Dependencies:
1170Sstevel@tonic-gate * It is possible to establish a dependency between the power states of two
1180Sstevel@tonic-gate * otherwise unrelated devices. This is currently done to ensure that the
1190Sstevel@tonic-gate * cdrom is always up whenever the console framebuffer is up, so that the user
1200Sstevel@tonic-gate * can insert a cdrom and see a popup as a result.
1210Sstevel@tonic-gate *
1220Sstevel@tonic-gate * The dependency terminology used in power.conf(4) is not easy to understand,
1230Sstevel@tonic-gate * so we've adopted a different terminology in the implementation. We write
1240Sstevel@tonic-gate * of a "keeps up" and a "kept up" device. A relationship can be established
1250Sstevel@tonic-gate * where one device keeps up another. That means that if the keepsup device
1260Sstevel@tonic-gate * has any component that is at a non-zero power level, all components of the
1270Sstevel@tonic-gate * "kept up" device must be brought to full power. This relationship is
1280Sstevel@tonic-gate * asynchronous. When the keeping device is powered up, a request is queued
1290Sstevel@tonic-gate * to a worker thread to bring up the kept device. The caller does not wait.
1300Sstevel@tonic-gate * Scan will not turn down a kept up device.
1310Sstevel@tonic-gate *
1320Sstevel@tonic-gate * Direct PM:
1330Sstevel@tonic-gate * A device may be directly power managed by a process. If a device is
1340Sstevel@tonic-gate * directly pm'd, then it will not be scanned, and dependencies will not be
1350Sstevel@tonic-gate * enforced. * If a directly pm'd device's driver requests a power change (via
1360Sstevel@tonic-gate * pm_raise_power(9F)), then the request is blocked and notification is sent
1370Sstevel@tonic-gate * to the controlling process, which must issue the requested power change for
1380Sstevel@tonic-gate * the driver to proceed.
1390Sstevel@tonic-gate *
1400Sstevel@tonic-gate */
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate #include <sys/types.h>
1430Sstevel@tonic-gate #include <sys/errno.h>
1440Sstevel@tonic-gate #include <sys/callb.h> /* callback registration during CPR */
1450Sstevel@tonic-gate #include <sys/conf.h> /* driver flags and functions */
1460Sstevel@tonic-gate #include <sys/open.h> /* OTYP_CHR definition */
1470Sstevel@tonic-gate #include <sys/stat.h> /* S_IFCHR definition */
1480Sstevel@tonic-gate #include <sys/pathname.h> /* name -> dev_info xlation */
1490Sstevel@tonic-gate #include <sys/ddi_impldefs.h> /* dev_info node fields */
1500Sstevel@tonic-gate #include <sys/kmem.h> /* memory alloc stuff */
1510Sstevel@tonic-gate #include <sys/debug.h>
1520Sstevel@tonic-gate #include <sys/archsystm.h>
1530Sstevel@tonic-gate #include <sys/pm.h>
1540Sstevel@tonic-gate #include <sys/ddi.h>
1550Sstevel@tonic-gate #include <sys/sunddi.h>
1560Sstevel@tonic-gate #include <sys/sunndi.h>
1570Sstevel@tonic-gate #include <sys/sunpm.h>
1580Sstevel@tonic-gate #include <sys/epm.h>
1590Sstevel@tonic-gate #include <sys/vfs.h>
1600Sstevel@tonic-gate #include <sys/mode.h>
1610Sstevel@tonic-gate #include <sys/mkdev.h>
1620Sstevel@tonic-gate #include <sys/promif.h>
1630Sstevel@tonic-gate #include <sys/consdev.h>
1640Sstevel@tonic-gate #include <sys/esunddi.h>
1650Sstevel@tonic-gate #include <sys/modctl.h>
1660Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
1670Sstevel@tonic-gate #include <sys/note.h>
1680Sstevel@tonic-gate #include <sys/taskq.h>
1690Sstevel@tonic-gate #include <sys/bootconf.h>
1700Sstevel@tonic-gate #include <sys/reboot.h>
1710Sstevel@tonic-gate #include <sys/spl.h>
1720Sstevel@tonic-gate #include <sys/disp.h>
1730Sstevel@tonic-gate #include <sys/sobject.h>
1740Sstevel@tonic-gate #include <sys/sunmdi.h>
1755295Srandyf #include <sys/systm.h>
1765295Srandyf #include <sys/cpuvar.h>
1775295Srandyf #include <sys/cyclic.h>
1785295Srandyf #include <sys/uadmin.h>
1795295Srandyf #include <sys/srn.h>
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate * PM LOCKING
1840Sstevel@tonic-gate * The list of locks:
1850Sstevel@tonic-gate * Global pm mutex locks.
1860Sstevel@tonic-gate *
1870Sstevel@tonic-gate * pm_scan_lock:
1880Sstevel@tonic-gate * It protects the timeout id of the scan thread, and the value
1893028Smh27603 * of autopm_enabled and cpupm. This lock is not held
1903028Smh27603 * concurrently with any other PM locks.
1910Sstevel@tonic-gate *
1920Sstevel@tonic-gate * pm_clone_lock: Protects the clone list and count of poll events
1930Sstevel@tonic-gate * pending for the pm driver.
1940Sstevel@tonic-gate * Lock ordering:
1950Sstevel@tonic-gate * pm_clone_lock -> pm_pscc_interest_rwlock,
1960Sstevel@tonic-gate * pm_clone_lock -> pm_pscc_direct_rwlock.
1970Sstevel@tonic-gate *
1980Sstevel@tonic-gate * pm_rsvp_lock:
1990Sstevel@tonic-gate * Used to synchronize the data structures used for processes
2000Sstevel@tonic-gate * to rendezvous with state change information when doing
2010Sstevel@tonic-gate * direct PM.
2020Sstevel@tonic-gate * Lock ordering:
2030Sstevel@tonic-gate * pm_rsvp_lock -> pm_pscc_interest_rwlock,
2040Sstevel@tonic-gate * pm_rsvp_lock -> pm_pscc_direct_rwlock,
2050Sstevel@tonic-gate * pm_rsvp_lock -> pm_clone_lock.
2060Sstevel@tonic-gate *
2070Sstevel@tonic-gate * ppm_lock: protects the list of registered ppm drivers
2080Sstevel@tonic-gate * Lock ordering:
2090Sstevel@tonic-gate * ppm_lock -> ppm driver unit_lock
2100Sstevel@tonic-gate *
2110Sstevel@tonic-gate * pm_compcnt_lock:
2120Sstevel@tonic-gate * Protects count of components that are not at their lowest
2130Sstevel@tonic-gate * power level.
2140Sstevel@tonic-gate * Lock ordering:
2150Sstevel@tonic-gate * pm_compcnt_lock -> ppm_lock.
2160Sstevel@tonic-gate *
2170Sstevel@tonic-gate * pm_dep_thread_lock:
2180Sstevel@tonic-gate * Protects work list for pm_dep_thread. Not taken concurrently
2190Sstevel@tonic-gate * with any other pm lock.
2200Sstevel@tonic-gate *
2210Sstevel@tonic-gate * pm_remdrv_lock:
2220Sstevel@tonic-gate * Serializes the operation of removing noinvol data structure
2230Sstevel@tonic-gate * entries for a branch of the tree when a driver has been
2240Sstevel@tonic-gate * removed from the system (modctl_rem_major).
2250Sstevel@tonic-gate * Lock ordering:
2260Sstevel@tonic-gate * pm_remdrv_lock -> pm_noinvol_rwlock.
2270Sstevel@tonic-gate *
2280Sstevel@tonic-gate * pm_cfb_lock: (High level spin lock)
2290Sstevel@tonic-gate * Protects the count of how many components of the console
2300Sstevel@tonic-gate * frame buffer are off (so we know if we have to bring up the
2310Sstevel@tonic-gate * console as a result of a prom_printf, etc.
2320Sstevel@tonic-gate * No other locks are taken while holding this lock.
2330Sstevel@tonic-gate *
2340Sstevel@tonic-gate * pm_loan_lock:
2350Sstevel@tonic-gate * Protects the lock_loan list. List is used to record that one
2360Sstevel@tonic-gate * thread has acquired a power lock but has launched another thread
2370Sstevel@tonic-gate * to complete its processing. An entry in the list indicates that
2380Sstevel@tonic-gate * the worker thread can borrow the lock held by the other thread,
2390Sstevel@tonic-gate * which must block on the completion of the worker. Use is
2400Sstevel@tonic-gate * specific to module loading.
2410Sstevel@tonic-gate * No other locks are taken while holding this lock.
2420Sstevel@tonic-gate *
2430Sstevel@tonic-gate * Global PM rwlocks
2440Sstevel@tonic-gate *
2450Sstevel@tonic-gate * pm_thresh_rwlock:
2460Sstevel@tonic-gate * Protects the list of thresholds recorded for future use (when
2470Sstevel@tonic-gate * devices attach).
2480Sstevel@tonic-gate * Lock ordering:
2490Sstevel@tonic-gate * pm_thresh_rwlock -> devi_pm_lock
2500Sstevel@tonic-gate *
2510Sstevel@tonic-gate * pm_noinvol_rwlock:
2520Sstevel@tonic-gate * Protects list of detached nodes that had noinvol registered.
2530Sstevel@tonic-gate * No other PM locks are taken while holding pm_noinvol_rwlock.
2540Sstevel@tonic-gate *
2550Sstevel@tonic-gate * pm_pscc_direct_rwlock:
2560Sstevel@tonic-gate * Protects the list that maps devices being directly power
2570Sstevel@tonic-gate * managed to the processes that manage them.
2580Sstevel@tonic-gate * Lock ordering:
2590Sstevel@tonic-gate * pm_pscc_direct_rwlock -> psce_lock
2600Sstevel@tonic-gate *
2610Sstevel@tonic-gate * pm_pscc_interest_rwlock;
2620Sstevel@tonic-gate * Protects the list that maps state change events to processes
2630Sstevel@tonic-gate * that want to know about them.
2640Sstevel@tonic-gate * Lock ordering:
2650Sstevel@tonic-gate * pm_pscc_interest_rwlock -> psce_lock
2660Sstevel@tonic-gate *
2670Sstevel@tonic-gate * per-dip locks:
2680Sstevel@tonic-gate *
2690Sstevel@tonic-gate * Each node has these per-dip locks, which are only used if the device is
2700Sstevel@tonic-gate * a candidate for power management (e.g. has pm components)
2710Sstevel@tonic-gate *
2720Sstevel@tonic-gate * devi_pm_lock:
2730Sstevel@tonic-gate * Protects all power management state of the node except for
2740Sstevel@tonic-gate * power level, which is protected by ndi_devi_enter().
2750Sstevel@tonic-gate * Encapsulated in macros PM_LOCK_DIP()/PM_UNLOCK_DIP().
2760Sstevel@tonic-gate * Lock ordering:
2770Sstevel@tonic-gate * devi_pm_lock -> pm_rsvp_lock,
2780Sstevel@tonic-gate * devi_pm_lock -> pm_dep_thread_lock,
2790Sstevel@tonic-gate * devi_pm_lock -> pm_noinvol_rwlock,
2800Sstevel@tonic-gate * devi_pm_lock -> power lock
2810Sstevel@tonic-gate *
2820Sstevel@tonic-gate * power lock (ndi_devi_enter()):
2830Sstevel@tonic-gate * Since changing power level is possibly a slow operation (30
2840Sstevel@tonic-gate * seconds to spin up a disk drive), this is locked separately.
2850Sstevel@tonic-gate * Since a call into the driver to change the power level of one
2860Sstevel@tonic-gate * component may result in a call back into the framework to change
2870Sstevel@tonic-gate * the power level of another, this lock allows re-entrancy by
2880Sstevel@tonic-gate * the same thread (ndi_devi_enter is used for this because
2890Sstevel@tonic-gate * the USB framework uses ndi_devi_enter in its power entry point,
2900Sstevel@tonic-gate * and use of any other lock would produce a deadlock.
2910Sstevel@tonic-gate *
2920Sstevel@tonic-gate * devi_pm_busy_lock:
2930Sstevel@tonic-gate * This lock protects the integrity of the busy count. It is
2940Sstevel@tonic-gate * only taken by pm_busy_component() and pm_idle_component and
2950Sstevel@tonic-gate * some code that adjust the busy time after the timer gets set
2960Sstevel@tonic-gate * up or after a CPR operation. It is per-dip to keep from
2970Sstevel@tonic-gate * single-threading all the disk drivers on a system.
2980Sstevel@tonic-gate * It could be per component instead, but most devices have
2990Sstevel@tonic-gate * only one component.
3000Sstevel@tonic-gate * No other PM locks are taken while holding this lock.
3010Sstevel@tonic-gate *
3020Sstevel@tonic-gate */
3030Sstevel@tonic-gate
3040Sstevel@tonic-gate static int stdout_is_framebuffer;
3050Sstevel@tonic-gate static kmutex_t e_pm_power_lock;
3060Sstevel@tonic-gate static kmutex_t pm_loan_lock;
3070Sstevel@tonic-gate kmutex_t pm_scan_lock;
3080Sstevel@tonic-gate callb_id_t pm_cpr_cb_id;
3090Sstevel@tonic-gate callb_id_t pm_panic_cb_id;
3100Sstevel@tonic-gate callb_id_t pm_halt_cb_id;
3110Sstevel@tonic-gate int pm_comps_notlowest; /* no. of comps not at lowest power */
3120Sstevel@tonic-gate int pm_powering_down; /* cpr is source of DDI_SUSPEND calls */
3130Sstevel@tonic-gate
3140Sstevel@tonic-gate clock_t pm_id_ticks = 5; /* ticks to wait before scan during idle-down */
3157157Smh27603 clock_t pm_default_min_scan = PM_DEFAULT_MIN_SCAN;
3167157Smh27603 clock_t pm_cpu_min_scan = PM_CPU_MIN_SCAN;
3177157Smh27603
3187157Smh27603 #define PM_MIN_SCAN(dip) (PM_ISCPU(dip) ? pm_cpu_min_scan : \
3197157Smh27603 pm_default_min_scan)
3200Sstevel@tonic-gate
3210Sstevel@tonic-gate static int pm_busop_set_power(dev_info_t *,
3220Sstevel@tonic-gate void *, pm_bus_power_op_t, void *, void *);
3230Sstevel@tonic-gate static int pm_busop_match_request(dev_info_t *, void *);
3240Sstevel@tonic-gate static int pm_all_to_normal_nexus(dev_info_t *, pm_canblock_t);
3254667Smh27603 static void e_pm_set_max_power(dev_info_t *, int, int);
3264667Smh27603 static int e_pm_get_max_power(dev_info_t *, int);
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate /*
3290Sstevel@tonic-gate * Dependency Processing is done thru a seperate thread.
3300Sstevel@tonic-gate */
3310Sstevel@tonic-gate kmutex_t pm_dep_thread_lock;
3320Sstevel@tonic-gate kcondvar_t pm_dep_thread_cv;
3330Sstevel@tonic-gate pm_dep_wk_t *pm_dep_thread_workq = NULL;
3340Sstevel@tonic-gate pm_dep_wk_t *pm_dep_thread_tail = NULL;
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate /*
3370Sstevel@tonic-gate * Autopm must be turned on by a PM_START_PM ioctl, so we don't end up
3380Sstevel@tonic-gate * power managing things in single user mode that have been suppressed via
3390Sstevel@tonic-gate * power.conf entries. Protected by pm_scan_lock.
3400Sstevel@tonic-gate */
3410Sstevel@tonic-gate int autopm_enabled;
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate /*
3443028Smh27603 * cpupm is turned on and off, by the PM_START_CPUPM and PM_STOP_CPUPM ioctls,
3453028Smh27603 * to define the power management behavior of CPU devices separate from
3463028Smh27603 * autopm. Protected by pm_scan_lock.
3473028Smh27603 */
3483028Smh27603 pm_cpupm_t cpupm = PM_CPUPM_NOTSET;
3493028Smh27603
3503028Smh27603 /*
3518906SEric.Saxe@Sun.COM * Defines the default mode of operation for CPU power management,
3528906SEric.Saxe@Sun.COM * either the polling implementation, or the event based dispatcher driven
3538906SEric.Saxe@Sun.COM * implementation.
3548906SEric.Saxe@Sun.COM */
3558906SEric.Saxe@Sun.COM pm_cpupm_t cpupm_default_mode = PM_CPUPM_EVENT;
3568906SEric.Saxe@Sun.COM
3578906SEric.Saxe@Sun.COM /*
3585295Srandyf * AutoS3 depends on autopm being enabled, and must be enabled by
3595295Srandyf * PM_START_AUTOS3 command.
3605295Srandyf */
3615295Srandyf int autoS3_enabled;
3625295Srandyf
3635295Srandyf #if !defined(__sparc)
3645295Srandyf /*
3655295Srandyf * on sparc these live in fillsysinfo.c
3665295Srandyf *
3675295Srandyf * If this variable is non-zero, cpr should return "not supported" when
3685295Srandyf * it is queried even though it would normally be supported on this platform.
3695295Srandyf */
3705295Srandyf int cpr_supported_override;
3715295Srandyf
3725295Srandyf /*
3735295Srandyf * Some platforms may need to support CPR even in the absence of
3745295Srandyf * having the correct platform id information. If this
3755295Srandyf * variable is non-zero, cpr should proceed even in the absence
3765295Srandyf * of otherwise being qualified.
3775295Srandyf */
3785295Srandyf int cpr_platform_enable = 0;
3795295Srandyf
3805295Srandyf #endif
3815295Srandyf
3825295Srandyf /*
3835295Srandyf * pm_S3_enabled indicates that we believe the platform can support S3,
3845295Srandyf * which we get from pmconfig(1M)
3855295Srandyf */
3865295Srandyf int pm_S3_enabled;
3875295Srandyf
3885295Srandyf /*
3890Sstevel@tonic-gate * This flag is true while processes are stopped for a checkpoint/resume.
3900Sstevel@tonic-gate * Controlling processes of direct pm'd devices are not available to
3910Sstevel@tonic-gate * participate in power level changes, so we bypass them when this is set.
3920Sstevel@tonic-gate */
3930Sstevel@tonic-gate static int pm_processes_stopped;
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate #ifdef DEBUG
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate /*
3980Sstevel@tonic-gate * see common/sys/epm.h for PMD_* values
3990Sstevel@tonic-gate */
4005295Srandyf
4010Sstevel@tonic-gate uint_t pm_debug = 0;
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate /*
4040Sstevel@tonic-gate * If pm_divertdebug is set, then no prom_printf calls will be made by
4050Sstevel@tonic-gate * PMD(), which will prevent debug output from bringing up the console
4060Sstevel@tonic-gate * frame buffer. Clearing this variable before setting pm_debug will result
4070Sstevel@tonic-gate * in PMD output going to the console.
4080Sstevel@tonic-gate *
4090Sstevel@tonic-gate * pm_divertdebug is incremented in pm_set_power() if dip == cfb_dip to avoid
4100Sstevel@tonic-gate * deadlocks and decremented at the end of pm_set_power()
4110Sstevel@tonic-gate */
4120Sstevel@tonic-gate uint_t pm_divertdebug = 1;
4135295Srandyf volatile uint_t pm_debug_to_console = 0;
4140Sstevel@tonic-gate kmutex_t pm_debug_lock; /* protects pm_divertdebug */
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate void prdeps(char *);
4170Sstevel@tonic-gate #endif
4180Sstevel@tonic-gate
4190Sstevel@tonic-gate /* Globals */
4200Sstevel@tonic-gate
4210Sstevel@tonic-gate /*
4220Sstevel@tonic-gate * List of recorded thresholds and dependencies
4230Sstevel@tonic-gate */
4240Sstevel@tonic-gate pm_thresh_rec_t *pm_thresh_head;
4250Sstevel@tonic-gate krwlock_t pm_thresh_rwlock;
4260Sstevel@tonic-gate
4270Sstevel@tonic-gate pm_pdr_t *pm_dep_head;
4280Sstevel@tonic-gate static int pm_unresolved_deps = 0;
4290Sstevel@tonic-gate static int pm_prop_deps = 0;
4300Sstevel@tonic-gate
4310Sstevel@tonic-gate /*
4320Sstevel@tonic-gate * List of devices that exported no-involuntary-power-cycles property
4330Sstevel@tonic-gate */
4340Sstevel@tonic-gate pm_noinvol_t *pm_noinvol_head;
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate /*
4370Sstevel@tonic-gate * Locks used in noinvol processing
4380Sstevel@tonic-gate */
4390Sstevel@tonic-gate krwlock_t pm_noinvol_rwlock;
4400Sstevel@tonic-gate kmutex_t pm_remdrv_lock;
4410Sstevel@tonic-gate
4420Sstevel@tonic-gate int pm_default_idle_threshold = PM_DEFAULT_SYS_IDLENESS;
4430Sstevel@tonic-gate int pm_system_idle_threshold;
4443028Smh27603 int pm_cpu_idle_threshold;
4453028Smh27603
4460Sstevel@tonic-gate /*
4470Sstevel@tonic-gate * By default nexus has 0 threshold, and depends on its children to keep it up
4480Sstevel@tonic-gate */
4490Sstevel@tonic-gate int pm_default_nexus_threshold = 0;
4500Sstevel@tonic-gate
4510Sstevel@tonic-gate /*
4520Sstevel@tonic-gate * Data structures shared with common/io/pm.c
4530Sstevel@tonic-gate */
4540Sstevel@tonic-gate kmutex_t pm_clone_lock;
4550Sstevel@tonic-gate kcondvar_t pm_clones_cv[PM_MAX_CLONE];
4560Sstevel@tonic-gate uint_t pm_poll_cnt[PM_MAX_CLONE]; /* count of events for poll */
4570Sstevel@tonic-gate unsigned char pm_interest[PM_MAX_CLONE];
4580Sstevel@tonic-gate struct pollhead pm_pollhead;
4590Sstevel@tonic-gate
4605295Srandyf /*
4615295Srandyf * Data structures shared with common/io/srn.c
4625295Srandyf */
4635295Srandyf kmutex_t srn_clone_lock; /* protects srn_signal, srn_inuse */
4645295Srandyf void (*srn_signal)(int type, int event);
4655295Srandyf int srn_inuse; /* stop srn detach */
4665295Srandyf
4670Sstevel@tonic-gate extern int hz;
4680Sstevel@tonic-gate extern char *platform_module_list[];
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate /*
4710Sstevel@tonic-gate * Wrappers for use in ddi_walk_devs
4720Sstevel@tonic-gate */
4730Sstevel@tonic-gate
4740Sstevel@tonic-gate static int pm_set_dev_thr_walk(dev_info_t *, void *);
4750Sstevel@tonic-gate static int pm_restore_direct_lvl_walk(dev_info_t *, void *);
4760Sstevel@tonic-gate static int pm_save_direct_lvl_walk(dev_info_t *, void *);
4770Sstevel@tonic-gate static int pm_discard_dep_walk(dev_info_t *, void *);
4780Sstevel@tonic-gate #ifdef DEBUG
4790Sstevel@tonic-gate static int pm_desc_pwrchk_walk(dev_info_t *, void *);
4800Sstevel@tonic-gate #endif
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate /*
4830Sstevel@tonic-gate * Routines for managing noinvol devices
4840Sstevel@tonic-gate */
4850Sstevel@tonic-gate int pm_noinvol_update(int, int, int, char *, dev_info_t *);
4860Sstevel@tonic-gate void pm_noinvol_update_node(dev_info_t *,
4870Sstevel@tonic-gate pm_bp_noinvol_t *req);
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate kmutex_t pm_rsvp_lock;
4900Sstevel@tonic-gate kmutex_t pm_compcnt_lock;
4910Sstevel@tonic-gate krwlock_t pm_pscc_direct_rwlock;
4920Sstevel@tonic-gate krwlock_t pm_pscc_interest_rwlock;
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate #define PSC_INTEREST 0 /* belongs to interest psc list */
4950Sstevel@tonic-gate #define PSC_DIRECT 1 /* belongs to direct psc list */
4960Sstevel@tonic-gate
4970Sstevel@tonic-gate pscc_t *pm_pscc_interest;
4980Sstevel@tonic-gate pscc_t *pm_pscc_direct;
4990Sstevel@tonic-gate
5008459SJerry.Gilliam@Sun.COM #define PM_MAJOR(dip) ddi_driver_major(dip)
5018459SJerry.Gilliam@Sun.COM #define PM_IS_NEXUS(dip) ((PM_MAJOR(dip) == DDI_MAJOR_T_NONE) ? 0 : \
5028459SJerry.Gilliam@Sun.COM NEXUS_DRV(devopsp[PM_MAJOR(dip)]))
5030Sstevel@tonic-gate #define POWERING_ON(old, new) ((old) == 0 && (new) != 0)
5040Sstevel@tonic-gate #define POWERING_OFF(old, new) ((old) != 0 && (new) == 0)
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate #define PM_INCR_NOTLOWEST(dip) { \
5070Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); \
5080Sstevel@tonic-gate if (!PM_IS_NEXUS(dip) || \
5090Sstevel@tonic-gate (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\
5100Sstevel@tonic-gate if (pm_comps_notlowest == 0) \
5110Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, PM_NOT_ALL_LOWEST);\
5120Sstevel@tonic-gate pm_comps_notlowest++; \
5130Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr notlowest->%d\n",\
5140Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_comps_notlowest)) \
5150Sstevel@tonic-gate } \
5160Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); \
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate #define PM_DECR_NOTLOWEST(dip) { \
5190Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock); \
5200Sstevel@tonic-gate if (!PM_IS_NEXUS(dip) || \
5210Sstevel@tonic-gate (DEVI(dip)->devi_pm_flags & (PMC_DEV_THRESH|PMC_COMP_THRESH))) {\
5220Sstevel@tonic-gate ASSERT(pm_comps_notlowest); \
5230Sstevel@tonic-gate pm_comps_notlowest--; \
5240Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr notlowest to " \
5250Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pm_comps_notlowest))\
5260Sstevel@tonic-gate if (pm_comps_notlowest == 0) \
5270Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip, PM_ALL_LOWEST); \
5280Sstevel@tonic-gate } \
5290Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock); \
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate /*
5330Sstevel@tonic-gate * console frame-buffer power-management is not enabled when
5340Sstevel@tonic-gate * debugging services are present. to override, set pm_cfb_override
5350Sstevel@tonic-gate * to non-zero.
5360Sstevel@tonic-gate */
5370Sstevel@tonic-gate uint_t pm_cfb_comps_off = 0; /* PM_LEVEL_UNKNOWN is considered on */
5380Sstevel@tonic-gate kmutex_t pm_cfb_lock;
5390Sstevel@tonic-gate int pm_cfb_enabled = 1; /* non-zero allows pm of console frame buffer */
5400Sstevel@tonic-gate #ifdef DEBUG
5410Sstevel@tonic-gate int pm_cfb_override = 1; /* non-zero allows pm of cfb with debuggers */
5420Sstevel@tonic-gate #else
5430Sstevel@tonic-gate int pm_cfb_override = 0; /* non-zero allows pm of cfb with debuggers */
5440Sstevel@tonic-gate #endif
5450Sstevel@tonic-gate
5460Sstevel@tonic-gate static dev_info_t *cfb_dip = 0;
5470Sstevel@tonic-gate static dev_info_t *cfb_dip_detaching = 0;
5480Sstevel@tonic-gate uint_t cfb_inuse = 0;
5490Sstevel@tonic-gate static ddi_softintr_t pm_soft_id;
550*11066Srafael.vanoni@sun.com static boolean_t pm_soft_pending;
5510Sstevel@tonic-gate int pm_scans_disabled = 0;
5520Sstevel@tonic-gate
5530Sstevel@tonic-gate /*
5540Sstevel@tonic-gate * A structure to record the fact that one thread has borrowed a lock held
5550Sstevel@tonic-gate * by another thread. The context requires that the lender block on the
5560Sstevel@tonic-gate * completion of the borrower.
5570Sstevel@tonic-gate */
5580Sstevel@tonic-gate typedef struct lock_loan {
5590Sstevel@tonic-gate struct lock_loan *pmlk_next;
5600Sstevel@tonic-gate kthread_t *pmlk_borrower;
5610Sstevel@tonic-gate kthread_t *pmlk_lender;
5620Sstevel@tonic-gate dev_info_t *pmlk_dip;
5630Sstevel@tonic-gate } lock_loan_t;
5640Sstevel@tonic-gate static lock_loan_t lock_loan_head; /* list head is a dummy element */
5650Sstevel@tonic-gate
5660Sstevel@tonic-gate #ifdef DEBUG
5675295Srandyf #ifdef PMDDEBUG
5680Sstevel@tonic-gate #define PMD_FUNC(func, name) char *(func) = (name);
5695295Srandyf #else /* !PMDDEBUG */
5700Sstevel@tonic-gate #define PMD_FUNC(func, name)
5715295Srandyf #endif /* PMDDEBUG */
5725295Srandyf #else /* !DEBUG */
5733839Skchow #define PMD_FUNC(func, name)
5745295Srandyf #endif /* DEBUG */
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate /*
5780Sstevel@tonic-gate * Must be called before first device (including pseudo) attach
5790Sstevel@tonic-gate */
5800Sstevel@tonic-gate void
pm_init_locks(void)5810Sstevel@tonic-gate pm_init_locks(void)
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate mutex_init(&pm_scan_lock, NULL, MUTEX_DRIVER, NULL);
5840Sstevel@tonic-gate mutex_init(&pm_rsvp_lock, NULL, MUTEX_DRIVER, NULL);
5850Sstevel@tonic-gate mutex_init(&pm_compcnt_lock, NULL, MUTEX_DRIVER, NULL);
5860Sstevel@tonic-gate mutex_init(&pm_dep_thread_lock, NULL, MUTEX_DRIVER, NULL);
5870Sstevel@tonic-gate mutex_init(&pm_remdrv_lock, NULL, MUTEX_DRIVER, NULL);
5880Sstevel@tonic-gate mutex_init(&pm_loan_lock, NULL, MUTEX_DRIVER, NULL);
5890Sstevel@tonic-gate rw_init(&pm_thresh_rwlock, NULL, RW_DEFAULT, NULL);
5900Sstevel@tonic-gate rw_init(&pm_noinvol_rwlock, NULL, RW_DEFAULT, NULL);
5910Sstevel@tonic-gate cv_init(&pm_dep_thread_cv, NULL, CV_DEFAULT, NULL);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate
5949694SScott.Rotondo@Sun.COM static int pm_reset_timestamps(dev_info_t *, void *);
5959694SScott.Rotondo@Sun.COM
5960Sstevel@tonic-gate static boolean_t
pm_cpr_callb(void * arg,int code)5970Sstevel@tonic-gate pm_cpr_callb(void *arg, int code)
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
6000Sstevel@tonic-gate static int auto_save;
6013028Smh27603 static pm_cpupm_t cpupm_save;
6020Sstevel@tonic-gate
6030Sstevel@tonic-gate switch (code) {
6040Sstevel@tonic-gate case CB_CODE_CPR_CHKPT:
6050Sstevel@tonic-gate /*
6060Sstevel@tonic-gate * Cancel scan or wait for scan in progress to finish
6070Sstevel@tonic-gate * Other threads may be trying to restart the scan, so we
6080Sstevel@tonic-gate * have to keep at it unil it sticks
6090Sstevel@tonic-gate */
6100Sstevel@tonic-gate mutex_enter(&pm_scan_lock);
6110Sstevel@tonic-gate ASSERT(!pm_scans_disabled);
6120Sstevel@tonic-gate pm_scans_disabled = 1;
6130Sstevel@tonic-gate auto_save = autopm_enabled;
6140Sstevel@tonic-gate autopm_enabled = 0;
6153028Smh27603 cpupm_save = cpupm;
6163028Smh27603 cpupm = PM_CPUPM_NOTSET;
6170Sstevel@tonic-gate mutex_exit(&pm_scan_lock);
6180Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_scan_stop_walk, NULL);
6190Sstevel@tonic-gate break;
6200Sstevel@tonic-gate
6210Sstevel@tonic-gate case CB_CODE_CPR_RESUME:
6220Sstevel@tonic-gate ASSERT(!autopm_enabled);
6233028Smh27603 ASSERT(cpupm == PM_CPUPM_NOTSET);
6240Sstevel@tonic-gate ASSERT(pm_scans_disabled);
6250Sstevel@tonic-gate pm_scans_disabled = 0;
6260Sstevel@tonic-gate /*
6270Sstevel@tonic-gate * Call pm_reset_timestamps to reset timestamps of each
6280Sstevel@tonic-gate * device to the time when the system is resumed so that their
6290Sstevel@tonic-gate * idleness can be re-calculated. That's to avoid devices from
6300Sstevel@tonic-gate * being powered down right after resume if the system was in
6310Sstevel@tonic-gate * suspended mode long enough.
6320Sstevel@tonic-gate */
6330Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_reset_timestamps, NULL);
6340Sstevel@tonic-gate
6350Sstevel@tonic-gate autopm_enabled = auto_save;
6363028Smh27603 cpupm = cpupm_save;
6370Sstevel@tonic-gate /*
6380Sstevel@tonic-gate * If there is any auto-pm device, get the scanning
6390Sstevel@tonic-gate * going. Otherwise don't bother.
6400Sstevel@tonic-gate */
6410Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_rescan_walk, NULL);
6420Sstevel@tonic-gate break;
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate return (B_TRUE);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate
6470Sstevel@tonic-gate /*
6480Sstevel@tonic-gate * This callback routine is called when there is a system panic. This function
6490Sstevel@tonic-gate * exists for prototype matching.
6500Sstevel@tonic-gate */
6510Sstevel@tonic-gate static boolean_t
pm_panic_callb(void * arg,int code)6520Sstevel@tonic-gate pm_panic_callb(void *arg, int code)
6530Sstevel@tonic-gate {
6540Sstevel@tonic-gate _NOTE(ARGUNUSED(arg, code))
6550Sstevel@tonic-gate void pm_cfb_check_and_powerup(void);
6560Sstevel@tonic-gate PMD(PMD_CFB, ("pm_panic_callb\n"))
6570Sstevel@tonic-gate pm_cfb_check_and_powerup();
6580Sstevel@tonic-gate return (B_TRUE);
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate
6610Sstevel@tonic-gate static boolean_t
pm_halt_callb(void * arg,int code)6620Sstevel@tonic-gate pm_halt_callb(void *arg, int code)
6630Sstevel@tonic-gate {
6640Sstevel@tonic-gate _NOTE(ARGUNUSED(arg, code))
6655295Srandyf return (B_TRUE);
6660Sstevel@tonic-gate }
6670Sstevel@tonic-gate
6689694SScott.Rotondo@Sun.COM static void pm_dep_thread(void);
6699694SScott.Rotondo@Sun.COM
6700Sstevel@tonic-gate /*
6710Sstevel@tonic-gate * This needs to be called after the root and platform drivers are loaded
6720Sstevel@tonic-gate * and be single-threaded with respect to driver attach/detach
6730Sstevel@tonic-gate */
6740Sstevel@tonic-gate void
pm_init(void)6750Sstevel@tonic-gate pm_init(void)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate PMD_FUNC(pmf, "pm_init")
6780Sstevel@tonic-gate char **mod;
6790Sstevel@tonic-gate extern pri_t minclsyspri;
6800Sstevel@tonic-gate
6810Sstevel@tonic-gate pm_comps_notlowest = 0;
6820Sstevel@tonic-gate pm_system_idle_threshold = pm_default_idle_threshold;
6833028Smh27603 pm_cpu_idle_threshold = 0;
6840Sstevel@tonic-gate
6850Sstevel@tonic-gate pm_cpr_cb_id = callb_add(pm_cpr_callb, (void *)NULL,
6860Sstevel@tonic-gate CB_CL_CPR_PM, "pm_cpr");
6870Sstevel@tonic-gate pm_panic_cb_id = callb_add(pm_panic_callb, (void *)NULL,
6884780Smh27603 CB_CL_PANIC, "pm_panic");
6890Sstevel@tonic-gate pm_halt_cb_id = callb_add(pm_halt_callb, (void *)NULL,
6904780Smh27603 CB_CL_HALT, "pm_halt");
6910Sstevel@tonic-gate
6920Sstevel@tonic-gate /*
6930Sstevel@tonic-gate * Create a thread to do dependency processing.
6940Sstevel@tonic-gate */
6950Sstevel@tonic-gate (void) thread_create(NULL, 0, (void (*)())pm_dep_thread, NULL, 0, &p0,
6960Sstevel@tonic-gate TS_RUN, minclsyspri);
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate /*
6990Sstevel@tonic-gate * loadrootmodules already loaded these ppm drivers, now get them
7000Sstevel@tonic-gate * attached so they can claim the root drivers as they attach
7010Sstevel@tonic-gate */
7020Sstevel@tonic-gate for (mod = platform_module_list; *mod; mod++) {
7030Sstevel@tonic-gate if (i_ddi_attach_hw_nodes(*mod) != DDI_SUCCESS) {
7040Sstevel@tonic-gate cmn_err(CE_WARN, "!cannot load platform pm driver %s\n",
7050Sstevel@tonic-gate *mod);
7060Sstevel@tonic-gate } else {
7070Sstevel@tonic-gate PMD(PMD_DHR, ("%s: %s (%s)\n", pmf, *mod,
7080Sstevel@tonic-gate ddi_major_to_name(ddi_name_to_major(*mod))))
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate }
7120Sstevel@tonic-gate
7130Sstevel@tonic-gate /*
7143028Smh27603 * pm_scan_init - create pm scan data structure. Called (if autopm or cpupm
7153028Smh27603 * enabled) when device becomes power managed or after a failed detach and
7163028Smh27603 * when autopm is started via PM_START_PM or PM_START_CPUPM ioctls, and after
7173028Smh27603 * a CPR resume to get all the devices scanning again.
7180Sstevel@tonic-gate */
7190Sstevel@tonic-gate void
pm_scan_init(dev_info_t * dip)7200Sstevel@tonic-gate pm_scan_init(dev_info_t *dip)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate PMD_FUNC(pmf, "scan_init")
7230Sstevel@tonic-gate pm_scan_t *scanp;
7240Sstevel@tonic-gate
7250Sstevel@tonic-gate ASSERT(!PM_ISBC(dip));
7260Sstevel@tonic-gate
7270Sstevel@tonic-gate PM_LOCK_DIP(dip);
7280Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip);
7290Sstevel@tonic-gate if (!scanp) {
7300Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): create scan data\n",
7310Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
7320Sstevel@tonic-gate scanp = kmem_zalloc(sizeof (pm_scan_t), KM_SLEEP);
7330Sstevel@tonic-gate DEVI(dip)->devi_pm_scan = scanp;
7340Sstevel@tonic-gate } else if (scanp->ps_scan_flags & PM_SCAN_STOP) {
7350Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): "
7360Sstevel@tonic-gate "clear PM_SCAN_STOP flag\n", pmf, PM_DEVICE(dip)))
7370Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_STOP;
7380Sstevel@tonic-gate }
7390Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
7400Sstevel@tonic-gate }
7410Sstevel@tonic-gate
7420Sstevel@tonic-gate /*
7430Sstevel@tonic-gate * pm_scan_fini - remove pm scan data structure when stopping pm on the device
7440Sstevel@tonic-gate */
7450Sstevel@tonic-gate void
pm_scan_fini(dev_info_t * dip)7460Sstevel@tonic-gate pm_scan_fini(dev_info_t *dip)
7470Sstevel@tonic-gate {
7480Sstevel@tonic-gate PMD_FUNC(pmf, "scan_fini")
7490Sstevel@tonic-gate pm_scan_t *scanp;
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
7520Sstevel@tonic-gate ASSERT(!PM_ISBC(dip));
7530Sstevel@tonic-gate PM_LOCK_DIP(dip);
7540Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip);
7550Sstevel@tonic-gate if (!scanp) {
7560Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
7570Sstevel@tonic-gate return;
7580Sstevel@tonic-gate }
7590Sstevel@tonic-gate
7600Sstevel@tonic-gate ASSERT(!scanp->ps_scan_id && !(scanp->ps_scan_flags &
7610Sstevel@tonic-gate (PM_SCANNING | PM_SCAN_DISPATCHED | PM_SCAN_AGAIN)));
7620Sstevel@tonic-gate
7630Sstevel@tonic-gate kmem_free(scanp, sizeof (pm_scan_t));
7640Sstevel@tonic-gate DEVI(dip)->devi_pm_scan = NULL;
7650Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate /*
7690Sstevel@tonic-gate * Given a pointer to a component struct, return the current power level
7700Sstevel@tonic-gate * (struct contains index unless it is a continuous level).
7710Sstevel@tonic-gate * Located here in hopes of getting both this and dev_is_needed into the
7720Sstevel@tonic-gate * cache together
7730Sstevel@tonic-gate */
7740Sstevel@tonic-gate static int
cur_power(pm_component_t * cp)7750Sstevel@tonic-gate cur_power(pm_component_t *cp)
7760Sstevel@tonic-gate {
7770Sstevel@tonic-gate if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN)
7780Sstevel@tonic-gate return (cp->pmc_cur_pwr);
7790Sstevel@tonic-gate
7800Sstevel@tonic-gate return (cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr]);
7810Sstevel@tonic-gate }
7820Sstevel@tonic-gate
7830Sstevel@tonic-gate static char *
pm_decode_direction(int direction)7840Sstevel@tonic-gate pm_decode_direction(int direction)
7850Sstevel@tonic-gate {
7860Sstevel@tonic-gate switch (direction) {
7870Sstevel@tonic-gate case PM_LEVEL_UPONLY:
7880Sstevel@tonic-gate return ("up");
7890Sstevel@tonic-gate
7900Sstevel@tonic-gate case PM_LEVEL_EXACT:
7910Sstevel@tonic-gate return ("exact");
7920Sstevel@tonic-gate
7930Sstevel@tonic-gate case PM_LEVEL_DOWNONLY:
7940Sstevel@tonic-gate return ("down");
7950Sstevel@tonic-gate
7960Sstevel@tonic-gate default:
7970Sstevel@tonic-gate return ("INVALID DIRECTION");
7980Sstevel@tonic-gate }
7990Sstevel@tonic-gate }
8000Sstevel@tonic-gate
8010Sstevel@tonic-gate char *
pm_decode_op(pm_bus_power_op_t op)8020Sstevel@tonic-gate pm_decode_op(pm_bus_power_op_t op)
8030Sstevel@tonic-gate {
8040Sstevel@tonic-gate switch (op) {
8050Sstevel@tonic-gate case BUS_POWER_CHILD_PWRCHG:
8060Sstevel@tonic-gate return ("CHILD_PWRCHG");
8070Sstevel@tonic-gate case BUS_POWER_NEXUS_PWRUP:
8080Sstevel@tonic-gate return ("NEXUS_PWRUP");
8090Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION:
8100Sstevel@tonic-gate return ("PRE_NOTIFICATION");
8110Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION:
8120Sstevel@tonic-gate return ("POST_NOTIFICATION");
8130Sstevel@tonic-gate case BUS_POWER_HAS_CHANGED:
8140Sstevel@tonic-gate return ("HAS_CHANGED");
8150Sstevel@tonic-gate case BUS_POWER_NOINVOL:
8160Sstevel@tonic-gate return ("NOINVOL");
8170Sstevel@tonic-gate default:
8180Sstevel@tonic-gate return ("UNKNOWN OP");
8190Sstevel@tonic-gate }
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate
8220Sstevel@tonic-gate /*
8230Sstevel@tonic-gate * Returns true if level is a possible (valid) power level for component
8240Sstevel@tonic-gate */
8250Sstevel@tonic-gate int
e_pm_valid_power(dev_info_t * dip,int cmpt,int level)8260Sstevel@tonic-gate e_pm_valid_power(dev_info_t *dip, int cmpt, int level)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate PMD_FUNC(pmf, "e_pm_valid_power")
8290Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, cmpt);
8300Sstevel@tonic-gate int i;
8310Sstevel@tonic-gate int *ip = cp->pmc_comp.pmc_lvals;
8320Sstevel@tonic-gate int limit = cp->pmc_comp.pmc_numlevels;
8330Sstevel@tonic-gate
8340Sstevel@tonic-gate if (level < 0)
8350Sstevel@tonic-gate return (0);
8360Sstevel@tonic-gate for (i = 0; i < limit; i++) {
8370Sstevel@tonic-gate if (level == *ip++)
8380Sstevel@tonic-gate return (1);
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate #ifdef DEBUG
8410Sstevel@tonic-gate if (pm_debug & PMD_FAIL) {
8420Sstevel@tonic-gate ip = cp->pmc_comp.pmc_lvals;
8430Sstevel@tonic-gate
8440Sstevel@tonic-gate for (i = 0; i < limit; i++)
8450Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: index=%d, level=%d\n",
8460Sstevel@tonic-gate pmf, i, *ip++))
8470Sstevel@tonic-gate }
8480Sstevel@tonic-gate #endif
8490Sstevel@tonic-gate return (0);
8500Sstevel@tonic-gate }
8510Sstevel@tonic-gate
8529694SScott.Rotondo@Sun.COM static int pm_start(dev_info_t *dip);
8530Sstevel@tonic-gate /*
8540Sstevel@tonic-gate * Returns true if device is pm'd (after calling pm_start if need be)
8550Sstevel@tonic-gate */
8560Sstevel@tonic-gate int
e_pm_valid_info(dev_info_t * dip,pm_info_t ** infop)8570Sstevel@tonic-gate e_pm_valid_info(dev_info_t *dip, pm_info_t **infop)
8580Sstevel@tonic-gate {
8590Sstevel@tonic-gate pm_info_t *info;
8600Sstevel@tonic-gate
8610Sstevel@tonic-gate /*
8620Sstevel@tonic-gate * Check if the device is power managed if not.
8630Sstevel@tonic-gate * To make the common case (device is power managed already)
8640Sstevel@tonic-gate * fast, we check without the lock. If device is not already
8650Sstevel@tonic-gate * power managed, then we take the lock and the long route through
8660Sstevel@tonic-gate * go get it managed. Devices never go unmanaged until they
8670Sstevel@tonic-gate * detach.
8680Sstevel@tonic-gate */
8690Sstevel@tonic-gate info = PM_GET_PM_INFO(dip);
8700Sstevel@tonic-gate if (!info) {
8710Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) {
8720Sstevel@tonic-gate return (0);
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate if (pm_start(dip) != DDI_SUCCESS) {
8750Sstevel@tonic-gate return (0);
8760Sstevel@tonic-gate }
8770Sstevel@tonic-gate info = PM_GET_PM_INFO(dip);
8780Sstevel@tonic-gate }
8790Sstevel@tonic-gate ASSERT(info);
8800Sstevel@tonic-gate if (infop != NULL)
8810Sstevel@tonic-gate *infop = info;
8820Sstevel@tonic-gate return (1);
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate
8850Sstevel@tonic-gate int
e_pm_valid_comp(dev_info_t * dip,int cmpt,pm_component_t ** cpp)8860Sstevel@tonic-gate e_pm_valid_comp(dev_info_t *dip, int cmpt, pm_component_t **cpp)
8870Sstevel@tonic-gate {
8880Sstevel@tonic-gate if (cmpt >= 0 && cmpt < PM_NUMCMPTS(dip)) {
8890Sstevel@tonic-gate if (cpp != NULL)
8900Sstevel@tonic-gate *cpp = PM_CP(dip, cmpt);
8910Sstevel@tonic-gate return (1);
8920Sstevel@tonic-gate } else {
8930Sstevel@tonic-gate return (0);
8940Sstevel@tonic-gate }
8950Sstevel@tonic-gate }
8960Sstevel@tonic-gate
8970Sstevel@tonic-gate /*
8980Sstevel@tonic-gate * Internal guts of ddi_dev_is_needed and pm_raise/lower_power
8990Sstevel@tonic-gate */
9000Sstevel@tonic-gate static int
dev_is_needed(dev_info_t * dip,int cmpt,int level,int direction)9010Sstevel@tonic-gate dev_is_needed(dev_info_t *dip, int cmpt, int level, int direction)
9020Sstevel@tonic-gate {
9030Sstevel@tonic-gate PMD_FUNC(pmf, "din")
9040Sstevel@tonic-gate pm_component_t *cp;
9050Sstevel@tonic-gate char *pathbuf;
9060Sstevel@tonic-gate int result;
9070Sstevel@tonic-gate
9080Sstevel@tonic-gate ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY);
9090Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp) ||
9100Sstevel@tonic-gate !e_pm_valid_power(dip, cmpt, level))
9110Sstevel@tonic-gate return (DDI_FAILURE);
9120Sstevel@tonic-gate
9130Sstevel@tonic-gate PMD(PMD_DIN, ("%s: %s@%s(%s#%d) cmpt=%d, dir=%s, new=%d, cur=%d\n",
9140Sstevel@tonic-gate pmf, PM_DEVICE(dip), cmpt, pm_decode_direction(direction),
9150Sstevel@tonic-gate level, cur_power(cp)))
9160Sstevel@tonic-gate
9170Sstevel@tonic-gate if (pm_set_power(dip, cmpt, level, direction,
9180Sstevel@tonic-gate PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) {
9190Sstevel@tonic-gate if (direction == PM_LEVEL_UPONLY) {
9200Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
9210Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
9220Sstevel@tonic-gate cmn_err(CE_WARN, "Device %s failed to power up.",
9230Sstevel@tonic-gate pathbuf);
9240Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
9250Sstevel@tonic-gate }
9260Sstevel@tonic-gate PMD(PMD_DIN | PMD_FAIL, ("%s: %s@%s(%s#%d) [%d] %s->%d failed, "
9270Sstevel@tonic-gate "errno %d\n", pmf, PM_DEVICE(dip), cmpt,
9280Sstevel@tonic-gate pm_decode_direction(direction), level, result))
9290Sstevel@tonic-gate return (DDI_FAILURE);
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate
9320Sstevel@tonic-gate PMD(PMD_RESCAN | PMD_DIN, ("%s: pm_rescan %s@%s(%s#%d)\n", pmf,
9330Sstevel@tonic-gate PM_DEVICE(dip)))
9340Sstevel@tonic-gate pm_rescan(dip);
9350Sstevel@tonic-gate return (DDI_SUCCESS);
9360Sstevel@tonic-gate }
9370Sstevel@tonic-gate
9380Sstevel@tonic-gate /*
9390Sstevel@tonic-gate * We can get multiple pm_rescan() threads, if one of them discovers
9400Sstevel@tonic-gate * that no scan is running at the moment, it kicks it into action.
9410Sstevel@tonic-gate * Otherwise, it tells the current scanning thread to scan again when
9420Sstevel@tonic-gate * it is done by asserting the PM_SCAN_AGAIN flag. The PM_SCANNING and
9430Sstevel@tonic-gate * PM_SCAN_AGAIN flags are used to regulate scan, to make sure only one
9440Sstevel@tonic-gate * thread at a time runs the pm_scan_dev() code.
9450Sstevel@tonic-gate */
9460Sstevel@tonic-gate void
pm_rescan(void * arg)9470Sstevel@tonic-gate pm_rescan(void *arg)
9480Sstevel@tonic-gate {
9490Sstevel@tonic-gate PMD_FUNC(pmf, "rescan")
9500Sstevel@tonic-gate dev_info_t *dip = (dev_info_t *)arg;
9510Sstevel@tonic-gate pm_info_t *info;
9520Sstevel@tonic-gate pm_scan_t *scanp;
9530Sstevel@tonic-gate timeout_id_t scanid;
9540Sstevel@tonic-gate
9550Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
9560Sstevel@tonic-gate PM_LOCK_DIP(dip);
9570Sstevel@tonic-gate info = PM_GET_PM_INFO(dip);
9580Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip);
9593028Smh27603 if (pm_scans_disabled || !PM_SCANABLE(dip) || !info || !scanp ||
9600Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP)) {
9610Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
9620Sstevel@tonic-gate return;
9630Sstevel@tonic-gate }
9640Sstevel@tonic-gate if (scanp->ps_scan_flags & PM_SCANNING) {
9650Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_AGAIN;
9660Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
9670Sstevel@tonic-gate return;
9680Sstevel@tonic-gate } else if (scanp->ps_scan_id) {
9690Sstevel@tonic-gate scanid = scanp->ps_scan_id;
9700Sstevel@tonic-gate scanp->ps_scan_id = 0;
9710Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): cancel timeout scanid %lx\n",
9720Sstevel@tonic-gate pmf, PM_DEVICE(dip), (ulong_t)scanid))
9730Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
9740Sstevel@tonic-gate (void) untimeout(scanid);
9750Sstevel@tonic-gate PM_LOCK_DIP(dip);
9760Sstevel@tonic-gate }
9770Sstevel@tonic-gate
9780Sstevel@tonic-gate /*
9790Sstevel@tonic-gate * Dispatching pm_scan during attach time is risky due to the fact that
9800Sstevel@tonic-gate * attach might soon fail and dip dissolved, and panic may happen while
9810Sstevel@tonic-gate * attempting to stop scan. So schedule a pm_rescan instead.
9820Sstevel@tonic-gate * (Note that if either of the first two terms are true, taskq_dispatch
9830Sstevel@tonic-gate * will not be invoked).
9840Sstevel@tonic-gate *
9850Sstevel@tonic-gate * Multiple pm_scan dispatching is unecessary and costly to keep track
9860Sstevel@tonic-gate * of. The PM_SCAN_DISPATCHED flag is used between pm_rescan and pm_scan
9870Sstevel@tonic-gate * to regulate the dispatching.
9880Sstevel@tonic-gate *
9890Sstevel@tonic-gate * Scan is stopped before the device is detached (in pm_detaching())
9900Sstevel@tonic-gate * but it may get re-started during the post_detach processing if the
9910Sstevel@tonic-gate * driver fails to detach.
9920Sstevel@tonic-gate */
9930Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip) ||
9940Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_DISPATCHED) ||
9950Sstevel@tonic-gate !taskq_dispatch(system_taskq, pm_scan, (void *)dip, TQ_NOSLEEP)) {
9960Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): attaching, pm_scan already "
9970Sstevel@tonic-gate "dispatched or dispatching failed\n", pmf, PM_DEVICE(dip)))
9980Sstevel@tonic-gate if (scanp->ps_scan_id) {
9990Sstevel@tonic-gate scanid = scanp->ps_scan_id;
10000Sstevel@tonic-gate scanp->ps_scan_id = 0;
10010Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10020Sstevel@tonic-gate (void) untimeout(scanid);
10030Sstevel@tonic-gate PM_LOCK_DIP(dip);
10040Sstevel@tonic-gate if (scanp->ps_scan_id) {
10050Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): a competing "
10060Sstevel@tonic-gate "thread scheduled pm_rescan, scanid %lx\n",
10070Sstevel@tonic-gate pmf, PM_DEVICE(dip),
10080Sstevel@tonic-gate (ulong_t)scanp->ps_scan_id))
10090Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10100Sstevel@tonic-gate return;
10110Sstevel@tonic-gate }
10120Sstevel@tonic-gate }
10130Sstevel@tonic-gate scanp->ps_scan_id = timeout(pm_rescan, (void *)dip,
10140Sstevel@tonic-gate (scanp->ps_idle_down ? pm_id_ticks :
10157157Smh27603 (PM_MIN_SCAN(dip) * hz)));
10160Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): scheduled next pm_rescan, "
10170Sstevel@tonic-gate "scanid %lx\n", pmf, PM_DEVICE(dip),
10180Sstevel@tonic-gate (ulong_t)scanp->ps_scan_id))
10190Sstevel@tonic-gate } else {
10200Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: dispatched pm_scan for %s@%s(%s#%d)\n",
10210Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
10220Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_DISPATCHED;
10230Sstevel@tonic-gate }
10240Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10250Sstevel@tonic-gate }
10260Sstevel@tonic-gate
10270Sstevel@tonic-gate void
pm_scan(void * arg)10280Sstevel@tonic-gate pm_scan(void *arg)
10290Sstevel@tonic-gate {
10300Sstevel@tonic-gate PMD_FUNC(pmf, "scan")
10310Sstevel@tonic-gate dev_info_t *dip = (dev_info_t *)arg;
10320Sstevel@tonic-gate pm_scan_t *scanp;
10330Sstevel@tonic-gate time_t nextscan;
10340Sstevel@tonic-gate
10350Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
10360Sstevel@tonic-gate
10370Sstevel@tonic-gate PM_LOCK_DIP(dip);
10380Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip);
10390Sstevel@tonic-gate ASSERT(scanp && PM_GET_PM_INFO(dip));
10400Sstevel@tonic-gate
10413028Smh27603 if (pm_scans_disabled || !PM_SCANABLE(dip) ||
10420Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP)) {
10430Sstevel@tonic-gate scanp->ps_scan_flags &= ~(PM_SCAN_AGAIN | PM_SCAN_DISPATCHED);
10440Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10450Sstevel@tonic-gate return;
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate
10480Sstevel@tonic-gate if (scanp->ps_idle_down) {
10490Sstevel@tonic-gate /*
10500Sstevel@tonic-gate * make sure we remember idledown was in affect until
10510Sstevel@tonic-gate * we've completed the scan
10520Sstevel@tonic-gate */
10530Sstevel@tonic-gate PMID_SET_SCANS(scanp->ps_idle_down)
10540Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown starts "
10550Sstevel@tonic-gate "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down))
10560Sstevel@tonic-gate }
10570Sstevel@tonic-gate
10580Sstevel@tonic-gate /* possible having two threads running pm_scan() */
10590Sstevel@tonic-gate if (scanp->ps_scan_flags & PM_SCANNING) {
10600Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_AGAIN;
10610Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: scanning, will scan %s@%s(%s#%d) again\n",
10620Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
10630Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED;
10640Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10650Sstevel@tonic-gate return;
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate
10680Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCANNING;
10690Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_DISPATCHED;
10700Sstevel@tonic-gate do {
10710Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCAN_AGAIN;
10720Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10730Sstevel@tonic-gate nextscan = pm_scan_dev(dip);
10740Sstevel@tonic-gate PM_LOCK_DIP(dip);
10750Sstevel@tonic-gate } while (scanp->ps_scan_flags & PM_SCAN_AGAIN);
10760Sstevel@tonic-gate
10770Sstevel@tonic-gate ASSERT(scanp->ps_scan_flags & PM_SCANNING);
10780Sstevel@tonic-gate scanp->ps_scan_flags &= ~PM_SCANNING;
10790Sstevel@tonic-gate
10800Sstevel@tonic-gate if (scanp->ps_idle_down) {
10810Sstevel@tonic-gate scanp->ps_idle_down &= ~PMID_SCANS;
10820Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d): idledown ends "
10830Sstevel@tonic-gate "(pmid %x)\n", pmf, PM_DEVICE(dip), scanp->ps_idle_down))
10840Sstevel@tonic-gate }
10850Sstevel@tonic-gate
10860Sstevel@tonic-gate /* schedule for next idle check */
10870Sstevel@tonic-gate if (nextscan != LONG_MAX) {
10880Sstevel@tonic-gate if (nextscan > (LONG_MAX / hz))
10890Sstevel@tonic-gate nextscan = (LONG_MAX - 1) / hz;
10900Sstevel@tonic-gate if (scanp->ps_scan_id) {
10910Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): while scanning "
10920Sstevel@tonic-gate "another rescan scheduled scanid(%lx)\n", pmf,
10930Sstevel@tonic-gate PM_DEVICE(dip), (ulong_t)scanp->ps_scan_id))
10940Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
10950Sstevel@tonic-gate return;
10960Sstevel@tonic-gate } else if (!(scanp->ps_scan_flags & PM_SCAN_STOP)) {
10970Sstevel@tonic-gate scanp->ps_scan_id = timeout(pm_rescan, (void *)dip,
10980Sstevel@tonic-gate (clock_t)(nextscan * hz));
10990Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: nextscan for %s@%s(%s#%d) in "
11000Sstevel@tonic-gate "%lx sec, scanid(%lx) \n", pmf, PM_DEVICE(dip),
11010Sstevel@tonic-gate (ulong_t)nextscan, (ulong_t)scanp->ps_scan_id))
11020Sstevel@tonic-gate }
11030Sstevel@tonic-gate }
11040Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
11050Sstevel@tonic-gate }
11060Sstevel@tonic-gate
11070Sstevel@tonic-gate void
pm_get_timestamps(dev_info_t * dip,time_t * valuep)11080Sstevel@tonic-gate pm_get_timestamps(dev_info_t *dip, time_t *valuep)
11090Sstevel@tonic-gate {
11100Sstevel@tonic-gate int components = PM_NUMCMPTS(dip);
11110Sstevel@tonic-gate int i;
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate ASSERT(components > 0);
11140Sstevel@tonic-gate PM_LOCK_BUSY(dip); /* so we get a consistent view */
11150Sstevel@tonic-gate for (i = 0; i < components; i++) {
11160Sstevel@tonic-gate valuep[i] = PM_CP(dip, i)->pmc_timestamp;
11170Sstevel@tonic-gate }
11180Sstevel@tonic-gate PM_UNLOCK_BUSY(dip);
11190Sstevel@tonic-gate }
11200Sstevel@tonic-gate
11210Sstevel@tonic-gate /*
11220Sstevel@tonic-gate * Returns true if device needs to be kept up because it exported the
11230Sstevel@tonic-gate * "no-involuntary-power-cycles" property or we're pretending it did (console
11240Sstevel@tonic-gate * fb case) or it is an ancestor of such a device and has used up the "one
11250Sstevel@tonic-gate * free cycle" allowed when all such leaf nodes have voluntarily powered down
11260Sstevel@tonic-gate * upon detach
11270Sstevel@tonic-gate */
11280Sstevel@tonic-gate int
pm_noinvol(dev_info_t * dip)11290Sstevel@tonic-gate pm_noinvol(dev_info_t *dip)
11300Sstevel@tonic-gate {
11310Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol")
11320Sstevel@tonic-gate
11330Sstevel@tonic-gate /*
11340Sstevel@tonic-gate * This doesn't change over the life of a driver, so no locking needed
11350Sstevel@tonic-gate */
11360Sstevel@tonic-gate if (PM_IS_CFB(dip)) {
11370Sstevel@tonic-gate PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB %s@%s(%s#%d)\n",
11380Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
11390Sstevel@tonic-gate return (1);
11400Sstevel@tonic-gate }
11410Sstevel@tonic-gate /*
11420Sstevel@tonic-gate * Not an issue if no such kids
11430Sstevel@tonic-gate */
11440Sstevel@tonic-gate if (DEVI(dip)->devi_pm_noinvolpm == 0) {
11450Sstevel@tonic-gate #ifdef DEBUG
11460Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd != 0) {
11470Sstevel@tonic-gate dev_info_t *pdip = dip;
11480Sstevel@tonic-gate do {
11490Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d) noinvol %d "
11500Sstevel@tonic-gate "volpmd %d\n", pmf, PM_DEVICE(pdip),
11510Sstevel@tonic-gate DEVI(pdip)->devi_pm_noinvolpm,
11520Sstevel@tonic-gate DEVI(pdip)->devi_pm_volpmd))
11530Sstevel@tonic-gate pdip = ddi_get_parent(pdip);
11540Sstevel@tonic-gate } while (pdip);
11550Sstevel@tonic-gate }
11560Sstevel@tonic-gate #endif
11570Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_volpmd == 0);
11580Sstevel@tonic-gate return (0);
11590Sstevel@tonic-gate }
11600Sstevel@tonic-gate
11610Sstevel@tonic-gate /*
11620Sstevel@tonic-gate * Since we now maintain the counts correct at every node, we no longer
11630Sstevel@tonic-gate * need to look up the tree. An ancestor cannot use up the free cycle
11640Sstevel@tonic-gate * without the children getting their counts adjusted.
11650Sstevel@tonic-gate */
11660Sstevel@tonic-gate
11670Sstevel@tonic-gate #ifdef DEBUG
11680Sstevel@tonic-gate if (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd)
11690Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s@%s(%s#%d)\n", pmf,
11700Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm, DEVI(dip)->devi_pm_volpmd,
11710Sstevel@tonic-gate PM_DEVICE(dip)))
11720Sstevel@tonic-gate #endif
11730Sstevel@tonic-gate return (DEVI(dip)->devi_pm_noinvolpm != DEVI(dip)->devi_pm_volpmd);
11740Sstevel@tonic-gate }
11750Sstevel@tonic-gate
11769694SScott.Rotondo@Sun.COM static int cur_threshold(dev_info_t *, int);
11779694SScott.Rotondo@Sun.COM static int pm_next_lower_power(pm_component_t *, int);
11789694SScott.Rotondo@Sun.COM
11790Sstevel@tonic-gate /*
11800Sstevel@tonic-gate * This function performs the actual scanning of the device.
11810Sstevel@tonic-gate * It attempts to power off the indicated device's components if they have
11820Sstevel@tonic-gate * been idle and other restrictions are met.
11830Sstevel@tonic-gate * pm_scan_dev calculates and returns when the next scan should happen for
11840Sstevel@tonic-gate * this device.
11850Sstevel@tonic-gate */
11860Sstevel@tonic-gate time_t
pm_scan_dev(dev_info_t * dip)11870Sstevel@tonic-gate pm_scan_dev(dev_info_t *dip)
11880Sstevel@tonic-gate {
11890Sstevel@tonic-gate PMD_FUNC(pmf, "scan_dev")
11900Sstevel@tonic-gate pm_scan_t *scanp;
11910Sstevel@tonic-gate time_t *timestamp, idletime, now, thresh;
11920Sstevel@tonic-gate time_t timeleft = 0;
11933839Skchow #ifdef PMDDEBUG
11943839Skchow int curpwr;
11953839Skchow #endif
11963839Skchow int i, nxtpwr, pwrndx, unused;
11970Sstevel@tonic-gate size_t size;
11980Sstevel@tonic-gate pm_component_t *cp;
11990Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
12000Sstevel@tonic-gate int circ;
12017157Smh27603 clock_t min_scan = pm_default_min_scan;
12020Sstevel@tonic-gate
12030Sstevel@tonic-gate /*
12040Sstevel@tonic-gate * skip attaching device
12050Sstevel@tonic-gate */
12060Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) {
12070Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) is attaching, timeleft(%lx)\n",
12087157Smh27603 pmf, PM_DEVICE(dip), min_scan))
12097157Smh27603 return (min_scan);
12100Sstevel@tonic-gate }
12110Sstevel@tonic-gate
12120Sstevel@tonic-gate PM_LOCK_DIP(dip);
12130Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip);
12147157Smh27603 min_scan = PM_MIN_SCAN(dip);
12150Sstevel@tonic-gate ASSERT(scanp && PM_GET_PM_INFO(dip));
12160Sstevel@tonic-gate
12170Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
12180Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d): kuc is %d\n", pmf, PM_DEVICE(dip),
12190Sstevel@tonic-gate PM_KUC(dip)))
12200Sstevel@tonic-gate
12210Sstevel@tonic-gate /* no scan under the following conditions */
12223028Smh27603 if (pm_scans_disabled || !PM_SCANABLE(dip) ||
12230Sstevel@tonic-gate (scanp->ps_scan_flags & PM_SCAN_STOP) ||
12240Sstevel@tonic-gate (PM_KUC(dip) != 0) ||
12250Sstevel@tonic-gate PM_ISDIRECT(dip) || pm_noinvol(dip)) {
12260Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
12270Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END, %s@%s(%s#%d)] no scan, "
12283028Smh27603 "scan_disabled(%d), apm_enabled(%d), cpupm(%d), "
12293028Smh27603 "kuc(%d), %s directpm, %s pm_noinvol\n",
12303028Smh27603 pmf, PM_DEVICE(dip), pm_scans_disabled, autopm_enabled,
12313028Smh27603 cpupm, PM_KUC(dip),
12320Sstevel@tonic-gate PM_ISDIRECT(dip) ? "is" : "is not",
12330Sstevel@tonic-gate pm_noinvol(dip) ? "is" : "is not"))
12340Sstevel@tonic-gate return (LONG_MAX);
12350Sstevel@tonic-gate }
12360Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
12370Sstevel@tonic-gate
12380Sstevel@tonic-gate if (!ndi_devi_tryenter(pdip, &circ)) {
12390Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) can't hold pdip",
12400Sstevel@tonic-gate pmf, PM_DEVICE(pdip)))
12410Sstevel@tonic-gate return ((time_t)1);
12420Sstevel@tonic-gate }
12430Sstevel@tonic-gate now = gethrestime_sec();
12440Sstevel@tonic-gate size = PM_NUMCMPTS(dip) * sizeof (time_t);
12450Sstevel@tonic-gate timestamp = kmem_alloc(size, KM_SLEEP);
12460Sstevel@tonic-gate pm_get_timestamps(dip, timestamp);
12470Sstevel@tonic-gate
12480Sstevel@tonic-gate /*
12490Sstevel@tonic-gate * Since we removed support for backwards compatible devices,
12500Sstevel@tonic-gate * (see big comment at top of file)
12510Sstevel@tonic-gate * it is no longer required to deal with component 0 last.
12520Sstevel@tonic-gate */
12530Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
12540Sstevel@tonic-gate /*
12550Sstevel@tonic-gate * If already off (an optimization, perhaps)
12560Sstevel@tonic-gate */
12570Sstevel@tonic-gate cp = PM_CP(dip, i);
12580Sstevel@tonic-gate pwrndx = cp->pmc_cur_pwr;
12593839Skchow #ifdef PMDDEBUG
12600Sstevel@tonic-gate curpwr = (pwrndx == PM_LEVEL_UNKNOWN) ?
12610Sstevel@tonic-gate PM_LEVEL_UNKNOWN :
12620Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[pwrndx];
12633839Skchow #endif
12640Sstevel@tonic-gate
12650Sstevel@tonic-gate if (pwrndx == 0) {
12660Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d off or "
12670Sstevel@tonic-gate "lowest\n", pmf, PM_DEVICE(dip), i))
12680Sstevel@tonic-gate /* skip device if off or at its lowest */
12690Sstevel@tonic-gate continue;
12700Sstevel@tonic-gate }
12710Sstevel@tonic-gate
12720Sstevel@tonic-gate thresh = cur_threshold(dip, i); /* comp i threshold */
12730Sstevel@tonic-gate if ((timestamp[i] == 0) || (cp->pmc_busycount > 0)) {
12740Sstevel@tonic-gate /* were busy or newly became busy by another thread */
12750Sstevel@tonic-gate if (timeleft == 0)
12767157Smh27603 timeleft = max(thresh, min_scan);
12770Sstevel@tonic-gate else
12780Sstevel@tonic-gate timeleft = min(
12797157Smh27603 timeleft, max(thresh, min_scan));
12800Sstevel@tonic-gate continue;
12810Sstevel@tonic-gate }
12820Sstevel@tonic-gate
12830Sstevel@tonic-gate idletime = now - timestamp[i]; /* idle time */
12840Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d idle time %lx\n",
12850Sstevel@tonic-gate pmf, PM_DEVICE(dip), i, idletime))
12860Sstevel@tonic-gate if (idletime >= thresh || PM_IS_PID(dip)) {
12870Sstevel@tonic-gate nxtpwr = pm_next_lower_power(cp, pwrndx);
12880Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, %d->%d\n",
12890Sstevel@tonic-gate pmf, PM_DEVICE(dip), i, curpwr, nxtpwr))
12900Sstevel@tonic-gate if (pm_set_power(dip, i, nxtpwr, PM_LEVEL_DOWNONLY,
12910Sstevel@tonic-gate PM_CANBLOCK_FAIL, 1, &unused) != DDI_SUCCESS &&
12920Sstevel@tonic-gate PM_CURPOWER(dip, i) != nxtpwr) {
12930Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
12940Sstevel@tonic-gate "%d->%d Failed\n", pmf, PM_DEVICE(dip),
12950Sstevel@tonic-gate i, curpwr, nxtpwr))
12967157Smh27603 timeleft = min_scan;
12970Sstevel@tonic-gate continue;
12980Sstevel@tonic-gate } else {
12990Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
13000Sstevel@tonic-gate "%d->%d, GOOD curpwr %d\n", pmf,
13010Sstevel@tonic-gate PM_DEVICE(dip), i, curpwr, nxtpwr,
13020Sstevel@tonic-gate cur_power(cp)))
13030Sstevel@tonic-gate
13040Sstevel@tonic-gate if (nxtpwr == 0) /* component went off */
13050Sstevel@tonic-gate continue;
13060Sstevel@tonic-gate
13070Sstevel@tonic-gate /*
13080Sstevel@tonic-gate * scan to next lower level
13090Sstevel@tonic-gate */
13100Sstevel@tonic-gate if (timeleft == 0)
13110Sstevel@tonic-gate timeleft = max(
13120Sstevel@tonic-gate 1, cur_threshold(dip, i));
13130Sstevel@tonic-gate else
13140Sstevel@tonic-gate timeleft = min(timeleft,
13150Sstevel@tonic-gate max(1, cur_threshold(dip, i)));
13160Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, "
13170Sstevel@tonic-gate "timeleft(%lx)\n", pmf, PM_DEVICE(dip),
13180Sstevel@tonic-gate i, timeleft))
13190Sstevel@tonic-gate }
13200Sstevel@tonic-gate } else { /* comp not idle long enough */
13210Sstevel@tonic-gate if (timeleft == 0)
13220Sstevel@tonic-gate timeleft = thresh - idletime;
13230Sstevel@tonic-gate else
13240Sstevel@tonic-gate timeleft = min(timeleft, (thresh - idletime));
13250Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: %s@%s(%s#%d) comp %d, timeleft="
13260Sstevel@tonic-gate "%lx\n", pmf, PM_DEVICE(dip), i, timeleft))
13270Sstevel@tonic-gate }
13280Sstevel@tonic-gate }
13290Sstevel@tonic-gate ndi_devi_exit(pdip, circ);
13300Sstevel@tonic-gate kmem_free(timestamp, size);
13310Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] timeleft(%lx)\n", pmf,
13320Sstevel@tonic-gate PM_DEVICE(dip), timeleft))
13330Sstevel@tonic-gate
13340Sstevel@tonic-gate /*
13350Sstevel@tonic-gate * if components are already at lowest level, timeleft is left 0
13360Sstevel@tonic-gate */
13370Sstevel@tonic-gate return ((timeleft == 0) ? LONG_MAX : timeleft);
13380Sstevel@tonic-gate }
13390Sstevel@tonic-gate
13400Sstevel@tonic-gate /*
13410Sstevel@tonic-gate * pm_scan_stop - cancel scheduled pm_rescan,
13420Sstevel@tonic-gate * wait for termination of dispatched pm_scan thread
13430Sstevel@tonic-gate * and active pm_scan_dev thread.
13440Sstevel@tonic-gate */
13450Sstevel@tonic-gate void
pm_scan_stop(dev_info_t * dip)13460Sstevel@tonic-gate pm_scan_stop(dev_info_t *dip)
13470Sstevel@tonic-gate {
13480Sstevel@tonic-gate PMD_FUNC(pmf, "scan_stop")
13490Sstevel@tonic-gate pm_scan_t *scanp;
13500Sstevel@tonic-gate timeout_id_t scanid;
13510Sstevel@tonic-gate
13520Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [BEGIN %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
13530Sstevel@tonic-gate PM_LOCK_DIP(dip);
13540Sstevel@tonic-gate scanp = PM_GET_PM_SCAN(dip);
13550Sstevel@tonic-gate if (!scanp) {
13560Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)] scan not initialized\n",
13570Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
13580Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
13590Sstevel@tonic-gate return;
13600Sstevel@tonic-gate }
13610Sstevel@tonic-gate scanp->ps_scan_flags |= PM_SCAN_STOP;
13620Sstevel@tonic-gate
13630Sstevel@tonic-gate /* cancel scheduled scan taskq */
13640Sstevel@tonic-gate while (scanp->ps_scan_id) {
13650Sstevel@tonic-gate scanid = scanp->ps_scan_id;
13660Sstevel@tonic-gate scanp->ps_scan_id = 0;
13670Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
13680Sstevel@tonic-gate (void) untimeout(scanid);
13690Sstevel@tonic-gate PM_LOCK_DIP(dip);
13700Sstevel@tonic-gate }
13710Sstevel@tonic-gate
13720Sstevel@tonic-gate while (scanp->ps_scan_flags & (PM_SCANNING | PM_SCAN_DISPATCHED)) {
13730Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
13740Sstevel@tonic-gate delay(1);
13750Sstevel@tonic-gate PM_LOCK_DIP(dip);
13760Sstevel@tonic-gate }
13770Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
13780Sstevel@tonic-gate PMD(PMD_SCAN, ("%s: [END %s@%s(%s#%d)]\n", pmf, PM_DEVICE(dip)))
13790Sstevel@tonic-gate }
13800Sstevel@tonic-gate
13810Sstevel@tonic-gate int
pm_scan_stop_walk(dev_info_t * dip,void * arg)13820Sstevel@tonic-gate pm_scan_stop_walk(dev_info_t *dip, void *arg)
13830Sstevel@tonic-gate {
13840Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
13850Sstevel@tonic-gate
13860Sstevel@tonic-gate if (!PM_GET_PM_SCAN(dip))
13870Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
13880Sstevel@tonic-gate ASSERT(!PM_ISBC(dip));
13890Sstevel@tonic-gate pm_scan_stop(dip);
13900Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
13910Sstevel@tonic-gate }
13920Sstevel@tonic-gate
13930Sstevel@tonic-gate /*
13940Sstevel@tonic-gate * Converts a power level value to its index
13950Sstevel@tonic-gate */
13960Sstevel@tonic-gate static int
power_val_to_index(pm_component_t * cp,int val)13970Sstevel@tonic-gate power_val_to_index(pm_component_t *cp, int val)
13980Sstevel@tonic-gate {
13990Sstevel@tonic-gate int limit, i, *ip;
14000Sstevel@tonic-gate
14010Sstevel@tonic-gate ASSERT(val != PM_LEVEL_UPONLY && val != PM_LEVEL_DOWNONLY &&
14020Sstevel@tonic-gate val != PM_LEVEL_EXACT);
14030Sstevel@tonic-gate /* convert power value into index (i) */
14040Sstevel@tonic-gate limit = cp->pmc_comp.pmc_numlevels;
14050Sstevel@tonic-gate ip = cp->pmc_comp.pmc_lvals;
14060Sstevel@tonic-gate for (i = 0; i < limit; i++)
14070Sstevel@tonic-gate if (val == *ip++)
14080Sstevel@tonic-gate return (i);
14090Sstevel@tonic-gate return (-1);
14100Sstevel@tonic-gate }
14110Sstevel@tonic-gate
14120Sstevel@tonic-gate /*
14130Sstevel@tonic-gate * Converts a numeric power level to a printable string
14140Sstevel@tonic-gate */
14150Sstevel@tonic-gate static char *
power_val_to_string(pm_component_t * cp,int val)14160Sstevel@tonic-gate power_val_to_string(pm_component_t *cp, int val)
14170Sstevel@tonic-gate {
14180Sstevel@tonic-gate int index;
14190Sstevel@tonic-gate
14200Sstevel@tonic-gate if (val == PM_LEVEL_UPONLY)
14210Sstevel@tonic-gate return ("<UPONLY>");
14220Sstevel@tonic-gate
14230Sstevel@tonic-gate if (val == PM_LEVEL_UNKNOWN ||
14240Sstevel@tonic-gate (index = power_val_to_index(cp, val)) == -1)
14250Sstevel@tonic-gate return ("<LEVEL_UNKNOWN>");
14260Sstevel@tonic-gate
14270Sstevel@tonic-gate return (cp->pmc_comp.pmc_lnames[index]);
14280Sstevel@tonic-gate }
14290Sstevel@tonic-gate
14300Sstevel@tonic-gate /*
14310Sstevel@tonic-gate * Return true if this node has been claimed by a ppm.
14320Sstevel@tonic-gate */
14330Sstevel@tonic-gate static int
pm_ppm_claimed(dev_info_t * dip)14340Sstevel@tonic-gate pm_ppm_claimed(dev_info_t *dip)
14350Sstevel@tonic-gate {
14360Sstevel@tonic-gate return (PPM(dip) != NULL);
14370Sstevel@tonic-gate }
14380Sstevel@tonic-gate
14390Sstevel@tonic-gate /*
14400Sstevel@tonic-gate * A node which was voluntarily power managed has just used up its "free cycle"
14410Sstevel@tonic-gate * and need is volpmd field cleared, and the same done to all its descendents
14420Sstevel@tonic-gate */
14430Sstevel@tonic-gate static void
pm_clear_volpm_dip(dev_info_t * dip)14440Sstevel@tonic-gate pm_clear_volpm_dip(dev_info_t *dip)
14450Sstevel@tonic-gate {
14460Sstevel@tonic-gate PMD_FUNC(pmf, "clear_volpm_dip")
14470Sstevel@tonic-gate
14480Sstevel@tonic-gate if (dip == NULL)
14490Sstevel@tonic-gate return;
14500Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm from %s@%s(%s#%d)\n", pmf,
14510Sstevel@tonic-gate PM_DEVICE(dip)))
14520Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0;
14530Sstevel@tonic-gate for (dip = ddi_get_child(dip); dip; dip = ddi_get_next_sibling(dip)) {
14540Sstevel@tonic-gate pm_clear_volpm_dip(dip);
14550Sstevel@tonic-gate }
14560Sstevel@tonic-gate }
14570Sstevel@tonic-gate
14580Sstevel@tonic-gate /*
14590Sstevel@tonic-gate * A node which was voluntarily power managed has used up the "free cycles"
14600Sstevel@tonic-gate * for the subtree that it is the root of. Scan through the list of detached
14610Sstevel@tonic-gate * nodes and adjust the counts of any that are descendents of the node.
14620Sstevel@tonic-gate */
14630Sstevel@tonic-gate static void
pm_clear_volpm_list(dev_info_t * dip)14640Sstevel@tonic-gate pm_clear_volpm_list(dev_info_t *dip)
14650Sstevel@tonic-gate {
14660Sstevel@tonic-gate PMD_FUNC(pmf, "clear_volpm_list")
14670Sstevel@tonic-gate char *pathbuf;
14680Sstevel@tonic-gate size_t len;
14690Sstevel@tonic-gate pm_noinvol_t *ip;
14700Sstevel@tonic-gate
14710Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
14720Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
14730Sstevel@tonic-gate len = strlen(pathbuf);
14740Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm list %s\n", pmf, pathbuf))
14750Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER);
14760Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
14770Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm: ni_path %s\n", pmf,
14780Sstevel@tonic-gate ip->ni_path))
14790Sstevel@tonic-gate if (strncmp(pathbuf, ip->ni_path, len) == 0 &&
14800Sstevel@tonic-gate ip->ni_path[len] == '/') {
14810Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: clear volpm: %s\n", pmf,
14820Sstevel@tonic-gate ip->ni_path))
14830Sstevel@tonic-gate ip->ni_volpmd = 0;
14840Sstevel@tonic-gate ip->ni_wasvolpmd = 0;
14850Sstevel@tonic-gate }
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
14880Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
14890Sstevel@tonic-gate }
14900Sstevel@tonic-gate
14910Sstevel@tonic-gate /*
14920Sstevel@tonic-gate * Powers a device, suspending or resuming the driver if it is a backward
14930Sstevel@tonic-gate * compatible device, calling into ppm to change power level.
14940Sstevel@tonic-gate * Called with the component's power lock held.
14950Sstevel@tonic-gate */
14960Sstevel@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)14970Sstevel@tonic-gate power_dev(dev_info_t *dip, int comp, int level, int old_level,
14980Sstevel@tonic-gate pm_canblock_t canblock, pm_ppm_devlist_t **devlist)
14990Sstevel@tonic-gate {
15000Sstevel@tonic-gate PMD_FUNC(pmf, "power_dev")
15010Sstevel@tonic-gate power_req_t power_req;
15020Sstevel@tonic-gate int power_op_ret; /* DDI_SUCCESS or DDI_FAILURE */
15030Sstevel@tonic-gate int resume_needed = 0;
15040Sstevel@tonic-gate int suspended = 0;
15050Sstevel@tonic-gate int result;
15063839Skchow #ifdef PMDDEBUG
15070Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, comp);
15083839Skchow #endif
15090Sstevel@tonic-gate int bc = PM_ISBC(dip);
15100Sstevel@tonic-gate int pm_all_components_off(dev_info_t *);
15110Sstevel@tonic-gate int clearvolpmd = 0;
15120Sstevel@tonic-gate char pathbuf[MAXNAMELEN];
15133839Skchow #ifdef PMDDEBUG
15140Sstevel@tonic-gate char *ppmname, *ppmaddr;
15150Sstevel@tonic-gate #endif
15160Sstevel@tonic-gate /*
15170Sstevel@tonic-gate * If this is comp 0 of a backwards compat device and we are
15180Sstevel@tonic-gate * going to take the power away, we need to detach it with
15190Sstevel@tonic-gate * DDI_PM_SUSPEND command.
15200Sstevel@tonic-gate */
15210Sstevel@tonic-gate if (bc && comp == 0 && POWERING_OFF(old_level, level)) {
15220Sstevel@tonic-gate if (devi_detach(dip, DDI_PM_SUSPEND) != DDI_SUCCESS) {
15230Sstevel@tonic-gate /* We could not suspend before turning cmpt zero off */
15240Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: could not suspend %s@%s(%s#%d)\n",
15250Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
15260Sstevel@tonic-gate return (DDI_FAILURE);
15270Sstevel@tonic-gate } else {
15280Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_SUSPENDED;
15290Sstevel@tonic-gate suspended++;
15300Sstevel@tonic-gate }
15310Sstevel@tonic-gate }
15320Sstevel@tonic-gate power_req.request_type = PMR_PPM_SET_POWER;
15330Sstevel@tonic-gate power_req.req.ppm_set_power_req.who = dip;
15340Sstevel@tonic-gate power_req.req.ppm_set_power_req.cmpt = comp;
15350Sstevel@tonic-gate power_req.req.ppm_set_power_req.old_level = old_level;
15360Sstevel@tonic-gate power_req.req.ppm_set_power_req.new_level = level;
15370Sstevel@tonic-gate power_req.req.ppm_set_power_req.canblock = canblock;
15380Sstevel@tonic-gate power_req.req.ppm_set_power_req.cookie = NULL;
15393839Skchow #ifdef PMDDEBUG
15400Sstevel@tonic-gate if (pm_ppm_claimed(dip)) {
15410Sstevel@tonic-gate ppmname = PM_NAME(PPM(dip));
15420Sstevel@tonic-gate ppmaddr = PM_ADDR(PPM(dip));
15430Sstevel@tonic-gate
15440Sstevel@tonic-gate } else {
15450Sstevel@tonic-gate ppmname = "noppm";
15460Sstevel@tonic-gate ppmaddr = "0";
15470Sstevel@tonic-gate }
15480Sstevel@tonic-gate PMD(PMD_PPM, ("%s: %s@%s(%s#%d):%s[%d] %s (%d) -> %s (%d) via %s@%s\n",
15490Sstevel@tonic-gate pmf, PM_DEVICE(dip), cp->pmc_comp.pmc_name, comp,
15500Sstevel@tonic-gate power_val_to_string(cp, old_level), old_level,
15510Sstevel@tonic-gate power_val_to_string(cp, level), level, ppmname, ppmaddr))
15520Sstevel@tonic-gate #endif
15530Sstevel@tonic-gate /*
15540Sstevel@tonic-gate * If non-bc noinvolpm device is turning first comp on, or noinvolpm
15550Sstevel@tonic-gate * bc device comp 0 is powering on, then we count it as a power cycle
15560Sstevel@tonic-gate * against its voluntary count.
15570Sstevel@tonic-gate */
15580Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd &&
15590Sstevel@tonic-gate (!bc && pm_all_components_off(dip) && level != 0) ||
15600Sstevel@tonic-gate (bc && comp == 0 && POWERING_ON(old_level, level)))
15610Sstevel@tonic-gate clearvolpmd = 1;
15620Sstevel@tonic-gate if ((power_op_ret = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
15630Sstevel@tonic-gate &power_req, &result)) == DDI_SUCCESS) {
15640Sstevel@tonic-gate /*
15650Sstevel@tonic-gate * Now do involuntary pm accounting; If we've just cycled power
15660Sstevel@tonic-gate * on a voluntarily pm'd node, and by inference on its entire
15670Sstevel@tonic-gate * subtree, we need to set the subtree (including those nodes
15680Sstevel@tonic-gate * already detached) volpmd counts to 0, and subtract out the
15690Sstevel@tonic-gate * value of the current node's volpmd count from the ancestors
15700Sstevel@tonic-gate */
15710Sstevel@tonic-gate if (clearvolpmd) {
15720Sstevel@tonic-gate int volpmd = DEVI(dip)->devi_pm_volpmd;
15730Sstevel@tonic-gate pm_clear_volpm_dip(dip);
15740Sstevel@tonic-gate pm_clear_volpm_list(dip);
15750Sstevel@tonic-gate if (volpmd) {
15760Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
15770Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_POWER,
15780Sstevel@tonic-gate volpmd, 0, pathbuf, dip);
15790Sstevel@tonic-gate }
15800Sstevel@tonic-gate }
15810Sstevel@tonic-gate } else {
15820Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't set comp %d (%s) of %s@%s(%s#%d) "
15830Sstevel@tonic-gate "to level %d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name,
15840Sstevel@tonic-gate PM_DEVICE(dip), level, power_val_to_string(cp, level)))
15850Sstevel@tonic-gate }
15860Sstevel@tonic-gate /*
15870Sstevel@tonic-gate * If some other devices were also powered up (e.g. other cpus in
15880Sstevel@tonic-gate * the same domain) return a pointer to that list
15890Sstevel@tonic-gate */
15900Sstevel@tonic-gate if (devlist) {
15910Sstevel@tonic-gate *devlist = (pm_ppm_devlist_t *)
15920Sstevel@tonic-gate power_req.req.ppm_set_power_req.cookie;
15930Sstevel@tonic-gate }
15940Sstevel@tonic-gate /*
15950Sstevel@tonic-gate * We will have to resume the device if the device is backwards compat
15960Sstevel@tonic-gate * device and either of the following is true:
15970Sstevel@tonic-gate * -This is comp 0 and we have successfully powered it up
15980Sstevel@tonic-gate * -This is comp 0 and we have failed to power it down. Resume is
15990Sstevel@tonic-gate * needed because we have suspended it above
16000Sstevel@tonic-gate */
16010Sstevel@tonic-gate
16020Sstevel@tonic-gate if (bc && comp == 0) {
16030Sstevel@tonic-gate ASSERT(PM_ISDIRECT(dip) || DEVI_IS_DETACHING(dip));
16040Sstevel@tonic-gate if (power_op_ret == DDI_SUCCESS) {
16050Sstevel@tonic-gate if (POWERING_ON(old_level, level)) {
16060Sstevel@tonic-gate /*
16070Sstevel@tonic-gate * It must be either suspended or resumed
16080Sstevel@tonic-gate * via pm_power_has_changed path
16090Sstevel@tonic-gate */
16100Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags &
16110Sstevel@tonic-gate PMC_SUSPENDED) ||
16120Sstevel@tonic-gate (PM_CP(dip, comp)->pmc_flags &
16130Sstevel@tonic-gate PM_PHC_WHILE_SET_POWER));
16140Sstevel@tonic-gate
16150Sstevel@tonic-gate resume_needed = suspended;
16160Sstevel@tonic-gate }
16170Sstevel@tonic-gate } else {
16180Sstevel@tonic-gate if (POWERING_OFF(old_level, level)) {
16190Sstevel@tonic-gate /*
16200Sstevel@tonic-gate * It must be either suspended or resumed
16210Sstevel@tonic-gate * via pm_power_has_changed path
16220Sstevel@tonic-gate */
16230Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags &
16240Sstevel@tonic-gate PMC_SUSPENDED) ||
16250Sstevel@tonic-gate (PM_CP(dip, comp)->pmc_flags &
16260Sstevel@tonic-gate PM_PHC_WHILE_SET_POWER));
16270Sstevel@tonic-gate
16280Sstevel@tonic-gate resume_needed = suspended;
16290Sstevel@tonic-gate }
16300Sstevel@tonic-gate }
16310Sstevel@tonic-gate }
16320Sstevel@tonic-gate if (resume_needed) {
16330Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED);
16340Sstevel@tonic-gate /* ppm is not interested in DDI_PM_RESUME */
16350Sstevel@tonic-gate if ((power_op_ret = devi_attach(dip, DDI_PM_RESUME)) ==
16360Sstevel@tonic-gate DDI_SUCCESS) {
16370Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED;
16380Sstevel@tonic-gate } else
16390Sstevel@tonic-gate cmn_err(CE_WARN, "!pm: Can't resume %s@%s(%s#%d)",
16400Sstevel@tonic-gate PM_DEVICE(dip));
16410Sstevel@tonic-gate }
16420Sstevel@tonic-gate return (power_op_ret);
16430Sstevel@tonic-gate }
16440Sstevel@tonic-gate
16450Sstevel@tonic-gate /*
16460Sstevel@tonic-gate * Return true if we are the owner or a borrower of the devi lock. See
16470Sstevel@tonic-gate * pm_lock_power_single() about borrowing the lock.
16480Sstevel@tonic-gate */
16490Sstevel@tonic-gate static int
pm_devi_lock_held(dev_info_t * dip)16500Sstevel@tonic-gate pm_devi_lock_held(dev_info_t *dip)
16510Sstevel@tonic-gate {
16520Sstevel@tonic-gate lock_loan_t *cur;
16530Sstevel@tonic-gate
16540Sstevel@tonic-gate if (DEVI_BUSY_OWNED(dip))
16554780Smh27603 return (1);
16560Sstevel@tonic-gate
16570Sstevel@tonic-gate /* return false if no locks borrowed */
16580Sstevel@tonic-gate if (lock_loan_head.pmlk_next == NULL)
16590Sstevel@tonic-gate return (0);
16600Sstevel@tonic-gate
16610Sstevel@tonic-gate mutex_enter(&pm_loan_lock);
16620Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */
16630Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
16640Sstevel@tonic-gate if (cur->pmlk_borrower == curthread)
16650Sstevel@tonic-gate break;
16660Sstevel@tonic-gate mutex_exit(&pm_loan_lock);
16670Sstevel@tonic-gate
16680Sstevel@tonic-gate return (cur != NULL && cur->pmlk_lender == DEVI(dip)->devi_busy_thread);
16690Sstevel@tonic-gate }
16700Sstevel@tonic-gate
16710Sstevel@tonic-gate /*
16720Sstevel@tonic-gate * pm_set_power: adjusts power level of device. Assumes device is power
16730Sstevel@tonic-gate * manageable & component exists.
16740Sstevel@tonic-gate *
16750Sstevel@tonic-gate * Cases which require us to bring up devices we keep up ("wekeepups") for
16760Sstevel@tonic-gate * backwards compatible devices:
16770Sstevel@tonic-gate * component 0 is off and we're bringing it up from 0
16780Sstevel@tonic-gate * bring up wekeepup first
16790Sstevel@tonic-gate * and recursively when component 0 is off and we bring some other
16800Sstevel@tonic-gate * component up from 0
16810Sstevel@tonic-gate * For devices which are not backward compatible, our dependency notion is much
16820Sstevel@tonic-gate * simpler. Unless all components are off, then wekeeps must be on.
16830Sstevel@tonic-gate * We don't treat component 0 differently.
16840Sstevel@tonic-gate * Canblock tells how to deal with a direct pm'd device.
16850Sstevel@tonic-gate * Scan arg tells us if we were called from scan, in which case we don't need
16860Sstevel@tonic-gate * to go back to the root node and walk down to change power.
16870Sstevel@tonic-gate */
16880Sstevel@tonic-gate int
pm_set_power(dev_info_t * dip,int comp,int level,int direction,pm_canblock_t canblock,int scan,int * retp)16890Sstevel@tonic-gate pm_set_power(dev_info_t *dip, int comp, int level, int direction,
16900Sstevel@tonic-gate pm_canblock_t canblock, int scan, int *retp)
16910Sstevel@tonic-gate {
16920Sstevel@tonic-gate PMD_FUNC(pmf, "set_power")
16930Sstevel@tonic-gate char *pathbuf;
16940Sstevel@tonic-gate pm_bp_child_pwrchg_t bpc;
16950Sstevel@tonic-gate pm_sp_misc_t pspm;
16960Sstevel@tonic-gate int ret = DDI_SUCCESS;
16970Sstevel@tonic-gate int unused = DDI_SUCCESS;
16980Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
16990Sstevel@tonic-gate
17000Sstevel@tonic-gate #ifdef DEBUG
17010Sstevel@tonic-gate int diverted = 0;
17020Sstevel@tonic-gate
17030Sstevel@tonic-gate /*
17040Sstevel@tonic-gate * This prevents operations on the console from calling prom_printf and
17050Sstevel@tonic-gate * either deadlocking or bringing up the console because of debug
17060Sstevel@tonic-gate * output
17070Sstevel@tonic-gate */
17080Sstevel@tonic-gate if (dip == cfb_dip) {
17090Sstevel@tonic-gate diverted++;
17100Sstevel@tonic-gate mutex_enter(&pm_debug_lock);
17110Sstevel@tonic-gate pm_divertdebug++;
17120Sstevel@tonic-gate mutex_exit(&pm_debug_lock);
17130Sstevel@tonic-gate }
17140Sstevel@tonic-gate #endif
17150Sstevel@tonic-gate ASSERT(direction == PM_LEVEL_UPONLY || direction == PM_LEVEL_DOWNONLY ||
17160Sstevel@tonic-gate direction == PM_LEVEL_EXACT);
17170Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d, dir=%s, new=%d\n",
17180Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, pm_decode_direction(direction), level))
17190Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
17200Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
17210Sstevel@tonic-gate bpc.bpc_dip = dip;
17220Sstevel@tonic-gate bpc.bpc_path = pathbuf;
17230Sstevel@tonic-gate bpc.bpc_comp = comp;
17240Sstevel@tonic-gate bpc.bpc_olevel = PM_CURPOWER(dip, comp);
17250Sstevel@tonic-gate bpc.bpc_nlevel = level;
17260Sstevel@tonic-gate pspm.pspm_direction = direction;
17270Sstevel@tonic-gate pspm.pspm_errnop = retp;
17280Sstevel@tonic-gate pspm.pspm_canblock = canblock;
17290Sstevel@tonic-gate pspm.pspm_scan = scan;
17300Sstevel@tonic-gate bpc.bpc_private = &pspm;
17310Sstevel@tonic-gate
17320Sstevel@tonic-gate /*
17330Sstevel@tonic-gate * If a config operation is being done (we've locked the parent) or
17340Sstevel@tonic-gate * we already hold the power lock (we've locked the node)
17350Sstevel@tonic-gate * then we can operate directly on the node because we have already
17360Sstevel@tonic-gate * brought up all the ancestors, otherwise, we have to go back to the
17370Sstevel@tonic-gate * top of the tree.
17380Sstevel@tonic-gate */
17390Sstevel@tonic-gate if (pm_devi_lock_held(pdip) || pm_devi_lock_held(dip))
17400Sstevel@tonic-gate ret = pm_busop_set_power(dip, NULL, BUS_POWER_CHILD_PWRCHG,
17410Sstevel@tonic-gate (void *)&bpc, (void *)&unused);
17420Sstevel@tonic-gate else
17430Sstevel@tonic-gate ret = pm_busop_bus_power(ddi_root_node(), NULL,
17440Sstevel@tonic-gate BUS_POWER_CHILD_PWRCHG, (void *)&bpc, (void *)&unused);
17450Sstevel@tonic-gate #ifdef DEBUG
17460Sstevel@tonic-gate if (ret != DDI_SUCCESS || *retp != DDI_SUCCESS) {
17470Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) can't change power, ret=%d, "
17480Sstevel@tonic-gate "errno=%d\n", pmf, PM_DEVICE(dip), ret, *retp))
17490Sstevel@tonic-gate }
17500Sstevel@tonic-gate if (diverted) {
17510Sstevel@tonic-gate mutex_enter(&pm_debug_lock);
17520Sstevel@tonic-gate pm_divertdebug--;
17530Sstevel@tonic-gate mutex_exit(&pm_debug_lock);
17540Sstevel@tonic-gate }
17550Sstevel@tonic-gate #endif
17560Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
17570Sstevel@tonic-gate return (ret);
17580Sstevel@tonic-gate }
17590Sstevel@tonic-gate
17602275Scth /*
17612275Scth * If holddip is set, then if a dip is found we return with the node held.
17622275Scth *
17632275Scth * This code uses the same locking scheme as e_ddi_hold_devi_by_path
17642275Scth * (resolve_pathname), but it does not drive attach.
17650Sstevel@tonic-gate */
17660Sstevel@tonic-gate dev_info_t *
pm_name_to_dip(char * pathname,int holddip)17670Sstevel@tonic-gate pm_name_to_dip(char *pathname, int holddip)
17680Sstevel@tonic-gate {
17692275Scth struct pathname pn;
17702275Scth char *component;
17712275Scth dev_info_t *parent, *child;
17722275Scth int circ;
17732275Scth
17742275Scth if ((pathname == NULL) || (*pathname != '/'))
17752275Scth return (NULL);
17762275Scth
17772275Scth /* setup pathname and allocate component */
17782275Scth if (pn_get(pathname, UIO_SYSSPACE, &pn))
17790Sstevel@tonic-gate return (NULL);
17802275Scth component = kmem_alloc(MAXNAMELEN, KM_SLEEP);
17812275Scth
17822275Scth /* start at top, process '/' component */
17832275Scth parent = child = ddi_root_node();
17842275Scth ndi_hold_devi(parent);
17852275Scth pn_skipslash(&pn);
17862275Scth ASSERT(i_ddi_devi_attached(parent));
17872275Scth
17882275Scth /* process components of pathname */
17892275Scth while (pn_pathleft(&pn)) {
17902275Scth (void) pn_getcomponent(&pn, component);
17912275Scth
17922275Scth /* enter parent and search for component child */
17932275Scth ndi_devi_enter(parent, &circ);
17942275Scth child = ndi_devi_findchild(parent, component);
17952275Scth if ((child == NULL) || !i_ddi_devi_attached(child)) {
17962275Scth child = NULL;
17972275Scth ndi_devi_exit(parent, circ);
17982275Scth ndi_rele_devi(parent);
17992275Scth goto out;
18002275Scth }
18012275Scth
18022275Scth /* attached child found, hold child and release parent */
18032275Scth ndi_hold_devi(child);
18042275Scth ndi_devi_exit(parent, circ);
18052275Scth ndi_rele_devi(parent);
18062275Scth
18072275Scth /* child becomes parent, and process next component */
18082275Scth parent = child;
18092275Scth pn_skipslash(&pn);
18102275Scth
18112275Scth /* loop with active ndi_devi_hold of child->parent */
18122275Scth }
18132275Scth
18142275Scth out:
18152275Scth pn_free(&pn);
18162275Scth kmem_free(component, MAXNAMELEN);
18172275Scth
18182275Scth /* if we are not asked to return with hold, drop current hold */
18192275Scth if (child && !holddip)
18202275Scth ndi_rele_devi(child);
18212275Scth return (child);
18220Sstevel@tonic-gate }
18230Sstevel@tonic-gate
18240Sstevel@tonic-gate /*
18250Sstevel@tonic-gate * Search for a dependency and mark it unsatisfied
18260Sstevel@tonic-gate */
18270Sstevel@tonic-gate static void
pm_unsatisfy(char * keeper,char * kept)18280Sstevel@tonic-gate pm_unsatisfy(char *keeper, char *kept)
18290Sstevel@tonic-gate {
18300Sstevel@tonic-gate PMD_FUNC(pmf, "unsatisfy")
18310Sstevel@tonic-gate pm_pdr_t *dp;
18320Sstevel@tonic-gate
18330Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf, keeper, kept))
18340Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
18350Sstevel@tonic-gate if (!dp->pdr_isprop) {
18360Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) == 0 &&
18370Sstevel@tonic-gate (dp->pdr_kept_count > 0) &&
18380Sstevel@tonic-gate strcmp(dp->pdr_kept_paths[0], kept) == 0) {
18390Sstevel@tonic-gate if (dp->pdr_satisfied) {
18400Sstevel@tonic-gate dp->pdr_satisfied = 0;
18410Sstevel@tonic-gate pm_unresolved_deps++;
18420Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: clear satisfied, "
18430Sstevel@tonic-gate "pm_unresolved_deps now %d\n", pmf,
18440Sstevel@tonic-gate pm_unresolved_deps))
18450Sstevel@tonic-gate }
18460Sstevel@tonic-gate }
18470Sstevel@tonic-gate }
18480Sstevel@tonic-gate }
18490Sstevel@tonic-gate }
18500Sstevel@tonic-gate
18510Sstevel@tonic-gate /*
18520Sstevel@tonic-gate * Device dip is being un power managed, it keeps up count other devices.
18530Sstevel@tonic-gate * We need to release any hold we have on the kept devices, and also
18540Sstevel@tonic-gate * mark the dependency no longer satisfied.
18550Sstevel@tonic-gate */
18560Sstevel@tonic-gate static void
pm_unkeeps(int count,char * keeper,char ** keptpaths,int pwr)18570Sstevel@tonic-gate pm_unkeeps(int count, char *keeper, char **keptpaths, int pwr)
18580Sstevel@tonic-gate {
18590Sstevel@tonic-gate PMD_FUNC(pmf, "unkeeps")
18600Sstevel@tonic-gate int i, j;
18610Sstevel@tonic-gate dev_info_t *kept;
18620Sstevel@tonic-gate dev_info_t *dip;
18630Sstevel@tonic-gate struct pm_component *cp;
18640Sstevel@tonic-gate int keeper_on = 0, circ;
18650Sstevel@tonic-gate
18660Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: count=%d, keeper=%s, keptpaths=%p\n", pmf, count,
18670Sstevel@tonic-gate keeper, (void *)keptpaths))
18680Sstevel@tonic-gate /*
18690Sstevel@tonic-gate * Try to grab keeper. Keeper may have gone away by now,
18700Sstevel@tonic-gate * in this case, used the passed in value pwr
18710Sstevel@tonic-gate */
18720Sstevel@tonic-gate dip = pm_name_to_dip(keeper, 1);
18730Sstevel@tonic-gate for (i = 0; i < count; i++) {
18740Sstevel@tonic-gate /* Release power hold */
18750Sstevel@tonic-gate kept = pm_name_to_dip(keptpaths[i], 1);
18760Sstevel@tonic-gate if (kept) {
18770Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)[%d]\n", pmf,
18780Sstevel@tonic-gate PM_DEVICE(kept), i))
18790Sstevel@tonic-gate /*
18800Sstevel@tonic-gate * We need to check if we skipped a bringup here
18810Sstevel@tonic-gate * because we could have failed the bringup
18820Sstevel@tonic-gate * (ie DIRECT PM device) and have
18830Sstevel@tonic-gate * not increment the count.
18840Sstevel@tonic-gate */
18850Sstevel@tonic-gate if ((dip != NULL) && (PM_GET_PM_INFO(dip) != NULL)) {
18860Sstevel@tonic-gate keeper_on = 0;
18870Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
18880Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(dip); j++) {
18894780Smh27603 cp = &DEVI(dip)->devi_pm_components[j];
18900Sstevel@tonic-gate if (cur_power(cp)) {
18910Sstevel@tonic-gate keeper_on++;
18920Sstevel@tonic-gate break;
18930Sstevel@tonic-gate }
18940Sstevel@tonic-gate }
18950Sstevel@tonic-gate if (keeper_on && (PM_SKBU(kept) == 0)) {
18960Sstevel@tonic-gate pm_rele_power(kept);
18970Sstevel@tonic-gate DEVI(kept)->devi_pm_flags
18984780Smh27603 &= ~PMC_SKIP_BRINGUP;
18990Sstevel@tonic-gate }
19000Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
19010Sstevel@tonic-gate } else if (pwr) {
19020Sstevel@tonic-gate if (PM_SKBU(kept) == 0) {
19030Sstevel@tonic-gate pm_rele_power(kept);
19040Sstevel@tonic-gate DEVI(kept)->devi_pm_flags
19050Sstevel@tonic-gate &= ~PMC_SKIP_BRINGUP;
19060Sstevel@tonic-gate }
19070Sstevel@tonic-gate }
19080Sstevel@tonic-gate ddi_release_devi(kept);
19090Sstevel@tonic-gate }
19100Sstevel@tonic-gate /*
19110Sstevel@tonic-gate * mark this dependency not satisfied
19120Sstevel@tonic-gate */
19130Sstevel@tonic-gate pm_unsatisfy(keeper, keptpaths[i]);
19140Sstevel@tonic-gate }
19150Sstevel@tonic-gate if (dip)
19160Sstevel@tonic-gate ddi_release_devi(dip);
19170Sstevel@tonic-gate }
19180Sstevel@tonic-gate
19190Sstevel@tonic-gate /*
19200Sstevel@tonic-gate * Device kept is being un power managed, it is kept up by keeper.
19210Sstevel@tonic-gate * We need to mark the dependency no longer satisfied.
19220Sstevel@tonic-gate */
19230Sstevel@tonic-gate static void
pm_unkepts(char * kept,char * keeper)19240Sstevel@tonic-gate pm_unkepts(char *kept, char *keeper)
19250Sstevel@tonic-gate {
19260Sstevel@tonic-gate PMD_FUNC(pmf, "unkepts")
19270Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: kept=%s, keeper=%s\n", pmf, kept, keeper))
19280Sstevel@tonic-gate ASSERT(keeper != NULL);
19290Sstevel@tonic-gate /*
19300Sstevel@tonic-gate * mark this dependency not satisfied
19310Sstevel@tonic-gate */
19320Sstevel@tonic-gate pm_unsatisfy(keeper, kept);
19330Sstevel@tonic-gate }
19340Sstevel@tonic-gate
19350Sstevel@tonic-gate /*
19360Sstevel@tonic-gate * Removes dependency information and hold on the kepts, if the path is a
19370Sstevel@tonic-gate * path of a keeper.
19380Sstevel@tonic-gate */
19390Sstevel@tonic-gate static void
pm_free_keeper(char * path,int pwr)19400Sstevel@tonic-gate pm_free_keeper(char *path, int pwr)
19410Sstevel@tonic-gate {
19420Sstevel@tonic-gate pm_pdr_t *dp;
19430Sstevel@tonic-gate int i;
19440Sstevel@tonic-gate size_t length;
19450Sstevel@tonic-gate
19460Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
19470Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, path) != 0)
19480Sstevel@tonic-gate continue;
19490Sstevel@tonic-gate /*
19500Sstevel@tonic-gate * Remove all our kept holds and the dependency records,
19510Sstevel@tonic-gate * then free up the kept lists.
19520Sstevel@tonic-gate */
19530Sstevel@tonic-gate pm_unkeeps(dp->pdr_kept_count, path, dp->pdr_kept_paths, pwr);
19540Sstevel@tonic-gate if (dp->pdr_kept_count) {
19550Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) {
19560Sstevel@tonic-gate length = strlen(dp->pdr_kept_paths[i]);
19570Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths[i], length + 1);
19580Sstevel@tonic-gate }
19590Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths,
19600Sstevel@tonic-gate dp->pdr_kept_count * sizeof (char **));
19610Sstevel@tonic-gate dp->pdr_kept_paths = NULL;
19620Sstevel@tonic-gate dp->pdr_kept_count = 0;
19630Sstevel@tonic-gate }
19640Sstevel@tonic-gate }
19650Sstevel@tonic-gate }
19660Sstevel@tonic-gate
19670Sstevel@tonic-gate /*
19680Sstevel@tonic-gate * Removes the device represented by path from the list of kepts, if the
19690Sstevel@tonic-gate * path is a path of a kept
19700Sstevel@tonic-gate */
19710Sstevel@tonic-gate static void
pm_free_kept(char * path)19720Sstevel@tonic-gate pm_free_kept(char *path)
19730Sstevel@tonic-gate {
19740Sstevel@tonic-gate pm_pdr_t *dp;
19750Sstevel@tonic-gate int i;
19760Sstevel@tonic-gate int j, count;
19770Sstevel@tonic-gate size_t length;
19780Sstevel@tonic-gate char **paths;
19790Sstevel@tonic-gate
19800Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
19810Sstevel@tonic-gate if (dp->pdr_kept_count == 0)
19820Sstevel@tonic-gate continue;
19830Sstevel@tonic-gate count = dp->pdr_kept_count;
19840Sstevel@tonic-gate /* Remove this device from the kept path lists */
19850Sstevel@tonic-gate for (i = 0; i < count; i++) {
19860Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], path) == 0) {
19870Sstevel@tonic-gate pm_unkepts(path, dp->pdr_keeper);
19880Sstevel@tonic-gate length = strlen(dp->pdr_kept_paths[i]) + 1;
19890Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths[i], length);
19900Sstevel@tonic-gate dp->pdr_kept_paths[i] = NULL;
19910Sstevel@tonic-gate dp->pdr_kept_count--;
19920Sstevel@tonic-gate }
19930Sstevel@tonic-gate }
19940Sstevel@tonic-gate /* Compact the kept paths array */
19950Sstevel@tonic-gate if (dp->pdr_kept_count) {
19960Sstevel@tonic-gate length = dp->pdr_kept_count * sizeof (char **);
19970Sstevel@tonic-gate paths = kmem_zalloc(length, KM_SLEEP);
19980Sstevel@tonic-gate j = 0;
19990Sstevel@tonic-gate for (i = 0; i < count; i++) {
20000Sstevel@tonic-gate if (dp->pdr_kept_paths[i] != NULL) {
20010Sstevel@tonic-gate paths[j] = dp->pdr_kept_paths[i];
20020Sstevel@tonic-gate j++;
20030Sstevel@tonic-gate }
20040Sstevel@tonic-gate }
20050Sstevel@tonic-gate ASSERT(j == dp->pdr_kept_count);
20060Sstevel@tonic-gate }
20070Sstevel@tonic-gate /* Now free the old array and point to the new one */
20080Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths, count * sizeof (char **));
20090Sstevel@tonic-gate if (dp->pdr_kept_count)
20100Sstevel@tonic-gate dp->pdr_kept_paths = paths;
20110Sstevel@tonic-gate else
20120Sstevel@tonic-gate dp->pdr_kept_paths = NULL;
20130Sstevel@tonic-gate }
20140Sstevel@tonic-gate }
20150Sstevel@tonic-gate
20160Sstevel@tonic-gate /*
20170Sstevel@tonic-gate * Free the dependency information for a device.
20180Sstevel@tonic-gate */
20190Sstevel@tonic-gate void
pm_free_keeps(char * path,int pwr)20200Sstevel@tonic-gate pm_free_keeps(char *path, int pwr)
20210Sstevel@tonic-gate {
20220Sstevel@tonic-gate PMD_FUNC(pmf, "free_keeps")
20230Sstevel@tonic-gate
20240Sstevel@tonic-gate #ifdef DEBUG
20250Sstevel@tonic-gate int doprdeps = 0;
20260Sstevel@tonic-gate void prdeps(char *);
20270Sstevel@tonic-gate
20280Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s\n", pmf, path))
20290Sstevel@tonic-gate if (pm_debug & PMD_KEEPS) {
20300Sstevel@tonic-gate doprdeps = 1;
20310Sstevel@tonic-gate prdeps("pm_free_keeps before");
20320Sstevel@tonic-gate }
20330Sstevel@tonic-gate #endif
20340Sstevel@tonic-gate /*
20350Sstevel@tonic-gate * First assume we are a keeper and remove all our kepts.
20360Sstevel@tonic-gate */
20370Sstevel@tonic-gate pm_free_keeper(path, pwr);
20380Sstevel@tonic-gate /*
20390Sstevel@tonic-gate * Now assume we a kept device, and remove all our records.
20400Sstevel@tonic-gate */
20410Sstevel@tonic-gate pm_free_kept(path);
20420Sstevel@tonic-gate #ifdef DEBUG
20430Sstevel@tonic-gate if (doprdeps) {
20440Sstevel@tonic-gate prdeps("pm_free_keeps after");
20450Sstevel@tonic-gate }
20460Sstevel@tonic-gate #endif
20470Sstevel@tonic-gate }
20480Sstevel@tonic-gate
20490Sstevel@tonic-gate static int
pm_is_kept(char * path)20500Sstevel@tonic-gate pm_is_kept(char *path)
20510Sstevel@tonic-gate {
20520Sstevel@tonic-gate pm_pdr_t *dp;
20530Sstevel@tonic-gate int i;
20540Sstevel@tonic-gate
20550Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
20560Sstevel@tonic-gate if (dp->pdr_kept_count == 0)
20570Sstevel@tonic-gate continue;
20580Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) {
20590Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], path) == 0)
20600Sstevel@tonic-gate return (1);
20610Sstevel@tonic-gate }
20620Sstevel@tonic-gate }
20630Sstevel@tonic-gate return (0);
20640Sstevel@tonic-gate }
20650Sstevel@tonic-gate
20660Sstevel@tonic-gate static void
e_pm_hold_rele_power(dev_info_t * dip,int cnt)20670Sstevel@tonic-gate e_pm_hold_rele_power(dev_info_t *dip, int cnt)
20680Sstevel@tonic-gate {
20690Sstevel@tonic-gate PMD_FUNC(pmf, "hold_rele_power")
20700Sstevel@tonic-gate int circ;
20710Sstevel@tonic-gate
20722009Sdm120769 if ((dip == NULL) ||
20732009Sdm120769 (PM_GET_PM_INFO(dip) == NULL) || PM_ISBC(dip))
20740Sstevel@tonic-gate return;
20752155Scth
20760Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
20770Sstevel@tonic-gate ASSERT(cnt >= 0 && PM_KUC(dip) >= 0 || cnt < 0 && PM_KUC(dip) > 0);
20780Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: kidsupcnt for %s@%s(%s#%d) %d->%d\n", pmf,
20790Sstevel@tonic-gate PM_DEVICE(dip), PM_KUC(dip), (PM_KUC(dip) + cnt)))
20802155Scth
20810Sstevel@tonic-gate PM_KUC(dip) += cnt;
20822155Scth
20830Sstevel@tonic-gate ASSERT(PM_KUC(dip) >= 0);
20840Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
20852155Scth
20860Sstevel@tonic-gate if (cnt < 0 && PM_KUC(dip) == 0)
20870Sstevel@tonic-gate pm_rescan(dip);
20880Sstevel@tonic-gate }
20890Sstevel@tonic-gate
20900Sstevel@tonic-gate #define MAX_PPM_HANDLERS 4
20910Sstevel@tonic-gate
20920Sstevel@tonic-gate kmutex_t ppm_lock; /* in case we ever do multi-threaded startup */
20930Sstevel@tonic-gate
20940Sstevel@tonic-gate struct ppm_callbacks {
20950Sstevel@tonic-gate int (*ppmc_func)(dev_info_t *);
20960Sstevel@tonic-gate dev_info_t *ppmc_dip;
20970Sstevel@tonic-gate } ppm_callbacks[MAX_PPM_HANDLERS + 1];
20980Sstevel@tonic-gate
20990Sstevel@tonic-gate
21000Sstevel@tonic-gate /*
21010Sstevel@tonic-gate * This routine calls into all the registered ppms to notify them
21020Sstevel@tonic-gate * that either all components of power-managed devices are at their
21030Sstevel@tonic-gate * lowest levels or no longer all are at their lowest levels.
21040Sstevel@tonic-gate */
21050Sstevel@tonic-gate static void
pm_ppm_notify_all_lowest(dev_info_t * dip,int mode)21060Sstevel@tonic-gate pm_ppm_notify_all_lowest(dev_info_t *dip, int mode)
21070Sstevel@tonic-gate {
21080Sstevel@tonic-gate struct ppm_callbacks *ppmcp;
21090Sstevel@tonic-gate power_req_t power_req;
21100Sstevel@tonic-gate int result = 0;
21110Sstevel@tonic-gate
21120Sstevel@tonic-gate power_req.request_type = PMR_PPM_ALL_LOWEST;
21130Sstevel@tonic-gate power_req.req.ppm_all_lowest_req.mode = mode;
21140Sstevel@tonic-gate mutex_enter(&ppm_lock);
21150Sstevel@tonic-gate for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++)
21160Sstevel@tonic-gate (void) pm_ctlops((dev_info_t *)ppmcp->ppmc_dip, dip,
21170Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
21180Sstevel@tonic-gate mutex_exit(&ppm_lock);
21195295Srandyf if (mode == PM_ALL_LOWEST) {
21205295Srandyf if (autoS3_enabled) {
21215295Srandyf PMD(PMD_SX, ("pm_ppm_notify_all_lowest triggering "
21225295Srandyf "autos3\n"))
21235295Srandyf mutex_enter(&srn_clone_lock);
21245295Srandyf if (srn_signal) {
21255295Srandyf srn_inuse++;
21265295Srandyf PMD(PMD_SX, ("(*srn_signal)(AUTOSX, 3)\n"))
21275295Srandyf (*srn_signal)(SRN_TYPE_AUTOSX, 3);
21285295Srandyf srn_inuse--;
21295295Srandyf } else {
21305295Srandyf PMD(PMD_SX, ("srn_signal NULL\n"))
21315295Srandyf }
21325295Srandyf mutex_exit(&srn_clone_lock);
21335295Srandyf } else {
21345295Srandyf PMD(PMD_SX, ("pm_ppm_notify_all_lowest autos3 "
21355295Srandyf "disabled\n"));
21365295Srandyf }
21375295Srandyf }
21380Sstevel@tonic-gate }
21390Sstevel@tonic-gate
21400Sstevel@tonic-gate static void
pm_set_pm_info(dev_info_t * dip,void * value)21410Sstevel@tonic-gate pm_set_pm_info(dev_info_t *dip, void *value)
21420Sstevel@tonic-gate {
21430Sstevel@tonic-gate DEVI(dip)->devi_pm_info = value;
21440Sstevel@tonic-gate }
21450Sstevel@tonic-gate
21460Sstevel@tonic-gate pm_rsvp_t *pm_blocked_list;
21470Sstevel@tonic-gate
21480Sstevel@tonic-gate /*
21490Sstevel@tonic-gate * Look up an entry in the blocked list by dip and component
21500Sstevel@tonic-gate */
21510Sstevel@tonic-gate static pm_rsvp_t *
pm_rsvp_lookup(dev_info_t * dip,int comp)21520Sstevel@tonic-gate pm_rsvp_lookup(dev_info_t *dip, int comp)
21530Sstevel@tonic-gate {
21540Sstevel@tonic-gate pm_rsvp_t *p;
21550Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock));
21560Sstevel@tonic-gate for (p = pm_blocked_list; p; p = p->pr_next)
21570Sstevel@tonic-gate if (p->pr_dip == dip && p->pr_comp == comp) {
21580Sstevel@tonic-gate return (p);
21590Sstevel@tonic-gate }
21600Sstevel@tonic-gate return (NULL);
21610Sstevel@tonic-gate }
21620Sstevel@tonic-gate
21630Sstevel@tonic-gate /*
21640Sstevel@tonic-gate * Called when a device which is direct power managed (or the parent or
21650Sstevel@tonic-gate * dependent of such a device) changes power, or when a pm clone is closed
21660Sstevel@tonic-gate * that was direct power managing a device. This call results in pm_blocked()
21670Sstevel@tonic-gate * (below) returning.
21680Sstevel@tonic-gate */
21690Sstevel@tonic-gate void
pm_proceed(dev_info_t * dip,int cmd,int comp,int newlevel)21700Sstevel@tonic-gate pm_proceed(dev_info_t *dip, int cmd, int comp, int newlevel)
21710Sstevel@tonic-gate {
21720Sstevel@tonic-gate PMD_FUNC(pmf, "proceed")
21730Sstevel@tonic-gate pm_rsvp_t *found = NULL;
21740Sstevel@tonic-gate pm_rsvp_t *p;
21750Sstevel@tonic-gate
21760Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock);
21770Sstevel@tonic-gate switch (cmd) {
21780Sstevel@tonic-gate /*
21790Sstevel@tonic-gate * we're giving up control, let any pending op continue
21800Sstevel@tonic-gate */
21810Sstevel@tonic-gate case PMP_RELEASE:
21820Sstevel@tonic-gate for (p = pm_blocked_list; p; p = p->pr_next) {
21830Sstevel@tonic-gate if (dip == p->pr_dip) {
21840Sstevel@tonic-gate p->pr_retval = PMP_RELEASE;
21850Sstevel@tonic-gate PMD(PMD_DPM, ("%s: RELEASE %s@%s(%s#%d)\n",
21860Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
21870Sstevel@tonic-gate cv_signal(&p->pr_cv);
21880Sstevel@tonic-gate }
21890Sstevel@tonic-gate }
21900Sstevel@tonic-gate break;
21910Sstevel@tonic-gate
21920Sstevel@tonic-gate /*
21930Sstevel@tonic-gate * process has done PM_SET_CURRENT_POWER; let a matching request
21940Sstevel@tonic-gate * succeed and a non-matching request for the same device fail
21950Sstevel@tonic-gate */
21960Sstevel@tonic-gate case PMP_SETPOWER:
21970Sstevel@tonic-gate found = pm_rsvp_lookup(dip, comp);
21980Sstevel@tonic-gate if (!found) /* if driver not waiting */
21990Sstevel@tonic-gate break;
22000Sstevel@tonic-gate /*
22010Sstevel@tonic-gate * This cannot be pm_lower_power, since that can only happen
22020Sstevel@tonic-gate * during detach or probe
22030Sstevel@tonic-gate */
22040Sstevel@tonic-gate if (found->pr_newlevel <= newlevel) {
22050Sstevel@tonic-gate found->pr_retval = PMP_SUCCEED;
22060Sstevel@tonic-gate PMD(PMD_DPM, ("%s: SUCCEED %s@%s(%s#%d)\n", pmf,
22070Sstevel@tonic-gate PM_DEVICE(dip)))
22080Sstevel@tonic-gate } else {
22090Sstevel@tonic-gate found->pr_retval = PMP_FAIL;
22100Sstevel@tonic-gate PMD(PMD_DPM, ("%s: FAIL %s@%s(%s#%d)\n", pmf,
22110Sstevel@tonic-gate PM_DEVICE(dip)))
22120Sstevel@tonic-gate }
22130Sstevel@tonic-gate cv_signal(&found->pr_cv);
22140Sstevel@tonic-gate break;
22150Sstevel@tonic-gate
22160Sstevel@tonic-gate default:
22170Sstevel@tonic-gate panic("pm_proceed unknown cmd %d", cmd);
22180Sstevel@tonic-gate }
22190Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock);
22200Sstevel@tonic-gate }
22210Sstevel@tonic-gate
22220Sstevel@tonic-gate /*
22230Sstevel@tonic-gate * This routine dispatches new work to the dependency thread. Caller must
22240Sstevel@tonic-gate * be prepared to block for memory if necessary.
22250Sstevel@tonic-gate */
22260Sstevel@tonic-gate void
pm_dispatch_to_dep_thread(int cmd,char * keeper,char * kept,int wait,int * res,int cached_pwr)22270Sstevel@tonic-gate pm_dispatch_to_dep_thread(int cmd, char *keeper, char *kept, int wait,
22280Sstevel@tonic-gate int *res, int cached_pwr)
22290Sstevel@tonic-gate {
22300Sstevel@tonic-gate pm_dep_wk_t *new_work;
22310Sstevel@tonic-gate
22320Sstevel@tonic-gate new_work = kmem_zalloc(sizeof (pm_dep_wk_t), KM_SLEEP);
22330Sstevel@tonic-gate new_work->pdw_type = cmd;
22340Sstevel@tonic-gate new_work->pdw_wait = wait;
22350Sstevel@tonic-gate new_work->pdw_done = 0;
22360Sstevel@tonic-gate new_work->pdw_ret = 0;
22370Sstevel@tonic-gate new_work->pdw_pwr = cached_pwr;
22380Sstevel@tonic-gate cv_init(&new_work->pdw_cv, NULL, CV_DEFAULT, NULL);
22390Sstevel@tonic-gate if (keeper != NULL) {
22400Sstevel@tonic-gate new_work->pdw_keeper = kmem_zalloc(strlen(keeper) + 1,
22410Sstevel@tonic-gate KM_SLEEP);
22420Sstevel@tonic-gate (void) strcpy(new_work->pdw_keeper, keeper);
22430Sstevel@tonic-gate }
22440Sstevel@tonic-gate if (kept != NULL) {
22450Sstevel@tonic-gate new_work->pdw_kept = kmem_zalloc(strlen(kept) + 1, KM_SLEEP);
22460Sstevel@tonic-gate (void) strcpy(new_work->pdw_kept, kept);
22470Sstevel@tonic-gate }
22480Sstevel@tonic-gate mutex_enter(&pm_dep_thread_lock);
22490Sstevel@tonic-gate if (pm_dep_thread_workq == NULL) {
22500Sstevel@tonic-gate pm_dep_thread_workq = new_work;
22510Sstevel@tonic-gate pm_dep_thread_tail = new_work;
22520Sstevel@tonic-gate new_work->pdw_next = NULL;
22530Sstevel@tonic-gate } else {
22540Sstevel@tonic-gate pm_dep_thread_tail->pdw_next = new_work;
22550Sstevel@tonic-gate pm_dep_thread_tail = new_work;
22560Sstevel@tonic-gate new_work->pdw_next = NULL;
22570Sstevel@tonic-gate }
22580Sstevel@tonic-gate cv_signal(&pm_dep_thread_cv);
22590Sstevel@tonic-gate /* If caller asked for it, wait till it is done. */
22600Sstevel@tonic-gate if (wait) {
22610Sstevel@tonic-gate while (!new_work->pdw_done)
22620Sstevel@tonic-gate cv_wait(&new_work->pdw_cv, &pm_dep_thread_lock);
22630Sstevel@tonic-gate /*
22640Sstevel@tonic-gate * Pass return status, if any, back.
22650Sstevel@tonic-gate */
22660Sstevel@tonic-gate if (res != NULL)
22670Sstevel@tonic-gate *res = new_work->pdw_ret;
22680Sstevel@tonic-gate /*
22690Sstevel@tonic-gate * If we asked to wait, it is our job to free the request
22700Sstevel@tonic-gate * structure.
22710Sstevel@tonic-gate */
22720Sstevel@tonic-gate if (new_work->pdw_keeper)
22730Sstevel@tonic-gate kmem_free(new_work->pdw_keeper,
22740Sstevel@tonic-gate strlen(new_work->pdw_keeper) + 1);
22750Sstevel@tonic-gate if (new_work->pdw_kept)
22760Sstevel@tonic-gate kmem_free(new_work->pdw_kept,
22770Sstevel@tonic-gate strlen(new_work->pdw_kept) + 1);
22780Sstevel@tonic-gate kmem_free(new_work, sizeof (pm_dep_wk_t));
22790Sstevel@tonic-gate }
22800Sstevel@tonic-gate mutex_exit(&pm_dep_thread_lock);
22810Sstevel@tonic-gate }
22820Sstevel@tonic-gate
22830Sstevel@tonic-gate /*
22840Sstevel@tonic-gate * Release the pm resource for this device.
22850Sstevel@tonic-gate */
22860Sstevel@tonic-gate void
pm_rem_info(dev_info_t * dip)22870Sstevel@tonic-gate pm_rem_info(dev_info_t *dip)
22880Sstevel@tonic-gate {
22890Sstevel@tonic-gate PMD_FUNC(pmf, "rem_info")
22900Sstevel@tonic-gate int i, count = 0;
22910Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
22920Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
22930Sstevel@tonic-gate char *pathbuf;
22940Sstevel@tonic-gate int work_type = PM_DEP_WK_DETACH;
22950Sstevel@tonic-gate
22960Sstevel@tonic-gate ASSERT(info);
22970Sstevel@tonic-gate
22980Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
22990Sstevel@tonic-gate if (PM_ISDIRECT(dip)) {
23000Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_DIRECT;
23010Sstevel@tonic-gate ASSERT(info->pmi_clone);
23020Sstevel@tonic-gate info->pmi_clone = 0;
23030Sstevel@tonic-gate pm_proceed(dip, PMP_RELEASE, -1, -1);
23040Sstevel@tonic-gate }
23050Sstevel@tonic-gate ASSERT(!PM_GET_PM_SCAN(dip));
23060Sstevel@tonic-gate
23070Sstevel@tonic-gate /*
23080Sstevel@tonic-gate * Now adjust parent's kidsupcnt. BC nodes we check only comp 0,
23090Sstevel@tonic-gate * Others we check all components. BC node that has already
23100Sstevel@tonic-gate * called pm_destroy_components() has zero component count.
23110Sstevel@tonic-gate * Parents that get notification are not adjusted because their
23120Sstevel@tonic-gate * kidsupcnt is always 0 (or 1 during configuration).
23130Sstevel@tonic-gate */
23140Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d) has %d components\n", pmf,
23150Sstevel@tonic-gate PM_DEVICE(dip), PM_NUMCMPTS(dip)))
23160Sstevel@tonic-gate
23170Sstevel@tonic-gate /* node is detached, so we can examine power without locking */
23180Sstevel@tonic-gate if (PM_ISBC(dip)) {
23190Sstevel@tonic-gate count = (PM_CURPOWER(dip, 0) != 0);
23200Sstevel@tonic-gate } else {
23210Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++)
23220Sstevel@tonic-gate count += (PM_CURPOWER(dip, i) != 0);
23230Sstevel@tonic-gate }
23240Sstevel@tonic-gate
23250Sstevel@tonic-gate if (PM_NUMCMPTS(dip) && pdip && !PM_WANTS_NOTIFICATION(pdip))
23260Sstevel@tonic-gate e_pm_hold_rele_power(pdip, -count);
23270Sstevel@tonic-gate
23280Sstevel@tonic-gate /* Schedule a request to clean up dependency records */
23290Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
23300Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
23310Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, pathbuf,
23320Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, (count > 0));
23330Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
23340Sstevel@tonic-gate
23350Sstevel@tonic-gate /*
23360Sstevel@tonic-gate * Adjust the pm_comps_notlowest count since this device is
23370Sstevel@tonic-gate * not being power-managed anymore.
23380Sstevel@tonic-gate */
23390Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
234010488SMark.Haywood@Sun.COM pm_component_t *cp = PM_CP(dip, i);
234110488SMark.Haywood@Sun.COM if (cp->pmc_cur_pwr != 0)
234210488SMark.Haywood@Sun.COM PM_DECR_NOTLOWEST(dip)
23430Sstevel@tonic-gate }
23440Sstevel@tonic-gate /*
23450Sstevel@tonic-gate * Once we clear the info pointer, it looks like it is not power
23460Sstevel@tonic-gate * managed to everybody else.
23470Sstevel@tonic-gate */
23480Sstevel@tonic-gate pm_set_pm_info(dip, NULL);
23490Sstevel@tonic-gate kmem_free(info, sizeof (pm_info_t));
23500Sstevel@tonic-gate }
23510Sstevel@tonic-gate
23520Sstevel@tonic-gate int
pm_get_norm_pwrs(dev_info_t * dip,int ** valuep,size_t * length)23530Sstevel@tonic-gate pm_get_norm_pwrs(dev_info_t *dip, int **valuep, size_t *length)
23540Sstevel@tonic-gate {
23550Sstevel@tonic-gate int components = PM_NUMCMPTS(dip);
23560Sstevel@tonic-gate int *bufp;
23570Sstevel@tonic-gate size_t size;
23580Sstevel@tonic-gate int i;
23590Sstevel@tonic-gate
23600Sstevel@tonic-gate if (components <= 0) {
23610Sstevel@tonic-gate cmn_err(CE_NOTE, "!pm: %s@%s(%s#%d) has no components, "
23620Sstevel@tonic-gate "can't get normal power values\n", PM_DEVICE(dip));
23630Sstevel@tonic-gate return (DDI_FAILURE);
23640Sstevel@tonic-gate } else {
23650Sstevel@tonic-gate size = components * sizeof (int);
23660Sstevel@tonic-gate bufp = kmem_alloc(size, KM_SLEEP);
23670Sstevel@tonic-gate for (i = 0; i < components; i++) {
23680Sstevel@tonic-gate bufp[i] = pm_get_normal_power(dip, i);
23690Sstevel@tonic-gate }
23700Sstevel@tonic-gate }
23710Sstevel@tonic-gate *length = size;
23720Sstevel@tonic-gate *valuep = bufp;
23730Sstevel@tonic-gate return (DDI_SUCCESS);
23740Sstevel@tonic-gate }
23750Sstevel@tonic-gate
23760Sstevel@tonic-gate static int
pm_reset_timestamps(dev_info_t * dip,void * arg)23770Sstevel@tonic-gate pm_reset_timestamps(dev_info_t *dip, void *arg)
23780Sstevel@tonic-gate {
23790Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
23800Sstevel@tonic-gate
23810Sstevel@tonic-gate int components;
23820Sstevel@tonic-gate int i;
23830Sstevel@tonic-gate
23840Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip))
23850Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
23860Sstevel@tonic-gate components = PM_NUMCMPTS(dip);
23870Sstevel@tonic-gate ASSERT(components > 0);
23880Sstevel@tonic-gate PM_LOCK_BUSY(dip);
23890Sstevel@tonic-gate for (i = 0; i < components; i++) {
23900Sstevel@tonic-gate struct pm_component *cp;
23910Sstevel@tonic-gate /*
23920Sstevel@tonic-gate * If the component was not marked as busy,
23930Sstevel@tonic-gate * reset its timestamp to now.
23940Sstevel@tonic-gate */
23950Sstevel@tonic-gate cp = PM_CP(dip, i);
23960Sstevel@tonic-gate if (cp->pmc_timestamp)
23970Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec();
23980Sstevel@tonic-gate }
23990Sstevel@tonic-gate PM_UNLOCK_BUSY(dip);
24000Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
24010Sstevel@tonic-gate }
24020Sstevel@tonic-gate
24030Sstevel@tonic-gate /*
24040Sstevel@tonic-gate * Convert a power level to an index into the levels array (or
24050Sstevel@tonic-gate * just PM_LEVEL_UNKNOWN in that special case).
24060Sstevel@tonic-gate */
24070Sstevel@tonic-gate static int
pm_level_to_index(dev_info_t * dip,pm_component_t * cp,int level)24080Sstevel@tonic-gate pm_level_to_index(dev_info_t *dip, pm_component_t *cp, int level)
24090Sstevel@tonic-gate {
24100Sstevel@tonic-gate PMD_FUNC(pmf, "level_to_index")
24110Sstevel@tonic-gate int i;
24120Sstevel@tonic-gate int limit = cp->pmc_comp.pmc_numlevels;
24130Sstevel@tonic-gate int *ip = cp->pmc_comp.pmc_lvals;
24140Sstevel@tonic-gate
24150Sstevel@tonic-gate if (level == PM_LEVEL_UNKNOWN)
24160Sstevel@tonic-gate return (level);
24170Sstevel@tonic-gate
24180Sstevel@tonic-gate for (i = 0; i < limit; i++) {
24190Sstevel@tonic-gate if (level == *ip++) {
24200Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d)[%d] to %x\n",
24210Sstevel@tonic-gate pmf, PM_DEVICE(dip),
24220Sstevel@tonic-gate (int)(cp - DEVI(dip)->devi_pm_components), level))
24230Sstevel@tonic-gate return (i);
24240Sstevel@tonic-gate }
24250Sstevel@tonic-gate }
24260Sstevel@tonic-gate panic("pm_level_to_index: level %d not found for device "
24270Sstevel@tonic-gate "%s@%s(%s#%d)", level, PM_DEVICE(dip));
24280Sstevel@tonic-gate /*NOTREACHED*/
24290Sstevel@tonic-gate }
24300Sstevel@tonic-gate
24310Sstevel@tonic-gate /*
24320Sstevel@tonic-gate * Internal function to set current power level
24330Sstevel@tonic-gate */
24340Sstevel@tonic-gate static void
e_pm_set_cur_pwr(dev_info_t * dip,pm_component_t * cp,int level)24350Sstevel@tonic-gate e_pm_set_cur_pwr(dev_info_t *dip, pm_component_t *cp, int level)
24360Sstevel@tonic-gate {
24370Sstevel@tonic-gate PMD_FUNC(pmf, "set_cur_pwr")
24380Sstevel@tonic-gate int curpwr = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
24390Sstevel@tonic-gate cp->pmc_phc_pwr : cp->pmc_cur_pwr);
24400Sstevel@tonic-gate
24410Sstevel@tonic-gate /*
24420Sstevel@tonic-gate * Nothing to adjust if current & new levels are the same.
24430Sstevel@tonic-gate */
24440Sstevel@tonic-gate if (curpwr != PM_LEVEL_UNKNOWN &&
24450Sstevel@tonic-gate level == cp->pmc_comp.pmc_lvals[curpwr])
24460Sstevel@tonic-gate return;
24470Sstevel@tonic-gate
24480Sstevel@tonic-gate /*
24490Sstevel@tonic-gate * Keep the count for comps doing transition to/from lowest
24500Sstevel@tonic-gate * level.
24510Sstevel@tonic-gate */
24520Sstevel@tonic-gate if (curpwr == 0) {
24530Sstevel@tonic-gate PM_INCR_NOTLOWEST(dip);
24540Sstevel@tonic-gate } else if (level == cp->pmc_comp.pmc_lvals[0]) {
24550Sstevel@tonic-gate PM_DECR_NOTLOWEST(dip);
24560Sstevel@tonic-gate }
24570Sstevel@tonic-gate cp->pmc_phc_pwr = PM_LEVEL_UNKNOWN;
24580Sstevel@tonic-gate cp->pmc_cur_pwr = pm_level_to_index(dip, cp, level);
24590Sstevel@tonic-gate }
24600Sstevel@tonic-gate
24619694SScott.Rotondo@Sun.COM static int pm_phc_impl(dev_info_t *, int, int, int);
24629694SScott.Rotondo@Sun.COM
24630Sstevel@tonic-gate /*
24640Sstevel@tonic-gate * This is the default method of setting the power of a device if no ppm
24650Sstevel@tonic-gate * driver has claimed it.
24660Sstevel@tonic-gate */
24670Sstevel@tonic-gate int
pm_power(dev_info_t * dip,int comp,int level)24680Sstevel@tonic-gate pm_power(dev_info_t *dip, int comp, int level)
24690Sstevel@tonic-gate {
24700Sstevel@tonic-gate PMD_FUNC(pmf, "power")
24710Sstevel@tonic-gate struct dev_ops *ops;
24720Sstevel@tonic-gate int (*fn)(dev_info_t *, int, int);
24730Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, comp);
24740Sstevel@tonic-gate int retval;
24750Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
24760Sstevel@tonic-gate
24770Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf,
24780Sstevel@tonic-gate PM_DEVICE(dip), comp, level))
24790Sstevel@tonic-gate if (!(ops = ddi_get_driver(dip))) {
24800Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) has no ops\n", pmf,
24810Sstevel@tonic-gate PM_DEVICE(dip)))
24820Sstevel@tonic-gate return (DDI_FAILURE);
24830Sstevel@tonic-gate }
24840Sstevel@tonic-gate if ((ops->devo_rev < 2) || !(fn = ops->devo_power)) {
24850Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s%s\n", pmf,
24860Sstevel@tonic-gate (ops->devo_rev < 2 ? " wrong devo_rev" : ""),
24870Sstevel@tonic-gate (!fn ? " devo_power NULL" : "")))
24880Sstevel@tonic-gate return (DDI_FAILURE);
24890Sstevel@tonic-gate }
24900Sstevel@tonic-gate cp->pmc_flags |= PM_POWER_OP;
24910Sstevel@tonic-gate retval = (*fn)(dip, comp, level);
24920Sstevel@tonic-gate cp->pmc_flags &= ~PM_POWER_OP;
24930Sstevel@tonic-gate if (retval == DDI_SUCCESS) {
24940Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level);
24950Sstevel@tonic-gate return (DDI_SUCCESS);
24960Sstevel@tonic-gate }
24970Sstevel@tonic-gate
24980Sstevel@tonic-gate /*
24990Sstevel@tonic-gate * If pm_power_has_changed() detected a deadlock with pm_power() it
25000Sstevel@tonic-gate * updated only the power level of the component. If our attempt to
25010Sstevel@tonic-gate * set the device new to a power level above has failed we sync the
25020Sstevel@tonic-gate * total power state via phc code now.
25030Sstevel@tonic-gate */
25040Sstevel@tonic-gate if (cp->pmc_flags & PM_PHC_WHILE_SET_POWER) {
25050Sstevel@tonic-gate int phc_lvl =
25060Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr];
25070Sstevel@tonic-gate
25080Sstevel@tonic-gate ASSERT(info);
25090Sstevel@tonic-gate (void) pm_phc_impl(dip, comp, phc_lvl, 0);
25100Sstevel@tonic-gate PMD(PMD_PHC, ("%s: phc %s@%s(%s#%d) comp=%d level=%d\n",
25114780Smh27603 pmf, PM_DEVICE(dip), comp, phc_lvl))
25120Sstevel@tonic-gate }
25130Sstevel@tonic-gate
25140Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't set comp=%d (%s) of %s@%s(%s#%d) to "
25150Sstevel@tonic-gate "level=%d (%s)\n", pmf, comp, cp->pmc_comp.pmc_name, PM_DEVICE(dip),
25160Sstevel@tonic-gate level, power_val_to_string(cp, level)));
25170Sstevel@tonic-gate return (DDI_FAILURE);
25180Sstevel@tonic-gate }
25190Sstevel@tonic-gate
25200Sstevel@tonic-gate int
pm_unmanage(dev_info_t * dip)25210Sstevel@tonic-gate pm_unmanage(dev_info_t *dip)
25220Sstevel@tonic-gate {
25230Sstevel@tonic-gate PMD_FUNC(pmf, "unmanage")
25240Sstevel@tonic-gate power_req_t power_req;
25250Sstevel@tonic-gate int result, retval = 0;
25260Sstevel@tonic-gate
25270Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
25280Sstevel@tonic-gate PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf,
25290Sstevel@tonic-gate PM_DEVICE(dip)))
25300Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNMANAGE;
25310Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
25320Sstevel@tonic-gate if (pm_ppm_claimed(dip))
25330Sstevel@tonic-gate retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
25340Sstevel@tonic-gate &power_req, &result);
25350Sstevel@tonic-gate #ifdef DEBUG
25360Sstevel@tonic-gate else
25370Sstevel@tonic-gate retval = pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
25380Sstevel@tonic-gate &power_req, &result);
25390Sstevel@tonic-gate #endif
25400Sstevel@tonic-gate ASSERT(retval == DDI_SUCCESS);
25410Sstevel@tonic-gate pm_rem_info(dip);
25420Sstevel@tonic-gate return (retval);
25430Sstevel@tonic-gate }
25440Sstevel@tonic-gate
25450Sstevel@tonic-gate int
pm_raise_power(dev_info_t * dip,int comp,int level)25460Sstevel@tonic-gate pm_raise_power(dev_info_t *dip, int comp, int level)
25470Sstevel@tonic-gate {
25480Sstevel@tonic-gate if (level < 0)
25490Sstevel@tonic-gate return (DDI_FAILURE);
25500Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
25510Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level))
25520Sstevel@tonic-gate return (DDI_FAILURE);
25530Sstevel@tonic-gate
25540Sstevel@tonic-gate return (dev_is_needed(dip, comp, level, PM_LEVEL_UPONLY));
25550Sstevel@tonic-gate }
25560Sstevel@tonic-gate
25570Sstevel@tonic-gate int
pm_lower_power(dev_info_t * dip,int comp,int level)25580Sstevel@tonic-gate pm_lower_power(dev_info_t *dip, int comp, int level)
25590Sstevel@tonic-gate {
25600Sstevel@tonic-gate PMD_FUNC(pmf, "pm_lower_power")
25610Sstevel@tonic-gate
25620Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
25630Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level)) {
25640Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) "
25650Sstevel@tonic-gate "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level))
25660Sstevel@tonic-gate return (DDI_FAILURE);
25670Sstevel@tonic-gate }
25680Sstevel@tonic-gate
25690Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) {
25700Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) not detaching\n",
25710Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
25720Sstevel@tonic-gate return (DDI_FAILURE);
25730Sstevel@tonic-gate }
25740Sstevel@tonic-gate
25750Sstevel@tonic-gate /*
25760Sstevel@tonic-gate * If we don't care about saving power, or we're treating this node
25770Sstevel@tonic-gate * specially, then this is a no-op
25780Sstevel@tonic-gate */
25793028Smh27603 if (!PM_SCANABLE(dip) || pm_noinvol(dip)) {
25803028Smh27603 PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) %s%s%s%s\n",
25813028Smh27603 pmf, PM_DEVICE(dip),
25820Sstevel@tonic-gate !autopm_enabled ? "!autopm_enabled " : "",
25838906SEric.Saxe@Sun.COM !PM_POLLING_CPUPM ? "!cpupm_polling " : "",
25843028Smh27603 PM_CPUPM_DISABLED ? "cpupm_disabled " : "",
25850Sstevel@tonic-gate pm_noinvol(dip) ? "pm_noinvol()" : ""))
25860Sstevel@tonic-gate return (DDI_SUCCESS);
25870Sstevel@tonic-gate }
25880Sstevel@tonic-gate
25890Sstevel@tonic-gate if (dev_is_needed(dip, comp, level, PM_LEVEL_DOWNONLY) != DDI_SUCCESS) {
25900Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) dev_is_needed failed\n", pmf,
25910Sstevel@tonic-gate PM_DEVICE(dip)))
25920Sstevel@tonic-gate return (DDI_FAILURE);
25930Sstevel@tonic-gate }
25940Sstevel@tonic-gate return (DDI_SUCCESS);
25950Sstevel@tonic-gate }
25960Sstevel@tonic-gate
25970Sstevel@tonic-gate /*
25980Sstevel@tonic-gate * Find the entries struct for a given dip in the blocked list, return it locked
25990Sstevel@tonic-gate */
26000Sstevel@tonic-gate static psce_t *
pm_psc_dip_to_direct(dev_info_t * dip,pscc_t ** psccp)26010Sstevel@tonic-gate pm_psc_dip_to_direct(dev_info_t *dip, pscc_t **psccp)
26020Sstevel@tonic-gate {
26030Sstevel@tonic-gate pscc_t *p;
26040Sstevel@tonic-gate psce_t *psce;
26050Sstevel@tonic-gate
26060Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_READER);
26070Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = p->pscc_next) {
26080Sstevel@tonic-gate if (p->pscc_dip == dip) {
26090Sstevel@tonic-gate *psccp = p;
26100Sstevel@tonic-gate psce = p->pscc_entries;
26110Sstevel@tonic-gate mutex_enter(&psce->psce_lock);
26120Sstevel@tonic-gate ASSERT(psce);
26130Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock);
26140Sstevel@tonic-gate return (psce);
26150Sstevel@tonic-gate }
26160Sstevel@tonic-gate }
26170Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock);
26180Sstevel@tonic-gate panic("sunpm: no entry for dip %p in direct list", (void *)dip);
26190Sstevel@tonic-gate /*NOTREACHED*/
26200Sstevel@tonic-gate }
26210Sstevel@tonic-gate
26220Sstevel@tonic-gate /*
26230Sstevel@tonic-gate * Write an entry indicating a power level change (to be passed to a process
26240Sstevel@tonic-gate * later) in the given psce.
26250Sstevel@tonic-gate * If we were called in the path that brings up the console fb in the
26260Sstevel@tonic-gate * case of entering the prom, we don't want to sleep. If the alloc fails, then
26270Sstevel@tonic-gate * we create a record that has a size of -1, a physaddr of NULL, and that
26280Sstevel@tonic-gate * has the overflow flag set.
26290Sstevel@tonic-gate */
26300Sstevel@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)26310Sstevel@tonic-gate psc_entry(ushort_t event, psce_t *psce, dev_info_t *dip, int comp, int new,
26320Sstevel@tonic-gate int old, int which, pm_canblock_t canblock)
26330Sstevel@tonic-gate {
26340Sstevel@tonic-gate char buf[MAXNAMELEN];
26350Sstevel@tonic-gate pm_state_change_t *p;
26360Sstevel@tonic-gate size_t size;
26370Sstevel@tonic-gate caddr_t physpath = NULL;
26380Sstevel@tonic-gate int overrun = 0;
26390Sstevel@tonic-gate
26400Sstevel@tonic-gate ASSERT(MUTEX_HELD(&psce->psce_lock));
26410Sstevel@tonic-gate (void) ddi_pathname(dip, buf);
26420Sstevel@tonic-gate size = strlen(buf) + 1;
26430Sstevel@tonic-gate p = psce->psce_in;
26440Sstevel@tonic-gate if (canblock == PM_CANBLOCK_BYPASS) {
26450Sstevel@tonic-gate physpath = kmem_alloc(size, KM_NOSLEEP);
26460Sstevel@tonic-gate if (physpath == NULL) {
26470Sstevel@tonic-gate /*
26480Sstevel@tonic-gate * mark current entry as overrun
26490Sstevel@tonic-gate */
26500Sstevel@tonic-gate p->flags |= PSC_EVENT_LOST;
26510Sstevel@tonic-gate size = (size_t)-1;
26520Sstevel@tonic-gate }
26530Sstevel@tonic-gate } else
26540Sstevel@tonic-gate physpath = kmem_alloc(size, KM_SLEEP);
26550Sstevel@tonic-gate if (p->size) { /* overflow; mark the next entry */
26560Sstevel@tonic-gate if (p->size != (size_t)-1)
26570Sstevel@tonic-gate kmem_free(p->physpath, p->size);
26580Sstevel@tonic-gate ASSERT(psce->psce_out == p);
26590Sstevel@tonic-gate if (p == psce->psce_last) {
26600Sstevel@tonic-gate psce->psce_first->flags |= PSC_EVENT_LOST;
26610Sstevel@tonic-gate psce->psce_out = psce->psce_first;
26620Sstevel@tonic-gate } else {
26630Sstevel@tonic-gate (p + 1)->flags |= PSC_EVENT_LOST;
26640Sstevel@tonic-gate psce->psce_out = (p + 1);
26650Sstevel@tonic-gate }
26660Sstevel@tonic-gate overrun++;
26670Sstevel@tonic-gate } else if (physpath == NULL) { /* alloc failed, mark this entry */
26680Sstevel@tonic-gate p->flags |= PSC_EVENT_LOST;
26690Sstevel@tonic-gate p->size = 0;
26700Sstevel@tonic-gate p->physpath = NULL;
26710Sstevel@tonic-gate }
26720Sstevel@tonic-gate if (which == PSC_INTEREST) {
26730Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock);
26740Sstevel@tonic-gate if (pm_comps_notlowest == 0)
26750Sstevel@tonic-gate p->flags |= PSC_ALL_LOWEST;
26760Sstevel@tonic-gate else
26770Sstevel@tonic-gate p->flags &= ~PSC_ALL_LOWEST;
26780Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock);
26790Sstevel@tonic-gate }
26800Sstevel@tonic-gate p->event = event;
26810Sstevel@tonic-gate p->timestamp = gethrestime_sec();
26820Sstevel@tonic-gate p->component = comp;
26830Sstevel@tonic-gate p->old_level = old;
26840Sstevel@tonic-gate p->new_level = new;
26850Sstevel@tonic-gate p->physpath = physpath;
26860Sstevel@tonic-gate p->size = size;
26870Sstevel@tonic-gate if (physpath != NULL)
26880Sstevel@tonic-gate (void) strcpy(p->physpath, buf);
26890Sstevel@tonic-gate if (p == psce->psce_last)
26900Sstevel@tonic-gate psce->psce_in = psce->psce_first;
26910Sstevel@tonic-gate else
26920Sstevel@tonic-gate psce->psce_in = ++p;
26930Sstevel@tonic-gate mutex_exit(&psce->psce_lock);
26940Sstevel@tonic-gate return (overrun);
26950Sstevel@tonic-gate }
26960Sstevel@tonic-gate
26970Sstevel@tonic-gate /*
26980Sstevel@tonic-gate * Find the next entry on the interest list. We keep a pointer to the item we
26990Sstevel@tonic-gate * last returned in the user's cooke. Returns a locked entries struct.
27000Sstevel@tonic-gate */
27010Sstevel@tonic-gate static psce_t *
psc_interest(void ** cookie,pscc_t ** psccp)27020Sstevel@tonic-gate psc_interest(void **cookie, pscc_t **psccp)
27030Sstevel@tonic-gate {
27040Sstevel@tonic-gate pscc_t *pscc;
27050Sstevel@tonic-gate pscc_t **cookiep = (pscc_t **)cookie;
27060Sstevel@tonic-gate
27070Sstevel@tonic-gate if (*cookiep == NULL)
27080Sstevel@tonic-gate pscc = pm_pscc_interest;
27090Sstevel@tonic-gate else
27100Sstevel@tonic-gate pscc = (*cookiep)->pscc_next;
27110Sstevel@tonic-gate if (pscc) {
27120Sstevel@tonic-gate *cookiep = pscc;
27130Sstevel@tonic-gate *psccp = pscc;
27140Sstevel@tonic-gate mutex_enter(&pscc->pscc_entries->psce_lock);
27150Sstevel@tonic-gate return (pscc->pscc_entries);
27160Sstevel@tonic-gate } else {
27170Sstevel@tonic-gate return (NULL);
27180Sstevel@tonic-gate }
27190Sstevel@tonic-gate }
27200Sstevel@tonic-gate
27210Sstevel@tonic-gate /*
27220Sstevel@tonic-gate * Create an entry for a process to pick up indicating a power level change.
27230Sstevel@tonic-gate */
27240Sstevel@tonic-gate static void
pm_enqueue_notify(ushort_t cmd,dev_info_t * dip,int comp,int newlevel,int oldlevel,pm_canblock_t canblock)27250Sstevel@tonic-gate pm_enqueue_notify(ushort_t cmd, dev_info_t *dip, int comp,
27260Sstevel@tonic-gate int newlevel, int oldlevel, pm_canblock_t canblock)
27270Sstevel@tonic-gate {
27280Sstevel@tonic-gate PMD_FUNC(pmf, "enqueue_notify")
27290Sstevel@tonic-gate pscc_t *pscc;
27300Sstevel@tonic-gate psce_t *psce;
27310Sstevel@tonic-gate void *cookie = NULL;
27320Sstevel@tonic-gate int overrun;
27330Sstevel@tonic-gate
27340Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock));
27350Sstevel@tonic-gate switch (cmd) {
27360Sstevel@tonic-gate case PSC_PENDING_CHANGE: /* only for controlling process */
27370Sstevel@tonic-gate PMD(PMD_DPM, ("%s: PENDING %s@%s(%s#%d), comp %d, %d -> %d\n",
27380Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, oldlevel, newlevel))
27390Sstevel@tonic-gate psce = pm_psc_dip_to_direct(dip, &pscc);
27400Sstevel@tonic-gate ASSERT(psce);
27410Sstevel@tonic-gate PMD(PMD_IOCTL, ("%s: PENDING: %s@%s(%s#%d) pm_poll_cnt[%d] "
27420Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone,
27430Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]))
27440Sstevel@tonic-gate overrun = psc_entry(cmd, psce, dip, comp, newlevel, oldlevel,
27450Sstevel@tonic-gate PSC_DIRECT, canblock);
27460Sstevel@tonic-gate PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone))
27470Sstevel@tonic-gate mutex_enter(&pm_clone_lock);
27480Sstevel@tonic-gate if (!overrun)
27490Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]++;
27500Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27510Sstevel@tonic-gate pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN));
27520Sstevel@tonic-gate mutex_exit(&pm_clone_lock);
27530Sstevel@tonic-gate break;
27540Sstevel@tonic-gate case PSC_HAS_CHANGED:
27550Sstevel@tonic-gate PMD(PMD_DPM, ("%s: HAS %s@%s(%s#%d), comp %d, %d -> %d\n",
27560Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, oldlevel, newlevel))
27570Sstevel@tonic-gate if (PM_ISDIRECT(dip) && canblock != PM_CANBLOCK_BYPASS) {
27580Sstevel@tonic-gate psce = pm_psc_dip_to_direct(dip, &pscc);
27590Sstevel@tonic-gate PMD(PMD_IOCTL, ("%s: HAS: %s@%s(%s#%d) pm_poll_cnt[%d] "
27600Sstevel@tonic-gate "%d\n", pmf, PM_DEVICE(dip), pscc->pscc_clone,
27610Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]))
27620Sstevel@tonic-gate overrun = psc_entry(cmd, psce, dip, comp, newlevel,
27630Sstevel@tonic-gate oldlevel, PSC_DIRECT, canblock);
27640Sstevel@tonic-gate PMD(PMD_DPM, ("%s: sig %d\n", pmf, pscc->pscc_clone))
27650Sstevel@tonic-gate mutex_enter(&pm_clone_lock);
27660Sstevel@tonic-gate if (!overrun)
27670Sstevel@tonic-gate pm_poll_cnt[pscc->pscc_clone]++;
27680Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27690Sstevel@tonic-gate pollwakeup(&pm_pollhead, (POLLRDNORM | POLLIN));
27700Sstevel@tonic-gate mutex_exit(&pm_clone_lock);
27710Sstevel@tonic-gate }
27720Sstevel@tonic-gate mutex_enter(&pm_clone_lock);
27730Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_READER);
27740Sstevel@tonic-gate while ((psce = psc_interest(&cookie, &pscc)) != NULL) {
27750Sstevel@tonic-gate (void) psc_entry(cmd, psce, dip, comp, newlevel,
27760Sstevel@tonic-gate oldlevel, PSC_INTEREST, canblock);
27770Sstevel@tonic-gate cv_signal(&pm_clones_cv[pscc->pscc_clone]);
27780Sstevel@tonic-gate }
27790Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock);
27800Sstevel@tonic-gate mutex_exit(&pm_clone_lock);
27810Sstevel@tonic-gate break;
27820Sstevel@tonic-gate #ifdef DEBUG
27830Sstevel@tonic-gate default:
27840Sstevel@tonic-gate ASSERT(0);
27850Sstevel@tonic-gate #endif
27860Sstevel@tonic-gate }
27870Sstevel@tonic-gate }
27880Sstevel@tonic-gate
27890Sstevel@tonic-gate static void
pm_enqueue_notify_others(pm_ppm_devlist_t ** listp,pm_canblock_t canblock)27900Sstevel@tonic-gate pm_enqueue_notify_others(pm_ppm_devlist_t **listp, pm_canblock_t canblock)
27910Sstevel@tonic-gate {
27920Sstevel@tonic-gate if (listp) {
27930Sstevel@tonic-gate pm_ppm_devlist_t *p, *next = NULL;
27940Sstevel@tonic-gate
27950Sstevel@tonic-gate for (p = *listp; p; p = next) {
27960Sstevel@tonic-gate next = p->ppd_next;
27970Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, p->ppd_who,
27980Sstevel@tonic-gate p->ppd_cmpt, p->ppd_new_level, p->ppd_old_level,
27990Sstevel@tonic-gate canblock);
28000Sstevel@tonic-gate kmem_free(p, sizeof (pm_ppm_devlist_t));
28010Sstevel@tonic-gate }
28020Sstevel@tonic-gate *listp = NULL;
28030Sstevel@tonic-gate }
28040Sstevel@tonic-gate }
28050Sstevel@tonic-gate
28060Sstevel@tonic-gate /*
28070Sstevel@tonic-gate * Try to get the power locks of the parent node and target (child)
28080Sstevel@tonic-gate * node. Return true if successful (with both locks held) or false
28090Sstevel@tonic-gate * (with no locks held).
28100Sstevel@tonic-gate */
28110Sstevel@tonic-gate static int
pm_try_parent_child_locks(dev_info_t * pdip,dev_info_t * dip,int * pcircp,int * circp)28120Sstevel@tonic-gate pm_try_parent_child_locks(dev_info_t *pdip,
28130Sstevel@tonic-gate dev_info_t *dip, int *pcircp, int *circp)
28140Sstevel@tonic-gate {
28150Sstevel@tonic-gate if (ndi_devi_tryenter(pdip, pcircp))
28160Sstevel@tonic-gate if (PM_TRY_LOCK_POWER(dip, circp)) {
28170Sstevel@tonic-gate return (1);
28180Sstevel@tonic-gate } else {
28190Sstevel@tonic-gate ndi_devi_exit(pdip, *pcircp);
28200Sstevel@tonic-gate }
28210Sstevel@tonic-gate return (0);
28220Sstevel@tonic-gate }
28230Sstevel@tonic-gate
28240Sstevel@tonic-gate /*
28250Sstevel@tonic-gate * Determine if the power lock owner is blocked by current thread.
28260Sstevel@tonic-gate * returns :
28270Sstevel@tonic-gate * 1 - If the thread owning the effective power lock (the first lock on
28280Sstevel@tonic-gate * which a thread blocks when it does PM_LOCK_POWER) is blocked by
28290Sstevel@tonic-gate * a mutex held by the current thread.
28300Sstevel@tonic-gate *
28310Sstevel@tonic-gate * 0 - otherwise
28320Sstevel@tonic-gate *
28330Sstevel@tonic-gate * Note : This function is called by pm_power_has_changed to determine whether
28340Sstevel@tonic-gate * it is executing in parallel with pm_set_power.
28350Sstevel@tonic-gate */
28360Sstevel@tonic-gate static int
pm_blocked_by_us(dev_info_t * dip)28370Sstevel@tonic-gate pm_blocked_by_us(dev_info_t *dip)
28380Sstevel@tonic-gate {
28390Sstevel@tonic-gate power_req_t power_req;
28400Sstevel@tonic-gate kthread_t *owner;
28410Sstevel@tonic-gate int result;
28420Sstevel@tonic-gate kmutex_t *mp;
28430Sstevel@tonic-gate dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm;
28440Sstevel@tonic-gate
28450Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_LOCK_OWNER;
28460Sstevel@tonic-gate power_req.req.ppm_power_lock_owner_req.who = dip;
28470Sstevel@tonic-gate if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req, &result) !=
28480Sstevel@tonic-gate DDI_SUCCESS) {
28490Sstevel@tonic-gate /*
28500Sstevel@tonic-gate * It is assumed that if the device is claimed by ppm, ppm
28510Sstevel@tonic-gate * will always implement this request type and it'll always
28520Sstevel@tonic-gate * return success. We panic here, if it fails.
28530Sstevel@tonic-gate */
28540Sstevel@tonic-gate panic("pm: Can't determine power lock owner of %s@%s(%s#%d)\n",
28550Sstevel@tonic-gate PM_DEVICE(dip));
28560Sstevel@tonic-gate /*NOTREACHED*/
28570Sstevel@tonic-gate }
28580Sstevel@tonic-gate
28590Sstevel@tonic-gate if ((owner = power_req.req.ppm_power_lock_owner_req.owner) != NULL &&
28600Sstevel@tonic-gate owner->t_state == TS_SLEEP &&
28610Sstevel@tonic-gate owner->t_sobj_ops &&
28620Sstevel@tonic-gate SOBJ_TYPE(owner->t_sobj_ops) == SOBJ_MUTEX &&
28630Sstevel@tonic-gate (mp = (kmutex_t *)owner->t_wchan) &&
28640Sstevel@tonic-gate mutex_owner(mp) == curthread)
28650Sstevel@tonic-gate return (1);
28660Sstevel@tonic-gate
28670Sstevel@tonic-gate return (0);
28680Sstevel@tonic-gate }
28690Sstevel@tonic-gate
28700Sstevel@tonic-gate /*
28710Sstevel@tonic-gate * Notify parent which wants to hear about a child's power changes.
28720Sstevel@tonic-gate */
28730Sstevel@tonic-gate static void
pm_notify_parent(dev_info_t * dip,dev_info_t * pdip,int comp,int old_level,int level)28740Sstevel@tonic-gate pm_notify_parent(dev_info_t *dip,
28750Sstevel@tonic-gate dev_info_t *pdip, int comp, int old_level, int level)
28760Sstevel@tonic-gate {
28770Sstevel@tonic-gate pm_bp_has_changed_t bphc;
28780Sstevel@tonic-gate pm_sp_misc_t pspm;
28790Sstevel@tonic-gate char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
28800Sstevel@tonic-gate int result = DDI_SUCCESS;
28810Sstevel@tonic-gate
28820Sstevel@tonic-gate bphc.bphc_dip = dip;
28830Sstevel@tonic-gate bphc.bphc_path = ddi_pathname(dip, pathbuf);
28840Sstevel@tonic-gate bphc.bphc_comp = comp;
28850Sstevel@tonic-gate bphc.bphc_olevel = old_level;
28860Sstevel@tonic-gate bphc.bphc_nlevel = level;
28870Sstevel@tonic-gate pspm.pspm_canblock = PM_CANBLOCK_BLOCK;
28880Sstevel@tonic-gate pspm.pspm_scan = 0;
28890Sstevel@tonic-gate bphc.bphc_private = &pspm;
28900Sstevel@tonic-gate (void) (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
28910Sstevel@tonic-gate BUS_POWER_HAS_CHANGED, (void *)&bphc, (void *)&result);
28920Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
28930Sstevel@tonic-gate }
28940Sstevel@tonic-gate
28950Sstevel@tonic-gate /*
28960Sstevel@tonic-gate * Check if we need to resume a BC device, and make the attach call as required.
28970Sstevel@tonic-gate */
28980Sstevel@tonic-gate static int
pm_check_and_resume(dev_info_t * dip,int comp,int old_level,int level)28990Sstevel@tonic-gate pm_check_and_resume(dev_info_t *dip, int comp, int old_level, int level)
29000Sstevel@tonic-gate {
29010Sstevel@tonic-gate int ret = DDI_SUCCESS;
29020Sstevel@tonic-gate
29030Sstevel@tonic-gate if (PM_ISBC(dip) && comp == 0 && old_level == 0 && level != 0) {
29040Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_flags & PMC_SUSPENDED);
29050Sstevel@tonic-gate /* ppm is not interested in DDI_PM_RESUME */
29060Sstevel@tonic-gate if ((ret = devi_attach(dip, DDI_PM_RESUME)) != DDI_SUCCESS)
29070Sstevel@tonic-gate /* XXX Should we mark it resumed, */
29080Sstevel@tonic-gate /* even though it failed? */
29090Sstevel@tonic-gate cmn_err(CE_WARN, "!pm: Can't resume %s@%s",
29100Sstevel@tonic-gate PM_NAME(dip), PM_ADDR(dip));
29110Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_SUSPENDED;
29120Sstevel@tonic-gate }
29130Sstevel@tonic-gate
29140Sstevel@tonic-gate return (ret);
29150Sstevel@tonic-gate }
29160Sstevel@tonic-gate
29170Sstevel@tonic-gate /*
29180Sstevel@tonic-gate * Tests outside the lock to see if we should bother to enqueue an entry
29190Sstevel@tonic-gate * for any watching process. If yes, then caller will take the lock and
29200Sstevel@tonic-gate * do the full protocol
29210Sstevel@tonic-gate */
29220Sstevel@tonic-gate static int
pm_watchers()29230Sstevel@tonic-gate pm_watchers()
29240Sstevel@tonic-gate {
29250Sstevel@tonic-gate if (pm_processes_stopped)
29260Sstevel@tonic-gate return (0);
29270Sstevel@tonic-gate return (pm_pscc_direct || pm_pscc_interest);
29280Sstevel@tonic-gate }
29290Sstevel@tonic-gate
29309694SScott.Rotondo@Sun.COM static int pm_phc_impl(dev_info_t *, int, int, int);
29319694SScott.Rotondo@Sun.COM
29320Sstevel@tonic-gate /*
29330Sstevel@tonic-gate * A driver is reporting that the power of one of its device's components
29340Sstevel@tonic-gate * has changed. Update the power state accordingly.
29350Sstevel@tonic-gate */
29360Sstevel@tonic-gate int
pm_power_has_changed(dev_info_t * dip,int comp,int level)29370Sstevel@tonic-gate pm_power_has_changed(dev_info_t *dip, int comp, int level)
29380Sstevel@tonic-gate {
29390Sstevel@tonic-gate PMD_FUNC(pmf, "pm_power_has_changed")
29400Sstevel@tonic-gate int ret;
29410Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
29420Sstevel@tonic-gate struct pm_component *cp;
29430Sstevel@tonic-gate int blocked, circ, pcirc, old_level;
29440Sstevel@tonic-gate
29450Sstevel@tonic-gate if (level < 0) {
29460Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: %s@%s(%s#%d): bad level=%d\n", pmf,
29470Sstevel@tonic-gate PM_DEVICE(dip), level))
29480Sstevel@tonic-gate return (DDI_FAILURE);
29490Sstevel@tonic-gate }
29500Sstevel@tonic-gate
29510Sstevel@tonic-gate PMD(PMD_KIDSUP | PMD_DEP, ("%s: %s@%s(%s#%d), comp=%d, level=%d\n", pmf,
29520Sstevel@tonic-gate PM_DEVICE(dip), comp, level))
29530Sstevel@tonic-gate
29540Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, &cp) ||
29550Sstevel@tonic-gate !e_pm_valid_power(dip, comp, level))
29560Sstevel@tonic-gate return (DDI_FAILURE);
29570Sstevel@tonic-gate
29580Sstevel@tonic-gate /*
29590Sstevel@tonic-gate * A driver thread calling pm_power_has_changed and another thread
29600Sstevel@tonic-gate * calling pm_set_power can deadlock. The problem is not resolvable
29610Sstevel@tonic-gate * by changing lock order, so we use pm_blocked_by_us() to detect
29620Sstevel@tonic-gate * this specific deadlock. If we can't get the lock immediately
29630Sstevel@tonic-gate * and we are deadlocked, just update the component's level, do
29640Sstevel@tonic-gate * notifications, and return. We intend to update the total power
29650Sstevel@tonic-gate * state later (if the other thread fails to set power to the
29660Sstevel@tonic-gate * desired level). If we were called because of a power change on a
29670Sstevel@tonic-gate * component that isn't involved in a set_power op, update all state
29680Sstevel@tonic-gate * immediately.
29690Sstevel@tonic-gate */
29700Sstevel@tonic-gate cp = PM_CP(dip, comp);
29710Sstevel@tonic-gate while (!pm_try_parent_child_locks(pdip, dip, &pcirc, &circ)) {
29720Sstevel@tonic-gate if (((blocked = pm_blocked_by_us(dip)) != 0) &&
29730Sstevel@tonic-gate (cp->pmc_flags & PM_POWER_OP)) {
29740Sstevel@tonic-gate if (pm_watchers()) {
29750Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock);
29760Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp,
29770Sstevel@tonic-gate level, cur_power(cp), PM_CANBLOCK_BLOCK);
29780Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock);
29790Sstevel@tonic-gate }
29800Sstevel@tonic-gate if (pdip && PM_WANTS_NOTIFICATION(pdip))
29810Sstevel@tonic-gate pm_notify_parent(dip,
29820Sstevel@tonic-gate pdip, comp, cur_power(cp), level);
29830Sstevel@tonic-gate (void) pm_check_and_resume(dip,
29840Sstevel@tonic-gate comp, cur_power(cp), level);
29850Sstevel@tonic-gate
29860Sstevel@tonic-gate /*
29870Sstevel@tonic-gate * Stash the old power index, update curpwr, and flag
29880Sstevel@tonic-gate * that the total power state needs to be synched.
29890Sstevel@tonic-gate */
29900Sstevel@tonic-gate cp->pmc_flags |= PM_PHC_WHILE_SET_POWER;
29910Sstevel@tonic-gate /*
29920Sstevel@tonic-gate * Several pm_power_has_changed calls could arrive
29930Sstevel@tonic-gate * while the set power path remains blocked. Keep the
29940Sstevel@tonic-gate * oldest old power and the newest new power of any
29950Sstevel@tonic-gate * sequence of phc calls which arrive during deadlock.
29960Sstevel@tonic-gate */
29970Sstevel@tonic-gate if (cp->pmc_phc_pwr == PM_LEVEL_UNKNOWN)
29980Sstevel@tonic-gate cp->pmc_phc_pwr = cp->pmc_cur_pwr;
29990Sstevel@tonic-gate cp->pmc_cur_pwr =
30000Sstevel@tonic-gate pm_level_to_index(dip, cp, level);
30010Sstevel@tonic-gate PMD(PMD_PHC, ("%s: deadlock for %s@%s(%s#%d), comp=%d, "
30020Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
30030Sstevel@tonic-gate return (DDI_SUCCESS);
30040Sstevel@tonic-gate } else
30050Sstevel@tonic-gate if (blocked) { /* blocked, but different cmpt? */
30060Sstevel@tonic-gate if (!ndi_devi_tryenter(pdip, &pcirc)) {
30070Sstevel@tonic-gate cmn_err(CE_NOTE,
30080Sstevel@tonic-gate "!pm: parent kuc not updated due "
30090Sstevel@tonic-gate "to possible deadlock.\n");
30100Sstevel@tonic-gate return (pm_phc_impl(dip,
30114780Smh27603 comp, level, 1));
30120Sstevel@tonic-gate }
30130Sstevel@tonic-gate old_level = cur_power(cp);
30140Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip) &&
30150Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0) &&
30160Sstevel@tonic-gate POWERING_ON(old_level, level))
30170Sstevel@tonic-gate pm_hold_power(pdip);
30180Sstevel@tonic-gate ret = pm_phc_impl(dip, comp, level, 1);
30190Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
30200Sstevel@tonic-gate if ((!PM_ISBC(dip) ||
30210Sstevel@tonic-gate comp == 0) && level == 0 &&
30220Sstevel@tonic-gate old_level != PM_LEVEL_UNKNOWN)
30230Sstevel@tonic-gate pm_rele_power(pdip);
30240Sstevel@tonic-gate }
30250Sstevel@tonic-gate ndi_devi_exit(pdip, pcirc);
30260Sstevel@tonic-gate /* child lock not held: deadlock */
30270Sstevel@tonic-gate return (ret);
30280Sstevel@tonic-gate }
30290Sstevel@tonic-gate delay(1);
30300Sstevel@tonic-gate PMD(PMD_PHC, ("%s: try lock again\n", pmf))
30310Sstevel@tonic-gate }
30320Sstevel@tonic-gate
30330Sstevel@tonic-gate /* non-deadlock case */
30340Sstevel@tonic-gate old_level = cur_power(cp);
30350Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip) &&
30360Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0) && POWERING_ON(old_level, level))
30370Sstevel@tonic-gate pm_hold_power(pdip);
30380Sstevel@tonic-gate ret = pm_phc_impl(dip, comp, level, 1);
30390Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
30400Sstevel@tonic-gate if ((!PM_ISBC(dip) || comp == 0) && level == 0 &&
30410Sstevel@tonic-gate old_level != PM_LEVEL_UNKNOWN)
30420Sstevel@tonic-gate pm_rele_power(pdip);
30430Sstevel@tonic-gate }
30440Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
30450Sstevel@tonic-gate ndi_devi_exit(pdip, pcirc);
30460Sstevel@tonic-gate return (ret);
30470Sstevel@tonic-gate }
30480Sstevel@tonic-gate
30490Sstevel@tonic-gate /*
30500Sstevel@tonic-gate * Account for power changes to a component of the the console frame buffer.
30510Sstevel@tonic-gate * If lowering power from full (or "unkown", which is treatd as full)
30520Sstevel@tonic-gate * we will increment the "components off" count of the fb device.
30530Sstevel@tonic-gate * Subsequent lowering of the same component doesn't affect the count. If
30540Sstevel@tonic-gate * raising a component back to full power, we will decrement the count.
30550Sstevel@tonic-gate *
30560Sstevel@tonic-gate * Return: the increment value for pm_cfb_comps_off (-1, 0, or 1)
30570Sstevel@tonic-gate */
30580Sstevel@tonic-gate static int
calc_cfb_comps_incr(dev_info_t * dip,int cmpt,int old,int new)30590Sstevel@tonic-gate calc_cfb_comps_incr(dev_info_t *dip, int cmpt, int old, int new)
30600Sstevel@tonic-gate {
30610Sstevel@tonic-gate struct pm_component *cp = PM_CP(dip, cmpt);
30620Sstevel@tonic-gate int on = (old == PM_LEVEL_UNKNOWN || old == cp->pmc_norm_pwr);
30630Sstevel@tonic-gate int want_normal = (new == cp->pmc_norm_pwr);
30640Sstevel@tonic-gate int incr = 0;
30650Sstevel@tonic-gate
30660Sstevel@tonic-gate if (on && !want_normal)
30670Sstevel@tonic-gate incr = 1;
30680Sstevel@tonic-gate else if (!on && want_normal)
30690Sstevel@tonic-gate incr = -1;
30700Sstevel@tonic-gate return (incr);
30710Sstevel@tonic-gate }
30720Sstevel@tonic-gate
30730Sstevel@tonic-gate /*
30740Sstevel@tonic-gate * Adjust the count of console frame buffer components < full power.
30750Sstevel@tonic-gate */
30760Sstevel@tonic-gate static void
update_comps_off(int incr,dev_info_t * dip)30770Sstevel@tonic-gate update_comps_off(int incr, dev_info_t *dip)
30780Sstevel@tonic-gate {
30790Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
30800Sstevel@tonic-gate pm_cfb_comps_off += incr;
30810Sstevel@tonic-gate ASSERT(pm_cfb_comps_off <= PM_NUMCMPTS(dip));
30820Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
30830Sstevel@tonic-gate }
30840Sstevel@tonic-gate
30850Sstevel@tonic-gate /*
30860Sstevel@tonic-gate * Update the power state in the framework (via the ppm). The 'notify'
30870Sstevel@tonic-gate * argument tells whether to notify watchers. Power lock is already held.
30880Sstevel@tonic-gate */
30890Sstevel@tonic-gate static int
pm_phc_impl(dev_info_t * dip,int comp,int level,int notify)30900Sstevel@tonic-gate pm_phc_impl(dev_info_t *dip, int comp, int level, int notify)
30910Sstevel@tonic-gate {
30920Sstevel@tonic-gate PMD_FUNC(pmf, "phc_impl")
30930Sstevel@tonic-gate power_req_t power_req;
30940Sstevel@tonic-gate int i, dodeps = 0;
30950Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
30960Sstevel@tonic-gate int result;
30970Sstevel@tonic-gate int old_level;
30980Sstevel@tonic-gate struct pm_component *cp;
30990Sstevel@tonic-gate int incr = 0;
31000Sstevel@tonic-gate dev_info_t *ppm = (dev_info_t *)DEVI(dip)->devi_pm_ppm;
31010Sstevel@tonic-gate int work_type = 0;
31020Sstevel@tonic-gate char *pathbuf;
31030Sstevel@tonic-gate
31040Sstevel@tonic-gate /* Must use "official" power level for this test. */
31050Sstevel@tonic-gate cp = PM_CP(dip, comp);
31060Sstevel@tonic-gate old_level = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
31070Sstevel@tonic-gate cp->pmc_phc_pwr : cp->pmc_cur_pwr);
31080Sstevel@tonic-gate if (old_level != PM_LEVEL_UNKNOWN)
31090Sstevel@tonic-gate old_level = cp->pmc_comp.pmc_lvals[old_level];
31100Sstevel@tonic-gate
31110Sstevel@tonic-gate if (level == old_level) {
31120Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), comp=%d is already at "
31130Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
31140Sstevel@tonic-gate return (DDI_SUCCESS);
31150Sstevel@tonic-gate }
31160Sstevel@tonic-gate
31170Sstevel@tonic-gate /*
31180Sstevel@tonic-gate * Tell ppm about this.
31190Sstevel@tonic-gate */
31200Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY;
31210Sstevel@tonic-gate power_req.req.ppm_notify_level_req.who = dip;
31220Sstevel@tonic-gate power_req.req.ppm_notify_level_req.cmpt = comp;
31230Sstevel@tonic-gate power_req.req.ppm_notify_level_req.new_level = level;
31240Sstevel@tonic-gate power_req.req.ppm_notify_level_req.old_level = old_level;
31250Sstevel@tonic-gate if (pm_ctlops(ppm, dip, DDI_CTLOPS_POWER, &power_req,
31260Sstevel@tonic-gate &result) == DDI_FAILURE) {
31270Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: pm_ctlops %s@%s(%s#%d) to %d failed\n",
31280Sstevel@tonic-gate pmf, PM_DEVICE(dip), level))
31290Sstevel@tonic-gate return (DDI_FAILURE);
31300Sstevel@tonic-gate }
31310Sstevel@tonic-gate
31320Sstevel@tonic-gate if (PM_IS_CFB(dip)) {
31330Sstevel@tonic-gate incr = calc_cfb_comps_incr(dip, comp, old_level, level);
31340Sstevel@tonic-gate
31350Sstevel@tonic-gate if (incr) {
31360Sstevel@tonic-gate update_comps_off(incr, dip);
31370Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) comp=%d %d->%d "
31380Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
31390Sstevel@tonic-gate comp, old_level, level, pm_cfb_comps_off))
31400Sstevel@tonic-gate }
31410Sstevel@tonic-gate }
31420Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, comp), level);
31430Sstevel@tonic-gate result = DDI_SUCCESS;
31440Sstevel@tonic-gate
31450Sstevel@tonic-gate if (notify) {
31460Sstevel@tonic-gate if (pdip && PM_WANTS_NOTIFICATION(pdip))
31470Sstevel@tonic-gate pm_notify_parent(dip, pdip, comp, old_level, level);
31480Sstevel@tonic-gate (void) pm_check_and_resume(dip, comp, old_level, level);
31490Sstevel@tonic-gate }
31500Sstevel@tonic-gate
31510Sstevel@tonic-gate /*
31520Sstevel@tonic-gate * Decrement the dependency kidsup count if we turn a device
31530Sstevel@tonic-gate * off.
31540Sstevel@tonic-gate */
31550Sstevel@tonic-gate if (POWERING_OFF(old_level, level)) {
31560Sstevel@tonic-gate dodeps = 1;
31570Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
31580Sstevel@tonic-gate cp = PM_CP(dip, i);
31590Sstevel@tonic-gate if (cur_power(cp)) {
31600Sstevel@tonic-gate dodeps = 0;
31610Sstevel@tonic-gate break;
31620Sstevel@tonic-gate }
31630Sstevel@tonic-gate }
31640Sstevel@tonic-gate if (dodeps)
31650Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_OFF;
31660Sstevel@tonic-gate }
31670Sstevel@tonic-gate
31680Sstevel@tonic-gate /*
31690Sstevel@tonic-gate * Increment if we turn it on. Check to see
31700Sstevel@tonic-gate * if other comps are already on, if so,
31710Sstevel@tonic-gate * dont increment.
31720Sstevel@tonic-gate */
31730Sstevel@tonic-gate if (POWERING_ON(old_level, level)) {
31740Sstevel@tonic-gate dodeps = 1;
31750Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
31760Sstevel@tonic-gate cp = PM_CP(dip, i);
31770Sstevel@tonic-gate if (comp == i)
31780Sstevel@tonic-gate continue;
31790Sstevel@tonic-gate /* -1 also treated as 0 in this case */
31800Sstevel@tonic-gate if (cur_power(cp) > 0) {
31810Sstevel@tonic-gate dodeps = 0;
31820Sstevel@tonic-gate break;
31830Sstevel@tonic-gate }
31840Sstevel@tonic-gate }
31850Sstevel@tonic-gate if (dodeps)
31860Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_ON;
31870Sstevel@tonic-gate }
31880Sstevel@tonic-gate
31890Sstevel@tonic-gate if (dodeps) {
31900Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
31910Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
31920Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, NULL,
31930Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0);
31940Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
31950Sstevel@tonic-gate }
31960Sstevel@tonic-gate
31970Sstevel@tonic-gate if (notify && (level != old_level) && pm_watchers()) {
31980Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock);
31990Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, level, old_level,
32000Sstevel@tonic-gate PM_CANBLOCK_BLOCK);
32010Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock);
32020Sstevel@tonic-gate }
32030Sstevel@tonic-gate
32040Sstevel@tonic-gate PMD(PMD_RESCAN, ("%s: %s@%s(%s#%d): pm_rescan\n", pmf, PM_DEVICE(dip)))
32050Sstevel@tonic-gate pm_rescan(dip);
32060Sstevel@tonic-gate return (DDI_SUCCESS);
32070Sstevel@tonic-gate }
32080Sstevel@tonic-gate
32090Sstevel@tonic-gate /*
32100Sstevel@tonic-gate * This function is called at startup time to notify pm of the existence
32110Sstevel@tonic-gate * of any platform power managers for this platform. As a result of
32120Sstevel@tonic-gate * this registration, each function provided will be called each time
32130Sstevel@tonic-gate * a device node is attached, until one returns true, and it must claim the
32140Sstevel@tonic-gate * device node (by returning non-zero) if it wants to be involved in the
32150Sstevel@tonic-gate * node's power management. If it does claim the node, then it will
32160Sstevel@tonic-gate * subsequently be notified of attach and detach events.
32170Sstevel@tonic-gate *
32180Sstevel@tonic-gate */
32190Sstevel@tonic-gate
32200Sstevel@tonic-gate int
pm_register_ppm(int (* func)(dev_info_t *),dev_info_t * dip)32210Sstevel@tonic-gate pm_register_ppm(int (*func)(dev_info_t *), dev_info_t *dip)
32220Sstevel@tonic-gate {
32230Sstevel@tonic-gate PMD_FUNC(pmf, "register_ppm")
32240Sstevel@tonic-gate struct ppm_callbacks *ppmcp;
32250Sstevel@tonic-gate pm_component_t *cp;
32260Sstevel@tonic-gate int i, pwr, result, circ;
32270Sstevel@tonic-gate power_req_t power_req;
32280Sstevel@tonic-gate struct ppm_notify_level_req *p = &power_req.req.ppm_notify_level_req;
32290Sstevel@tonic-gate void pm_ppm_claim(dev_info_t *);
32300Sstevel@tonic-gate
32310Sstevel@tonic-gate mutex_enter(&ppm_lock);
32320Sstevel@tonic-gate ppmcp = ppm_callbacks;
32330Sstevel@tonic-gate for (i = 0; i < MAX_PPM_HANDLERS; i++, ppmcp++) {
32340Sstevel@tonic-gate if (ppmcp->ppmc_func == NULL) {
32350Sstevel@tonic-gate ppmcp->ppmc_func = func;
32360Sstevel@tonic-gate ppmcp->ppmc_dip = dip;
32370Sstevel@tonic-gate break;
32380Sstevel@tonic-gate }
32390Sstevel@tonic-gate }
32400Sstevel@tonic-gate mutex_exit(&ppm_lock);
32410Sstevel@tonic-gate
32420Sstevel@tonic-gate if (i >= MAX_PPM_HANDLERS)
32430Sstevel@tonic-gate return (DDI_FAILURE);
32440Sstevel@tonic-gate while ((dip = ddi_get_parent(dip)) != NULL) {
32455295Srandyf if (dip != ddi_root_node() && PM_GET_PM_INFO(dip) == NULL)
32460Sstevel@tonic-gate continue;
32470Sstevel@tonic-gate pm_ppm_claim(dip);
32485295Srandyf /* don't bother with the not power-manageable nodes */
32495295Srandyf if (pm_ppm_claimed(dip) && PM_GET_PM_INFO(dip)) {
32500Sstevel@tonic-gate /*
32510Sstevel@tonic-gate * Tell ppm about this.
32520Sstevel@tonic-gate */
32530Sstevel@tonic-gate power_req.request_type = PMR_PPM_POWER_CHANGE_NOTIFY;
32540Sstevel@tonic-gate p->old_level = PM_LEVEL_UNKNOWN;
32550Sstevel@tonic-gate p->who = dip;
32560Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
32570Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
32580Sstevel@tonic-gate cp = PM_CP(dip, i);
32590Sstevel@tonic-gate pwr = cp->pmc_cur_pwr;
32600Sstevel@tonic-gate if (pwr != PM_LEVEL_UNKNOWN) {
32610Sstevel@tonic-gate p->cmpt = i;
32620Sstevel@tonic-gate p->new_level = cur_power(cp);
32630Sstevel@tonic-gate p->old_level = PM_LEVEL_UNKNOWN;
32640Sstevel@tonic-gate if (pm_ctlops(PPM(dip), dip,
32650Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req,
32660Sstevel@tonic-gate &result) == DDI_FAILURE) {
32670Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: pc "
32680Sstevel@tonic-gate "%s@%s(%s#%d) to %d "
32690Sstevel@tonic-gate "fails\n", pmf,
32700Sstevel@tonic-gate PM_DEVICE(dip), pwr))
32710Sstevel@tonic-gate }
32720Sstevel@tonic-gate }
32730Sstevel@tonic-gate }
32740Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
32750Sstevel@tonic-gate }
32760Sstevel@tonic-gate }
32770Sstevel@tonic-gate return (DDI_SUCCESS);
32780Sstevel@tonic-gate }
32790Sstevel@tonic-gate
32800Sstevel@tonic-gate /*
32810Sstevel@tonic-gate * Call the ppm's that have registered and adjust the devinfo struct as
32820Sstevel@tonic-gate * appropriate. First one to claim it gets it. The sets of devices claimed
32830Sstevel@tonic-gate * by each ppm are assumed to be disjoint.
32840Sstevel@tonic-gate */
32850Sstevel@tonic-gate void
pm_ppm_claim(dev_info_t * dip)32860Sstevel@tonic-gate pm_ppm_claim(dev_info_t *dip)
32870Sstevel@tonic-gate {
32880Sstevel@tonic-gate struct ppm_callbacks *ppmcp;
32890Sstevel@tonic-gate
32900Sstevel@tonic-gate if (PPM(dip)) {
32910Sstevel@tonic-gate return;
32920Sstevel@tonic-gate }
32930Sstevel@tonic-gate mutex_enter(&ppm_lock);
32940Sstevel@tonic-gate for (ppmcp = ppm_callbacks; ppmcp->ppmc_func; ppmcp++) {
32950Sstevel@tonic-gate if ((*ppmcp->ppmc_func)(dip)) {
32960Sstevel@tonic-gate DEVI(dip)->devi_pm_ppm =
32970Sstevel@tonic-gate (struct dev_info *)ppmcp->ppmc_dip;
32980Sstevel@tonic-gate mutex_exit(&ppm_lock);
32990Sstevel@tonic-gate return;
33000Sstevel@tonic-gate }
33010Sstevel@tonic-gate }
33020Sstevel@tonic-gate mutex_exit(&ppm_lock);
33030Sstevel@tonic-gate }
33040Sstevel@tonic-gate
33050Sstevel@tonic-gate /*
33060Sstevel@tonic-gate * Node is being detached so stop autopm until we see if it succeeds, in which
33070Sstevel@tonic-gate * case pm_stop will be called. For backwards compatible devices we bring the
33080Sstevel@tonic-gate * device up to full power on the assumption the detach will succeed.
33090Sstevel@tonic-gate */
33100Sstevel@tonic-gate void
pm_detaching(dev_info_t * dip)33110Sstevel@tonic-gate pm_detaching(dev_info_t *dip)
33120Sstevel@tonic-gate {
33130Sstevel@tonic-gate PMD_FUNC(pmf, "detaching")
33140Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
33150Sstevel@tonic-gate int iscons;
33160Sstevel@tonic-gate
33170Sstevel@tonic-gate PMD(PMD_REMDEV, ("%s: %s@%s(%s#%d), %d comps\n", pmf, PM_DEVICE(dip),
33180Sstevel@tonic-gate PM_NUMCMPTS(dip)))
33190Sstevel@tonic-gate if (info == NULL)
33200Sstevel@tonic-gate return;
33210Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip));
33220Sstevel@tonic-gate PM_LOCK_DIP(dip);
33230Sstevel@tonic-gate info->pmi_dev_pm_state |= PM_DETACHING;
33240Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
33250Sstevel@tonic-gate if (!PM_ISBC(dip))
33260Sstevel@tonic-gate pm_scan_stop(dip);
33270Sstevel@tonic-gate
33280Sstevel@tonic-gate /*
33290Sstevel@tonic-gate * console and old-style devices get brought up when detaching.
33300Sstevel@tonic-gate */
33310Sstevel@tonic-gate iscons = PM_IS_CFB(dip);
33320Sstevel@tonic-gate if (iscons || PM_ISBC(dip)) {
33330Sstevel@tonic-gate (void) pm_all_to_normal(dip, PM_CANBLOCK_BYPASS);
33340Sstevel@tonic-gate if (iscons) {
33350Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
33360Sstevel@tonic-gate while (cfb_inuse) {
33370Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
33380Sstevel@tonic-gate PMD(PMD_CFB, ("%s: delay; cfb_inuse\n", pmf))
33390Sstevel@tonic-gate delay(1);
33400Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
33410Sstevel@tonic-gate }
33420Sstevel@tonic-gate ASSERT(cfb_dip_detaching == NULL);
33430Sstevel@tonic-gate ASSERT(cfb_dip);
33440Sstevel@tonic-gate cfb_dip_detaching = cfb_dip; /* case detach fails */
33450Sstevel@tonic-gate cfb_dip = NULL;
33460Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
33470Sstevel@tonic-gate }
33480Sstevel@tonic-gate }
33490Sstevel@tonic-gate }
33500Sstevel@tonic-gate
33510Sstevel@tonic-gate /*
33520Sstevel@tonic-gate * Node failed to detach. If it used to be autopm'd, make it so again.
33530Sstevel@tonic-gate */
33540Sstevel@tonic-gate void
pm_detach_failed(dev_info_t * dip)33550Sstevel@tonic-gate pm_detach_failed(dev_info_t *dip)
33560Sstevel@tonic-gate {
33570Sstevel@tonic-gate PMD_FUNC(pmf, "detach_failed")
33580Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
33590Sstevel@tonic-gate int pm_all_at_normal(dev_info_t *);
33600Sstevel@tonic-gate
33610Sstevel@tonic-gate if (info == NULL)
33620Sstevel@tonic-gate return;
33630Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip));
33640Sstevel@tonic-gate if (info->pmi_dev_pm_state & PM_DETACHING) {
33650Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_DETACHING;
33660Sstevel@tonic-gate if (info->pmi_dev_pm_state & PM_ALLNORM_DEFERRED) {
33670Sstevel@tonic-gate /* Make sure the operation is still needed */
33680Sstevel@tonic-gate if (!pm_all_at_normal(dip)) {
33690Sstevel@tonic-gate if (pm_all_to_normal(dip,
33700Sstevel@tonic-gate PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
33710Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: could not bring "
33720Sstevel@tonic-gate "%s@%s(%s#%d) to normal\n", pmf,
33730Sstevel@tonic-gate PM_DEVICE(dip)))
33740Sstevel@tonic-gate }
33750Sstevel@tonic-gate }
33760Sstevel@tonic-gate info->pmi_dev_pm_state &= ~PM_ALLNORM_DEFERRED;
33770Sstevel@tonic-gate }
33780Sstevel@tonic-gate }
33790Sstevel@tonic-gate if (!PM_ISBC(dip)) {
33800Sstevel@tonic-gate mutex_enter(&pm_scan_lock);
33813028Smh27603 if (PM_SCANABLE(dip))
33820Sstevel@tonic-gate pm_scan_init(dip);
33830Sstevel@tonic-gate mutex_exit(&pm_scan_lock);
33840Sstevel@tonic-gate pm_rescan(dip);
33850Sstevel@tonic-gate }
33860Sstevel@tonic-gate }
33870Sstevel@tonic-gate
33880Sstevel@tonic-gate /* generic Backwards Compatible component */
33890Sstevel@tonic-gate static char *bc_names[] = {"off", "on"};
33900Sstevel@tonic-gate
33910Sstevel@tonic-gate static pm_comp_t bc_comp = {"unknown", 2, NULL, NULL, &bc_names[0]};
33920Sstevel@tonic-gate
33930Sstevel@tonic-gate static void
e_pm_default_levels(dev_info_t * dip,pm_component_t * cp,int norm)33940Sstevel@tonic-gate e_pm_default_levels(dev_info_t *dip, pm_component_t *cp, int norm)
33950Sstevel@tonic-gate {
33960Sstevel@tonic-gate pm_comp_t *pmc;
33970Sstevel@tonic-gate pmc = &cp->pmc_comp;
33980Sstevel@tonic-gate pmc->pmc_numlevels = 2;
33990Sstevel@tonic-gate pmc->pmc_lvals[0] = 0;
34000Sstevel@tonic-gate pmc->pmc_lvals[1] = norm;
34010Sstevel@tonic-gate e_pm_set_cur_pwr(dip, cp, norm);
34020Sstevel@tonic-gate }
34030Sstevel@tonic-gate
34040Sstevel@tonic-gate static void
e_pm_default_components(dev_info_t * dip,int cmpts)34050Sstevel@tonic-gate e_pm_default_components(dev_info_t *dip, int cmpts)
34060Sstevel@tonic-gate {
34070Sstevel@tonic-gate int i;
34080Sstevel@tonic-gate pm_component_t *p = DEVI(dip)->devi_pm_components;
34090Sstevel@tonic-gate
34100Sstevel@tonic-gate p = DEVI(dip)->devi_pm_components;
34110Sstevel@tonic-gate for (i = 0; i < cmpts; i++, p++) {
34120Sstevel@tonic-gate p->pmc_comp = bc_comp; /* struct assignment */
34130Sstevel@tonic-gate p->pmc_comp.pmc_lvals = kmem_zalloc(2 * sizeof (int),
34140Sstevel@tonic-gate KM_SLEEP);
34150Sstevel@tonic-gate p->pmc_comp.pmc_thresh = kmem_alloc(2 * sizeof (int),
34160Sstevel@tonic-gate KM_SLEEP);
34170Sstevel@tonic-gate p->pmc_comp.pmc_numlevels = 2;
34180Sstevel@tonic-gate p->pmc_comp.pmc_thresh[0] = INT_MAX;
34190Sstevel@tonic-gate p->pmc_comp.pmc_thresh[1] = INT_MAX;
34200Sstevel@tonic-gate }
34210Sstevel@tonic-gate }
34220Sstevel@tonic-gate
34230Sstevel@tonic-gate /*
34240Sstevel@tonic-gate * Called from functions that require components to exist already to allow
34250Sstevel@tonic-gate * for their creation by parsing the pm-components property.
34260Sstevel@tonic-gate * Device will not be power managed as a result of this call
34270Sstevel@tonic-gate * No locking needed because we're single threaded by the ndi_devi_enter
34280Sstevel@tonic-gate * done while attaching, and the device isn't visible until after it has
34290Sstevel@tonic-gate * attached
34300Sstevel@tonic-gate */
34310Sstevel@tonic-gate int
pm_premanage(dev_info_t * dip,int style)34320Sstevel@tonic-gate pm_premanage(dev_info_t *dip, int style)
34330Sstevel@tonic-gate {
34340Sstevel@tonic-gate PMD_FUNC(pmf, "premanage")
34350Sstevel@tonic-gate pm_comp_t *pcp, *compp;
34360Sstevel@tonic-gate int cmpts, i, norm, error;
34370Sstevel@tonic-gate pm_component_t *p = DEVI(dip)->devi_pm_components;
34380Sstevel@tonic-gate pm_comp_t *pm_autoconfig(dev_info_t *, int *);
34390Sstevel@tonic-gate
34400Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
34410Sstevel@tonic-gate /*
34420Sstevel@tonic-gate * If this dip has already been processed, don't mess with it
34430Sstevel@tonic-gate */
34440Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE)
34450Sstevel@tonic-gate return (DDI_SUCCESS);
34460Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_FAILED) {
34470Sstevel@tonic-gate return (DDI_FAILURE);
34480Sstevel@tonic-gate }
34490Sstevel@tonic-gate /*
34500Sstevel@tonic-gate * Look up pm-components property and create components accordingly
34510Sstevel@tonic-gate * If that fails, fall back to backwards compatibility
34520Sstevel@tonic-gate */
34530Sstevel@tonic-gate if ((compp = pm_autoconfig(dip, &error)) == NULL) {
34540Sstevel@tonic-gate /*
34550Sstevel@tonic-gate * If error is set, the property existed but was not well formed
34560Sstevel@tonic-gate */
34570Sstevel@tonic-gate if (error || (style == PM_STYLE_NEW)) {
34580Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_FAILED;
34590Sstevel@tonic-gate return (DDI_FAILURE);
34600Sstevel@tonic-gate }
34610Sstevel@tonic-gate /*
34620Sstevel@tonic-gate * If they don't have the pm-components property, then we
34630Sstevel@tonic-gate * want the old "no pm until PM_SET_DEVICE_THRESHOLDS ioctl"
34640Sstevel@tonic-gate * behavior driver must have called pm_create_components, and
34650Sstevel@tonic-gate * we need to flesh out dummy components
34660Sstevel@tonic-gate */
34670Sstevel@tonic-gate if ((cmpts = PM_NUMCMPTS(dip)) == 0) {
34680Sstevel@tonic-gate /*
34690Sstevel@tonic-gate * Not really failure, but we don't want the
34700Sstevel@tonic-gate * caller to treat it as success
34710Sstevel@tonic-gate */
34720Sstevel@tonic-gate return (DDI_FAILURE);
34730Sstevel@tonic-gate }
34740Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_BC;
34750Sstevel@tonic-gate e_pm_default_components(dip, cmpts);
34760Sstevel@tonic-gate for (i = 0; i < cmpts; i++) {
34770Sstevel@tonic-gate /*
34780Sstevel@tonic-gate * if normal power not set yet, we don't really know
34790Sstevel@tonic-gate * what *ANY* of the power values are. If normal
34800Sstevel@tonic-gate * power is set, then we assume for this backwards
34810Sstevel@tonic-gate * compatible case that the values are 0, normal power.
34820Sstevel@tonic-gate */
34830Sstevel@tonic-gate norm = pm_get_normal_power(dip, i);
34840Sstevel@tonic-gate if (norm == (uint_t)-1) {
34850Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d)[%d]\n", pmf,
34860Sstevel@tonic-gate PM_DEVICE(dip), i))
34870Sstevel@tonic-gate return (DDI_FAILURE);
34880Sstevel@tonic-gate }
34890Sstevel@tonic-gate /*
34900Sstevel@tonic-gate * Components of BC devices start at their normal power,
34910Sstevel@tonic-gate * so count them to be not at their lowest power.
34920Sstevel@tonic-gate */
34930Sstevel@tonic-gate PM_INCR_NOTLOWEST(dip);
34940Sstevel@tonic-gate e_pm_default_levels(dip, PM_CP(dip, i), norm);
34950Sstevel@tonic-gate }
34960Sstevel@tonic-gate } else {
34970Sstevel@tonic-gate /*
34980Sstevel@tonic-gate * e_pm_create_components was called from pm_autoconfig(), it
34990Sstevel@tonic-gate * creates components with no descriptions (or known levels)
35000Sstevel@tonic-gate */
35010Sstevel@tonic-gate cmpts = PM_NUMCMPTS(dip);
35020Sstevel@tonic-gate ASSERT(cmpts != 0);
35030Sstevel@tonic-gate pcp = compp;
35040Sstevel@tonic-gate p = DEVI(dip)->devi_pm_components;
35050Sstevel@tonic-gate for (i = 0; i < cmpts; i++, p++) {
35060Sstevel@tonic-gate p->pmc_comp = *pcp++; /* struct assignment */
35070Sstevel@tonic-gate ASSERT(PM_CP(dip, i)->pmc_cur_pwr == 0);
35080Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN);
35090Sstevel@tonic-gate }
35103028Smh27603 if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
35113028Smh27603 pm_set_device_threshold(dip, pm_cpu_idle_threshold,
35123028Smh27603 PMC_CPU_THRESH);
35133028Smh27603 else
35143028Smh27603 pm_set_device_threshold(dip, pm_system_idle_threshold,
35153028Smh27603 PMC_DEF_THRESH);
35160Sstevel@tonic-gate kmem_free(compp, cmpts * sizeof (pm_comp_t));
35170Sstevel@tonic-gate }
35180Sstevel@tonic-gate return (DDI_SUCCESS);
35190Sstevel@tonic-gate }
35200Sstevel@tonic-gate
35210Sstevel@tonic-gate /*
35220Sstevel@tonic-gate * Called from during or after the device's attach to let us know it is ready
35230Sstevel@tonic-gate * to play autopm. Look up the pm model and manage the device accordingly.
35240Sstevel@tonic-gate * Returns system call errno value.
35250Sstevel@tonic-gate * If DDI_ATTACH and DDI_DETACH were in same namespace, this would be
35260Sstevel@tonic-gate * a little cleaner
35270Sstevel@tonic-gate *
35280Sstevel@tonic-gate * Called with dip lock held, return with dip lock unheld.
35290Sstevel@tonic-gate */
35300Sstevel@tonic-gate
35310Sstevel@tonic-gate int
e_pm_manage(dev_info_t * dip,int style)35320Sstevel@tonic-gate e_pm_manage(dev_info_t *dip, int style)
35330Sstevel@tonic-gate {
35340Sstevel@tonic-gate PMD_FUNC(pmf, "e_manage")
35350Sstevel@tonic-gate pm_info_t *info;
35360Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
35370Sstevel@tonic-gate int pm_thresh_specd(dev_info_t *);
35380Sstevel@tonic-gate int count;
35390Sstevel@tonic-gate char *pathbuf;
35400Sstevel@tonic-gate
35410Sstevel@tonic-gate if (pm_premanage(dip, style) != DDI_SUCCESS) {
35420Sstevel@tonic-gate return (DDI_FAILURE);
35430Sstevel@tonic-gate }
35440Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
35450Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip) == NULL);
35460Sstevel@tonic-gate info = kmem_zalloc(sizeof (pm_info_t), KM_SLEEP);
35470Sstevel@tonic-gate
35480Sstevel@tonic-gate /*
35490Sstevel@tonic-gate * Now set up parent's kidsupcnt. BC nodes are assumed to start
35500Sstevel@tonic-gate * out at their normal power, so they are "up", others start out
35510Sstevel@tonic-gate * unknown, which is effectively "up". Parent which want notification
35520Sstevel@tonic-gate * get kidsupcnt of 0 always.
35530Sstevel@tonic-gate */
35540Sstevel@tonic-gate count = (PM_ISBC(dip)) ? 1 : PM_NUMCMPTS(dip);
35550Sstevel@tonic-gate if (count && pdip && !PM_WANTS_NOTIFICATION(pdip))
35560Sstevel@tonic-gate e_pm_hold_rele_power(pdip, count);
35570Sstevel@tonic-gate
35580Sstevel@tonic-gate pm_set_pm_info(dip, info);
35590Sstevel@tonic-gate /*
35600Sstevel@tonic-gate * Apply any recorded thresholds
35610Sstevel@tonic-gate */
35620Sstevel@tonic-gate (void) pm_thresh_specd(dip);
35630Sstevel@tonic-gate
35640Sstevel@tonic-gate /*
35650Sstevel@tonic-gate * Do dependency processing.
35660Sstevel@tonic-gate */
35670Sstevel@tonic-gate pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
35680Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
35690Sstevel@tonic-gate pm_dispatch_to_dep_thread(PM_DEP_WK_ATTACH, pathbuf, pathbuf,
35700Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0);
35710Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
35720Sstevel@tonic-gate
35730Sstevel@tonic-gate if (!PM_ISBC(dip)) {
35740Sstevel@tonic-gate mutex_enter(&pm_scan_lock);
35753028Smh27603 if (PM_SCANABLE(dip)) {
35760Sstevel@tonic-gate pm_scan_init(dip);
35770Sstevel@tonic-gate mutex_exit(&pm_scan_lock);
35780Sstevel@tonic-gate pm_rescan(dip);
35790Sstevel@tonic-gate } else {
35800Sstevel@tonic-gate mutex_exit(&pm_scan_lock);
35810Sstevel@tonic-gate }
35820Sstevel@tonic-gate }
35830Sstevel@tonic-gate return (0);
35840Sstevel@tonic-gate }
35850Sstevel@tonic-gate
35860Sstevel@tonic-gate /*
35870Sstevel@tonic-gate * This is the obsolete exported interface for a driver to find out its
35880Sstevel@tonic-gate * "normal" (max) power.
35890Sstevel@tonic-gate * We only get components destroyed while no power management is
35900Sstevel@tonic-gate * going on (and the device is detached), so we don't need a mutex here
35910Sstevel@tonic-gate */
35920Sstevel@tonic-gate int
pm_get_normal_power(dev_info_t * dip,int comp)35930Sstevel@tonic-gate pm_get_normal_power(dev_info_t *dip, int comp)
35940Sstevel@tonic-gate {
35950Sstevel@tonic-gate
35960Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip)) {
35970Sstevel@tonic-gate return (PM_CP(dip, comp)->pmc_norm_pwr);
35980Sstevel@tonic-gate }
35990Sstevel@tonic-gate return (DDI_FAILURE);
36000Sstevel@tonic-gate }
36010Sstevel@tonic-gate
36020Sstevel@tonic-gate /*
36030Sstevel@tonic-gate * Fetches the current power level. Return DDI_SUCCESS or DDI_FAILURE.
36040Sstevel@tonic-gate */
36050Sstevel@tonic-gate int
pm_get_current_power(dev_info_t * dip,int comp,int * levelp)36060Sstevel@tonic-gate pm_get_current_power(dev_info_t *dip, int comp, int *levelp)
36070Sstevel@tonic-gate {
36080Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip)) {
36090Sstevel@tonic-gate *levelp = PM_CURPOWER(dip, comp);
36100Sstevel@tonic-gate return (DDI_SUCCESS);
36110Sstevel@tonic-gate }
36120Sstevel@tonic-gate return (DDI_FAILURE);
36130Sstevel@tonic-gate }
36140Sstevel@tonic-gate
36150Sstevel@tonic-gate /*
36160Sstevel@tonic-gate * Returns current threshold of indicated component
36170Sstevel@tonic-gate */
36180Sstevel@tonic-gate static int
cur_threshold(dev_info_t * dip,int comp)36190Sstevel@tonic-gate cur_threshold(dev_info_t *dip, int comp)
36200Sstevel@tonic-gate {
36210Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, comp);
36220Sstevel@tonic-gate int pwr;
36230Sstevel@tonic-gate
36240Sstevel@tonic-gate if (PM_ISBC(dip)) {
36250Sstevel@tonic-gate /*
36260Sstevel@tonic-gate * backwards compatible nodes only have one threshold
36270Sstevel@tonic-gate */
36280Sstevel@tonic-gate return (cp->pmc_comp.pmc_thresh[1]);
36290Sstevel@tonic-gate }
36300Sstevel@tonic-gate pwr = cp->pmc_cur_pwr;
36310Sstevel@tonic-gate if (pwr == PM_LEVEL_UNKNOWN) {
36320Sstevel@tonic-gate int thresh;
36330Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH)
36340Sstevel@tonic-gate thresh = pm_default_nexus_threshold;
36353028Smh27603 else if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
36363028Smh27603 thresh = pm_cpu_idle_threshold;
36370Sstevel@tonic-gate else
36380Sstevel@tonic-gate thresh = pm_system_idle_threshold;
36390Sstevel@tonic-gate return (thresh);
36400Sstevel@tonic-gate }
36410Sstevel@tonic-gate ASSERT(cp->pmc_comp.pmc_thresh);
36420Sstevel@tonic-gate return (cp->pmc_comp.pmc_thresh[pwr]);
36430Sstevel@tonic-gate }
36440Sstevel@tonic-gate
36450Sstevel@tonic-gate /*
36460Sstevel@tonic-gate * Compute next lower component power level given power index.
36470Sstevel@tonic-gate */
36480Sstevel@tonic-gate static int
pm_next_lower_power(pm_component_t * cp,int pwrndx)36490Sstevel@tonic-gate pm_next_lower_power(pm_component_t *cp, int pwrndx)
36500Sstevel@tonic-gate {
36510Sstevel@tonic-gate int nxt_pwr;
36520Sstevel@tonic-gate
36530Sstevel@tonic-gate if (pwrndx == PM_LEVEL_UNKNOWN) {
36540Sstevel@tonic-gate nxt_pwr = cp->pmc_comp.pmc_lvals[0];
36550Sstevel@tonic-gate } else {
36560Sstevel@tonic-gate pwrndx--;
36570Sstevel@tonic-gate ASSERT(pwrndx >= 0);
36580Sstevel@tonic-gate nxt_pwr = cp->pmc_comp.pmc_lvals[pwrndx];
36590Sstevel@tonic-gate }
36600Sstevel@tonic-gate return (nxt_pwr);
36610Sstevel@tonic-gate }
36620Sstevel@tonic-gate
36630Sstevel@tonic-gate /*
36644667Smh27603 * Update the maxpower (normal) power of a component. Note that the
36654667Smh27603 * component's power level is only changed if it's current power level
36664667Smh27603 * is higher than the new max power.
36674667Smh27603 */
36684667Smh27603 int
pm_update_maxpower(dev_info_t * dip,int comp,int level)36694667Smh27603 pm_update_maxpower(dev_info_t *dip, int comp, int level)
36704667Smh27603 {
36714667Smh27603 PMD_FUNC(pmf, "update_maxpower")
36724667Smh27603 int old;
36734667Smh27603 int result;
36744667Smh27603
36754667Smh27603 if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, comp, NULL) ||
36764667Smh27603 !e_pm_valid_power(dip, comp, level)) {
36774667Smh27603 PMD(PMD_FAIL, ("%s: validation checks failed for %s@%s(%s#%d) "
36784667Smh27603 "comp=%d level=%d\n", pmf, PM_DEVICE(dip), comp, level))
36794667Smh27603 return (DDI_FAILURE);
36804667Smh27603 }
36814667Smh27603 old = e_pm_get_max_power(dip, comp);
36824667Smh27603 e_pm_set_max_power(dip, comp, level);
36834667Smh27603
36844667Smh27603 if (pm_set_power(dip, comp, level, PM_LEVEL_DOWNONLY,
36854667Smh27603 PM_CANBLOCK_BLOCK, 0, &result) != DDI_SUCCESS) {
36864667Smh27603 e_pm_set_max_power(dip, comp, old);
36874667Smh27603 PMD(PMD_FAIL, ("%s: %s@%s(%s#%d) pm_set_power failed\n", pmf,
36884667Smh27603 PM_DEVICE(dip)))
36894667Smh27603 return (DDI_FAILURE);
36904667Smh27603 }
36914667Smh27603 return (DDI_SUCCESS);
36924667Smh27603 }
36934667Smh27603
36944667Smh27603 /*
36950Sstevel@tonic-gate * Bring all components of device to normal power
36960Sstevel@tonic-gate */
36970Sstevel@tonic-gate int
pm_all_to_normal(dev_info_t * dip,pm_canblock_t canblock)36980Sstevel@tonic-gate pm_all_to_normal(dev_info_t *dip, pm_canblock_t canblock)
36990Sstevel@tonic-gate {
37000Sstevel@tonic-gate PMD_FUNC(pmf, "all_to_normal")
37010Sstevel@tonic-gate int *normal;
37020Sstevel@tonic-gate int i, ncomps, result;
37030Sstevel@tonic-gate size_t size;
37040Sstevel@tonic-gate int changefailed = 0;
37050Sstevel@tonic-gate
37060Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
37070Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip));
37080Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
37090Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get norm pwrs for "
37100Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
37110Sstevel@tonic-gate return (DDI_FAILURE);
37120Sstevel@tonic-gate }
37130Sstevel@tonic-gate ncomps = PM_NUMCMPTS(dip);
37140Sstevel@tonic-gate for (i = 0; i < ncomps; i++) {
37150Sstevel@tonic-gate if (pm_set_power(dip, i, normal[i],
37160Sstevel@tonic-gate PM_LEVEL_UPONLY, canblock, 0, &result) != DDI_SUCCESS) {
37170Sstevel@tonic-gate changefailed++;
37180Sstevel@tonic-gate PMD(PMD_ALLNORM | PMD_FAIL, ("%s: failed to set "
37190Sstevel@tonic-gate "%s@%s(%s#%d)[%d] to %d, errno %d\n", pmf,
37200Sstevel@tonic-gate PM_DEVICE(dip), i, normal[i], result))
37210Sstevel@tonic-gate }
37220Sstevel@tonic-gate }
37230Sstevel@tonic-gate kmem_free(normal, size);
37240Sstevel@tonic-gate if (changefailed) {
37250Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) "
37260Sstevel@tonic-gate "to full power\n", pmf, changefailed, PM_DEVICE(dip)))
37270Sstevel@tonic-gate return (DDI_FAILURE);
37280Sstevel@tonic-gate }
37290Sstevel@tonic-gate return (DDI_SUCCESS);
37300Sstevel@tonic-gate }
37310Sstevel@tonic-gate
37320Sstevel@tonic-gate /*
37330Sstevel@tonic-gate * Returns true if all components of device are at normal power
37340Sstevel@tonic-gate */
37350Sstevel@tonic-gate int
pm_all_at_normal(dev_info_t * dip)37360Sstevel@tonic-gate pm_all_at_normal(dev_info_t *dip)
37370Sstevel@tonic-gate {
37380Sstevel@tonic-gate PMD_FUNC(pmf, "all_at_normal")
37390Sstevel@tonic-gate int *normal;
37400Sstevel@tonic-gate int i;
37410Sstevel@tonic-gate size_t size;
37420Sstevel@tonic-gate
37430Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
37440Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
37450Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get normal power\n", pmf))
37460Sstevel@tonic-gate return (DDI_FAILURE);
37470Sstevel@tonic-gate }
37480Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
37490Sstevel@tonic-gate int current = PM_CURPOWER(dip, i);
37500Sstevel@tonic-gate if (normal[i] > current) {
37510Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d) comp=%d, "
37520Sstevel@tonic-gate "norm=%d, cur=%d\n", pmf, PM_DEVICE(dip), i,
37530Sstevel@tonic-gate normal[i], current))
37540Sstevel@tonic-gate break;
37550Sstevel@tonic-gate }
37560Sstevel@tonic-gate }
37570Sstevel@tonic-gate kmem_free(normal, size);
37580Sstevel@tonic-gate if (i != PM_NUMCMPTS(dip)) {
37590Sstevel@tonic-gate return (0);
37600Sstevel@tonic-gate }
37610Sstevel@tonic-gate return (1);
37620Sstevel@tonic-gate }
37630Sstevel@tonic-gate
37649694SScott.Rotondo@Sun.COM static void bring_pmdep_up(dev_info_t *, int);
37659694SScott.Rotondo@Sun.COM
37660Sstevel@tonic-gate static void
bring_wekeeps_up(char * keeper)37670Sstevel@tonic-gate bring_wekeeps_up(char *keeper)
37680Sstevel@tonic-gate {
37690Sstevel@tonic-gate PMD_FUNC(pmf, "bring_wekeeps_up")
37700Sstevel@tonic-gate int i;
37710Sstevel@tonic-gate pm_pdr_t *dp;
37720Sstevel@tonic-gate pm_info_t *wku_info;
37730Sstevel@tonic-gate char *kept_path;
37740Sstevel@tonic-gate dev_info_t *kept;
37750Sstevel@tonic-gate
37760Sstevel@tonic-gate if (panicstr) {
37770Sstevel@tonic-gate return;
37780Sstevel@tonic-gate }
37790Sstevel@tonic-gate /*
37800Sstevel@tonic-gate * We process the request even if the keeper detaches because
37810Sstevel@tonic-gate * detach processing expects this to increment kidsupcnt of kept.
37820Sstevel@tonic-gate */
37830Sstevel@tonic-gate PMD(PMD_BRING, ("%s: keeper= %s\n", pmf, keeper))
37840Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
37850Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0)
37860Sstevel@tonic-gate continue;
37870Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) {
37880Sstevel@tonic-gate kept_path = dp->pdr_kept_paths[i];
37890Sstevel@tonic-gate if (kept_path == NULL)
37900Sstevel@tonic-gate continue;
37910Sstevel@tonic-gate ASSERT(kept_path[0] != '\0');
37920Sstevel@tonic-gate if ((kept = pm_name_to_dip(kept_path, 1)) == NULL)
37930Sstevel@tonic-gate continue;
37940Sstevel@tonic-gate wku_info = PM_GET_PM_INFO(kept);
37950Sstevel@tonic-gate if (wku_info == NULL) {
37960Sstevel@tonic-gate if (kept)
37970Sstevel@tonic-gate ddi_release_devi(kept);
37980Sstevel@tonic-gate continue;
37990Sstevel@tonic-gate }
38000Sstevel@tonic-gate /*
38010Sstevel@tonic-gate * Don't mess with it if it is being detached, it isn't
38020Sstevel@tonic-gate * safe to call its power entry point
38030Sstevel@tonic-gate */
38040Sstevel@tonic-gate if (wku_info->pmi_dev_pm_state & PM_DETACHING) {
38050Sstevel@tonic-gate if (kept)
38060Sstevel@tonic-gate ddi_release_devi(kept);
38070Sstevel@tonic-gate continue;
38080Sstevel@tonic-gate }
38090Sstevel@tonic-gate bring_pmdep_up(kept, 1);
38100Sstevel@tonic-gate ddi_release_devi(kept);
38110Sstevel@tonic-gate }
38120Sstevel@tonic-gate }
38130Sstevel@tonic-gate }
38140Sstevel@tonic-gate
38150Sstevel@tonic-gate /*
38160Sstevel@tonic-gate * Bring up the 'kept' device passed as argument
38170Sstevel@tonic-gate */
38180Sstevel@tonic-gate static void
bring_pmdep_up(dev_info_t * kept_dip,int hold)38190Sstevel@tonic-gate bring_pmdep_up(dev_info_t *kept_dip, int hold)
38200Sstevel@tonic-gate {
38210Sstevel@tonic-gate PMD_FUNC(pmf, "bring_pmdep_up")
38220Sstevel@tonic-gate int is_all_at_normal = 0;
38230Sstevel@tonic-gate
38240Sstevel@tonic-gate /*
38250Sstevel@tonic-gate * If the kept device has been unmanaged, do nothing.
38260Sstevel@tonic-gate */
38270Sstevel@tonic-gate if (!PM_GET_PM_INFO(kept_dip))
38280Sstevel@tonic-gate return;
38290Sstevel@tonic-gate
38300Sstevel@tonic-gate /* Just ignore DIRECT PM device till they are released. */
38310Sstevel@tonic-gate if (!pm_processes_stopped && PM_ISDIRECT(kept_dip) &&
38320Sstevel@tonic-gate !(is_all_at_normal = pm_all_at_normal(kept_dip))) {
38330Sstevel@tonic-gate PMD(PMD_BRING, ("%s: can't bring up PM_DIRECT %s@%s(%s#%d) "
38340Sstevel@tonic-gate "controlling process did something else\n", pmf,
38350Sstevel@tonic-gate PM_DEVICE(kept_dip)))
38360Sstevel@tonic-gate DEVI(kept_dip)->devi_pm_flags |= PMC_SKIP_BRINGUP;
38370Sstevel@tonic-gate return;
38380Sstevel@tonic-gate }
38390Sstevel@tonic-gate /* if we got here the keeper had a transition from OFF->ON */
38400Sstevel@tonic-gate if (hold)
38410Sstevel@tonic-gate pm_hold_power(kept_dip);
38420Sstevel@tonic-gate
38430Sstevel@tonic-gate if (!is_all_at_normal)
38440Sstevel@tonic-gate (void) pm_all_to_normal(kept_dip, PM_CANBLOCK_FAIL);
38450Sstevel@tonic-gate }
38460Sstevel@tonic-gate
38470Sstevel@tonic-gate /*
38480Sstevel@tonic-gate * A bunch of stuff that belongs only to the next routine (or two)
38490Sstevel@tonic-gate */
38500Sstevel@tonic-gate
38510Sstevel@tonic-gate static const char namestr[] = "NAME=";
38520Sstevel@tonic-gate static const int nameln = sizeof (namestr) - 1;
38530Sstevel@tonic-gate static const char pmcompstr[] = "pm-components";
38540Sstevel@tonic-gate
38550Sstevel@tonic-gate struct pm_comp_pkg {
38560Sstevel@tonic-gate pm_comp_t *comp;
38570Sstevel@tonic-gate struct pm_comp_pkg *next;
38580Sstevel@tonic-gate };
38590Sstevel@tonic-gate
38600Sstevel@tonic-gate #define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
38610Sstevel@tonic-gate
38620Sstevel@tonic-gate #define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
38630Sstevel@tonic-gate ((ch) >= 'A' && (ch) <= 'F'))
38640Sstevel@tonic-gate
38650Sstevel@tonic-gate /*
38660Sstevel@tonic-gate * Rather than duplicate this code ...
38670Sstevel@tonic-gate * (this code excerpted from the function that follows it)
38680Sstevel@tonic-gate */
38690Sstevel@tonic-gate #define FINISH_COMP { \
38700Sstevel@tonic-gate ASSERT(compp); \
38710Sstevel@tonic-gate compp->pmc_lnames_sz = size; \
38720Sstevel@tonic-gate tp = compp->pmc_lname_buf = kmem_alloc(size, KM_SLEEP); \
38730Sstevel@tonic-gate compp->pmc_numlevels = level; \
38740Sstevel@tonic-gate compp->pmc_lnames = kmem_alloc(level * sizeof (char *), KM_SLEEP); \
38750Sstevel@tonic-gate compp->pmc_lvals = kmem_alloc(level * sizeof (int), KM_SLEEP); \
38760Sstevel@tonic-gate compp->pmc_thresh = kmem_alloc(level * sizeof (int), KM_SLEEP); \
38770Sstevel@tonic-gate /* copy string out of prop array into buffer */ \
38780Sstevel@tonic-gate for (j = 0; j < level; j++) { \
38790Sstevel@tonic-gate compp->pmc_thresh[j] = INT_MAX; /* only [0] sticks */ \
38800Sstevel@tonic-gate compp->pmc_lvals[j] = lvals[j]; \
38810Sstevel@tonic-gate (void) strcpy(tp, lnames[j]); \
38820Sstevel@tonic-gate compp->pmc_lnames[j] = tp; \
38830Sstevel@tonic-gate tp += lszs[j]; \
38840Sstevel@tonic-gate } \
38850Sstevel@tonic-gate ASSERT(tp > compp->pmc_lname_buf && tp <= \
38860Sstevel@tonic-gate compp->pmc_lname_buf + compp->pmc_lnames_sz); \
38870Sstevel@tonic-gate }
38880Sstevel@tonic-gate
38890Sstevel@tonic-gate /*
38900Sstevel@tonic-gate * Create (empty) component data structures.
38910Sstevel@tonic-gate */
38920Sstevel@tonic-gate static void
e_pm_create_components(dev_info_t * dip,int num_components)38930Sstevel@tonic-gate e_pm_create_components(dev_info_t *dip, int num_components)
38940Sstevel@tonic-gate {
38950Sstevel@tonic-gate struct pm_component *compp, *ocompp;
38960Sstevel@tonic-gate int i, size = 0;
38970Sstevel@tonic-gate
38980Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
38990Sstevel@tonic-gate ASSERT(!DEVI(dip)->devi_pm_components);
39000Sstevel@tonic-gate ASSERT(!(DEVI(dip)->devi_pm_flags & PMC_COMPONENTS_DONE));
39010Sstevel@tonic-gate size = sizeof (struct pm_component) * num_components;
39020Sstevel@tonic-gate
39030Sstevel@tonic-gate compp = kmem_zalloc(size, KM_SLEEP);
39040Sstevel@tonic-gate ocompp = compp;
39050Sstevel@tonic-gate DEVI(dip)->devi_pm_comp_size = size;
39060Sstevel@tonic-gate DEVI(dip)->devi_pm_num_components = num_components;
39070Sstevel@tonic-gate PM_LOCK_BUSY(dip);
39080Sstevel@tonic-gate for (i = 0; i < num_components; i++) {
39090Sstevel@tonic-gate compp->pmc_timestamp = gethrestime_sec();
39100Sstevel@tonic-gate compp->pmc_norm_pwr = (uint_t)-1;
39110Sstevel@tonic-gate compp++;
39120Sstevel@tonic-gate }
39130Sstevel@tonic-gate PM_UNLOCK_BUSY(dip);
39140Sstevel@tonic-gate DEVI(dip)->devi_pm_components = ocompp;
39150Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMPONENTS_DONE;
39160Sstevel@tonic-gate }
39170Sstevel@tonic-gate
39180Sstevel@tonic-gate /*
39190Sstevel@tonic-gate * Parse hex or decimal value from char string
39200Sstevel@tonic-gate */
39210Sstevel@tonic-gate static char *
pm_parsenum(char * cp,int * valp)39220Sstevel@tonic-gate pm_parsenum(char *cp, int *valp)
39230Sstevel@tonic-gate {
39240Sstevel@tonic-gate int ch, offset;
39250Sstevel@tonic-gate char numbuf[256];
39260Sstevel@tonic-gate char *np = numbuf;
39270Sstevel@tonic-gate int value = 0;
39280Sstevel@tonic-gate
39290Sstevel@tonic-gate ch = *cp++;
39300Sstevel@tonic-gate if (isdigit(ch)) {
39310Sstevel@tonic-gate if (ch == '0') {
39320Sstevel@tonic-gate if ((ch = *cp++) == 'x' || ch == 'X') {
39330Sstevel@tonic-gate ch = *cp++;
39340Sstevel@tonic-gate while (isxdigit(ch)) {
39350Sstevel@tonic-gate *np++ = (char)ch;
39360Sstevel@tonic-gate ch = *cp++;
39370Sstevel@tonic-gate }
39380Sstevel@tonic-gate *np = 0;
39390Sstevel@tonic-gate cp--;
39400Sstevel@tonic-gate goto hexval;
39410Sstevel@tonic-gate } else {
39420Sstevel@tonic-gate goto digit;
39430Sstevel@tonic-gate }
39440Sstevel@tonic-gate } else {
39450Sstevel@tonic-gate digit:
39460Sstevel@tonic-gate while (isdigit(ch)) {
39470Sstevel@tonic-gate *np++ = (char)ch;
39480Sstevel@tonic-gate ch = *cp++;
39490Sstevel@tonic-gate }
39500Sstevel@tonic-gate *np = 0;
39510Sstevel@tonic-gate cp--;
39520Sstevel@tonic-gate goto decval;
39530Sstevel@tonic-gate }
39540Sstevel@tonic-gate } else
39550Sstevel@tonic-gate return (NULL);
39560Sstevel@tonic-gate
39570Sstevel@tonic-gate hexval:
39580Sstevel@tonic-gate for (np = numbuf; *np; np++) {
39590Sstevel@tonic-gate if (*np >= 'a' && *np <= 'f')
39600Sstevel@tonic-gate offset = 'a' - 10;
39610Sstevel@tonic-gate else if (*np >= 'A' && *np <= 'F')
39620Sstevel@tonic-gate offset = 'A' - 10;
39630Sstevel@tonic-gate else if (*np >= '0' && *np <= '9')
39640Sstevel@tonic-gate offset = '0';
39650Sstevel@tonic-gate value *= 16;
39660Sstevel@tonic-gate value += *np - offset;
39670Sstevel@tonic-gate }
39680Sstevel@tonic-gate *valp = value;
39690Sstevel@tonic-gate return (cp);
39700Sstevel@tonic-gate
39710Sstevel@tonic-gate decval:
39720Sstevel@tonic-gate offset = '0';
39730Sstevel@tonic-gate for (np = numbuf; *np; np++) {
39740Sstevel@tonic-gate value *= 10;
39750Sstevel@tonic-gate value += *np - offset;
39760Sstevel@tonic-gate }
39770Sstevel@tonic-gate *valp = value;
39780Sstevel@tonic-gate return (cp);
39790Sstevel@tonic-gate }
39800Sstevel@tonic-gate
39810Sstevel@tonic-gate /*
39820Sstevel@tonic-gate * Set max (previously documented as "normal") power.
39830Sstevel@tonic-gate */
39840Sstevel@tonic-gate static void
e_pm_set_max_power(dev_info_t * dip,int component_number,int level)39850Sstevel@tonic-gate e_pm_set_max_power(dev_info_t *dip, int component_number, int level)
39860Sstevel@tonic-gate {
39870Sstevel@tonic-gate PM_CP(dip, component_number)->pmc_norm_pwr = level;
39880Sstevel@tonic-gate }
39890Sstevel@tonic-gate
39900Sstevel@tonic-gate /*
39914667Smh27603 * Get max (previously documented as "normal") power.
39924667Smh27603 */
39934667Smh27603 static int
e_pm_get_max_power(dev_info_t * dip,int component_number)39944667Smh27603 e_pm_get_max_power(dev_info_t *dip, int component_number)
39954667Smh27603 {
39964667Smh27603 return (PM_CP(dip, component_number)->pmc_norm_pwr);
39974667Smh27603 }
39984667Smh27603
39994667Smh27603 /*
40000Sstevel@tonic-gate * Internal routine for destroying components
40010Sstevel@tonic-gate * It is called even when there might not be any, so it must be forgiving.
40020Sstevel@tonic-gate */
40030Sstevel@tonic-gate static void
e_pm_destroy_components(dev_info_t * dip)40040Sstevel@tonic-gate e_pm_destroy_components(dev_info_t *dip)
40050Sstevel@tonic-gate {
40060Sstevel@tonic-gate int i;
40070Sstevel@tonic-gate struct pm_component *cp;
40080Sstevel@tonic-gate
40090Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
40100Sstevel@tonic-gate if (PM_NUMCMPTS(dip) == 0)
40110Sstevel@tonic-gate return;
40120Sstevel@tonic-gate cp = DEVI(dip)->devi_pm_components;
40130Sstevel@tonic-gate ASSERT(cp);
40140Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++, cp++) {
40150Sstevel@tonic-gate int nlevels = cp->pmc_comp.pmc_numlevels;
40160Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lvals, nlevels * sizeof (int));
40170Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_thresh, nlevels * sizeof (int));
40180Sstevel@tonic-gate /*
40190Sstevel@tonic-gate * For BC nodes, the rest is static in bc_comp, so skip it
40200Sstevel@tonic-gate */
40210Sstevel@tonic-gate if (PM_ISBC(dip))
40220Sstevel@tonic-gate continue;
40230Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_name, cp->pmc_comp.pmc_name_sz);
40240Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lnames, nlevels * sizeof (char *));
40250Sstevel@tonic-gate kmem_free(cp->pmc_comp.pmc_lname_buf,
40264780Smh27603 cp->pmc_comp.pmc_lnames_sz);
40270Sstevel@tonic-gate }
40280Sstevel@tonic-gate kmem_free(DEVI(dip)->devi_pm_components, DEVI(dip)->devi_pm_comp_size);
40290Sstevel@tonic-gate DEVI(dip)->devi_pm_components = NULL;
40300Sstevel@tonic-gate DEVI(dip)->devi_pm_num_components = 0;
40310Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &=
40320Sstevel@tonic-gate ~(PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED);
40330Sstevel@tonic-gate }
40340Sstevel@tonic-gate
40350Sstevel@tonic-gate /*
40360Sstevel@tonic-gate * Read the pm-components property (if there is one) and use it to set up
40370Sstevel@tonic-gate * components. Returns a pointer to an array of component structures if
40380Sstevel@tonic-gate * pm-components found and successfully parsed, else returns NULL.
40390Sstevel@tonic-gate * Sets error return *errp to true to indicate a failure (as opposed to no
40400Sstevel@tonic-gate * property being present).
40410Sstevel@tonic-gate */
40420Sstevel@tonic-gate pm_comp_t *
pm_autoconfig(dev_info_t * dip,int * errp)40430Sstevel@tonic-gate pm_autoconfig(dev_info_t *dip, int *errp)
40440Sstevel@tonic-gate {
40450Sstevel@tonic-gate PMD_FUNC(pmf, "autoconfig")
40460Sstevel@tonic-gate uint_t nelems;
40470Sstevel@tonic-gate char **pp;
40480Sstevel@tonic-gate pm_comp_t *compp = NULL;
40490Sstevel@tonic-gate int i, j, level, components = 0;
40500Sstevel@tonic-gate size_t size = 0;
40510Sstevel@tonic-gate struct pm_comp_pkg *p, *ptail;
40520Sstevel@tonic-gate struct pm_comp_pkg *phead = NULL;
40530Sstevel@tonic-gate int *lvals = NULL;
40540Sstevel@tonic-gate int *lszs = NULL;
40550Sstevel@tonic-gate int *np = NULL;
40560Sstevel@tonic-gate int npi = 0;
40570Sstevel@tonic-gate char **lnames = NULL;
40580Sstevel@tonic-gate char *cp, *tp;
40590Sstevel@tonic-gate pm_comp_t *ret = NULL;
40600Sstevel@tonic-gate
40610Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
40620Sstevel@tonic-gate *errp = 0; /* assume success */
40630Sstevel@tonic-gate if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
40640Sstevel@tonic-gate (char *)pmcompstr, &pp, &nelems) != DDI_PROP_SUCCESS) {
40650Sstevel@tonic-gate return (NULL);
40660Sstevel@tonic-gate }
40670Sstevel@tonic-gate
40680Sstevel@tonic-gate if (nelems < 3) { /* need at least one name and two levels */
40690Sstevel@tonic-gate goto errout;
40700Sstevel@tonic-gate }
40710Sstevel@tonic-gate
40720Sstevel@tonic-gate /*
40730Sstevel@tonic-gate * pm_create_components is no longer allowed
40740Sstevel@tonic-gate */
40750Sstevel@tonic-gate if (PM_NUMCMPTS(dip) != 0) {
40760Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) has %d comps\n",
40770Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_NUMCMPTS(dip)))
40780Sstevel@tonic-gate goto errout;
40790Sstevel@tonic-gate }
40800Sstevel@tonic-gate
40810Sstevel@tonic-gate lvals = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
40820Sstevel@tonic-gate lszs = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
40830Sstevel@tonic-gate lnames = kmem_alloc(nelems * sizeof (char *), KM_SLEEP);
40840Sstevel@tonic-gate np = kmem_alloc(nelems * sizeof (int), KM_SLEEP);
40850Sstevel@tonic-gate
40860Sstevel@tonic-gate level = 0;
40870Sstevel@tonic-gate phead = NULL;
40880Sstevel@tonic-gate for (i = 0; i < nelems; i++) {
40890Sstevel@tonic-gate cp = pp[i];
40900Sstevel@tonic-gate if (!isdigit(*cp)) { /* must be name */
40910Sstevel@tonic-gate if (strncmp(cp, namestr, nameln) != 0) {
40920Sstevel@tonic-gate goto errout;
40930Sstevel@tonic-gate }
40940Sstevel@tonic-gate if (i != 0) {
40950Sstevel@tonic-gate if (level == 0) { /* no level spec'd */
40960Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: no level spec'd\n",
40970Sstevel@tonic-gate pmf))
40980Sstevel@tonic-gate goto errout;
40990Sstevel@tonic-gate }
41000Sstevel@tonic-gate np[npi++] = lvals[level - 1];
41010Sstevel@tonic-gate /* finish up previous component levels */
41020Sstevel@tonic-gate FINISH_COMP;
41030Sstevel@tonic-gate }
41040Sstevel@tonic-gate cp += nameln;
41050Sstevel@tonic-gate if (!*cp) {
41060Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: nsa\n", pmf))
41070Sstevel@tonic-gate goto errout;
41080Sstevel@tonic-gate }
41090Sstevel@tonic-gate p = kmem_zalloc(sizeof (*phead), KM_SLEEP);
41100Sstevel@tonic-gate if (phead == NULL) {
41110Sstevel@tonic-gate phead = ptail = p;
41120Sstevel@tonic-gate } else {
41130Sstevel@tonic-gate ptail->next = p;
41140Sstevel@tonic-gate ptail = p;
41150Sstevel@tonic-gate }
41160Sstevel@tonic-gate compp = p->comp = kmem_zalloc(sizeof (pm_comp_t),
41170Sstevel@tonic-gate KM_SLEEP);
41180Sstevel@tonic-gate compp->pmc_name_sz = strlen(cp) + 1;
41190Sstevel@tonic-gate compp->pmc_name = kmem_zalloc(compp->pmc_name_sz,
41200Sstevel@tonic-gate KM_SLEEP);
41210Sstevel@tonic-gate (void) strncpy(compp->pmc_name, cp, compp->pmc_name_sz);
41220Sstevel@tonic-gate components++;
41230Sstevel@tonic-gate level = 0;
41240Sstevel@tonic-gate } else { /* better be power level <num>=<name> */
41250Sstevel@tonic-gate #ifdef DEBUG
41260Sstevel@tonic-gate tp = cp;
41270Sstevel@tonic-gate #endif
41280Sstevel@tonic-gate if (i == 0 ||
41290Sstevel@tonic-gate (cp = pm_parsenum(cp, &lvals[level])) == NULL) {
41300Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: parsenum(%s)\n", pmf, tp))
41310Sstevel@tonic-gate goto errout;
41320Sstevel@tonic-gate }
41330Sstevel@tonic-gate #ifdef DEBUG
41340Sstevel@tonic-gate tp = cp;
41350Sstevel@tonic-gate #endif
41360Sstevel@tonic-gate if (*cp++ != '=' || !*cp) {
41370Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: ex =, got %s\n", pmf, tp))
41380Sstevel@tonic-gate goto errout;
41390Sstevel@tonic-gate }
41400Sstevel@tonic-gate
41410Sstevel@tonic-gate lszs[level] = strlen(cp) + 1;
41420Sstevel@tonic-gate size += lszs[level];
41430Sstevel@tonic-gate lnames[level] = cp; /* points into prop string */
41440Sstevel@tonic-gate level++;
41450Sstevel@tonic-gate }
41460Sstevel@tonic-gate }
41470Sstevel@tonic-gate np[npi++] = lvals[level - 1];
41480Sstevel@tonic-gate if (level == 0) { /* ended with a name */
41490Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: ewn\n", pmf))
41500Sstevel@tonic-gate goto errout;
41510Sstevel@tonic-gate }
41520Sstevel@tonic-gate FINISH_COMP;
41530Sstevel@tonic-gate
41540Sstevel@tonic-gate
41550Sstevel@tonic-gate /*
41560Sstevel@tonic-gate * Now we have a list of components--we have to return instead an
41570Sstevel@tonic-gate * array of them, but we can just copy the top level and leave
41580Sstevel@tonic-gate * the rest as is
41590Sstevel@tonic-gate */
41600Sstevel@tonic-gate (void) e_pm_create_components(dip, components);
41610Sstevel@tonic-gate for (i = 0; i < components; i++)
41620Sstevel@tonic-gate e_pm_set_max_power(dip, i, np[i]);
41630Sstevel@tonic-gate
41640Sstevel@tonic-gate ret = kmem_zalloc(components * sizeof (pm_comp_t), KM_SLEEP);
41650Sstevel@tonic-gate for (i = 0, p = phead; i < components; i++) {
41660Sstevel@tonic-gate ASSERT(p);
41670Sstevel@tonic-gate /*
41680Sstevel@tonic-gate * Now sanity-check values: levels must be monotonically
41690Sstevel@tonic-gate * increasing
41700Sstevel@tonic-gate */
41710Sstevel@tonic-gate if (p->comp->pmc_numlevels < 2) {
41720Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) only %d "
41730Sstevel@tonic-gate "levels\n", pmf,
41740Sstevel@tonic-gate p->comp->pmc_name, PM_DEVICE(dip),
41750Sstevel@tonic-gate p->comp->pmc_numlevels))
41760Sstevel@tonic-gate goto errout;
41770Sstevel@tonic-gate }
41780Sstevel@tonic-gate for (j = 0; j < p->comp->pmc_numlevels; j++) {
41790Sstevel@tonic-gate if ((p->comp->pmc_lvals[j] < 0) || ((j > 0) &&
41800Sstevel@tonic-gate (p->comp->pmc_lvals[j] <=
41810Sstevel@tonic-gate p->comp->pmc_lvals[j - 1]))) {
41820Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp %s of %s@%s(%s#%d) "
41830Sstevel@tonic-gate "not mono. incr, %d follows %d\n", pmf,
41840Sstevel@tonic-gate p->comp->pmc_name, PM_DEVICE(dip),
41850Sstevel@tonic-gate p->comp->pmc_lvals[j],
41860Sstevel@tonic-gate p->comp->pmc_lvals[j - 1]))
41870Sstevel@tonic-gate goto errout;
41880Sstevel@tonic-gate }
41890Sstevel@tonic-gate }
41900Sstevel@tonic-gate ret[i] = *p->comp; /* struct assignment */
41910Sstevel@tonic-gate for (j = 0; j < i; j++) {
41920Sstevel@tonic-gate /*
41930Sstevel@tonic-gate * Test for unique component names
41940Sstevel@tonic-gate */
41950Sstevel@tonic-gate if (strcmp(ret[j].pmc_name, ret[i].pmc_name) == 0) {
41960Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s of %s@%s(%s#%d) not "
41970Sstevel@tonic-gate "unique\n", pmf, ret[j].pmc_name,
41980Sstevel@tonic-gate PM_DEVICE(dip)))
41990Sstevel@tonic-gate goto errout;
42000Sstevel@tonic-gate }
42010Sstevel@tonic-gate }
42020Sstevel@tonic-gate ptail = p;
42030Sstevel@tonic-gate p = p->next;
42040Sstevel@tonic-gate phead = p; /* errout depends on phead making sense */
42050Sstevel@tonic-gate kmem_free(ptail->comp, sizeof (*ptail->comp));
42060Sstevel@tonic-gate kmem_free(ptail, sizeof (*ptail));
42070Sstevel@tonic-gate }
42080Sstevel@tonic-gate out:
42090Sstevel@tonic-gate ddi_prop_free(pp);
42100Sstevel@tonic-gate if (lvals)
42110Sstevel@tonic-gate kmem_free(lvals, nelems * sizeof (int));
42120Sstevel@tonic-gate if (lszs)
42130Sstevel@tonic-gate kmem_free(lszs, nelems * sizeof (int));
42140Sstevel@tonic-gate if (lnames)
42150Sstevel@tonic-gate kmem_free(lnames, nelems * sizeof (char *));
42160Sstevel@tonic-gate if (np)
42170Sstevel@tonic-gate kmem_free(np, nelems * sizeof (int));
42180Sstevel@tonic-gate return (ret);
42190Sstevel@tonic-gate
42200Sstevel@tonic-gate errout:
42210Sstevel@tonic-gate e_pm_destroy_components(dip);
42220Sstevel@tonic-gate *errp = 1; /* signal failure */
42230Sstevel@tonic-gate cmn_err(CE_CONT, "!pm: %s property ", pmcompstr);
42240Sstevel@tonic-gate for (i = 0; i < nelems - 1; i++)
42250Sstevel@tonic-gate cmn_err(CE_CONT, "!'%s', ", pp[i]);
42260Sstevel@tonic-gate if (nelems != 0)
42270Sstevel@tonic-gate cmn_err(CE_CONT, "!'%s'", pp[nelems - 1]);
42280Sstevel@tonic-gate cmn_err(CE_CONT, "! for %s@%s(%s#%d) is ill-formed.\n", PM_DEVICE(dip));
42290Sstevel@tonic-gate for (p = phead; p; ) {
42300Sstevel@tonic-gate pm_comp_t *pp;
42310Sstevel@tonic-gate int n;
42320Sstevel@tonic-gate
42330Sstevel@tonic-gate ptail = p;
42340Sstevel@tonic-gate /*
42350Sstevel@tonic-gate * Free component data structures
42360Sstevel@tonic-gate */
42370Sstevel@tonic-gate pp = p->comp;
42380Sstevel@tonic-gate n = pp->pmc_numlevels;
42390Sstevel@tonic-gate if (pp->pmc_name_sz) {
42400Sstevel@tonic-gate kmem_free(pp->pmc_name, pp->pmc_name_sz);
42410Sstevel@tonic-gate }
42420Sstevel@tonic-gate if (pp->pmc_lnames_sz) {
42430Sstevel@tonic-gate kmem_free(pp->pmc_lname_buf, pp->pmc_lnames_sz);
42440Sstevel@tonic-gate }
42450Sstevel@tonic-gate if (pp->pmc_lnames) {
42460Sstevel@tonic-gate kmem_free(pp->pmc_lnames, n * (sizeof (char *)));
42470Sstevel@tonic-gate }
42480Sstevel@tonic-gate if (pp->pmc_thresh) {
42490Sstevel@tonic-gate kmem_free(pp->pmc_thresh, n * (sizeof (int)));
42500Sstevel@tonic-gate }
42510Sstevel@tonic-gate if (pp->pmc_lvals) {
42520Sstevel@tonic-gate kmem_free(pp->pmc_lvals, n * (sizeof (int)));
42530Sstevel@tonic-gate }
42540Sstevel@tonic-gate p = ptail->next;
42550Sstevel@tonic-gate kmem_free(ptail, sizeof (*ptail));
42560Sstevel@tonic-gate }
42570Sstevel@tonic-gate if (ret != NULL)
42580Sstevel@tonic-gate kmem_free(ret, components * sizeof (pm_comp_t));
42590Sstevel@tonic-gate ret = NULL;
42600Sstevel@tonic-gate goto out;
42610Sstevel@tonic-gate }
42620Sstevel@tonic-gate
42630Sstevel@tonic-gate /*
42640Sstevel@tonic-gate * Set threshold values for a devices components by dividing the target
42650Sstevel@tonic-gate * threshold (base) by the number of transitions and assign each transition
42660Sstevel@tonic-gate * that threshold. This will get the entire device down in the target time if
42670Sstevel@tonic-gate * all components are idle and even if there are dependencies among components.
42680Sstevel@tonic-gate *
42690Sstevel@tonic-gate * Devices may well get powered all the way down before the target time, but
42700Sstevel@tonic-gate * at least the EPA will be happy.
42710Sstevel@tonic-gate */
42720Sstevel@tonic-gate void
pm_set_device_threshold(dev_info_t * dip,int base,int flag)42730Sstevel@tonic-gate pm_set_device_threshold(dev_info_t *dip, int base, int flag)
42740Sstevel@tonic-gate {
42750Sstevel@tonic-gate PMD_FUNC(pmf, "set_device_threshold")
42760Sstevel@tonic-gate int target_threshold = (base * 95) / 100;
42770Sstevel@tonic-gate int level, comp; /* loop counters */
42780Sstevel@tonic-gate int transitions = 0;
42790Sstevel@tonic-gate int ncomp = PM_NUMCMPTS(dip);
42800Sstevel@tonic-gate int thresh;
42810Sstevel@tonic-gate int remainder;
42820Sstevel@tonic-gate pm_comp_t *pmc;
42830Sstevel@tonic-gate int i, circ;
42840Sstevel@tonic-gate
42850Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
42860Sstevel@tonic-gate PM_LOCK_DIP(dip);
42870Sstevel@tonic-gate /*
42880Sstevel@tonic-gate * First we handle the easy one. If we're setting the default
42890Sstevel@tonic-gate * threshold for a node with children, then we set it to the
42900Sstevel@tonic-gate * default nexus threshold (currently 0) and mark it as default
42910Sstevel@tonic-gate * nexus threshold instead
42920Sstevel@tonic-gate */
42930Sstevel@tonic-gate if (PM_IS_NEXUS(dip)) {
42940Sstevel@tonic-gate if (flag == PMC_DEF_THRESH) {
42950Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: [%s@%s(%s#%d) NEXDEF]\n", pmf,
42960Sstevel@tonic-gate PM_DEVICE(dip)))
42970Sstevel@tonic-gate thresh = pm_default_nexus_threshold;
42980Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) {
42990Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp;
43000Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels;
43010Sstevel@tonic-gate level++) {
43020Sstevel@tonic-gate pmc->pmc_thresh[level] = thresh;
43030Sstevel@tonic-gate }
43040Sstevel@tonic-gate }
43050Sstevel@tonic-gate DEVI(dip)->devi_pm_dev_thresh =
43060Sstevel@tonic-gate pm_default_nexus_threshold;
43070Sstevel@tonic-gate /*
43080Sstevel@tonic-gate * If the nexus node is being reconfigured back to
43090Sstevel@tonic-gate * the default threshold, adjust the notlowest count.
43100Sstevel@tonic-gate */
43110Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags &
43120Sstevel@tonic-gate (PMC_DEV_THRESH|PMC_COMP_THRESH)) {
43130Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
43140Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
43150Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == 0)
43160Sstevel@tonic-gate continue;
43170Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock);
43180Sstevel@tonic-gate ASSERT(pm_comps_notlowest);
43190Sstevel@tonic-gate pm_comps_notlowest--;
43200Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) decr "
43210Sstevel@tonic-gate "notlowest to %d\n", pmf,
43220Sstevel@tonic-gate PM_DEVICE(dip), pm_comps_notlowest))
43230Sstevel@tonic-gate if (pm_comps_notlowest == 0)
43240Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip,
43250Sstevel@tonic-gate PM_ALL_LOWEST);
43260Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock);
43270Sstevel@tonic-gate }
43280Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
43290Sstevel@tonic-gate }
43300Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
43310Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NEXDEF_THRESH;
43320Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
43330Sstevel@tonic-gate return;
43340Sstevel@tonic-gate } else if (DEVI(dip)->devi_pm_flags & PMC_NEXDEF_THRESH) {
43350Sstevel@tonic-gate /*
43360Sstevel@tonic-gate * If the nexus node is being configured for a
43370Sstevel@tonic-gate * non-default threshold, include that node in
43380Sstevel@tonic-gate * the notlowest accounting.
43390Sstevel@tonic-gate */
43400Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
43410Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
43420Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == 0)
43430Sstevel@tonic-gate continue;
43440Sstevel@tonic-gate mutex_enter(&pm_compcnt_lock);
43450Sstevel@tonic-gate if (pm_comps_notlowest == 0)
43460Sstevel@tonic-gate pm_ppm_notify_all_lowest(dip,
43470Sstevel@tonic-gate PM_NOT_ALL_LOWEST);
43480Sstevel@tonic-gate pm_comps_notlowest++;
43490Sstevel@tonic-gate PMD(PMD_LEVEL, ("%s: %s@%s(%s#%d) incr "
43500Sstevel@tonic-gate "notlowest to %d\n", pmf,
43510Sstevel@tonic-gate PM_DEVICE(dip), pm_comps_notlowest))
43520Sstevel@tonic-gate mutex_exit(&pm_compcnt_lock);
43530Sstevel@tonic-gate }
43540Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
43550Sstevel@tonic-gate }
43560Sstevel@tonic-gate }
43570Sstevel@tonic-gate /*
43580Sstevel@tonic-gate * Compute the total number of transitions for all components
43590Sstevel@tonic-gate * of the device. Distribute the threshold evenly over them
43600Sstevel@tonic-gate */
43610Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) {
43620Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp;
43630Sstevel@tonic-gate ASSERT(pmc->pmc_numlevels > 1);
43640Sstevel@tonic-gate transitions += pmc->pmc_numlevels - 1;
43650Sstevel@tonic-gate }
43660Sstevel@tonic-gate ASSERT(transitions);
43670Sstevel@tonic-gate thresh = target_threshold / transitions;
43680Sstevel@tonic-gate
43690Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) {
43700Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp;
43710Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) {
43720Sstevel@tonic-gate pmc->pmc_thresh[level] = thresh;
43730Sstevel@tonic-gate }
43740Sstevel@tonic-gate }
43750Sstevel@tonic-gate
43760Sstevel@tonic-gate #ifdef DEBUG
43770Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) {
43780Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp;
43790Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) {
43800Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: thresh before %s@%s(%s#%d) "
43810Sstevel@tonic-gate "comp=%d, level=%d, %d\n", pmf, PM_DEVICE(dip),
43820Sstevel@tonic-gate comp, level, pmc->pmc_thresh[level]))
43830Sstevel@tonic-gate }
43840Sstevel@tonic-gate }
43850Sstevel@tonic-gate #endif
43860Sstevel@tonic-gate /*
43870Sstevel@tonic-gate * Distribute any remainder till they are all gone
43880Sstevel@tonic-gate */
43890Sstevel@tonic-gate remainder = target_threshold - thresh * transitions;
43900Sstevel@tonic-gate level = 1;
43910Sstevel@tonic-gate #ifdef DEBUG
43920Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: remainder=%d target_threshold=%d thresh=%d "
43930Sstevel@tonic-gate "trans=%d\n", pmf, remainder, target_threshold, thresh,
43940Sstevel@tonic-gate transitions))
43950Sstevel@tonic-gate #endif
43960Sstevel@tonic-gate while (remainder > 0) {
43970Sstevel@tonic-gate comp = 0;
43980Sstevel@tonic-gate while (remainder && (comp < ncomp)) {
43990Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp;
44000Sstevel@tonic-gate if (level < pmc->pmc_numlevels) {
44010Sstevel@tonic-gate pmc->pmc_thresh[level] += 1;
44020Sstevel@tonic-gate remainder--;
44030Sstevel@tonic-gate }
44040Sstevel@tonic-gate comp++;
44050Sstevel@tonic-gate }
44060Sstevel@tonic-gate level++;
44070Sstevel@tonic-gate }
44080Sstevel@tonic-gate #ifdef DEBUG
44090Sstevel@tonic-gate for (comp = 0; comp < ncomp; comp++) {
44100Sstevel@tonic-gate pmc = &PM_CP(dip, comp)->pmc_comp;
44110Sstevel@tonic-gate for (level = 1; level < pmc->pmc_numlevels; level++) {
44120Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: thresh after %s@%s(%s#%d) "
44130Sstevel@tonic-gate "comp=%d level=%d, %d\n", pmf, PM_DEVICE(dip),
44140Sstevel@tonic-gate comp, level, pmc->pmc_thresh[level]))
44150Sstevel@tonic-gate }
44160Sstevel@tonic-gate }
44170Sstevel@tonic-gate #endif
44180Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip));
44190Sstevel@tonic-gate DEVI(dip)->devi_pm_dev_thresh = base;
44200Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
44210Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= flag;
44220Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
44230Sstevel@tonic-gate }
44240Sstevel@tonic-gate
44250Sstevel@tonic-gate /*
44260Sstevel@tonic-gate * Called when there is no old-style platform power management driver
44270Sstevel@tonic-gate */
44280Sstevel@tonic-gate static int
ddi_no_platform_power(power_req_t * req)44290Sstevel@tonic-gate ddi_no_platform_power(power_req_t *req)
44300Sstevel@tonic-gate {
44310Sstevel@tonic-gate _NOTE(ARGUNUSED(req))
44320Sstevel@tonic-gate return (DDI_FAILURE);
44330Sstevel@tonic-gate }
44340Sstevel@tonic-gate
44350Sstevel@tonic-gate /*
44360Sstevel@tonic-gate * This function calls the entry point supplied by the platform-specific
44370Sstevel@tonic-gate * pm driver to bring the device component 'pm_cmpt' to power level 'pm_level'.
44380Sstevel@tonic-gate * The use of global for getting the function name from platform-specific
44390Sstevel@tonic-gate * pm driver is not ideal, but it is simple and efficient.
44400Sstevel@tonic-gate * The previous property lookup was being done in the idle loop on swift
44410Sstevel@tonic-gate * systems without pmc chips and hurt deskbench performance as well as
44420Sstevel@tonic-gate * violating scheduler locking rules
44430Sstevel@tonic-gate */
44440Sstevel@tonic-gate int (*pm_platform_power)(power_req_t *) = ddi_no_platform_power;
44450Sstevel@tonic-gate
44460Sstevel@tonic-gate /*
44470Sstevel@tonic-gate * Old obsolete interface for a device to request a power change (but only
44480Sstevel@tonic-gate * an increase in power)
44490Sstevel@tonic-gate */
44500Sstevel@tonic-gate int
ddi_dev_is_needed(dev_info_t * dip,int cmpt,int level)44510Sstevel@tonic-gate ddi_dev_is_needed(dev_info_t *dip, int cmpt, int level)
44520Sstevel@tonic-gate {
44530Sstevel@tonic-gate return (pm_raise_power(dip, cmpt, level));
44540Sstevel@tonic-gate }
44550Sstevel@tonic-gate
44560Sstevel@tonic-gate /*
44570Sstevel@tonic-gate * The old obsolete interface to platform power management. Only used by
44580Sstevel@tonic-gate * Gypsy platform and APM on X86.
44590Sstevel@tonic-gate */
44600Sstevel@tonic-gate int
ddi_power(dev_info_t * dip,int pm_cmpt,int pm_level)44610Sstevel@tonic-gate ddi_power(dev_info_t *dip, int pm_cmpt, int pm_level)
44620Sstevel@tonic-gate {
44630Sstevel@tonic-gate power_req_t request;
44640Sstevel@tonic-gate
44650Sstevel@tonic-gate request.request_type = PMR_SET_POWER;
44660Sstevel@tonic-gate request.req.set_power_req.who = dip;
44670Sstevel@tonic-gate request.req.set_power_req.cmpt = pm_cmpt;
44680Sstevel@tonic-gate request.req.set_power_req.level = pm_level;
44690Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
44700Sstevel@tonic-gate }
44710Sstevel@tonic-gate
44720Sstevel@tonic-gate /*
44730Sstevel@tonic-gate * A driver can invoke this from its detach routine when DDI_SUSPEND is
44740Sstevel@tonic-gate * passed. Returns true if subsequent processing could result in power being
44750Sstevel@tonic-gate * removed from the device. The arg is not currently used because it is
44760Sstevel@tonic-gate * implicit in the operation of cpr/DR.
44770Sstevel@tonic-gate */
44780Sstevel@tonic-gate int
ddi_removing_power(dev_info_t * dip)44790Sstevel@tonic-gate ddi_removing_power(dev_info_t *dip)
44800Sstevel@tonic-gate {
44810Sstevel@tonic-gate _NOTE(ARGUNUSED(dip))
44820Sstevel@tonic-gate return (pm_powering_down);
44830Sstevel@tonic-gate }
44840Sstevel@tonic-gate
44850Sstevel@tonic-gate /*
44860Sstevel@tonic-gate * Returns true if a device indicates that its parent handles suspend/resume
44870Sstevel@tonic-gate * processing for it.
44880Sstevel@tonic-gate */
44890Sstevel@tonic-gate int
e_ddi_parental_suspend_resume(dev_info_t * dip)44900Sstevel@tonic-gate e_ddi_parental_suspend_resume(dev_info_t *dip)
44910Sstevel@tonic-gate {
44920Sstevel@tonic-gate return (DEVI(dip)->devi_pm_flags & PMC_PARENTAL_SR);
44930Sstevel@tonic-gate }
44940Sstevel@tonic-gate
44950Sstevel@tonic-gate /*
44960Sstevel@tonic-gate * Called for devices which indicate that their parent does suspend/resume
44970Sstevel@tonic-gate * handling for them
44980Sstevel@tonic-gate */
44990Sstevel@tonic-gate int
e_ddi_suspend(dev_info_t * dip,ddi_detach_cmd_t cmd)45000Sstevel@tonic-gate e_ddi_suspend(dev_info_t *dip, ddi_detach_cmd_t cmd)
45010Sstevel@tonic-gate {
45020Sstevel@tonic-gate power_req_t request;
45030Sstevel@tonic-gate request.request_type = PMR_SUSPEND;
45040Sstevel@tonic-gate request.req.suspend_req.who = dip;
45050Sstevel@tonic-gate request.req.suspend_req.cmd = cmd;
45060Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
45070Sstevel@tonic-gate }
45080Sstevel@tonic-gate
45090Sstevel@tonic-gate /*
45100Sstevel@tonic-gate * Called for devices which indicate that their parent does suspend/resume
45110Sstevel@tonic-gate * handling for them
45120Sstevel@tonic-gate */
45130Sstevel@tonic-gate int
e_ddi_resume(dev_info_t * dip,ddi_attach_cmd_t cmd)45140Sstevel@tonic-gate e_ddi_resume(dev_info_t *dip, ddi_attach_cmd_t cmd)
45150Sstevel@tonic-gate {
45160Sstevel@tonic-gate power_req_t request;
45170Sstevel@tonic-gate request.request_type = PMR_RESUME;
45180Sstevel@tonic-gate request.req.resume_req.who = dip;
45190Sstevel@tonic-gate request.req.resume_req.cmd = cmd;
45200Sstevel@tonic-gate return (ddi_ctlops(dip, dip, DDI_CTLOPS_POWER, &request, NULL));
45210Sstevel@tonic-gate }
45220Sstevel@tonic-gate
45230Sstevel@tonic-gate /*
45240Sstevel@tonic-gate * Old obsolete exported interface for drivers to create components.
45250Sstevel@tonic-gate * This is now handled by exporting the pm-components property.
45260Sstevel@tonic-gate */
45270Sstevel@tonic-gate int
pm_create_components(dev_info_t * dip,int num_components)45280Sstevel@tonic-gate pm_create_components(dev_info_t *dip, int num_components)
45290Sstevel@tonic-gate {
45300Sstevel@tonic-gate PMD_FUNC(pmf, "pm_create_components")
45310Sstevel@tonic-gate
45320Sstevel@tonic-gate if (num_components < 1)
45330Sstevel@tonic-gate return (DDI_FAILURE);
45340Sstevel@tonic-gate
45350Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip)) {
45360Sstevel@tonic-gate return (DDI_FAILURE);
45370Sstevel@tonic-gate }
45380Sstevel@tonic-gate
45390Sstevel@tonic-gate /* don't need to lock dip because attach is single threaded */
45400Sstevel@tonic-gate if (DEVI(dip)->devi_pm_components) {
45410Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s@%s(%s#%d) already has %d\n", pmf,
45420Sstevel@tonic-gate PM_DEVICE(dip), PM_NUMCMPTS(dip)))
45430Sstevel@tonic-gate return (DDI_FAILURE);
45440Sstevel@tonic-gate }
45450Sstevel@tonic-gate e_pm_create_components(dip, num_components);
45460Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_BC;
45470Sstevel@tonic-gate e_pm_default_components(dip, num_components);
45480Sstevel@tonic-gate return (DDI_SUCCESS);
45490Sstevel@tonic-gate }
45500Sstevel@tonic-gate
45510Sstevel@tonic-gate /*
45520Sstevel@tonic-gate * Obsolete interface previously called by drivers to destroy their components
45530Sstevel@tonic-gate * at detach time. This is now done automatically. However, we need to keep
45540Sstevel@tonic-gate * this for the old drivers.
45550Sstevel@tonic-gate */
45560Sstevel@tonic-gate void
pm_destroy_components(dev_info_t * dip)45570Sstevel@tonic-gate pm_destroy_components(dev_info_t *dip)
45580Sstevel@tonic-gate {
45590Sstevel@tonic-gate PMD_FUNC(pmf, "pm_destroy_components")
45600Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
45610Sstevel@tonic-gate
45620Sstevel@tonic-gate PMD(PMD_REMDEV | PMD_KIDSUP, ("%s: %s@%s(%s#%d)\n", pmf,
45630Sstevel@tonic-gate PM_DEVICE(dip)))
45640Sstevel@tonic-gate ASSERT(DEVI_IS_DETACHING(dip));
45650Sstevel@tonic-gate #ifdef DEBUG
45660Sstevel@tonic-gate if (!PM_ISBC(dip))
45670Sstevel@tonic-gate cmn_err(CE_WARN, "!driver exporting pm-components property "
45680Sstevel@tonic-gate "(%s@%s) calls pm_destroy_components", PM_NAME(dip),
45690Sstevel@tonic-gate PM_ADDR(dip));
45700Sstevel@tonic-gate #endif
45710Sstevel@tonic-gate /*
45720Sstevel@tonic-gate * We ignore this unless this is an old-style driver, except for
45730Sstevel@tonic-gate * printing the message above
45740Sstevel@tonic-gate */
45750Sstevel@tonic-gate if (PM_NUMCMPTS(dip) == 0 || !PM_ISBC(dip)) {
45760Sstevel@tonic-gate PMD(PMD_REMDEV, ("%s: ignore %s@%s(%s#%d)\n", pmf,
45770Sstevel@tonic-gate PM_DEVICE(dip)))
45780Sstevel@tonic-gate return;
45790Sstevel@tonic-gate }
45800Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip));
45810Sstevel@tonic-gate
45820Sstevel@tonic-gate /*
45830Sstevel@tonic-gate * pm_unmanage will clear info pointer later, after dealing with
45840Sstevel@tonic-gate * dependencies
45850Sstevel@tonic-gate */
45860Sstevel@tonic-gate ASSERT(!PM_GET_PM_SCAN(dip)); /* better be gone already */
45870Sstevel@tonic-gate /*
45880Sstevel@tonic-gate * Now adjust parent's kidsupcnt. We check only comp 0.
45890Sstevel@tonic-gate * Parents that get notification are not adjusted because their
45900Sstevel@tonic-gate * kidsupcnt is always 0 (or 1 during probe and attach).
45910Sstevel@tonic-gate */
45920Sstevel@tonic-gate if ((PM_CURPOWER(dip, 0) != 0) && pdip && !PM_WANTS_NOTIFICATION(pdip))
45930Sstevel@tonic-gate pm_rele_power(pdip);
45940Sstevel@tonic-gate #ifdef DEBUG
45950Sstevel@tonic-gate else {
45960Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: kuc stays %s@%s(%s#%d) comps gone\n",
45970Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
45980Sstevel@tonic-gate }
45990Sstevel@tonic-gate #endif
46000Sstevel@tonic-gate e_pm_destroy_components(dip);
46010Sstevel@tonic-gate /*
46020Sstevel@tonic-gate * Forget we ever knew anything about the components of this device
46030Sstevel@tonic-gate */
46040Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &=
46050Sstevel@tonic-gate ~(PMC_BC | PMC_COMPONENTS_DONE | PMC_COMPONENTS_FAILED);
46060Sstevel@tonic-gate }
46070Sstevel@tonic-gate
46080Sstevel@tonic-gate /*
46090Sstevel@tonic-gate * Exported interface for a driver to set a component busy.
46100Sstevel@tonic-gate */
46110Sstevel@tonic-gate int
pm_busy_component(dev_info_t * dip,int cmpt)46120Sstevel@tonic-gate pm_busy_component(dev_info_t *dip, int cmpt)
46130Sstevel@tonic-gate {
46140Sstevel@tonic-gate struct pm_component *cp;
46150Sstevel@tonic-gate
46160Sstevel@tonic-gate ASSERT(dip != NULL);
46170Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp))
46180Sstevel@tonic-gate return (DDI_FAILURE);
46190Sstevel@tonic-gate PM_LOCK_BUSY(dip);
46200Sstevel@tonic-gate cp->pmc_busycount++;
46210Sstevel@tonic-gate cp->pmc_timestamp = 0;
46220Sstevel@tonic-gate PM_UNLOCK_BUSY(dip);
46230Sstevel@tonic-gate return (DDI_SUCCESS);
46240Sstevel@tonic-gate }
46250Sstevel@tonic-gate
46260Sstevel@tonic-gate /*
46270Sstevel@tonic-gate * Exported interface for a driver to set a component idle.
46280Sstevel@tonic-gate */
46290Sstevel@tonic-gate int
pm_idle_component(dev_info_t * dip,int cmpt)46300Sstevel@tonic-gate pm_idle_component(dev_info_t *dip, int cmpt)
46310Sstevel@tonic-gate {
46320Sstevel@tonic-gate PMD_FUNC(pmf, "pm_idle_component")
46330Sstevel@tonic-gate struct pm_component *cp;
46340Sstevel@tonic-gate pm_scan_t *scanp = PM_GET_PM_SCAN(dip);
46350Sstevel@tonic-gate
46360Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) || !e_pm_valid_comp(dip, cmpt, &cp))
46370Sstevel@tonic-gate return (DDI_FAILURE);
46380Sstevel@tonic-gate
46390Sstevel@tonic-gate PM_LOCK_BUSY(dip);
46400Sstevel@tonic-gate if (cp->pmc_busycount) {
46410Sstevel@tonic-gate if (--(cp->pmc_busycount) == 0)
46420Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec();
46430Sstevel@tonic-gate } else {
46440Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec();
46450Sstevel@tonic-gate }
46460Sstevel@tonic-gate
46470Sstevel@tonic-gate PM_UNLOCK_BUSY(dip);
46480Sstevel@tonic-gate
46490Sstevel@tonic-gate /*
46500Sstevel@tonic-gate * if device becomes idle during idle down period, try scan it down
46510Sstevel@tonic-gate */
46520Sstevel@tonic-gate if (scanp && PM_IS_PID(dip)) {
46530Sstevel@tonic-gate PMD(PMD_IDLEDOWN, ("%s: %s@%s(%s#%d) idle.\n", pmf,
46540Sstevel@tonic-gate PM_DEVICE(dip)))
46550Sstevel@tonic-gate pm_rescan(dip);
46560Sstevel@tonic-gate return (DDI_SUCCESS);
46570Sstevel@tonic-gate }
46580Sstevel@tonic-gate
46590Sstevel@tonic-gate /*
46600Sstevel@tonic-gate * handle scan not running with nexus threshold == 0
46610Sstevel@tonic-gate */
46620Sstevel@tonic-gate
46630Sstevel@tonic-gate if (PM_IS_NEXUS(dip) && (cp->pmc_busycount == 0)) {
46640Sstevel@tonic-gate pm_rescan(dip);
46650Sstevel@tonic-gate }
46660Sstevel@tonic-gate
46670Sstevel@tonic-gate return (DDI_SUCCESS);
46680Sstevel@tonic-gate }
46690Sstevel@tonic-gate
46700Sstevel@tonic-gate /*
46710Sstevel@tonic-gate * This is the old obsolete interface called by drivers to set their normal
46720Sstevel@tonic-gate * power. Thus we can't fix its behavior or return a value.
46730Sstevel@tonic-gate * This functionality is replaced by the pm-component property.
46740Sstevel@tonic-gate * We'll only get components destroyed while no power management is
46750Sstevel@tonic-gate * going on (and the device is detached), so we don't need a mutex here
46760Sstevel@tonic-gate */
46770Sstevel@tonic-gate void
pm_set_normal_power(dev_info_t * dip,int comp,int level)46780Sstevel@tonic-gate pm_set_normal_power(dev_info_t *dip, int comp, int level)
46790Sstevel@tonic-gate {
46800Sstevel@tonic-gate PMD_FUNC(pmf, "set_normal_power")
46810Sstevel@tonic-gate #ifdef DEBUG
46820Sstevel@tonic-gate if (!PM_ISBC(dip))
46830Sstevel@tonic-gate cmn_err(CE_WARN, "!call to pm_set_normal_power() by %s@%s "
46840Sstevel@tonic-gate "(driver exporting pm-components property) ignored",
46850Sstevel@tonic-gate PM_NAME(dip), PM_ADDR(dip));
46860Sstevel@tonic-gate #endif
46870Sstevel@tonic-gate if (PM_ISBC(dip)) {
46880Sstevel@tonic-gate PMD(PMD_NORM, ("%s: %s@%s(%s#%d) set normal power comp=%d, "
46890Sstevel@tonic-gate "level=%d\n", pmf, PM_DEVICE(dip), comp, level))
46900Sstevel@tonic-gate e_pm_set_max_power(dip, comp, level);
46910Sstevel@tonic-gate e_pm_default_levels(dip, PM_CP(dip, comp), level);
46920Sstevel@tonic-gate }
46930Sstevel@tonic-gate }
46940Sstevel@tonic-gate
46950Sstevel@tonic-gate /*
46960Sstevel@tonic-gate * Called on a successfully detached driver to free pm resources
46970Sstevel@tonic-gate */
46980Sstevel@tonic-gate static void
pm_stop(dev_info_t * dip)46990Sstevel@tonic-gate pm_stop(dev_info_t *dip)
47000Sstevel@tonic-gate {
47010Sstevel@tonic-gate PMD_FUNC(pmf, "stop")
47020Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
47030Sstevel@tonic-gate
47040Sstevel@tonic-gate ASSERT(!PM_IAM_LOCKING_DIP(dip));
47050Sstevel@tonic-gate /* stopping scan, destroy scan data structure */
47060Sstevel@tonic-gate if (!PM_ISBC(dip)) {
47070Sstevel@tonic-gate pm_scan_stop(dip);
47080Sstevel@tonic-gate pm_scan_fini(dip);
47090Sstevel@tonic-gate }
47100Sstevel@tonic-gate
47110Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) != NULL) {
47120Sstevel@tonic-gate if (pm_unmanage(dip) == DDI_SUCCESS) {
47130Sstevel@tonic-gate /*
47140Sstevel@tonic-gate * Old style driver may have called
47150Sstevel@tonic-gate * pm_destroy_components already, but just in case ...
47160Sstevel@tonic-gate */
47170Sstevel@tonic-gate e_pm_destroy_components(dip);
47180Sstevel@tonic-gate } else {
47190Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: can't pm_unmanage %s@%s(%s#%d)\n",
47200Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
47210Sstevel@tonic-gate }
47220Sstevel@tonic-gate } else {
47230Sstevel@tonic-gate if (PM_NUMCMPTS(dip))
47240Sstevel@tonic-gate e_pm_destroy_components(dip);
47250Sstevel@tonic-gate else {
47260Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NOPMKID) {
47270Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= ~PMC_NOPMKID;
47280Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
47290Sstevel@tonic-gate pm_rele_power(pdip);
47302155Scth } else if (pdip &&
47312155Scth MDI_VHCI(pdip) && MDI_CLIENT(dip)) {
47320Sstevel@tonic-gate (void) mdi_power(pdip,
47330Sstevel@tonic-gate MDI_PM_RELE_POWER,
47340Sstevel@tonic-gate (void *)dip, NULL, 0);
47350Sstevel@tonic-gate }
47360Sstevel@tonic-gate }
47370Sstevel@tonic-gate }
47380Sstevel@tonic-gate }
47390Sstevel@tonic-gate }
47400Sstevel@tonic-gate
47410Sstevel@tonic-gate /*
47420Sstevel@tonic-gate * The node is the subject of a reparse pm props ioctl. Throw away the old
47430Sstevel@tonic-gate * info and start over.
47440Sstevel@tonic-gate */
47450Sstevel@tonic-gate int
e_new_pm_props(dev_info_t * dip)47460Sstevel@tonic-gate e_new_pm_props(dev_info_t *dip)
47470Sstevel@tonic-gate {
47480Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) != NULL) {
47490Sstevel@tonic-gate pm_stop(dip);
47500Sstevel@tonic-gate
47510Sstevel@tonic-gate if (e_pm_manage(dip, PM_STYLE_NEW) != DDI_SUCCESS) {
47520Sstevel@tonic-gate return (DDI_FAILURE);
47530Sstevel@tonic-gate }
47540Sstevel@tonic-gate }
47550Sstevel@tonic-gate e_pm_props(dip);
47560Sstevel@tonic-gate return (DDI_SUCCESS);
47570Sstevel@tonic-gate }
47580Sstevel@tonic-gate
47590Sstevel@tonic-gate /*
47600Sstevel@tonic-gate * Device has been attached, so process its pm properties
47610Sstevel@tonic-gate */
47620Sstevel@tonic-gate void
e_pm_props(dev_info_t * dip)47630Sstevel@tonic-gate e_pm_props(dev_info_t *dip)
47640Sstevel@tonic-gate {
47650Sstevel@tonic-gate char *pp;
47660Sstevel@tonic-gate int len;
47670Sstevel@tonic-gate int flags = 0;
47680Sstevel@tonic-gate int propflag = DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP;
47690Sstevel@tonic-gate
47700Sstevel@tonic-gate /*
47710Sstevel@tonic-gate * It doesn't matter if we do this more than once, we should always
47720Sstevel@tonic-gate * get the same answers, and if not, then the last one in is the
47730Sstevel@tonic-gate * best one.
47740Sstevel@tonic-gate */
47750Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-hardware-state",
47760Sstevel@tonic-gate (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) {
47770Sstevel@tonic-gate if (strcmp(pp, "needs-suspend-resume") == 0) {
47780Sstevel@tonic-gate flags = PMC_NEEDS_SR;
47790Sstevel@tonic-gate } else if (strcmp(pp, "no-suspend-resume") == 0) {
47800Sstevel@tonic-gate flags = PMC_NO_SR;
47810Sstevel@tonic-gate } else if (strcmp(pp, "parental-suspend-resume") == 0) {
47820Sstevel@tonic-gate flags = PMC_PARENTAL_SR;
47830Sstevel@tonic-gate } else {
47840Sstevel@tonic-gate cmn_err(CE_NOTE, "!device %s@%s has unrecognized "
47850Sstevel@tonic-gate "%s property value '%s'", PM_NAME(dip),
47860Sstevel@tonic-gate PM_ADDR(dip), "pm-hardware-state", pp);
47870Sstevel@tonic-gate }
47880Sstevel@tonic-gate kmem_free(pp, len);
47890Sstevel@tonic-gate }
47900Sstevel@tonic-gate /*
47910Sstevel@tonic-gate * This next segment (PMC_WANTS_NOTIFY) is in
47920Sstevel@tonic-gate * support of nexus drivers which will want to be involved in
47930Sstevel@tonic-gate * (or at least notified of) their child node's power level transitions.
47940Sstevel@tonic-gate * "pm-want-child-notification?" is defined by the parent.
47950Sstevel@tonic-gate */
47960Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag,
47970Sstevel@tonic-gate "pm-want-child-notification?") && PM_HAS_BUS_POWER(dip))
47980Sstevel@tonic-gate flags |= PMC_WANTS_NOTIFY;
47990Sstevel@tonic-gate ASSERT(PM_HAS_BUS_POWER(dip) || !ddi_prop_exists(DDI_DEV_T_ANY,
48000Sstevel@tonic-gate dip, propflag, "pm-want-child-notification?"));
48010Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, propflag,
48020Sstevel@tonic-gate "no-involuntary-power-cycles"))
48030Sstevel@tonic-gate flags |= PMC_NO_INVOL;
48043028Smh27603 /*
48053028Smh27603 * Is the device a CPU device?
48063028Smh27603 */
48073028Smh27603 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, propflag, "pm-class",
48083028Smh27603 (caddr_t)&pp, &len) == DDI_PROP_SUCCESS) {
48093028Smh27603 if (strcmp(pp, "CPU") == 0) {
48103028Smh27603 flags |= PMC_CPU_DEVICE;
48113028Smh27603 } else {
48123028Smh27603 cmn_err(CE_NOTE, "!device %s@%s has unrecognized "
48133028Smh27603 "%s property value '%s'", PM_NAME(dip),
48144780Smh27603 PM_ADDR(dip), "pm-class", pp);
48153028Smh27603 }
48163028Smh27603 kmem_free(pp, len);
48173028Smh27603 }
48180Sstevel@tonic-gate /* devfs single threads us */
48190Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= flags;
48200Sstevel@tonic-gate }
48210Sstevel@tonic-gate
48220Sstevel@tonic-gate /*
48230Sstevel@tonic-gate * This is the DDI_CTLOPS_POWER handler that is used when there is no ppm
48240Sstevel@tonic-gate * driver which has claimed a node.
48250Sstevel@tonic-gate * Sets old_power in arg struct.
48260Sstevel@tonic-gate */
48270Sstevel@tonic-gate static int
pm_default_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)48280Sstevel@tonic-gate pm_default_ctlops(dev_info_t *dip, dev_info_t *rdip,
48290Sstevel@tonic-gate ddi_ctl_enum_t ctlop, void *arg, void *result)
48300Sstevel@tonic-gate {
48310Sstevel@tonic-gate _NOTE(ARGUNUSED(dip))
48320Sstevel@tonic-gate PMD_FUNC(pmf, "ctlops")
48330Sstevel@tonic-gate power_req_t *reqp = (power_req_t *)arg;
48340Sstevel@tonic-gate int retval;
48350Sstevel@tonic-gate dev_info_t *target_dip;
48360Sstevel@tonic-gate int new_level, old_level, cmpt;
48373839Skchow #ifdef PMDDEBUG
48380Sstevel@tonic-gate char *format;
48390Sstevel@tonic-gate #endif
48400Sstevel@tonic-gate
48410Sstevel@tonic-gate /*
48420Sstevel@tonic-gate * The interface for doing the actual power level changes is now
48430Sstevel@tonic-gate * through the DDI_CTLOPS_POWER bus_ctl, so that we can plug in
48440Sstevel@tonic-gate * different platform-specific power control drivers.
48450Sstevel@tonic-gate *
48460Sstevel@tonic-gate * This driver implements the "default" version of this interface.
48470Sstevel@tonic-gate * If no ppm driver has been installed then this interface is called
48480Sstevel@tonic-gate * instead.
48490Sstevel@tonic-gate */
48500Sstevel@tonic-gate ASSERT(dip == NULL);
48510Sstevel@tonic-gate switch (ctlop) {
48520Sstevel@tonic-gate case DDI_CTLOPS_POWER:
48530Sstevel@tonic-gate switch (reqp->request_type) {
48540Sstevel@tonic-gate case PMR_PPM_SET_POWER:
48550Sstevel@tonic-gate {
48560Sstevel@tonic-gate target_dip = reqp->req.ppm_set_power_req.who;
48570Sstevel@tonic-gate ASSERT(target_dip == rdip);
48580Sstevel@tonic-gate new_level = reqp->req.ppm_set_power_req.new_level;
48590Sstevel@tonic-gate cmpt = reqp->req.ppm_set_power_req.cmpt;
48600Sstevel@tonic-gate /* pass back old power for the PM_LEVEL_UNKNOWN case */
48610Sstevel@tonic-gate old_level = PM_CURPOWER(target_dip, cmpt);
48620Sstevel@tonic-gate reqp->req.ppm_set_power_req.old_level = old_level;
48630Sstevel@tonic-gate retval = pm_power(target_dip, cmpt, new_level);
48640Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PPM_SET_POWER %s@%s(%s#%d)[%d] %d->"
48650Sstevel@tonic-gate "%d %s\n", pmf, PM_DEVICE(target_dip), cmpt,
48660Sstevel@tonic-gate old_level, new_level, (retval == DDI_SUCCESS ?
48670Sstevel@tonic-gate "chd" : "no chg")))
48680Sstevel@tonic-gate return (retval);
48690Sstevel@tonic-gate }
48700Sstevel@tonic-gate
48710Sstevel@tonic-gate case PMR_PPM_PRE_DETACH:
48720Sstevel@tonic-gate case PMR_PPM_POST_DETACH:
48730Sstevel@tonic-gate case PMR_PPM_PRE_ATTACH:
48740Sstevel@tonic-gate case PMR_PPM_POST_ATTACH:
48750Sstevel@tonic-gate case PMR_PPM_PRE_PROBE:
48760Sstevel@tonic-gate case PMR_PPM_POST_PROBE:
48770Sstevel@tonic-gate case PMR_PPM_PRE_RESUME:
48780Sstevel@tonic-gate case PMR_PPM_INIT_CHILD:
48790Sstevel@tonic-gate case PMR_PPM_UNINIT_CHILD:
48803839Skchow #ifdef PMDDEBUG
48810Sstevel@tonic-gate switch (reqp->request_type) {
48820Sstevel@tonic-gate case PMR_PPM_PRE_DETACH:
48830Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_DETACH "
48840Sstevel@tonic-gate "%s@%s(%s#%d)\n";
48850Sstevel@tonic-gate break;
48860Sstevel@tonic-gate case PMR_PPM_POST_DETACH:
48870Sstevel@tonic-gate format = "%s: PMR_PPM_POST_DETACH "
48880Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n";
48890Sstevel@tonic-gate break;
48900Sstevel@tonic-gate case PMR_PPM_PRE_ATTACH:
48910Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_ATTACH "
48920Sstevel@tonic-gate "%s@%s(%s#%d)\n";
48930Sstevel@tonic-gate break;
48940Sstevel@tonic-gate case PMR_PPM_POST_ATTACH:
48950Sstevel@tonic-gate format = "%s: PMR_PPM_POST_ATTACH "
48960Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n";
48970Sstevel@tonic-gate break;
48980Sstevel@tonic-gate case PMR_PPM_PRE_PROBE:
48990Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_PROBE "
49000Sstevel@tonic-gate "%s@%s(%s#%d)\n";
49010Sstevel@tonic-gate break;
49020Sstevel@tonic-gate case PMR_PPM_POST_PROBE:
49030Sstevel@tonic-gate format = "%s: PMR_PPM_POST_PROBE "
49040Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n";
49050Sstevel@tonic-gate break;
49060Sstevel@tonic-gate case PMR_PPM_PRE_RESUME:
49070Sstevel@tonic-gate format = "%s: PMR_PPM_PRE_RESUME "
49080Sstevel@tonic-gate "%s@%s(%s#%d) rets %d\n";
49090Sstevel@tonic-gate break;
49100Sstevel@tonic-gate case PMR_PPM_INIT_CHILD:
49110Sstevel@tonic-gate format = "%s: PMR_PPM_INIT_CHILD "
49120Sstevel@tonic-gate "%s@%s(%s#%d)\n";
49130Sstevel@tonic-gate break;
49140Sstevel@tonic-gate case PMR_PPM_UNINIT_CHILD:
49150Sstevel@tonic-gate format = "%s: PMR_PPM_UNINIT_CHILD "
49160Sstevel@tonic-gate "%s@%s(%s#%d)\n";
49170Sstevel@tonic-gate break;
49180Sstevel@tonic-gate default:
49190Sstevel@tonic-gate break;
49200Sstevel@tonic-gate }
49210Sstevel@tonic-gate PMD(PMD_PPM, (format, pmf, PM_DEVICE(rdip),
49220Sstevel@tonic-gate reqp->req.ppm_config_req.result))
49230Sstevel@tonic-gate #endif
49240Sstevel@tonic-gate return (DDI_SUCCESS);
49250Sstevel@tonic-gate
49260Sstevel@tonic-gate case PMR_PPM_POWER_CHANGE_NOTIFY:
49270Sstevel@tonic-gate /*
49280Sstevel@tonic-gate * Nothing for us to do
49290Sstevel@tonic-gate */
49300Sstevel@tonic-gate ASSERT(reqp->req.ppm_notify_level_req.who == rdip);
49310Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PMR_PPM_POWER_CHANGE_NOTIFY "
49320Sstevel@tonic-gate "%s@%s(%s#%d)[%d] %d->%d\n", pmf,
49330Sstevel@tonic-gate PM_DEVICE(reqp->req.ppm_notify_level_req.who),
49340Sstevel@tonic-gate reqp->req.ppm_notify_level_req.cmpt,
49350Sstevel@tonic-gate PM_CURPOWER(reqp->req.ppm_notify_level_req.who,
49360Sstevel@tonic-gate reqp->req.ppm_notify_level_req.cmpt),
49370Sstevel@tonic-gate reqp->req.ppm_notify_level_req.new_level))
49380Sstevel@tonic-gate return (DDI_SUCCESS);
49390Sstevel@tonic-gate
49400Sstevel@tonic-gate case PMR_PPM_UNMANAGE:
49410Sstevel@tonic-gate PMD(PMD_PPM, ("%s: PMR_PPM_UNMANAGE %s@%s(%s#%d)\n",
49420Sstevel@tonic-gate pmf, PM_DEVICE(rdip)))
49430Sstevel@tonic-gate return (DDI_SUCCESS);
49440Sstevel@tonic-gate
49450Sstevel@tonic-gate case PMR_PPM_LOCK_POWER:
49460Sstevel@tonic-gate pm_lock_power_single(reqp->req.ppm_lock_power_req.who,
49470Sstevel@tonic-gate reqp->req.ppm_lock_power_req.circp);
49480Sstevel@tonic-gate return (DDI_SUCCESS);
49490Sstevel@tonic-gate
49500Sstevel@tonic-gate case PMR_PPM_UNLOCK_POWER:
49510Sstevel@tonic-gate pm_unlock_power_single(
49520Sstevel@tonic-gate reqp->req.ppm_unlock_power_req.who,
49530Sstevel@tonic-gate reqp->req.ppm_unlock_power_req.circ);
49540Sstevel@tonic-gate return (DDI_SUCCESS);
49550Sstevel@tonic-gate
49560Sstevel@tonic-gate case PMR_PPM_TRY_LOCK_POWER:
49570Sstevel@tonic-gate *(int *)result = pm_try_locking_power_single(
49580Sstevel@tonic-gate reqp->req.ppm_lock_power_req.who,
49590Sstevel@tonic-gate reqp->req.ppm_lock_power_req.circp);
49600Sstevel@tonic-gate return (DDI_SUCCESS);
49610Sstevel@tonic-gate
49620Sstevel@tonic-gate case PMR_PPM_POWER_LOCK_OWNER:
49630Sstevel@tonic-gate target_dip = reqp->req.ppm_power_lock_owner_req.who;
49640Sstevel@tonic-gate ASSERT(target_dip == rdip);
49650Sstevel@tonic-gate reqp->req.ppm_power_lock_owner_req.owner =
49660Sstevel@tonic-gate DEVI(rdip)->devi_busy_thread;
49670Sstevel@tonic-gate return (DDI_SUCCESS);
49680Sstevel@tonic-gate default:
49690Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: default!\n", pmf))
49700Sstevel@tonic-gate return (DDI_FAILURE);
49710Sstevel@tonic-gate }
49720Sstevel@tonic-gate
49730Sstevel@tonic-gate default:
49740Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: unknown\n", pmf))
49750Sstevel@tonic-gate return (DDI_FAILURE);
49760Sstevel@tonic-gate }
49770Sstevel@tonic-gate }
49780Sstevel@tonic-gate
49790Sstevel@tonic-gate /*
49800Sstevel@tonic-gate * We overload the bus_ctl ops here--perhaps we ought to have a distinct
49810Sstevel@tonic-gate * power_ops struct for this functionality instead?
49820Sstevel@tonic-gate * However, we only ever do this on a ppm driver.
49830Sstevel@tonic-gate */
49840Sstevel@tonic-gate int
pm_ctlops(dev_info_t * d,dev_info_t * r,ddi_ctl_enum_t op,void * a,void * v)49850Sstevel@tonic-gate pm_ctlops(dev_info_t *d, dev_info_t *r, ddi_ctl_enum_t op, void *a, void *v)
49860Sstevel@tonic-gate {
49870Sstevel@tonic-gate int (*fp)();
49880Sstevel@tonic-gate
49890Sstevel@tonic-gate /* if no ppm handler, call the default routine */
49900Sstevel@tonic-gate if (d == NULL) {
49910Sstevel@tonic-gate return (pm_default_ctlops(d, r, op, a, v));
49920Sstevel@tonic-gate }
49930Sstevel@tonic-gate if (!d || !r)
49940Sstevel@tonic-gate return (DDI_FAILURE);
49950Sstevel@tonic-gate ASSERT(DEVI(d)->devi_ops && DEVI(d)->devi_ops->devo_bus_ops &&
49964780Smh27603 DEVI(d)->devi_ops->devo_bus_ops->bus_ctl);
49970Sstevel@tonic-gate
49980Sstevel@tonic-gate fp = DEVI(d)->devi_ops->devo_bus_ops->bus_ctl;
49990Sstevel@tonic-gate return ((*fp)(d, r, op, a, v));
50000Sstevel@tonic-gate }
50010Sstevel@tonic-gate
50020Sstevel@tonic-gate /*
50030Sstevel@tonic-gate * Called on a node when attach completes or the driver makes its first pm
50040Sstevel@tonic-gate * call (whichever comes first).
50050Sstevel@tonic-gate * In the attach case, device may not be power manageable at all.
50060Sstevel@tonic-gate * Don't need to lock the dip because we're single threaded by the devfs code
50070Sstevel@tonic-gate */
50080Sstevel@tonic-gate static int
pm_start(dev_info_t * dip)50090Sstevel@tonic-gate pm_start(dev_info_t *dip)
50100Sstevel@tonic-gate {
50110Sstevel@tonic-gate PMD_FUNC(pmf, "start")
50120Sstevel@tonic-gate int ret;
50130Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
50140Sstevel@tonic-gate int e_pm_manage(dev_info_t *, int);
50150Sstevel@tonic-gate void pm_noinvol_specd(dev_info_t *dip);
50160Sstevel@tonic-gate
50170Sstevel@tonic-gate e_pm_props(dip);
50180Sstevel@tonic-gate pm_noinvol_specd(dip);
50190Sstevel@tonic-gate /*
50200Sstevel@tonic-gate * If this dip has already been processed, don't mess with it
50210Sstevel@tonic-gate * (but decrement the speculative count we did above, as whatever
50220Sstevel@tonic-gate * code put it under pm already will have dealt with it)
50230Sstevel@tonic-gate */
50240Sstevel@tonic-gate if (PM_GET_PM_INFO(dip)) {
50250Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: pm already done for %s@%s(%s#%d)\n",
50260Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
50270Sstevel@tonic-gate return (0);
50280Sstevel@tonic-gate }
50290Sstevel@tonic-gate ret = e_pm_manage(dip, PM_STYLE_UNKNOWN);
50300Sstevel@tonic-gate
50310Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL) {
50320Sstevel@tonic-gate /*
50330Sstevel@tonic-gate * keep the kidsupcount increment as is
50340Sstevel@tonic-gate */
50350Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NOPMKID;
50360Sstevel@tonic-gate if (pdip && !PM_WANTS_NOTIFICATION(pdip)) {
50370Sstevel@tonic-gate pm_hold_power(pdip);
50382155Scth } else if (pdip && MDI_VHCI(pdip) && MDI_CLIENT(dip)) {
50390Sstevel@tonic-gate (void) mdi_power(pdip, MDI_PM_HOLD_POWER,
50400Sstevel@tonic-gate (void *)dip, NULL, 0);
50410Sstevel@tonic-gate }
50420Sstevel@tonic-gate
50430Sstevel@tonic-gate PMD(PMD_KIDSUP, ("%s: pm of %s@%s(%s#%d) failed, parent "
50440Sstevel@tonic-gate "left up\n", pmf, PM_DEVICE(dip)))
50450Sstevel@tonic-gate }
50460Sstevel@tonic-gate
50470Sstevel@tonic-gate return (ret);
50480Sstevel@tonic-gate }
50490Sstevel@tonic-gate
50500Sstevel@tonic-gate /*
50510Sstevel@tonic-gate * Keep a list of recorded thresholds. For now we just keep a list and
50520Sstevel@tonic-gate * search it linearly. We don't expect too many entries. Can always hash it
50530Sstevel@tonic-gate * later if we need to.
50540Sstevel@tonic-gate */
50550Sstevel@tonic-gate void
pm_record_thresh(pm_thresh_rec_t * rp)50560Sstevel@tonic-gate pm_record_thresh(pm_thresh_rec_t *rp)
50570Sstevel@tonic-gate {
50580Sstevel@tonic-gate pm_thresh_rec_t *pptr, *ptr;
50590Sstevel@tonic-gate
50600Sstevel@tonic-gate ASSERT(*rp->ptr_physpath);
50610Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER);
50620Sstevel@tonic-gate for (pptr = NULL, ptr = pm_thresh_head;
50630Sstevel@tonic-gate ptr; pptr = ptr, ptr = ptr->ptr_next) {
50640Sstevel@tonic-gate if (strcmp(rp->ptr_physpath, ptr->ptr_physpath) == 0) {
50650Sstevel@tonic-gate /* replace this one */
50660Sstevel@tonic-gate rp->ptr_next = ptr->ptr_next;
50670Sstevel@tonic-gate if (pptr) {
50680Sstevel@tonic-gate pptr->ptr_next = rp;
50690Sstevel@tonic-gate } else {
50700Sstevel@tonic-gate pm_thresh_head = rp;
50710Sstevel@tonic-gate }
50720Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock);
50730Sstevel@tonic-gate kmem_free(ptr, ptr->ptr_size);
50740Sstevel@tonic-gate return;
50750Sstevel@tonic-gate }
50760Sstevel@tonic-gate continue;
50770Sstevel@tonic-gate }
50780Sstevel@tonic-gate /*
50790Sstevel@tonic-gate * There was not a match in the list, insert this one in front
50800Sstevel@tonic-gate */
50810Sstevel@tonic-gate if (pm_thresh_head) {
50820Sstevel@tonic-gate rp->ptr_next = pm_thresh_head;
50830Sstevel@tonic-gate pm_thresh_head = rp;
50840Sstevel@tonic-gate } else {
50850Sstevel@tonic-gate rp->ptr_next = NULL;
50860Sstevel@tonic-gate pm_thresh_head = rp;
50870Sstevel@tonic-gate }
50880Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock);
50890Sstevel@tonic-gate }
50900Sstevel@tonic-gate
50910Sstevel@tonic-gate /*
50920Sstevel@tonic-gate * Create a new dependency record and hang a new dependency entry off of it
50930Sstevel@tonic-gate */
50940Sstevel@tonic-gate pm_pdr_t *
newpdr(char * kept,char * keeps,int isprop)50950Sstevel@tonic-gate newpdr(char *kept, char *keeps, int isprop)
50960Sstevel@tonic-gate {
50970Sstevel@tonic-gate size_t size = strlen(kept) + strlen(keeps) + 2 + sizeof (pm_pdr_t);
50980Sstevel@tonic-gate pm_pdr_t *p = kmem_zalloc(size, KM_SLEEP);
50990Sstevel@tonic-gate p->pdr_size = size;
51000Sstevel@tonic-gate p->pdr_isprop = isprop;
51010Sstevel@tonic-gate p->pdr_kept_paths = NULL;
51020Sstevel@tonic-gate p->pdr_kept_count = 0;
51030Sstevel@tonic-gate p->pdr_kept = (char *)((intptr_t)p + sizeof (pm_pdr_t));
51040Sstevel@tonic-gate (void) strcpy(p->pdr_kept, kept);
51050Sstevel@tonic-gate p->pdr_keeper = (char *)((intptr_t)p->pdr_kept + strlen(kept) + 1);
51060Sstevel@tonic-gate (void) strcpy(p->pdr_keeper, keeps);
51070Sstevel@tonic-gate ASSERT((intptr_t)p->pdr_keeper + strlen(p->pdr_keeper) + 1 <=
51080Sstevel@tonic-gate (intptr_t)p + size);
51090Sstevel@tonic-gate ASSERT((intptr_t)p->pdr_kept + strlen(p->pdr_kept) + 1 <=
51100Sstevel@tonic-gate (intptr_t)p + size);
51110Sstevel@tonic-gate return (p);
51120Sstevel@tonic-gate }
51130Sstevel@tonic-gate
51140Sstevel@tonic-gate /*
51150Sstevel@tonic-gate * Keep a list of recorded dependencies. We only keep the
51160Sstevel@tonic-gate * keeper -> kept list for simplification. At this point We do not
51170Sstevel@tonic-gate * care about whether the devices are attached or not yet,
51180Sstevel@tonic-gate * this would be done in pm_keeper() and pm_kept().
51190Sstevel@tonic-gate * If a PM_RESET_PM happens, then we tear down and forget the dependencies,
51200Sstevel@tonic-gate * and it is up to the user to issue the ioctl again if they want it
51210Sstevel@tonic-gate * (e.g. pmconfig)
51220Sstevel@tonic-gate * Returns true if dependency already exists in the list.
51230Sstevel@tonic-gate */
51240Sstevel@tonic-gate int
pm_record_keeper(char * kept,char * keeper,int isprop)51250Sstevel@tonic-gate pm_record_keeper(char *kept, char *keeper, int isprop)
51260Sstevel@tonic-gate {
51270Sstevel@tonic-gate PMD_FUNC(pmf, "record_keeper")
51280Sstevel@tonic-gate pm_pdr_t *npdr, *ppdr, *pdr;
51290Sstevel@tonic-gate
51300Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s, %s\n", pmf, kept, keeper))
51310Sstevel@tonic-gate ASSERT(kept && keeper);
51320Sstevel@tonic-gate #ifdef DEBUG
51330Sstevel@tonic-gate if (pm_debug & PMD_KEEPS)
51340Sstevel@tonic-gate prdeps("pm_record_keeper entry");
51350Sstevel@tonic-gate #endif
51360Sstevel@tonic-gate for (ppdr = NULL, pdr = pm_dep_head; pdr;
51370Sstevel@tonic-gate ppdr = pdr, pdr = pdr->pdr_next) {
51380Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: check %s, %s\n", pmf, pdr->pdr_kept,
51390Sstevel@tonic-gate pdr->pdr_keeper))
51400Sstevel@tonic-gate if (strcmp(kept, pdr->pdr_kept) == 0 &&
51410Sstevel@tonic-gate strcmp(keeper, pdr->pdr_keeper) == 0) {
51420Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: match\n", pmf))
51430Sstevel@tonic-gate return (1);
51440Sstevel@tonic-gate }
51450Sstevel@tonic-gate }
51460Sstevel@tonic-gate /*
51470Sstevel@tonic-gate * We did not find any match, so we have to make an entry
51480Sstevel@tonic-gate */
51490Sstevel@tonic-gate npdr = newpdr(kept, keeper, isprop);
51500Sstevel@tonic-gate if (ppdr) {
51510Sstevel@tonic-gate ASSERT(ppdr->pdr_next == NULL);
51520Sstevel@tonic-gate ppdr->pdr_next = npdr;
51530Sstevel@tonic-gate } else {
51540Sstevel@tonic-gate ASSERT(pm_dep_head == NULL);
51550Sstevel@tonic-gate pm_dep_head = npdr;
51560Sstevel@tonic-gate }
51570Sstevel@tonic-gate #ifdef DEBUG
51580Sstevel@tonic-gate if (pm_debug & PMD_KEEPS)
51590Sstevel@tonic-gate prdeps("pm_record_keeper after new record");
51600Sstevel@tonic-gate #endif
51610Sstevel@tonic-gate if (!isprop)
51620Sstevel@tonic-gate pm_unresolved_deps++;
51630Sstevel@tonic-gate else
51640Sstevel@tonic-gate pm_prop_deps++;
51650Sstevel@tonic-gate return (0);
51660Sstevel@tonic-gate }
51670Sstevel@tonic-gate
51680Sstevel@tonic-gate /*
51690Sstevel@tonic-gate * Look up this device in the set of devices we've seen ioctls for
51700Sstevel@tonic-gate * to see if we are holding a threshold spec for it. If so, make it so.
51710Sstevel@tonic-gate * At ioctl time, we were given the physical path of the device.
51720Sstevel@tonic-gate */
51730Sstevel@tonic-gate int
pm_thresh_specd(dev_info_t * dip)51740Sstevel@tonic-gate pm_thresh_specd(dev_info_t *dip)
51750Sstevel@tonic-gate {
51760Sstevel@tonic-gate void pm_apply_recorded_thresh(dev_info_t *, pm_thresh_rec_t *);
51770Sstevel@tonic-gate char *path = 0;
51780Sstevel@tonic-gate char pathbuf[MAXNAMELEN];
51790Sstevel@tonic-gate pm_thresh_rec_t *rp;
51800Sstevel@tonic-gate
51810Sstevel@tonic-gate path = ddi_pathname(dip, pathbuf);
51820Sstevel@tonic-gate
51830Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_READER);
51840Sstevel@tonic-gate for (rp = pm_thresh_head; rp; rp = rp->ptr_next) {
51850Sstevel@tonic-gate if (strcmp(rp->ptr_physpath, path) != 0)
51860Sstevel@tonic-gate continue;
51870Sstevel@tonic-gate pm_apply_recorded_thresh(dip, rp);
51880Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock);
51890Sstevel@tonic-gate return (1);
51900Sstevel@tonic-gate }
51910Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock);
51920Sstevel@tonic-gate return (0);
51930Sstevel@tonic-gate }
51940Sstevel@tonic-gate
51950Sstevel@tonic-gate static int
pm_set_keeping(dev_info_t * keeper,dev_info_t * kept)51960Sstevel@tonic-gate pm_set_keeping(dev_info_t *keeper, dev_info_t *kept)
51970Sstevel@tonic-gate {
51980Sstevel@tonic-gate PMD_FUNC(pmf, "set_keeping")
51990Sstevel@tonic-gate pm_info_t *kept_info;
52000Sstevel@tonic-gate int j, up = 0, circ;
52010Sstevel@tonic-gate void prdeps(char *);
52020Sstevel@tonic-gate
52030Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), kept=%s@%s(%s#%d)\n", pmf,
52040Sstevel@tonic-gate PM_DEVICE(keeper), PM_DEVICE(kept)))
52050Sstevel@tonic-gate #ifdef DEBUG
52060Sstevel@tonic-gate if (pm_debug & PMD_KEEPS)
52070Sstevel@tonic-gate prdeps("Before PAD\n");
52080Sstevel@tonic-gate #endif
52090Sstevel@tonic-gate ASSERT(keeper != kept);
52100Sstevel@tonic-gate if (PM_GET_PM_INFO(keeper) == NULL) {
52110Sstevel@tonic-gate cmn_err(CE_CONT, "!device %s@%s(%s#%d) keeps up device "
521210303SBick.Torrejon@Sun.COM "%s@%s(%s#%d), but the former is not power managed",
52130Sstevel@tonic-gate PM_DEVICE(keeper), PM_DEVICE(kept));
52140Sstevel@tonic-gate PMD((PMD_FAIL | PMD_KEEPS), ("%s: keeper %s@%s(%s#%d) is not"
52150Sstevel@tonic-gate "power managed\n", pmf, PM_DEVICE(keeper)))
52160Sstevel@tonic-gate return (0);
52170Sstevel@tonic-gate }
52180Sstevel@tonic-gate kept_info = PM_GET_PM_INFO(kept);
52190Sstevel@tonic-gate ASSERT(kept_info);
52200Sstevel@tonic-gate PM_LOCK_POWER(keeper, &circ);
52210Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(keeper); j++) {
52220Sstevel@tonic-gate if (PM_CURPOWER(keeper, j)) {
52230Sstevel@tonic-gate up++;
52240Sstevel@tonic-gate break;
52250Sstevel@tonic-gate }
52260Sstevel@tonic-gate }
52270Sstevel@tonic-gate if (up) {
52280Sstevel@tonic-gate /* Bringup and maintain a hold on the kept */
52290Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: place a hold on kept %s@%s(%s#%d)\n", pmf,
52300Sstevel@tonic-gate PM_DEVICE(kept)))
52310Sstevel@tonic-gate bring_pmdep_up(kept, 1);
52320Sstevel@tonic-gate }
52330Sstevel@tonic-gate PM_UNLOCK_POWER(keeper, circ);
52340Sstevel@tonic-gate #ifdef DEBUG
52350Sstevel@tonic-gate if (pm_debug & PMD_KEEPS)
52360Sstevel@tonic-gate prdeps("After PAD\n");
52370Sstevel@tonic-gate #endif
52380Sstevel@tonic-gate return (1);
52390Sstevel@tonic-gate }
52400Sstevel@tonic-gate
52410Sstevel@tonic-gate /*
52420Sstevel@tonic-gate * Should this device keep up another device?
52430Sstevel@tonic-gate * Look up this device in the set of devices we've seen ioctls for
52440Sstevel@tonic-gate * to see if we are holding a dependency spec for it. If so, make it so.
52450Sstevel@tonic-gate * Because we require the kept device to be attached already in order to
52460Sstevel@tonic-gate * make the list entry (and hold it), we only need to look for keepers.
52470Sstevel@tonic-gate * At ioctl time, we were given the physical path of the device.
52480Sstevel@tonic-gate */
52490Sstevel@tonic-gate int
pm_keeper(char * keeper)52500Sstevel@tonic-gate pm_keeper(char *keeper)
52510Sstevel@tonic-gate {
52520Sstevel@tonic-gate PMD_FUNC(pmf, "keeper")
52530Sstevel@tonic-gate int pm_apply_recorded_dep(dev_info_t *, pm_pdr_t *);
52540Sstevel@tonic-gate dev_info_t *dip;
52550Sstevel@tonic-gate pm_pdr_t *dp;
52560Sstevel@tonic-gate dev_info_t *kept = NULL;
52570Sstevel@tonic-gate int ret = 0;
52580Sstevel@tonic-gate int i;
52590Sstevel@tonic-gate
52600Sstevel@tonic-gate if (!pm_unresolved_deps && !pm_prop_deps)
52610Sstevel@tonic-gate return (0);
52620Sstevel@tonic-gate ASSERT(keeper != NULL);
52630Sstevel@tonic-gate dip = pm_name_to_dip(keeper, 1);
52640Sstevel@tonic-gate if (dip == NULL)
52650Sstevel@tonic-gate return (0);
52660Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s\n", pmf, keeper))
52670Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
52680Sstevel@tonic-gate if (!dp->pdr_isprop) {
52690Sstevel@tonic-gate if (!pm_unresolved_deps)
52700Sstevel@tonic-gate continue;
52710Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper %s\n", pmf, dp->pdr_keeper))
52720Sstevel@tonic-gate if (dp->pdr_satisfied) {
52730Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: satisfied\n", pmf))
52740Sstevel@tonic-gate continue;
52750Sstevel@tonic-gate }
52760Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) == 0) {
52770Sstevel@tonic-gate ret += pm_apply_recorded_dep(dip, dp);
52780Sstevel@tonic-gate }
52790Sstevel@tonic-gate } else {
52800Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0)
52810Sstevel@tonic-gate continue;
52820Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) {
52830Sstevel@tonic-gate if (dp->pdr_kept_paths[i] == NULL)
52840Sstevel@tonic-gate continue;
52850Sstevel@tonic-gate kept = pm_name_to_dip(dp->pdr_kept_paths[i], 1);
52860Sstevel@tonic-gate if (kept == NULL)
52870Sstevel@tonic-gate continue;
52880Sstevel@tonic-gate ASSERT(ddi_prop_exists(DDI_DEV_T_ANY, kept,
52890Sstevel@tonic-gate DDI_PROP_DONTPASS, dp->pdr_kept));
52900Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d), "
52910Sstevel@tonic-gate "kept=%s@%s(%s#%d) keptcnt=%d\n",
52920Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_DEVICE(kept),
52930Sstevel@tonic-gate dp->pdr_kept_count))
52940Sstevel@tonic-gate if (kept != dip) {
52950Sstevel@tonic-gate ret += pm_set_keeping(dip, kept);
52960Sstevel@tonic-gate }
52970Sstevel@tonic-gate ddi_release_devi(kept);
52980Sstevel@tonic-gate }
52990Sstevel@tonic-gate
53000Sstevel@tonic-gate }
53010Sstevel@tonic-gate }
53020Sstevel@tonic-gate ddi_release_devi(dip);
53030Sstevel@tonic-gate return (ret);
53040Sstevel@tonic-gate }
53050Sstevel@tonic-gate
53060Sstevel@tonic-gate /*
53070Sstevel@tonic-gate * Should this device be kept up by another device?
53080Sstevel@tonic-gate * Look up all dependency recorded from PM_ADD_DEPENDENT and
53090Sstevel@tonic-gate * PM_ADD_DEPENDENT_PROPERTY ioctls. Record down on the keeper's
53100Sstevel@tonic-gate * kept device lists.
53110Sstevel@tonic-gate */
53120Sstevel@tonic-gate static int
pm_kept(char * keptp)53130Sstevel@tonic-gate pm_kept(char *keptp)
53140Sstevel@tonic-gate {
53150Sstevel@tonic-gate PMD_FUNC(pmf, "kept")
53160Sstevel@tonic-gate pm_pdr_t *dp;
53170Sstevel@tonic-gate int found = 0;
53180Sstevel@tonic-gate int ret = 0;
53190Sstevel@tonic-gate dev_info_t *keeper;
53200Sstevel@tonic-gate dev_info_t *kept;
53210Sstevel@tonic-gate size_t length;
53220Sstevel@tonic-gate int i;
53230Sstevel@tonic-gate char **paths;
53240Sstevel@tonic-gate char *path;
53250Sstevel@tonic-gate
53260Sstevel@tonic-gate ASSERT(keptp != NULL);
53270Sstevel@tonic-gate kept = pm_name_to_dip(keptp, 1);
53280Sstevel@tonic-gate if (kept == NULL)
53290Sstevel@tonic-gate return (0);
53300Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(kept)))
53310Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
53320Sstevel@tonic-gate if (dp->pdr_isprop) {
53330Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: property %s\n", pmf, dp->pdr_kept))
53340Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, kept,
53350Sstevel@tonic-gate DDI_PROP_DONTPASS, dp->pdr_kept)) {
53360Sstevel@tonic-gate /*
53370Sstevel@tonic-gate * Dont allow self dependency.
53380Sstevel@tonic-gate */
53390Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keptp) == 0)
53400Sstevel@tonic-gate continue;
53410Sstevel@tonic-gate keeper = pm_name_to_dip(dp->pdr_keeper, 1);
53420Sstevel@tonic-gate if (keeper == NULL)
53430Sstevel@tonic-gate continue;
53440Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: adding to kepts path list "
53450Sstevel@tonic-gate "%p\n", pmf, (void *)kept))
53460Sstevel@tonic-gate #ifdef DEBUG
53470Sstevel@tonic-gate if (pm_debug & PMD_DEP)
53480Sstevel@tonic-gate prdeps("Before Adding from pm_kept\n");
53490Sstevel@tonic-gate #endif
53500Sstevel@tonic-gate /*
53510Sstevel@tonic-gate * Add ourselves to the dip list.
53520Sstevel@tonic-gate */
53530Sstevel@tonic-gate if (dp->pdr_kept_count == 0) {
53540Sstevel@tonic-gate length = strlen(keptp) + 1;
53550Sstevel@tonic-gate path =
53560Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP);
53570Sstevel@tonic-gate paths = kmem_alloc(sizeof (char **),
53584780Smh27603 KM_SLEEP);
53590Sstevel@tonic-gate (void) strcpy(path, keptp);
53600Sstevel@tonic-gate paths[0] = path;
53610Sstevel@tonic-gate dp->pdr_kept_paths = paths;
53620Sstevel@tonic-gate dp->pdr_kept_count++;
53630Sstevel@tonic-gate } else {
53640Sstevel@tonic-gate /* Check to see if already on list */
53650Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count;
53660Sstevel@tonic-gate i++) {
53670Sstevel@tonic-gate if (strcmp(keptp,
53680Sstevel@tonic-gate dp->pdr_kept_paths[i])
53690Sstevel@tonic-gate == 0) {
53700Sstevel@tonic-gate found++;
53710Sstevel@tonic-gate break;
53720Sstevel@tonic-gate }
53730Sstevel@tonic-gate }
53740Sstevel@tonic-gate if (found) {
53750Sstevel@tonic-gate ddi_release_devi(keeper);
53760Sstevel@tonic-gate continue;
53770Sstevel@tonic-gate }
53780Sstevel@tonic-gate length = dp->pdr_kept_count *
53790Sstevel@tonic-gate sizeof (char **);
53800Sstevel@tonic-gate paths = kmem_alloc(
53810Sstevel@tonic-gate length + sizeof (char **),
53820Sstevel@tonic-gate KM_SLEEP);
53830Sstevel@tonic-gate if (dp->pdr_kept_count) {
53840Sstevel@tonic-gate bcopy(dp->pdr_kept_paths,
53850Sstevel@tonic-gate paths, length);
53860Sstevel@tonic-gate kmem_free(dp->pdr_kept_paths,
53874780Smh27603 length);
53880Sstevel@tonic-gate }
53890Sstevel@tonic-gate dp->pdr_kept_paths = paths;
53900Sstevel@tonic-gate length = strlen(keptp) + 1;
53910Sstevel@tonic-gate path =
53920Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP);
53930Sstevel@tonic-gate (void) strcpy(path, keptp);
53940Sstevel@tonic-gate dp->pdr_kept_paths[i] = path;
53950Sstevel@tonic-gate dp->pdr_kept_count++;
53960Sstevel@tonic-gate }
53970Sstevel@tonic-gate #ifdef DEBUG
53980Sstevel@tonic-gate if (pm_debug & PMD_DEP)
53990Sstevel@tonic-gate prdeps("After from pm_kept\n");
54000Sstevel@tonic-gate #endif
54010Sstevel@tonic-gate if (keeper) {
54020Sstevel@tonic-gate ret += pm_set_keeping(keeper, kept);
54030Sstevel@tonic-gate ddi_release_devi(keeper);
54040Sstevel@tonic-gate }
54050Sstevel@tonic-gate }
54060Sstevel@tonic-gate } else {
54070Sstevel@tonic-gate /*
54080Sstevel@tonic-gate * pm_keeper would be called later to do
54090Sstevel@tonic-gate * the actual pm_set_keeping.
54100Sstevel@tonic-gate */
54110Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: adding to kepts path list %p\n",
54120Sstevel@tonic-gate pmf, (void *)kept))
54130Sstevel@tonic-gate #ifdef DEBUG
54140Sstevel@tonic-gate if (pm_debug & PMD_DEP)
54150Sstevel@tonic-gate prdeps("Before Adding from pm_kept\n");
54160Sstevel@tonic-gate #endif
54170Sstevel@tonic-gate if (strcmp(keptp, dp->pdr_kept) == 0) {
54180Sstevel@tonic-gate if (dp->pdr_kept_paths == NULL) {
54190Sstevel@tonic-gate length = strlen(keptp) + 1;
54200Sstevel@tonic-gate path =
54210Sstevel@tonic-gate kmem_alloc(length, KM_SLEEP);
54220Sstevel@tonic-gate paths = kmem_alloc(sizeof (char **),
54234780Smh27603 KM_SLEEP);
54240Sstevel@tonic-gate (void) strcpy(path, keptp);
54250Sstevel@tonic-gate paths[0] = path;
54260Sstevel@tonic-gate dp->pdr_kept_paths = paths;
54270Sstevel@tonic-gate dp->pdr_kept_count++;
54280Sstevel@tonic-gate }
54290Sstevel@tonic-gate }
54300Sstevel@tonic-gate #ifdef DEBUG
54310Sstevel@tonic-gate if (pm_debug & PMD_DEP)
54324780Smh27603 prdeps("After from pm_kept\n");
54330Sstevel@tonic-gate #endif
54340Sstevel@tonic-gate }
54350Sstevel@tonic-gate }
54360Sstevel@tonic-gate ddi_release_devi(kept);
54370Sstevel@tonic-gate return (ret);
54380Sstevel@tonic-gate }
54390Sstevel@tonic-gate
54400Sstevel@tonic-gate /*
54410Sstevel@tonic-gate * Apply a recorded dependency. dp specifies the dependency, and
54420Sstevel@tonic-gate * keeper is already known to be the device that keeps up the other (kept) one.
54430Sstevel@tonic-gate * We have to the whole tree for the "kept" device, then apply
54440Sstevel@tonic-gate * the dependency (which may already be applied).
54450Sstevel@tonic-gate */
54460Sstevel@tonic-gate int
pm_apply_recorded_dep(dev_info_t * keeper,pm_pdr_t * dp)54470Sstevel@tonic-gate pm_apply_recorded_dep(dev_info_t *keeper, pm_pdr_t *dp)
54480Sstevel@tonic-gate {
54490Sstevel@tonic-gate PMD_FUNC(pmf, "apply_recorded_dep")
54500Sstevel@tonic-gate dev_info_t *kept = NULL;
54510Sstevel@tonic-gate int ret = 0;
54520Sstevel@tonic-gate char *keptp = NULL;
54530Sstevel@tonic-gate
54540Sstevel@tonic-gate /*
54550Sstevel@tonic-gate * Device to Device dependency can only be 1 to 1.
54560Sstevel@tonic-gate */
54570Sstevel@tonic-gate if (dp->pdr_kept_paths == NULL)
54580Sstevel@tonic-gate return (0);
54590Sstevel@tonic-gate keptp = dp->pdr_kept_paths[0];
54600Sstevel@tonic-gate if (keptp == NULL)
54610Sstevel@tonic-gate return (0);
54620Sstevel@tonic-gate ASSERT(*keptp != '\0');
54630Sstevel@tonic-gate kept = pm_name_to_dip(keptp, 1);
54640Sstevel@tonic-gate if (kept == NULL)
54650Sstevel@tonic-gate return (0);
54660Sstevel@tonic-gate if (kept) {
54670Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s, kept=%s\n", pmf,
54680Sstevel@tonic-gate dp->pdr_keeper, keptp))
54690Sstevel@tonic-gate if (pm_set_keeping(keeper, kept)) {
54700Sstevel@tonic-gate ASSERT(dp->pdr_satisfied == 0);
54710Sstevel@tonic-gate dp->pdr_satisfied = 1;
54720Sstevel@tonic-gate ASSERT(pm_unresolved_deps);
54730Sstevel@tonic-gate pm_unresolved_deps--;
54740Sstevel@tonic-gate ret++;
54750Sstevel@tonic-gate }
54760Sstevel@tonic-gate }
54770Sstevel@tonic-gate ddi_release_devi(kept);
54780Sstevel@tonic-gate
54790Sstevel@tonic-gate return (ret);
54800Sstevel@tonic-gate }
54810Sstevel@tonic-gate
54820Sstevel@tonic-gate /*
54830Sstevel@tonic-gate * Called from common/io/pm.c
54840Sstevel@tonic-gate */
54850Sstevel@tonic-gate int
pm_cur_power(pm_component_t * cp)54860Sstevel@tonic-gate pm_cur_power(pm_component_t *cp)
54870Sstevel@tonic-gate {
54880Sstevel@tonic-gate return (cur_power(cp));
54890Sstevel@tonic-gate }
54900Sstevel@tonic-gate
54910Sstevel@tonic-gate /*
54920Sstevel@tonic-gate * External interface to sanity-check a power level.
54930Sstevel@tonic-gate */
54940Sstevel@tonic-gate int
pm_valid_power(dev_info_t * dip,int comp,int level)54950Sstevel@tonic-gate pm_valid_power(dev_info_t *dip, int comp, int level)
54960Sstevel@tonic-gate {
54970Sstevel@tonic-gate PMD_FUNC(pmf, "valid_power")
54980Sstevel@tonic-gate
54990Sstevel@tonic-gate if (comp >= 0 && comp < PM_NUMCMPTS(dip) && level >= 0)
55000Sstevel@tonic-gate return (e_pm_valid_power(dip, comp, level));
55010Sstevel@tonic-gate else {
55020Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: comp=%d, ncomp=%d, level=%d\n",
55030Sstevel@tonic-gate pmf, comp, PM_NUMCMPTS(dip), level))
55040Sstevel@tonic-gate return (0);
55050Sstevel@tonic-gate }
55060Sstevel@tonic-gate }
55070Sstevel@tonic-gate
55080Sstevel@tonic-gate /*
55090Sstevel@tonic-gate * Called when a device that is direct power managed needs to change state.
55100Sstevel@tonic-gate * This routine arranges to block the request until the process managing
55110Sstevel@tonic-gate * the device makes the change (or some other incompatible change) or
55120Sstevel@tonic-gate * the process closes /dev/pm.
55130Sstevel@tonic-gate */
55140Sstevel@tonic-gate static int
pm_block(dev_info_t * dip,int comp,int newpower,int oldpower)55150Sstevel@tonic-gate pm_block(dev_info_t *dip, int comp, int newpower, int oldpower)
55160Sstevel@tonic-gate {
55170Sstevel@tonic-gate pm_rsvp_t *new = kmem_zalloc(sizeof (*new), KM_SLEEP);
55180Sstevel@tonic-gate int ret = 0;
55190Sstevel@tonic-gate void pm_dequeue_blocked(pm_rsvp_t *);
55200Sstevel@tonic-gate void pm_enqueue_blocked(pm_rsvp_t *);
55210Sstevel@tonic-gate
55220Sstevel@tonic-gate ASSERT(!pm_processes_stopped);
55230Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip));
55240Sstevel@tonic-gate new->pr_dip = dip;
55250Sstevel@tonic-gate new->pr_comp = comp;
55260Sstevel@tonic-gate new->pr_newlevel = newpower;
55270Sstevel@tonic-gate new->pr_oldlevel = oldpower;
55280Sstevel@tonic-gate cv_init(&new->pr_cv, NULL, CV_DEFAULT, NULL);
55290Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock);
55300Sstevel@tonic-gate pm_enqueue_blocked(new);
55310Sstevel@tonic-gate pm_enqueue_notify(PSC_PENDING_CHANGE, dip, comp, newpower, oldpower,
55320Sstevel@tonic-gate PM_CANBLOCK_BLOCK);
55330Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
55340Sstevel@tonic-gate /*
55350Sstevel@tonic-gate * truss may make the cv_wait_sig return prematurely
55360Sstevel@tonic-gate */
55370Sstevel@tonic-gate while (ret == 0) {
55380Sstevel@tonic-gate /*
55390Sstevel@tonic-gate * Normally there will be no user context involved, but if
55400Sstevel@tonic-gate * there is (e.g. we are here via an ioctl call to a driver)
55410Sstevel@tonic-gate * then we should allow the process to abort the request,
55420Sstevel@tonic-gate * or we get an unkillable process if the same thread does
55430Sstevel@tonic-gate * PM_DIRECT_PM and pm_raise_power
55440Sstevel@tonic-gate */
55450Sstevel@tonic-gate if (cv_wait_sig(&new->pr_cv, &pm_rsvp_lock) == 0) {
55460Sstevel@tonic-gate ret = PMP_FAIL;
55470Sstevel@tonic-gate } else {
55480Sstevel@tonic-gate ret = new->pr_retval;
55490Sstevel@tonic-gate }
55500Sstevel@tonic-gate }
55510Sstevel@tonic-gate pm_dequeue_blocked(new);
55520Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock);
55530Sstevel@tonic-gate cv_destroy(&new->pr_cv);
55540Sstevel@tonic-gate kmem_free(new, sizeof (*new));
55550Sstevel@tonic-gate return (ret);
55560Sstevel@tonic-gate }
55570Sstevel@tonic-gate
55580Sstevel@tonic-gate /*
55590Sstevel@tonic-gate * Returns true if the process is interested in power level changes (has issued
55600Sstevel@tonic-gate * PM_GET_STATE_CHANGE ioctl).
55610Sstevel@tonic-gate */
55620Sstevel@tonic-gate int
pm_interest_registered(int clone)55630Sstevel@tonic-gate pm_interest_registered(int clone)
55640Sstevel@tonic-gate {
55650Sstevel@tonic-gate ASSERT(clone >= 0 && clone < PM_MAX_CLONE - 1);
55660Sstevel@tonic-gate return (pm_interest[clone]);
55670Sstevel@tonic-gate }
55680Sstevel@tonic-gate
55699694SScott.Rotondo@Sun.COM static void pm_enqueue_pscc(pscc_t *, pscc_t **);
55709694SScott.Rotondo@Sun.COM
55710Sstevel@tonic-gate /*
55720Sstevel@tonic-gate * Process with clone has just done PM_DIRECT_PM on dip, or has asked to
55730Sstevel@tonic-gate * watch all state transitions (dip == NULL). Set up data
55740Sstevel@tonic-gate * structs to communicate with process about state changes.
55750Sstevel@tonic-gate */
55760Sstevel@tonic-gate void
pm_register_watcher(int clone,dev_info_t * dip)55770Sstevel@tonic-gate pm_register_watcher(int clone, dev_info_t *dip)
55780Sstevel@tonic-gate {
55790Sstevel@tonic-gate pscc_t *p;
55800Sstevel@tonic-gate psce_t *psce;
55810Sstevel@tonic-gate
55820Sstevel@tonic-gate /*
55830Sstevel@tonic-gate * We definitely need a control struct, then we have to search to see
55840Sstevel@tonic-gate * there is already an entries struct (in the dip != NULL case).
55850Sstevel@tonic-gate */
55860Sstevel@tonic-gate pscc_t *pscc = kmem_zalloc(sizeof (*pscc), KM_SLEEP);
55870Sstevel@tonic-gate pscc->pscc_clone = clone;
55880Sstevel@tonic-gate pscc->pscc_dip = dip;
55890Sstevel@tonic-gate
55900Sstevel@tonic-gate if (dip) {
55910Sstevel@tonic-gate int found = 0;
55920Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_WRITER);
55930Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = p->pscc_next) {
55940Sstevel@tonic-gate /*
55950Sstevel@tonic-gate * Already an entry for this clone, so just use it
55960Sstevel@tonic-gate * for the new one (for the case where a single
55970Sstevel@tonic-gate * process is watching multiple devices)
55980Sstevel@tonic-gate */
55990Sstevel@tonic-gate if (p->pscc_clone == clone) {
56000Sstevel@tonic-gate pscc->pscc_entries = p->pscc_entries;
56010Sstevel@tonic-gate pscc->pscc_entries->psce_references++;
56020Sstevel@tonic-gate found++;
56035020Smh27603 break;
56040Sstevel@tonic-gate }
56050Sstevel@tonic-gate }
56060Sstevel@tonic-gate if (!found) { /* create a new one */
56070Sstevel@tonic-gate psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP);
56080Sstevel@tonic-gate mutex_init(&psce->psce_lock, NULL, MUTEX_DEFAULT, NULL);
56090Sstevel@tonic-gate psce->psce_first =
56100Sstevel@tonic-gate kmem_zalloc(sizeof (pm_state_change_t) * PSCCOUNT,
56110Sstevel@tonic-gate KM_SLEEP);
56120Sstevel@tonic-gate psce->psce_in = psce->psce_out = psce->psce_first;
56130Sstevel@tonic-gate psce->psce_last = &psce->psce_first[PSCCOUNT - 1];
56140Sstevel@tonic-gate psce->psce_references = 1;
56150Sstevel@tonic-gate pscc->pscc_entries = psce;
56160Sstevel@tonic-gate }
56170Sstevel@tonic-gate pm_enqueue_pscc(pscc, &pm_pscc_direct);
56180Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock);
56190Sstevel@tonic-gate } else {
56200Sstevel@tonic-gate ASSERT(!pm_interest_registered(clone));
56210Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_WRITER);
56220Sstevel@tonic-gate #ifdef DEBUG
56230Sstevel@tonic-gate for (p = pm_pscc_interest; p; p = p->pscc_next) {
56240Sstevel@tonic-gate /*
56250Sstevel@tonic-gate * Should not be an entry for this clone!
56260Sstevel@tonic-gate */
56270Sstevel@tonic-gate ASSERT(p->pscc_clone != clone);
56280Sstevel@tonic-gate }
56290Sstevel@tonic-gate #endif
56300Sstevel@tonic-gate psce = kmem_zalloc(sizeof (psce_t), KM_SLEEP);
56310Sstevel@tonic-gate psce->psce_first = kmem_zalloc(sizeof (pm_state_change_t) *
56320Sstevel@tonic-gate PSCCOUNT, KM_SLEEP);
56330Sstevel@tonic-gate psce->psce_in = psce->psce_out = psce->psce_first;
56340Sstevel@tonic-gate psce->psce_last = &psce->psce_first[PSCCOUNT - 1];
56350Sstevel@tonic-gate psce->psce_references = 1;
56360Sstevel@tonic-gate pscc->pscc_entries = psce;
56370Sstevel@tonic-gate pm_enqueue_pscc(pscc, &pm_pscc_interest);
56380Sstevel@tonic-gate pm_interest[clone] = 1;
56390Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock);
56400Sstevel@tonic-gate }
56410Sstevel@tonic-gate }
56420Sstevel@tonic-gate
56430Sstevel@tonic-gate /*
56440Sstevel@tonic-gate * Remove the given entry from the blocked list
56450Sstevel@tonic-gate */
56460Sstevel@tonic-gate void
pm_dequeue_blocked(pm_rsvp_t * p)56470Sstevel@tonic-gate pm_dequeue_blocked(pm_rsvp_t *p)
56480Sstevel@tonic-gate {
56490Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock));
56500Sstevel@tonic-gate if (pm_blocked_list == p) {
56510Sstevel@tonic-gate ASSERT(p->pr_prev == NULL);
56520Sstevel@tonic-gate if (p->pr_next != NULL)
56530Sstevel@tonic-gate p->pr_next->pr_prev = NULL;
56540Sstevel@tonic-gate pm_blocked_list = p->pr_next;
56550Sstevel@tonic-gate } else {
56560Sstevel@tonic-gate ASSERT(p->pr_prev != NULL);
56570Sstevel@tonic-gate p->pr_prev->pr_next = p->pr_next;
56580Sstevel@tonic-gate if (p->pr_next != NULL)
56590Sstevel@tonic-gate p->pr_next->pr_prev = p->pr_prev;
56600Sstevel@tonic-gate }
56610Sstevel@tonic-gate }
56620Sstevel@tonic-gate
56630Sstevel@tonic-gate /*
56640Sstevel@tonic-gate * Remove the given control struct from the given list
56650Sstevel@tonic-gate */
56660Sstevel@tonic-gate static void
pm_dequeue_pscc(pscc_t * p,pscc_t ** list)56670Sstevel@tonic-gate pm_dequeue_pscc(pscc_t *p, pscc_t **list)
56680Sstevel@tonic-gate {
56690Sstevel@tonic-gate if (*list == p) {
56700Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL);
56710Sstevel@tonic-gate if (p->pscc_next != NULL)
56720Sstevel@tonic-gate p->pscc_next->pscc_prev = NULL;
56730Sstevel@tonic-gate *list = p->pscc_next;
56740Sstevel@tonic-gate } else {
56750Sstevel@tonic-gate ASSERT(p->pscc_prev != NULL);
56760Sstevel@tonic-gate p->pscc_prev->pscc_next = p->pscc_next;
56770Sstevel@tonic-gate if (p->pscc_next != NULL)
56780Sstevel@tonic-gate p->pscc_next->pscc_prev = p->pscc_prev;
56790Sstevel@tonic-gate }
56800Sstevel@tonic-gate }
56810Sstevel@tonic-gate
56820Sstevel@tonic-gate /*
56830Sstevel@tonic-gate * Stick the control struct specified on the front of the list
56840Sstevel@tonic-gate */
56850Sstevel@tonic-gate static void
pm_enqueue_pscc(pscc_t * p,pscc_t ** list)56860Sstevel@tonic-gate pm_enqueue_pscc(pscc_t *p, pscc_t **list)
56870Sstevel@tonic-gate {
56880Sstevel@tonic-gate pscc_t *h; /* entry at head of list */
56890Sstevel@tonic-gate if ((h = *list) == NULL) {
56900Sstevel@tonic-gate *list = p;
56910Sstevel@tonic-gate ASSERT(p->pscc_next == NULL);
56920Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL);
56930Sstevel@tonic-gate } else {
56940Sstevel@tonic-gate p->pscc_next = h;
56950Sstevel@tonic-gate ASSERT(h->pscc_prev == NULL);
56960Sstevel@tonic-gate h->pscc_prev = p;
56970Sstevel@tonic-gate ASSERT(p->pscc_prev == NULL);
56980Sstevel@tonic-gate *list = p;
56990Sstevel@tonic-gate }
57000Sstevel@tonic-gate }
57010Sstevel@tonic-gate
57020Sstevel@tonic-gate /*
57030Sstevel@tonic-gate * If dip is NULL, process is closing "clone" clean up all its registrations.
57040Sstevel@tonic-gate * Otherwise only clean up those for dip because process is just giving up
57050Sstevel@tonic-gate * control of a direct device.
57060Sstevel@tonic-gate */
57070Sstevel@tonic-gate void
pm_deregister_watcher(int clone,dev_info_t * dip)57080Sstevel@tonic-gate pm_deregister_watcher(int clone, dev_info_t *dip)
57090Sstevel@tonic-gate {
57100Sstevel@tonic-gate pscc_t *p, *pn;
57110Sstevel@tonic-gate psce_t *psce;
57120Sstevel@tonic-gate int found = 0;
57130Sstevel@tonic-gate
57140Sstevel@tonic-gate if (dip == NULL) {
57150Sstevel@tonic-gate rw_enter(&pm_pscc_interest_rwlock, RW_WRITER);
57160Sstevel@tonic-gate for (p = pm_pscc_interest; p; p = pn) {
57170Sstevel@tonic-gate pn = p->pscc_next;
57180Sstevel@tonic-gate if (p->pscc_clone == clone) {
57190Sstevel@tonic-gate pm_dequeue_pscc(p, &pm_pscc_interest);
57200Sstevel@tonic-gate psce = p->pscc_entries;
57210Sstevel@tonic-gate ASSERT(psce->psce_references == 1);
57220Sstevel@tonic-gate mutex_destroy(&psce->psce_lock);
57230Sstevel@tonic-gate kmem_free(psce->psce_first,
57240Sstevel@tonic-gate sizeof (pm_state_change_t) * PSCCOUNT);
57250Sstevel@tonic-gate kmem_free(psce, sizeof (*psce));
57260Sstevel@tonic-gate kmem_free(p, sizeof (*p));
57270Sstevel@tonic-gate }
57280Sstevel@tonic-gate }
57290Sstevel@tonic-gate pm_interest[clone] = 0;
57300Sstevel@tonic-gate rw_exit(&pm_pscc_interest_rwlock);
57310Sstevel@tonic-gate }
57320Sstevel@tonic-gate found = 0;
57330Sstevel@tonic-gate rw_enter(&pm_pscc_direct_rwlock, RW_WRITER);
57340Sstevel@tonic-gate for (p = pm_pscc_direct; p; p = pn) {
57350Sstevel@tonic-gate pn = p->pscc_next;
57360Sstevel@tonic-gate if ((dip && p->pscc_dip == dip) ||
57370Sstevel@tonic-gate (dip == NULL && clone == p->pscc_clone)) {
57380Sstevel@tonic-gate ASSERT(clone == p->pscc_clone);
57390Sstevel@tonic-gate found++;
57400Sstevel@tonic-gate /*
57410Sstevel@tonic-gate * Remove from control list
57420Sstevel@tonic-gate */
57430Sstevel@tonic-gate pm_dequeue_pscc(p, &pm_pscc_direct);
57440Sstevel@tonic-gate /*
57450Sstevel@tonic-gate * If we're the last reference, free the
57460Sstevel@tonic-gate * entries struct.
57470Sstevel@tonic-gate */
57480Sstevel@tonic-gate psce = p->pscc_entries;
57490Sstevel@tonic-gate ASSERT(psce);
57500Sstevel@tonic-gate if (psce->psce_references == 1) {
57510Sstevel@tonic-gate kmem_free(psce->psce_first,
57520Sstevel@tonic-gate PSCCOUNT * sizeof (pm_state_change_t));
57530Sstevel@tonic-gate kmem_free(psce, sizeof (*psce));
57540Sstevel@tonic-gate } else {
57550Sstevel@tonic-gate psce->psce_references--;
57560Sstevel@tonic-gate }
57570Sstevel@tonic-gate kmem_free(p, sizeof (*p));
57580Sstevel@tonic-gate }
57590Sstevel@tonic-gate }
57600Sstevel@tonic-gate ASSERT(dip == NULL || found);
57610Sstevel@tonic-gate rw_exit(&pm_pscc_direct_rwlock);
57620Sstevel@tonic-gate }
57630Sstevel@tonic-gate
57640Sstevel@tonic-gate /*
57650Sstevel@tonic-gate * Search the indicated list for an entry that matches clone, and return a
57660Sstevel@tonic-gate * pointer to it. To be interesting, the entry must have something ready to
57670Sstevel@tonic-gate * be passed up to the controlling process.
57680Sstevel@tonic-gate * The returned entry will be locked upon return from this call.
57690Sstevel@tonic-gate */
57700Sstevel@tonic-gate static psce_t *
pm_psc_find_clone(int clone,pscc_t ** list,krwlock_t * lock)57710Sstevel@tonic-gate pm_psc_find_clone(int clone, pscc_t **list, krwlock_t *lock)
57720Sstevel@tonic-gate {
57730Sstevel@tonic-gate pscc_t *p;
57740Sstevel@tonic-gate psce_t *psce;
57750Sstevel@tonic-gate rw_enter(lock, RW_READER);
57760Sstevel@tonic-gate for (p = *list; p; p = p->pscc_next) {
57770Sstevel@tonic-gate if (clone == p->pscc_clone) {
57780Sstevel@tonic-gate psce = p->pscc_entries;
57790Sstevel@tonic-gate mutex_enter(&psce->psce_lock);
57800Sstevel@tonic-gate if (psce->psce_out->size) {
57810Sstevel@tonic-gate rw_exit(lock);
57820Sstevel@tonic-gate return (psce);
57830Sstevel@tonic-gate } else {
57840Sstevel@tonic-gate mutex_exit(&psce->psce_lock);
57850Sstevel@tonic-gate }
57860Sstevel@tonic-gate }
57870Sstevel@tonic-gate }
57880Sstevel@tonic-gate rw_exit(lock);
57890Sstevel@tonic-gate return (NULL);
57900Sstevel@tonic-gate }
57910Sstevel@tonic-gate
57929694SScott.Rotondo@Sun.COM static psce_t *pm_psc_find_clone(int, pscc_t **, krwlock_t *);
57930Sstevel@tonic-gate /*
57940Sstevel@tonic-gate * Find an entry for a particular clone in the direct list.
57950Sstevel@tonic-gate */
57960Sstevel@tonic-gate psce_t *
pm_psc_clone_to_direct(int clone)57970Sstevel@tonic-gate pm_psc_clone_to_direct(int clone)
57980Sstevel@tonic-gate {
57990Sstevel@tonic-gate return (pm_psc_find_clone(clone, &pm_pscc_direct,
58000Sstevel@tonic-gate &pm_pscc_direct_rwlock));
58010Sstevel@tonic-gate }
58020Sstevel@tonic-gate
58030Sstevel@tonic-gate /*
58040Sstevel@tonic-gate * Find an entry for a particular clone in the interest list.
58050Sstevel@tonic-gate */
58060Sstevel@tonic-gate psce_t *
pm_psc_clone_to_interest(int clone)58070Sstevel@tonic-gate pm_psc_clone_to_interest(int clone)
58080Sstevel@tonic-gate {
58090Sstevel@tonic-gate return (pm_psc_find_clone(clone, &pm_pscc_interest,
58100Sstevel@tonic-gate &pm_pscc_interest_rwlock));
58110Sstevel@tonic-gate }
58120Sstevel@tonic-gate
58130Sstevel@tonic-gate /*
58140Sstevel@tonic-gate * Put the given entry at the head of the blocked list
58150Sstevel@tonic-gate */
58160Sstevel@tonic-gate void
pm_enqueue_blocked(pm_rsvp_t * p)58170Sstevel@tonic-gate pm_enqueue_blocked(pm_rsvp_t *p)
58180Sstevel@tonic-gate {
58190Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pm_rsvp_lock));
58200Sstevel@tonic-gate ASSERT(p->pr_next == NULL);
58210Sstevel@tonic-gate ASSERT(p->pr_prev == NULL);
58220Sstevel@tonic-gate if (pm_blocked_list != NULL) {
58230Sstevel@tonic-gate p->pr_next = pm_blocked_list;
58240Sstevel@tonic-gate ASSERT(pm_blocked_list->pr_prev == NULL);
58250Sstevel@tonic-gate pm_blocked_list->pr_prev = p;
58260Sstevel@tonic-gate pm_blocked_list = p;
58270Sstevel@tonic-gate } else {
58280Sstevel@tonic-gate pm_blocked_list = p;
58290Sstevel@tonic-gate }
58300Sstevel@tonic-gate }
58310Sstevel@tonic-gate
58320Sstevel@tonic-gate /*
58330Sstevel@tonic-gate * Sets every power managed device back to its default threshold
58340Sstevel@tonic-gate */
58350Sstevel@tonic-gate void
pm_all_to_default_thresholds(void)58360Sstevel@tonic-gate pm_all_to_default_thresholds(void)
58370Sstevel@tonic-gate {
58380Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_set_dev_thr_walk,
58390Sstevel@tonic-gate (void *) &pm_system_idle_threshold);
58400Sstevel@tonic-gate }
58410Sstevel@tonic-gate
58420Sstevel@tonic-gate static int
pm_set_dev_thr_walk(dev_info_t * dip,void * arg)58430Sstevel@tonic-gate pm_set_dev_thr_walk(dev_info_t *dip, void *arg)
58440Sstevel@tonic-gate {
58450Sstevel@tonic-gate int thr = (int)(*(int *)arg);
58460Sstevel@tonic-gate
58470Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip))
58480Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
58490Sstevel@tonic-gate pm_set_device_threshold(dip, thr, PMC_DEF_THRESH);
58500Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
58510Sstevel@tonic-gate }
58520Sstevel@tonic-gate
58530Sstevel@tonic-gate /*
58540Sstevel@tonic-gate * Returns the current threshold value (in seconds) for the indicated component
58550Sstevel@tonic-gate */
58560Sstevel@tonic-gate int
pm_current_threshold(dev_info_t * dip,int comp,int * threshp)58570Sstevel@tonic-gate pm_current_threshold(dev_info_t *dip, int comp, int *threshp)
58580Sstevel@tonic-gate {
58590Sstevel@tonic-gate if (comp < 0 || comp >= PM_NUMCMPTS(dip)) {
58600Sstevel@tonic-gate return (DDI_FAILURE);
58610Sstevel@tonic-gate } else {
58620Sstevel@tonic-gate *threshp = cur_threshold(dip, comp);
58630Sstevel@tonic-gate return (DDI_SUCCESS);
58640Sstevel@tonic-gate }
58650Sstevel@tonic-gate }
58660Sstevel@tonic-gate
58670Sstevel@tonic-gate /*
58680Sstevel@tonic-gate * To be called when changing the power level of a component of a device.
58690Sstevel@tonic-gate * On some platforms, changing power on one device may require that power
58700Sstevel@tonic-gate * be changed on other, related devices in the same transaction. Thus, we
58710Sstevel@tonic-gate * always pass this request to the platform power manager so that all the
58720Sstevel@tonic-gate * affected devices will be locked.
58730Sstevel@tonic-gate */
58740Sstevel@tonic-gate void
pm_lock_power(dev_info_t * dip,int * circp)58750Sstevel@tonic-gate pm_lock_power(dev_info_t *dip, int *circp)
58760Sstevel@tonic-gate {
58770Sstevel@tonic-gate power_req_t power_req;
58780Sstevel@tonic-gate int result;
58790Sstevel@tonic-gate
58800Sstevel@tonic-gate power_req.request_type = PMR_PPM_LOCK_POWER;
58810Sstevel@tonic-gate power_req.req.ppm_lock_power_req.who = dip;
58820Sstevel@tonic-gate power_req.req.ppm_lock_power_req.circp = circp;
58830Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
58840Sstevel@tonic-gate }
58850Sstevel@tonic-gate
58860Sstevel@tonic-gate /*
58870Sstevel@tonic-gate * Release the lock (or locks) acquired to change the power of a device.
58880Sstevel@tonic-gate * See comments for pm_lock_power.
58890Sstevel@tonic-gate */
58900Sstevel@tonic-gate void
pm_unlock_power(dev_info_t * dip,int circ)58910Sstevel@tonic-gate pm_unlock_power(dev_info_t *dip, int circ)
58920Sstevel@tonic-gate {
58930Sstevel@tonic-gate power_req_t power_req;
58940Sstevel@tonic-gate int result;
58950Sstevel@tonic-gate
58960Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNLOCK_POWER;
58970Sstevel@tonic-gate power_req.req.ppm_unlock_power_req.who = dip;
58980Sstevel@tonic-gate power_req.req.ppm_unlock_power_req.circ = circ;
58990Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
59000Sstevel@tonic-gate }
59010Sstevel@tonic-gate
59020Sstevel@tonic-gate
59030Sstevel@tonic-gate /*
59040Sstevel@tonic-gate * Attempt (without blocking) to acquire the lock(s) needed to change the
59050Sstevel@tonic-gate * power of a component of a device. See comments for pm_lock_power.
59060Sstevel@tonic-gate *
59070Sstevel@tonic-gate * Return: 1 if lock(s) acquired, 0 if not.
59080Sstevel@tonic-gate */
59090Sstevel@tonic-gate int
pm_try_locking_power(dev_info_t * dip,int * circp)59100Sstevel@tonic-gate pm_try_locking_power(dev_info_t *dip, int *circp)
59110Sstevel@tonic-gate {
59120Sstevel@tonic-gate power_req_t power_req;
59130Sstevel@tonic-gate int result;
59140Sstevel@tonic-gate
59150Sstevel@tonic-gate power_req.request_type = PMR_PPM_TRY_LOCK_POWER;
59160Sstevel@tonic-gate power_req.req.ppm_lock_power_req.who = dip;
59170Sstevel@tonic-gate power_req.req.ppm_lock_power_req.circp = circp;
59180Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req, &result);
59190Sstevel@tonic-gate return (result);
59200Sstevel@tonic-gate }
59210Sstevel@tonic-gate
59220Sstevel@tonic-gate
59230Sstevel@tonic-gate /*
59240Sstevel@tonic-gate * Lock power state of a device.
59250Sstevel@tonic-gate *
59260Sstevel@tonic-gate * The implementation handles a special case where another thread may have
59270Sstevel@tonic-gate * acquired the lock and created/launched this thread to do the work. If
59280Sstevel@tonic-gate * the lock cannot be acquired immediately, we check to see if this thread
59290Sstevel@tonic-gate * is registered as a borrower of the lock. If so, we may proceed without
59300Sstevel@tonic-gate * the lock. This assumes that the lending thread blocks on the completion
59310Sstevel@tonic-gate * of this thread.
59320Sstevel@tonic-gate *
59330Sstevel@tonic-gate * Note 1: for use by ppm only.
59340Sstevel@tonic-gate *
59350Sstevel@tonic-gate * Note 2: On failing to get the lock immediately, we search lock_loan list
59360Sstevel@tonic-gate * for curthread (as borrower of the lock). On a hit, we check that the
59370Sstevel@tonic-gate * lending thread already owns the lock we want. It is safe to compare
59380Sstevel@tonic-gate * devi_busy_thread and thread id of the lender because in the == case (the
59390Sstevel@tonic-gate * only one we care about) we know that the owner is blocked. Similarly,
59400Sstevel@tonic-gate * If we find that curthread isn't registered as a lock borrower, it is safe
59410Sstevel@tonic-gate * to use the blocking call (ndi_devi_enter) because we know that if we
59420Sstevel@tonic-gate * weren't already listed as a borrower (upstream on the call stack) we won't
59430Sstevel@tonic-gate * become one.
59440Sstevel@tonic-gate */
59450Sstevel@tonic-gate void
pm_lock_power_single(dev_info_t * dip,int * circp)59460Sstevel@tonic-gate pm_lock_power_single(dev_info_t *dip, int *circp)
59470Sstevel@tonic-gate {
59480Sstevel@tonic-gate lock_loan_t *cur;
59490Sstevel@tonic-gate
59500Sstevel@tonic-gate /* if the lock is available, we are done. */
59510Sstevel@tonic-gate if (ndi_devi_tryenter(dip, circp))
59520Sstevel@tonic-gate return;
59530Sstevel@tonic-gate
59540Sstevel@tonic-gate mutex_enter(&pm_loan_lock);
59550Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */
59560Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
59570Sstevel@tonic-gate if (cur->pmlk_borrower == curthread)
59580Sstevel@tonic-gate break;
59590Sstevel@tonic-gate mutex_exit(&pm_loan_lock);
59600Sstevel@tonic-gate
59610Sstevel@tonic-gate /* if this thread not already registered, it is safe to block */
59620Sstevel@tonic-gate if (cur == NULL)
59630Sstevel@tonic-gate ndi_devi_enter(dip, circp);
59640Sstevel@tonic-gate else {
59650Sstevel@tonic-gate /* registered: does lender own the lock we want? */
59660Sstevel@tonic-gate if (cur->pmlk_lender == DEVI(dip)->devi_busy_thread) {
59670Sstevel@tonic-gate ASSERT(cur->pmlk_dip == NULL || cur->pmlk_dip == dip);
59680Sstevel@tonic-gate cur->pmlk_dip = dip;
59690Sstevel@tonic-gate } else /* no: just block for it */
59700Sstevel@tonic-gate ndi_devi_enter(dip, circp);
59710Sstevel@tonic-gate
59720Sstevel@tonic-gate }
59730Sstevel@tonic-gate }
59740Sstevel@tonic-gate
59750Sstevel@tonic-gate /*
59760Sstevel@tonic-gate * Drop the lock on the device's power state. See comment for
59770Sstevel@tonic-gate * pm_lock_power_single() for special implementation considerations.
59780Sstevel@tonic-gate *
59790Sstevel@tonic-gate * Note: for use by ppm only.
59800Sstevel@tonic-gate */
59810Sstevel@tonic-gate void
pm_unlock_power_single(dev_info_t * dip,int circ)59820Sstevel@tonic-gate pm_unlock_power_single(dev_info_t *dip, int circ)
59830Sstevel@tonic-gate {
59840Sstevel@tonic-gate lock_loan_t *cur;
59850Sstevel@tonic-gate
59860Sstevel@tonic-gate /* optimization: mutex not needed to check empty list */
59870Sstevel@tonic-gate if (lock_loan_head.pmlk_next == NULL) {
59880Sstevel@tonic-gate ndi_devi_exit(dip, circ);
59890Sstevel@tonic-gate return;
59900Sstevel@tonic-gate }
59910Sstevel@tonic-gate
59920Sstevel@tonic-gate mutex_enter(&pm_loan_lock);
59930Sstevel@tonic-gate /* see if our thread is registered as a lock borrower. */
59940Sstevel@tonic-gate for (cur = lock_loan_head.pmlk_next; cur; cur = cur->pmlk_next)
59950Sstevel@tonic-gate if (cur->pmlk_borrower == curthread)
59960Sstevel@tonic-gate break;
59970Sstevel@tonic-gate mutex_exit(&pm_loan_lock);
59980Sstevel@tonic-gate
59990Sstevel@tonic-gate if (cur == NULL || cur->pmlk_dip != dip)
60000Sstevel@tonic-gate /* we acquired the lock directly, so return it */
60010Sstevel@tonic-gate ndi_devi_exit(dip, circ);
60020Sstevel@tonic-gate }
60030Sstevel@tonic-gate
60040Sstevel@tonic-gate /*
60050Sstevel@tonic-gate * Try to take the lock for changing the power level of a component.
60060Sstevel@tonic-gate *
60070Sstevel@tonic-gate * Note: for use by ppm only.
60080Sstevel@tonic-gate */
60090Sstevel@tonic-gate int
pm_try_locking_power_single(dev_info_t * dip,int * circp)60100Sstevel@tonic-gate pm_try_locking_power_single(dev_info_t *dip, int *circp)
60110Sstevel@tonic-gate {
60120Sstevel@tonic-gate return (ndi_devi_tryenter(dip, circp));
60130Sstevel@tonic-gate }
60140Sstevel@tonic-gate
60150Sstevel@tonic-gate #ifdef DEBUG
60160Sstevel@tonic-gate /*
60170Sstevel@tonic-gate * The following are used only to print out data structures for debugging
60180Sstevel@tonic-gate */
60190Sstevel@tonic-gate void
prdeps(char * msg)60200Sstevel@tonic-gate prdeps(char *msg)
60210Sstevel@tonic-gate {
60220Sstevel@tonic-gate
60230Sstevel@tonic-gate pm_pdr_t *rp;
60240Sstevel@tonic-gate int i;
60250Sstevel@tonic-gate
60260Sstevel@tonic-gate pm_log("pm_dep_head %s %p\n", msg, (void *)pm_dep_head);
60270Sstevel@tonic-gate for (rp = pm_dep_head; rp; rp = rp->pdr_next) {
60280Sstevel@tonic-gate pm_log("%p: %s keeper %s, kept %s, kept count %d, next %p\n",
60290Sstevel@tonic-gate (void *)rp, (rp->pdr_isprop ? "property" : "device"),
60300Sstevel@tonic-gate rp->pdr_keeper, rp->pdr_kept, rp->pdr_kept_count,
60310Sstevel@tonic-gate (void *)rp->pdr_next);
60320Sstevel@tonic-gate if (rp->pdr_kept_count != 0) {
60330Sstevel@tonic-gate pm_log("kept list = ");
60340Sstevel@tonic-gate i = 0;
60350Sstevel@tonic-gate while (i < rp->pdr_kept_count) {
60360Sstevel@tonic-gate pm_log("%s ", rp->pdr_kept_paths[i]);
60370Sstevel@tonic-gate i++;
60380Sstevel@tonic-gate }
60390Sstevel@tonic-gate pm_log("\n");
60400Sstevel@tonic-gate }
60410Sstevel@tonic-gate }
60420Sstevel@tonic-gate }
60430Sstevel@tonic-gate
60440Sstevel@tonic-gate void
pr_noinvol(char * hdr)60450Sstevel@tonic-gate pr_noinvol(char *hdr)
60460Sstevel@tonic-gate {
60470Sstevel@tonic-gate pm_noinvol_t *ip;
60480Sstevel@tonic-gate
60490Sstevel@tonic-gate pm_log("%s\n", hdr);
60500Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER);
60510Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next)
60520Sstevel@tonic-gate pm_log("\tmaj %d, flags %x, noinvolpm %d %s\n",
60530Sstevel@tonic-gate ip->ni_major, ip->ni_flags, ip->ni_noinvolpm, ip->ni_path);
60540Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
60550Sstevel@tonic-gate }
60560Sstevel@tonic-gate #endif
60570Sstevel@tonic-gate
60580Sstevel@tonic-gate /*
60590Sstevel@tonic-gate * Attempt to apply the thresholds indicated by rp to the node specified by
60600Sstevel@tonic-gate * dip.
60610Sstevel@tonic-gate */
60620Sstevel@tonic-gate void
pm_apply_recorded_thresh(dev_info_t * dip,pm_thresh_rec_t * rp)60630Sstevel@tonic-gate pm_apply_recorded_thresh(dev_info_t *dip, pm_thresh_rec_t *rp)
60640Sstevel@tonic-gate {
60650Sstevel@tonic-gate PMD_FUNC(pmf, "apply_recorded_thresh")
60660Sstevel@tonic-gate int i, j;
60670Sstevel@tonic-gate int comps = PM_NUMCMPTS(dip);
60680Sstevel@tonic-gate struct pm_component *cp;
60690Sstevel@tonic-gate pm_pte_t *ep;
60700Sstevel@tonic-gate int pm_valid_thresh(dev_info_t *, pm_thresh_rec_t *);
60710Sstevel@tonic-gate
60720Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: part: %s@%s(%s#%d), rp %p, %s\n", pmf,
60730Sstevel@tonic-gate PM_DEVICE(dip), (void *)rp, rp->ptr_physpath))
60740Sstevel@tonic-gate PM_LOCK_DIP(dip);
60750Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip) || !pm_valid_thresh(dip, rp)) {
60760Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_GET_PM_INFO %p\n",
60770Sstevel@tonic-gate pmf, PM_DEVICE(dip), (void*)PM_GET_PM_INFO(dip)))
60780Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) PM_ISBC %d\n",
60790Sstevel@tonic-gate pmf, PM_DEVICE(dip), PM_ISBC(dip)))
60800Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: part: %s@%s(%s#%d) pm_valid_thresh %d\n",
60810Sstevel@tonic-gate pmf, PM_DEVICE(dip), pm_valid_thresh(dip, rp)))
60820Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
60830Sstevel@tonic-gate return;
60840Sstevel@tonic-gate }
60850Sstevel@tonic-gate
60860Sstevel@tonic-gate ep = rp->ptr_entries;
60870Sstevel@tonic-gate /*
60880Sstevel@tonic-gate * Here we do the special case of a device threshold
60890Sstevel@tonic-gate */
60900Sstevel@tonic-gate if (rp->ptr_numcomps == 0) { /* PM_SET_DEVICE_THRESHOLD product */
60910Sstevel@tonic-gate ASSERT(ep && ep->pte_numthresh == 1);
60920Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: set dev thr %s@%s(%s#%d) to 0x%x\n",
60930Sstevel@tonic-gate pmf, PM_DEVICE(dip), ep->pte_thresh[0]))
60940Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
60950Sstevel@tonic-gate pm_set_device_threshold(dip, ep->pte_thresh[0], PMC_DEV_THRESH);
60963028Smh27603 if (PM_SCANABLE(dip))
60970Sstevel@tonic-gate pm_rescan(dip);
60980Sstevel@tonic-gate return;
60990Sstevel@tonic-gate }
61000Sstevel@tonic-gate for (i = 0; i < comps; i++) {
61010Sstevel@tonic-gate cp = PM_CP(dip, i);
61020Sstevel@tonic-gate for (j = 0; j < ep->pte_numthresh; j++) {
61030Sstevel@tonic-gate PMD(PMD_THRESH, ("%s: set thr %d for %s@%s(%s#%d)[%d] "
61040Sstevel@tonic-gate "to %x\n", pmf, j, PM_DEVICE(dip),
61050Sstevel@tonic-gate i, ep->pte_thresh[j]))
61060Sstevel@tonic-gate cp->pmc_comp.pmc_thresh[j + 1] = ep->pte_thresh[j];
61070Sstevel@tonic-gate }
61080Sstevel@tonic-gate ep++;
61090Sstevel@tonic-gate }
61100Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &= PMC_THRESH_NONE;
61110Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_COMP_THRESH;
61120Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
61130Sstevel@tonic-gate
61143028Smh27603 if (PM_SCANABLE(dip))
61150Sstevel@tonic-gate pm_rescan(dip);
61160Sstevel@tonic-gate }
61170Sstevel@tonic-gate
61180Sstevel@tonic-gate /*
61190Sstevel@tonic-gate * Returns true if the threshold specified by rp could be applied to dip
61200Sstevel@tonic-gate * (that is, the number of components and transitions are the same)
61210Sstevel@tonic-gate */
61220Sstevel@tonic-gate int
pm_valid_thresh(dev_info_t * dip,pm_thresh_rec_t * rp)61230Sstevel@tonic-gate pm_valid_thresh(dev_info_t *dip, pm_thresh_rec_t *rp)
61240Sstevel@tonic-gate {
61250Sstevel@tonic-gate PMD_FUNC(pmf, "valid_thresh")
61260Sstevel@tonic-gate int comps, i;
61270Sstevel@tonic-gate pm_component_t *cp;
61280Sstevel@tonic-gate pm_pte_t *ep;
61290Sstevel@tonic-gate
61300Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
61310Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s: no pm_info or BC\n", pmf,
61320Sstevel@tonic-gate rp->ptr_physpath))
61330Sstevel@tonic-gate return (0);
61340Sstevel@tonic-gate }
61350Sstevel@tonic-gate /*
61360Sstevel@tonic-gate * Special case: we represent the PM_SET_DEVICE_THRESHOLD case by
61370Sstevel@tonic-gate * an entry with numcomps == 0, (since we don't know how many
61380Sstevel@tonic-gate * components there are in advance). This is always a valid
61390Sstevel@tonic-gate * spec.
61400Sstevel@tonic-gate */
61410Sstevel@tonic-gate if (rp->ptr_numcomps == 0) {
61420Sstevel@tonic-gate ASSERT(rp->ptr_entries && rp->ptr_entries->pte_numthresh == 1);
61430Sstevel@tonic-gate return (1);
61440Sstevel@tonic-gate }
61450Sstevel@tonic-gate if (rp->ptr_numcomps != (comps = PM_NUMCMPTS(dip))) {
61460Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: comp # mm (dip %d cmd %d) for %s\n",
61470Sstevel@tonic-gate pmf, PM_NUMCMPTS(dip), rp->ptr_numcomps, rp->ptr_physpath))
61480Sstevel@tonic-gate return (0);
61490Sstevel@tonic-gate }
61500Sstevel@tonic-gate ep = rp->ptr_entries;
61510Sstevel@tonic-gate for (i = 0; i < comps; i++) {
61520Sstevel@tonic-gate cp = PM_CP(dip, i);
61530Sstevel@tonic-gate if ((ep + i)->pte_numthresh !=
61540Sstevel@tonic-gate cp->pmc_comp.pmc_numlevels - 1) {
61550Sstevel@tonic-gate PMD(PMD_ERROR, ("%s: %s[%d]: thresh=%d, record=%d\n",
61560Sstevel@tonic-gate pmf, rp->ptr_physpath, i,
61570Sstevel@tonic-gate cp->pmc_comp.pmc_numlevels - 1,
61580Sstevel@tonic-gate (ep + i)->pte_numthresh))
61590Sstevel@tonic-gate return (0);
61600Sstevel@tonic-gate }
61610Sstevel@tonic-gate }
61620Sstevel@tonic-gate return (1);
61630Sstevel@tonic-gate }
61640Sstevel@tonic-gate
61650Sstevel@tonic-gate /*
61660Sstevel@tonic-gate * Remove any recorded threshold for device physpath
61670Sstevel@tonic-gate * We know there will be at most one.
61680Sstevel@tonic-gate */
61690Sstevel@tonic-gate void
pm_unrecord_threshold(char * physpath)61700Sstevel@tonic-gate pm_unrecord_threshold(char *physpath)
61710Sstevel@tonic-gate {
61720Sstevel@tonic-gate pm_thresh_rec_t *pptr, *ptr;
61730Sstevel@tonic-gate
61740Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER);
61750Sstevel@tonic-gate for (pptr = NULL, ptr = pm_thresh_head; ptr; ptr = ptr->ptr_next) {
61760Sstevel@tonic-gate if (strcmp(physpath, ptr->ptr_physpath) == 0) {
61770Sstevel@tonic-gate if (pptr) {
61780Sstevel@tonic-gate pptr->ptr_next = ptr->ptr_next;
61790Sstevel@tonic-gate } else {
61800Sstevel@tonic-gate ASSERT(pm_thresh_head == ptr);
61810Sstevel@tonic-gate pm_thresh_head = ptr->ptr_next;
61820Sstevel@tonic-gate }
61830Sstevel@tonic-gate kmem_free(ptr, ptr->ptr_size);
61840Sstevel@tonic-gate break;
61850Sstevel@tonic-gate }
61860Sstevel@tonic-gate pptr = ptr;
61870Sstevel@tonic-gate }
61880Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock);
61890Sstevel@tonic-gate }
61900Sstevel@tonic-gate
61910Sstevel@tonic-gate /*
61920Sstevel@tonic-gate * Discard all recorded thresholds. We are returning to the default pm state.
61930Sstevel@tonic-gate */
61940Sstevel@tonic-gate void
pm_discard_thresholds(void)61950Sstevel@tonic-gate pm_discard_thresholds(void)
61960Sstevel@tonic-gate {
61970Sstevel@tonic-gate pm_thresh_rec_t *rp;
61980Sstevel@tonic-gate rw_enter(&pm_thresh_rwlock, RW_WRITER);
61990Sstevel@tonic-gate while (pm_thresh_head) {
62000Sstevel@tonic-gate rp = pm_thresh_head;
62010Sstevel@tonic-gate pm_thresh_head = rp->ptr_next;
62020Sstevel@tonic-gate kmem_free(rp, rp->ptr_size);
62030Sstevel@tonic-gate }
62040Sstevel@tonic-gate rw_exit(&pm_thresh_rwlock);
62050Sstevel@tonic-gate }
62060Sstevel@tonic-gate
62070Sstevel@tonic-gate /*
62080Sstevel@tonic-gate * Discard all recorded dependencies. We are returning to the default pm state.
62090Sstevel@tonic-gate */
62100Sstevel@tonic-gate void
pm_discard_dependencies(void)62110Sstevel@tonic-gate pm_discard_dependencies(void)
62120Sstevel@tonic-gate {
62130Sstevel@tonic-gate pm_pdr_t *rp;
62140Sstevel@tonic-gate int i;
62150Sstevel@tonic-gate size_t length;
62160Sstevel@tonic-gate
62170Sstevel@tonic-gate #ifdef DEBUG
62180Sstevel@tonic-gate if (pm_debug & PMD_DEP)
62190Sstevel@tonic-gate prdeps("Before discard\n");
62200Sstevel@tonic-gate #endif
62210Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_discard_dep_walk, NULL);
62220Sstevel@tonic-gate
62230Sstevel@tonic-gate #ifdef DEBUG
62240Sstevel@tonic-gate if (pm_debug & PMD_DEP)
62250Sstevel@tonic-gate prdeps("After discard\n");
62260Sstevel@tonic-gate #endif
62270Sstevel@tonic-gate while (pm_dep_head) {
62280Sstevel@tonic-gate rp = pm_dep_head;
62290Sstevel@tonic-gate if (!rp->pdr_isprop) {
62300Sstevel@tonic-gate ASSERT(rp->pdr_satisfied == 0);
62310Sstevel@tonic-gate ASSERT(pm_unresolved_deps);
62320Sstevel@tonic-gate pm_unresolved_deps--;
62330Sstevel@tonic-gate } else {
62340Sstevel@tonic-gate ASSERT(pm_prop_deps);
62350Sstevel@tonic-gate pm_prop_deps--;
62360Sstevel@tonic-gate }
62370Sstevel@tonic-gate pm_dep_head = rp->pdr_next;
62380Sstevel@tonic-gate if (rp->pdr_kept_count) {
62390Sstevel@tonic-gate for (i = 0; i < rp->pdr_kept_count; i++) {
62400Sstevel@tonic-gate length = strlen(rp->pdr_kept_paths[i]) + 1;
62410Sstevel@tonic-gate kmem_free(rp->pdr_kept_paths[i], length);
62420Sstevel@tonic-gate }
62430Sstevel@tonic-gate kmem_free(rp->pdr_kept_paths,
62444780Smh27603 rp->pdr_kept_count * sizeof (char **));
62450Sstevel@tonic-gate }
62460Sstevel@tonic-gate kmem_free(rp, rp->pdr_size);
62470Sstevel@tonic-gate }
62480Sstevel@tonic-gate }
62490Sstevel@tonic-gate
62500Sstevel@tonic-gate
62510Sstevel@tonic-gate static int
pm_discard_dep_walk(dev_info_t * dip,void * arg)62520Sstevel@tonic-gate pm_discard_dep_walk(dev_info_t *dip, void *arg)
62530Sstevel@tonic-gate {
62540Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
62550Sstevel@tonic-gate char *pathbuf;
62560Sstevel@tonic-gate
62570Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL)
62580Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
62590Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62600Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
62610Sstevel@tonic-gate pm_free_keeper(pathbuf, 0);
62620Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
62630Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
62640Sstevel@tonic-gate }
62650Sstevel@tonic-gate
62660Sstevel@tonic-gate static int
pm_kept_walk(dev_info_t * dip,void * arg)62670Sstevel@tonic-gate pm_kept_walk(dev_info_t *dip, void *arg)
62680Sstevel@tonic-gate {
62690Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
62700Sstevel@tonic-gate char *pathbuf;
62710Sstevel@tonic-gate
62720Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62730Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
62740Sstevel@tonic-gate (void) pm_kept(pathbuf);
62750Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
62760Sstevel@tonic-gate
62770Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
62780Sstevel@tonic-gate }
62790Sstevel@tonic-gate
62800Sstevel@tonic-gate static int
pm_keeper_walk(dev_info_t * dip,void * arg)62810Sstevel@tonic-gate pm_keeper_walk(dev_info_t *dip, void *arg)
62820Sstevel@tonic-gate {
62830Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
62840Sstevel@tonic-gate char *pathbuf;
62850Sstevel@tonic-gate
62860Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62870Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
62880Sstevel@tonic-gate (void) pm_keeper(pathbuf);
62890Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
62900Sstevel@tonic-gate
62910Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
62920Sstevel@tonic-gate }
62930Sstevel@tonic-gate
62940Sstevel@tonic-gate static char *
pdw_type_decode(int type)62950Sstevel@tonic-gate pdw_type_decode(int type)
62960Sstevel@tonic-gate {
62970Sstevel@tonic-gate switch (type) {
62980Sstevel@tonic-gate case PM_DEP_WK_POWER_ON:
62990Sstevel@tonic-gate return ("power on");
63000Sstevel@tonic-gate case PM_DEP_WK_POWER_OFF:
63010Sstevel@tonic-gate return ("power off");
63020Sstevel@tonic-gate case PM_DEP_WK_DETACH:
63030Sstevel@tonic-gate return ("detach");
63040Sstevel@tonic-gate case PM_DEP_WK_REMOVE_DEP:
63050Sstevel@tonic-gate return ("remove dep");
63060Sstevel@tonic-gate case PM_DEP_WK_BRINGUP_SELF:
63070Sstevel@tonic-gate return ("bringup self");
63080Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER:
63090Sstevel@tonic-gate return ("add dependent");
63100Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER_PROP:
63110Sstevel@tonic-gate return ("add dependent property");
63120Sstevel@tonic-gate case PM_DEP_WK_KEPT:
63130Sstevel@tonic-gate return ("kept");
63140Sstevel@tonic-gate case PM_DEP_WK_KEEPER:
63150Sstevel@tonic-gate return ("keeper");
63160Sstevel@tonic-gate case PM_DEP_WK_ATTACH:
63170Sstevel@tonic-gate return ("attach");
63180Sstevel@tonic-gate case PM_DEP_WK_CHECK_KEPT:
63190Sstevel@tonic-gate return ("check kept");
63200Sstevel@tonic-gate case PM_DEP_WK_CPR_SUSPEND:
63210Sstevel@tonic-gate return ("suspend");
63220Sstevel@tonic-gate case PM_DEP_WK_CPR_RESUME:
63230Sstevel@tonic-gate return ("resume");
63240Sstevel@tonic-gate default:
63250Sstevel@tonic-gate return ("unknown");
63260Sstevel@tonic-gate }
63270Sstevel@tonic-gate
63280Sstevel@tonic-gate }
63290Sstevel@tonic-gate
63300Sstevel@tonic-gate static void
pm_rele_dep(char * keeper)63310Sstevel@tonic-gate pm_rele_dep(char *keeper)
63320Sstevel@tonic-gate {
63330Sstevel@tonic-gate PMD_FUNC(pmf, "rele_dep")
63340Sstevel@tonic-gate pm_pdr_t *dp;
63350Sstevel@tonic-gate char *kept_path = NULL;
63360Sstevel@tonic-gate dev_info_t *kept = NULL;
63370Sstevel@tonic-gate int count = 0;
63380Sstevel@tonic-gate
63390Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
63400Sstevel@tonic-gate if (strcmp(dp->pdr_keeper, keeper) != 0)
63410Sstevel@tonic-gate continue;
63420Sstevel@tonic-gate for (count = 0; count < dp->pdr_kept_count; count++) {
63430Sstevel@tonic-gate kept_path = dp->pdr_kept_paths[count];
63440Sstevel@tonic-gate if (kept_path == NULL)
63450Sstevel@tonic-gate continue;
63460Sstevel@tonic-gate kept = pm_name_to_dip(kept_path, 1);
63470Sstevel@tonic-gate if (kept) {
63480Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: release kept=%s@%s(%s#%d) "
63490Sstevel@tonic-gate "of keeper=%s\n", pmf, PM_DEVICE(kept),
63500Sstevel@tonic-gate keeper))
63510Sstevel@tonic-gate ASSERT(DEVI(kept)->devi_pm_kidsupcnt > 0);
63520Sstevel@tonic-gate pm_rele_power(kept);
63530Sstevel@tonic-gate ddi_release_devi(kept);
63540Sstevel@tonic-gate }
63550Sstevel@tonic-gate }
63560Sstevel@tonic-gate }
63570Sstevel@tonic-gate }
63580Sstevel@tonic-gate
63590Sstevel@tonic-gate /*
63600Sstevel@tonic-gate * Called when we are just released from direct PM. Bring ourself up
63610Sstevel@tonic-gate * if our keeper is up since dependency is not honored while a kept
63620Sstevel@tonic-gate * device is under direct PM.
63630Sstevel@tonic-gate */
63640Sstevel@tonic-gate static void
pm_bring_self_up(char * keptpath)63650Sstevel@tonic-gate pm_bring_self_up(char *keptpath)
63660Sstevel@tonic-gate {
63670Sstevel@tonic-gate PMD_FUNC(pmf, "bring_self_up")
63680Sstevel@tonic-gate dev_info_t *kept;
63690Sstevel@tonic-gate dev_info_t *keeper;
63700Sstevel@tonic-gate pm_pdr_t *dp;
63710Sstevel@tonic-gate int i, j;
63720Sstevel@tonic-gate int up = 0, circ;
63730Sstevel@tonic-gate
63740Sstevel@tonic-gate kept = pm_name_to_dip(keptpath, 1);
63750Sstevel@tonic-gate if (kept == NULL)
63760Sstevel@tonic-gate return;
63770Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: kept=%s@%s(%s#%d)\n", pmf, PM_DEVICE(kept)))
63780Sstevel@tonic-gate for (dp = pm_dep_head; dp; dp = dp->pdr_next) {
63790Sstevel@tonic-gate if (dp->pdr_kept_count == 0)
63800Sstevel@tonic-gate continue;
63810Sstevel@tonic-gate for (i = 0; i < dp->pdr_kept_count; i++) {
63820Sstevel@tonic-gate if (strcmp(dp->pdr_kept_paths[i], keptpath) != 0)
63830Sstevel@tonic-gate continue;
63840Sstevel@tonic-gate keeper = pm_name_to_dip(dp->pdr_keeper, 1);
63850Sstevel@tonic-gate if (keeper) {
63860Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: keeper=%s@%s(%s#%d)\n",
63870Sstevel@tonic-gate pmf, PM_DEVICE(keeper)))
63880Sstevel@tonic-gate PM_LOCK_POWER(keeper, &circ);
63890Sstevel@tonic-gate for (j = 0; j < PM_NUMCMPTS(keeper);
63900Sstevel@tonic-gate j++) {
63910Sstevel@tonic-gate if (PM_CURPOWER(keeper, j)) {
63920Sstevel@tonic-gate PMD(PMD_KEEPS, ("%s: comp="
63930Sstevel@tonic-gate "%d is up\n", pmf, j))
63940Sstevel@tonic-gate up++;
63950Sstevel@tonic-gate }
63960Sstevel@tonic-gate }
63970Sstevel@tonic-gate if (up) {
63980Sstevel@tonic-gate if (PM_SKBU(kept))
63990Sstevel@tonic-gate DEVI(kept)->devi_pm_flags &=
64000Sstevel@tonic-gate ~PMC_SKIP_BRINGUP;
64010Sstevel@tonic-gate bring_pmdep_up(kept, 1);
64020Sstevel@tonic-gate }
64030Sstevel@tonic-gate PM_UNLOCK_POWER(keeper, circ);
64040Sstevel@tonic-gate ddi_release_devi(keeper);
64050Sstevel@tonic-gate }
64060Sstevel@tonic-gate }
64070Sstevel@tonic-gate }
64080Sstevel@tonic-gate ddi_release_devi(kept);
64090Sstevel@tonic-gate }
64100Sstevel@tonic-gate
64110Sstevel@tonic-gate static void
pm_process_dep_request(pm_dep_wk_t * work)64120Sstevel@tonic-gate pm_process_dep_request(pm_dep_wk_t *work)
64130Sstevel@tonic-gate {
64140Sstevel@tonic-gate PMD_FUNC(pmf, "dep_req")
64150Sstevel@tonic-gate int ret;
64160Sstevel@tonic-gate
64170Sstevel@tonic-gate PMD(PMD_DEP, ("%s: work=%s\n", pmf,
64180Sstevel@tonic-gate pdw_type_decode(work->pdw_type)))
64190Sstevel@tonic-gate PMD(PMD_DEP, ("%s: keeper=%s, kept=%s\n", pmf,
64200Sstevel@tonic-gate (work->pdw_keeper ? work->pdw_keeper : "NULL"),
64210Sstevel@tonic-gate (work->pdw_kept ? work->pdw_kept : "NULL")))
64220Sstevel@tonic-gate
64230Sstevel@tonic-gate switch (work->pdw_type) {
64240Sstevel@tonic-gate case PM_DEP_WK_POWER_ON:
64250Sstevel@tonic-gate /* Bring up the kept devices and put a hold on them */
64260Sstevel@tonic-gate bring_wekeeps_up(work->pdw_keeper);
64270Sstevel@tonic-gate break;
64280Sstevel@tonic-gate case PM_DEP_WK_POWER_OFF:
64290Sstevel@tonic-gate /* Release the kept devices */
64300Sstevel@tonic-gate pm_rele_dep(work->pdw_keeper);
64310Sstevel@tonic-gate break;
64320Sstevel@tonic-gate case PM_DEP_WK_DETACH:
64330Sstevel@tonic-gate pm_free_keeps(work->pdw_keeper, work->pdw_pwr);
64340Sstevel@tonic-gate break;
64350Sstevel@tonic-gate case PM_DEP_WK_REMOVE_DEP:
64360Sstevel@tonic-gate pm_discard_dependencies();
64370Sstevel@tonic-gate break;
64380Sstevel@tonic-gate case PM_DEP_WK_BRINGUP_SELF:
64390Sstevel@tonic-gate /*
64400Sstevel@tonic-gate * We deferred satisfying our dependency till now, so satisfy
64410Sstevel@tonic-gate * it again and bring ourselves up.
64420Sstevel@tonic-gate */
64430Sstevel@tonic-gate pm_bring_self_up(work->pdw_kept);
64440Sstevel@tonic-gate break;
64450Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER:
64460Sstevel@tonic-gate (void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 0);
64470Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
64480Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
64490Sstevel@tonic-gate break;
64500Sstevel@tonic-gate case PM_DEP_WK_RECORD_KEEPER_PROP:
64510Sstevel@tonic-gate (void) pm_record_keeper(work->pdw_kept, work->pdw_keeper, 1);
64520Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
64530Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
64540Sstevel@tonic-gate break;
64550Sstevel@tonic-gate case PM_DEP_WK_KEPT:
64560Sstevel@tonic-gate ret = pm_kept(work->pdw_kept);
64570Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_KEPT: pm_kept returns %d\n", pmf,
64580Sstevel@tonic-gate ret))
64590Sstevel@tonic-gate break;
64600Sstevel@tonic-gate case PM_DEP_WK_KEEPER:
64610Sstevel@tonic-gate ret = pm_keeper(work->pdw_keeper);
64620Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_KEEPER: pm_keeper returns %d\n",
64630Sstevel@tonic-gate pmf, ret))
64640Sstevel@tonic-gate break;
64650Sstevel@tonic-gate case PM_DEP_WK_ATTACH:
64660Sstevel@tonic-gate ret = pm_keeper(work->pdw_keeper);
64670Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_keeper returns %d\n",
64680Sstevel@tonic-gate pmf, ret))
64690Sstevel@tonic-gate ret = pm_kept(work->pdw_kept);
64700Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_ATTACH: pm_kept returns %d\n",
64710Sstevel@tonic-gate pmf, ret))
64720Sstevel@tonic-gate break;
64730Sstevel@tonic-gate case PM_DEP_WK_CHECK_KEPT:
64740Sstevel@tonic-gate ret = pm_is_kept(work->pdw_kept);
64750Sstevel@tonic-gate PMD(PMD_DEP, ("%s: PM_DEP_WK_CHECK_KEPT: kept=%s, ret=%d\n",
64760Sstevel@tonic-gate pmf, work->pdw_kept, ret))
64770Sstevel@tonic-gate break;
64780Sstevel@tonic-gate case PM_DEP_WK_CPR_SUSPEND:
64790Sstevel@tonic-gate pm_discard_dependencies();
64800Sstevel@tonic-gate break;
64810Sstevel@tonic-gate case PM_DEP_WK_CPR_RESUME:
64820Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_kept_walk, NULL);
64830Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_keeper_walk, NULL);
64840Sstevel@tonic-gate break;
64850Sstevel@tonic-gate default:
64860Sstevel@tonic-gate ASSERT(0);
64870Sstevel@tonic-gate break;
64880Sstevel@tonic-gate }
64890Sstevel@tonic-gate /*
64900Sstevel@tonic-gate * Free the work structure if the requester is not waiting
64910Sstevel@tonic-gate * Otherwise it is the requester's responsiblity to free it.
64920Sstevel@tonic-gate */
64930Sstevel@tonic-gate if (!work->pdw_wait) {
64940Sstevel@tonic-gate if (work->pdw_keeper)
64950Sstevel@tonic-gate kmem_free(work->pdw_keeper,
64960Sstevel@tonic-gate strlen(work->pdw_keeper) + 1);
64970Sstevel@tonic-gate if (work->pdw_kept)
64980Sstevel@tonic-gate kmem_free(work->pdw_kept, strlen(work->pdw_kept) + 1);
64990Sstevel@tonic-gate kmem_free(work, sizeof (pm_dep_wk_t));
65000Sstevel@tonic-gate } else {
65010Sstevel@tonic-gate /*
65020Sstevel@tonic-gate * Notify requester if it is waiting for it.
65030Sstevel@tonic-gate */
65040Sstevel@tonic-gate work->pdw_ret = ret;
65050Sstevel@tonic-gate work->pdw_done = 1;
65060Sstevel@tonic-gate cv_signal(&work->pdw_cv);
65070Sstevel@tonic-gate }
65080Sstevel@tonic-gate }
65090Sstevel@tonic-gate
65100Sstevel@tonic-gate /*
65110Sstevel@tonic-gate * Process PM dependency requests.
65120Sstevel@tonic-gate */
65130Sstevel@tonic-gate static void
pm_dep_thread(void)65140Sstevel@tonic-gate pm_dep_thread(void)
65150Sstevel@tonic-gate {
65160Sstevel@tonic-gate pm_dep_wk_t *work;
65170Sstevel@tonic-gate callb_cpr_t cprinfo;
65180Sstevel@tonic-gate
65190Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &pm_dep_thread_lock, callb_generic_cpr,
65200Sstevel@tonic-gate "pm_dep_thread");
65210Sstevel@tonic-gate for (;;) {
65220Sstevel@tonic-gate mutex_enter(&pm_dep_thread_lock);
65230Sstevel@tonic-gate if (pm_dep_thread_workq == NULL) {
65240Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo);
65250Sstevel@tonic-gate cv_wait(&pm_dep_thread_cv, &pm_dep_thread_lock);
65260Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, &pm_dep_thread_lock);
65270Sstevel@tonic-gate }
65280Sstevel@tonic-gate work = pm_dep_thread_workq;
65290Sstevel@tonic-gate pm_dep_thread_workq = work->pdw_next;
65300Sstevel@tonic-gate if (pm_dep_thread_tail == work)
65310Sstevel@tonic-gate pm_dep_thread_tail = work->pdw_next;
65320Sstevel@tonic-gate mutex_exit(&pm_dep_thread_lock);
65330Sstevel@tonic-gate pm_process_dep_request(work);
65340Sstevel@tonic-gate
65350Sstevel@tonic-gate }
65360Sstevel@tonic-gate /*NOTREACHED*/
65370Sstevel@tonic-gate }
65380Sstevel@tonic-gate
65390Sstevel@tonic-gate /*
65400Sstevel@tonic-gate * Set the power level of the indicated device to unknown (if it is not a
65410Sstevel@tonic-gate * backwards compatible device), as it has just been resumed, and it won't
65420Sstevel@tonic-gate * know if the power was removed or not. Adjust parent's kidsupcnt if necessary.
65430Sstevel@tonic-gate */
65440Sstevel@tonic-gate void
pm_forget_power_level(dev_info_t * dip)65450Sstevel@tonic-gate pm_forget_power_level(dev_info_t *dip)
65460Sstevel@tonic-gate {
65470Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
65480Sstevel@tonic-gate int i, count = 0;
65490Sstevel@tonic-gate
65500Sstevel@tonic-gate if (!PM_ISBC(dip)) {
65510Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++)
65520Sstevel@tonic-gate count += (PM_CURPOWER(dip, i) == 0);
65530Sstevel@tonic-gate
65540Sstevel@tonic-gate if (count && pdip && !PM_WANTS_NOTIFICATION(pdip))
65550Sstevel@tonic-gate e_pm_hold_rele_power(pdip, count);
65560Sstevel@tonic-gate
65570Sstevel@tonic-gate /*
65580Sstevel@tonic-gate * Count this as a power cycle if we care
65590Sstevel@tonic-gate */
65600Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd &&
65610Sstevel@tonic-gate PM_CP(dip, 0)->pmc_cur_pwr == 0)
65620Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0;
65630Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++)
65640Sstevel@tonic-gate e_pm_set_cur_pwr(dip, PM_CP(dip, i), PM_LEVEL_UNKNOWN);
65650Sstevel@tonic-gate }
65660Sstevel@tonic-gate }
65670Sstevel@tonic-gate
65680Sstevel@tonic-gate /*
65690Sstevel@tonic-gate * This function advises the caller whether it should make a power-off
65700Sstevel@tonic-gate * transition at this time or not. If the transition is not advised
65710Sstevel@tonic-gate * at this time, the time that the next power-off transition can
65720Sstevel@tonic-gate * be made from now is returned through "intervalp" pointer.
65730Sstevel@tonic-gate * This function returns:
65740Sstevel@tonic-gate *
65750Sstevel@tonic-gate * 1 power-off advised
65760Sstevel@tonic-gate * 0 power-off not advised, intervalp will point to seconds from
65770Sstevel@tonic-gate * now that a power-off is advised. If it is passed the number
65780Sstevel@tonic-gate * of years that policy specifies the device should last,
65790Sstevel@tonic-gate * a large number is returned as the time interval.
65800Sstevel@tonic-gate * -1 error
65810Sstevel@tonic-gate */
65820Sstevel@tonic-gate int
pm_trans_check(struct pm_trans_data * datap,time_t * intervalp)65830Sstevel@tonic-gate pm_trans_check(struct pm_trans_data *datap, time_t *intervalp)
65840Sstevel@tonic-gate {
65850Sstevel@tonic-gate PMD_FUNC(pmf, "pm_trans_check")
65860Sstevel@tonic-gate char dbuf[DC_SCSI_MFR_LEN];
65870Sstevel@tonic-gate struct pm_scsi_cycles *scp;
65880Sstevel@tonic-gate int service_years, service_weeks, full_years;
65890Sstevel@tonic-gate time_t now, service_seconds, tdiff;
65900Sstevel@tonic-gate time_t within_year, when_allowed;
65910Sstevel@tonic-gate char *ptr;
65920Sstevel@tonic-gate int lower_bound_cycles, upper_bound_cycles, cycles_allowed;
65930Sstevel@tonic-gate int cycles_diff, cycles_over;
659410131SJane.Chu@Sun.COM struct pm_smart_count *smart_p;
65950Sstevel@tonic-gate
65960Sstevel@tonic-gate if (datap == NULL) {
65970Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: NULL data pointer!\n", pmf))
65980Sstevel@tonic-gate return (-1);
65990Sstevel@tonic-gate }
66000Sstevel@tonic-gate
66010Sstevel@tonic-gate if (datap->format == DC_SCSI_FORMAT) {
66020Sstevel@tonic-gate /*
66030Sstevel@tonic-gate * Power cycles of the scsi drives are distributed
66040Sstevel@tonic-gate * over 5 years with the following percentage ratio:
66050Sstevel@tonic-gate *
66060Sstevel@tonic-gate * 30%, 25%, 20%, 15%, and 10%
66070Sstevel@tonic-gate *
66080Sstevel@tonic-gate * The power cycle quota for each year is distributed
66090Sstevel@tonic-gate * linearly through out the year. The equation for
66100Sstevel@tonic-gate * determining the expected cycles is:
66110Sstevel@tonic-gate *
66120Sstevel@tonic-gate * e = a * (n / y)
66130Sstevel@tonic-gate *
66140Sstevel@tonic-gate * e = expected cycles
66150Sstevel@tonic-gate * a = allocated cycles for this year
66160Sstevel@tonic-gate * n = number of seconds since beginning of this year
66170Sstevel@tonic-gate * y = number of seconds in a year
66180Sstevel@tonic-gate *
66190Sstevel@tonic-gate * Note that beginning of the year starts the day that
66200Sstevel@tonic-gate * the drive has been put on service.
66210Sstevel@tonic-gate *
66220Sstevel@tonic-gate * If the drive has passed its expected cycles, we
66230Sstevel@tonic-gate * can determine when it can start to power cycle
66240Sstevel@tonic-gate * again to keep it on track to meet the 5-year
66250Sstevel@tonic-gate * life expectancy. The equation for determining
66260Sstevel@tonic-gate * when to power cycle is:
66270Sstevel@tonic-gate *
66280Sstevel@tonic-gate * w = y * (c / a)
66290Sstevel@tonic-gate *
66300Sstevel@tonic-gate * w = when it can power cycle again
66310Sstevel@tonic-gate * y = number of seconds in a year
66320Sstevel@tonic-gate * c = current number of cycles
66330Sstevel@tonic-gate * a = allocated cycles for the year
66340Sstevel@tonic-gate *
66350Sstevel@tonic-gate */
66360Sstevel@tonic-gate char pcnt[DC_SCSI_NPY] = { 30, 55, 75, 90, 100 };
66370Sstevel@tonic-gate
66380Sstevel@tonic-gate scp = &datap->un.scsi_cycles;
66390Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: format=%d, lifemax=%d, ncycles=%d, "
66400Sstevel@tonic-gate "svc_date=%s, svc_flag=%d\n", pmf, datap->format,
66410Sstevel@tonic-gate scp->lifemax, scp->ncycles, scp->svc_date, scp->flag))
66420Sstevel@tonic-gate if (scp->ncycles < 0 || scp->flag != 0) {
66430Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: ncycles < 0 || flag != 0\n", pmf))
66440Sstevel@tonic-gate return (-1);
66450Sstevel@tonic-gate }
66460Sstevel@tonic-gate
66470Sstevel@tonic-gate if (scp->ncycles > scp->lifemax) {
66480Sstevel@tonic-gate *intervalp = (LONG_MAX / hz);
66490Sstevel@tonic-gate return (0);
66500Sstevel@tonic-gate }
66510Sstevel@tonic-gate
66520Sstevel@tonic-gate /*
66530Sstevel@tonic-gate * convert service date to time_t
66540Sstevel@tonic-gate */
66550Sstevel@tonic-gate bcopy(scp->svc_date, dbuf, DC_SCSI_YEAR_LEN);
66560Sstevel@tonic-gate dbuf[DC_SCSI_YEAR_LEN] = '\0';
66570Sstevel@tonic-gate ptr = dbuf;
66580Sstevel@tonic-gate service_years = stoi(&ptr) - EPOCH_YEAR;
66590Sstevel@tonic-gate bcopy(&scp->svc_date[DC_SCSI_YEAR_LEN], dbuf,
66600Sstevel@tonic-gate DC_SCSI_WEEK_LEN);
66610Sstevel@tonic-gate dbuf[DC_SCSI_WEEK_LEN] = '\0';
66620Sstevel@tonic-gate
66630Sstevel@tonic-gate /*
66640Sstevel@tonic-gate * scsi standard does not specify WW data,
66650Sstevel@tonic-gate * could be (00-51) or (01-52)
66660Sstevel@tonic-gate */
66670Sstevel@tonic-gate ptr = dbuf;
66680Sstevel@tonic-gate service_weeks = stoi(&ptr);
66690Sstevel@tonic-gate if (service_years < 0 ||
66700Sstevel@tonic-gate service_weeks < 0 || service_weeks > 52) {
66710Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: service year %d and week %d\n",
66720Sstevel@tonic-gate pmf, service_years, service_weeks))
66730Sstevel@tonic-gate return (-1);
66740Sstevel@tonic-gate }
66750Sstevel@tonic-gate
66760Sstevel@tonic-gate /*
66770Sstevel@tonic-gate * calculate service date in seconds-since-epoch,
66780Sstevel@tonic-gate * adding one day for each leap-year.
66790Sstevel@tonic-gate *
66800Sstevel@tonic-gate * (years-since-epoch + 2) fixes integer truncation,
66810Sstevel@tonic-gate * example: (8) leap-years during [1972, 2000]
66820Sstevel@tonic-gate * (2000 - 1970) = 30; and (30 + 2) / 4 = 8;
66830Sstevel@tonic-gate */
66840Sstevel@tonic-gate service_seconds = (service_years * DC_SPY) +
66850Sstevel@tonic-gate (service_weeks * DC_SPW) +
66860Sstevel@tonic-gate (((service_years + 2) / 4) * DC_SPD);
66870Sstevel@tonic-gate
66880Sstevel@tonic-gate now = gethrestime_sec();
66890Sstevel@tonic-gate /*
66900Sstevel@tonic-gate * since the granularity of 'svc_date' is day not second,
66910Sstevel@tonic-gate * 'now' should be rounded up to full day.
66920Sstevel@tonic-gate */
66930Sstevel@tonic-gate now = ((now + DC_SPD -1) / DC_SPD) * DC_SPD;
66940Sstevel@tonic-gate if (service_seconds > now) {
66950Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: service date (%ld) later "
66960Sstevel@tonic-gate "than now (%ld)!\n", pmf, service_seconds, now))
66970Sstevel@tonic-gate return (-1);
66980Sstevel@tonic-gate }
66990Sstevel@tonic-gate
67000Sstevel@tonic-gate tdiff = now - service_seconds;
67010Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: age is %ld sec\n", pmf, tdiff))
67020Sstevel@tonic-gate
67030Sstevel@tonic-gate /*
67040Sstevel@tonic-gate * NOTE - Leap years are not considered in the calculations
67050Sstevel@tonic-gate * below.
67060Sstevel@tonic-gate */
67070Sstevel@tonic-gate full_years = (tdiff / DC_SPY);
67080Sstevel@tonic-gate if ((full_years >= DC_SCSI_NPY) &&
67090Sstevel@tonic-gate (scp->ncycles <= scp->lifemax))
67100Sstevel@tonic-gate return (1);
67110Sstevel@tonic-gate
67120Sstevel@tonic-gate /*
67130Sstevel@tonic-gate * Determine what is the normal cycle usage for the
67140Sstevel@tonic-gate * device at the beginning and the end of this year.
67150Sstevel@tonic-gate */
67160Sstevel@tonic-gate lower_bound_cycles = (!full_years) ? 0 :
67170Sstevel@tonic-gate ((scp->lifemax * pcnt[full_years - 1]) / 100);
67180Sstevel@tonic-gate upper_bound_cycles = (scp->lifemax * pcnt[full_years]) / 100;
67190Sstevel@tonic-gate
67200Sstevel@tonic-gate if (scp->ncycles <= lower_bound_cycles)
67210Sstevel@tonic-gate return (1);
67220Sstevel@tonic-gate
67230Sstevel@tonic-gate /*
67240Sstevel@tonic-gate * The linear slope that determines how many cycles
67250Sstevel@tonic-gate * are allowed this year is number of seconds
67260Sstevel@tonic-gate * passed this year over total number of seconds in a year.
67270Sstevel@tonic-gate */
67280Sstevel@tonic-gate cycles_diff = (upper_bound_cycles - lower_bound_cycles);
67290Sstevel@tonic-gate within_year = (tdiff % DC_SPY);
67300Sstevel@tonic-gate cycles_allowed = lower_bound_cycles +
67310Sstevel@tonic-gate (((uint64_t)cycles_diff * (uint64_t)within_year) / DC_SPY);
67320Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: lived %d yrs and %ld secs\n", pmf,
67330Sstevel@tonic-gate full_years, within_year))
67340Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: # of cycles allowed %d\n", pmf,
67350Sstevel@tonic-gate cycles_allowed))
67360Sstevel@tonic-gate
67370Sstevel@tonic-gate if (scp->ncycles <= cycles_allowed)
67380Sstevel@tonic-gate return (1);
67390Sstevel@tonic-gate
67400Sstevel@tonic-gate /*
67410Sstevel@tonic-gate * The transition is not advised now but we can
67420Sstevel@tonic-gate * determine when the next transition can be made.
67430Sstevel@tonic-gate *
67440Sstevel@tonic-gate * Depending on how many cycles the device has been
67450Sstevel@tonic-gate * over-used, we may need to skip years with
67460Sstevel@tonic-gate * different percentage quota in order to determine
67470Sstevel@tonic-gate * when the next transition can be made.
67480Sstevel@tonic-gate */
67490Sstevel@tonic-gate cycles_over = (scp->ncycles - lower_bound_cycles);
67500Sstevel@tonic-gate while (cycles_over > cycles_diff) {
67510Sstevel@tonic-gate full_years++;
67520Sstevel@tonic-gate if (full_years >= DC_SCSI_NPY) {
67530Sstevel@tonic-gate *intervalp = (LONG_MAX / hz);
67540Sstevel@tonic-gate return (0);
67550Sstevel@tonic-gate }
67560Sstevel@tonic-gate cycles_over -= cycles_diff;
67570Sstevel@tonic-gate lower_bound_cycles = upper_bound_cycles;
67580Sstevel@tonic-gate upper_bound_cycles =
67590Sstevel@tonic-gate (scp->lifemax * pcnt[full_years]) / 100;
67600Sstevel@tonic-gate cycles_diff = (upper_bound_cycles - lower_bound_cycles);
67610Sstevel@tonic-gate }
67620Sstevel@tonic-gate
67630Sstevel@tonic-gate /*
67640Sstevel@tonic-gate * The linear slope that determines when the next transition
67650Sstevel@tonic-gate * can be made is the relative position of used cycles within a
67660Sstevel@tonic-gate * year over total number of cycles within that year.
67670Sstevel@tonic-gate */
67680Sstevel@tonic-gate when_allowed = service_seconds + (full_years * DC_SPY) +
67690Sstevel@tonic-gate (((uint64_t)DC_SPY * (uint64_t)cycles_over) / cycles_diff);
67700Sstevel@tonic-gate *intervalp = (when_allowed - now);
67710Sstevel@tonic-gate if (*intervalp > (LONG_MAX / hz))
67720Sstevel@tonic-gate *intervalp = (LONG_MAX / hz);
67730Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: no cycle is allowed in %ld secs\n", pmf,
67740Sstevel@tonic-gate *intervalp))
67750Sstevel@tonic-gate return (0);
677610131SJane.Chu@Sun.COM } else if (datap->format == DC_SMART_FORMAT) {
677710131SJane.Chu@Sun.COM /*
677810131SJane.Chu@Sun.COM * power cycles of SATA disks are reported from SMART
677910131SJane.Chu@Sun.COM * attributes.
678010131SJane.Chu@Sun.COM */
678110131SJane.Chu@Sun.COM smart_p = &datap->un.smart_count;
678210131SJane.Chu@Sun.COM if (smart_p->consumed >= smart_p->allowed) {
678310131SJane.Chu@Sun.COM *intervalp = (LONG_MAX / hz);
678410131SJane.Chu@Sun.COM PMD(PMD_TCHECK, ("%s: exceeded lifemax cycles.\n", pmf))
678510131SJane.Chu@Sun.COM return (0);
678610131SJane.Chu@Sun.COM } else
678710131SJane.Chu@Sun.COM return (1);
67880Sstevel@tonic-gate }
67890Sstevel@tonic-gate
67900Sstevel@tonic-gate PMD(PMD_TCHECK, ("%s: unknown format!\n", pmf))
67910Sstevel@tonic-gate return (-1);
67920Sstevel@tonic-gate }
67930Sstevel@tonic-gate
67940Sstevel@tonic-gate /*
67950Sstevel@tonic-gate * Nexus drivers call into pm framework to indicate which child driver is about
67960Sstevel@tonic-gate * to be installed. In some platforms, ppm may need to configure the hardware
67970Sstevel@tonic-gate * for successful installation of a driver.
67980Sstevel@tonic-gate */
67990Sstevel@tonic-gate int
pm_init_child(dev_info_t * dip)68000Sstevel@tonic-gate pm_init_child(dev_info_t *dip)
68010Sstevel@tonic-gate {
68020Sstevel@tonic-gate power_req_t power_req;
68030Sstevel@tonic-gate
68040Sstevel@tonic-gate ASSERT(ddi_binding_name(dip));
68050Sstevel@tonic-gate ASSERT(ddi_get_name_addr(dip));
68060Sstevel@tonic-gate pm_ppm_claim(dip);
68070Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */
68080Sstevel@tonic-gate power_req.request_type = PMR_PPM_INIT_CHILD;
68090Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
68100Sstevel@tonic-gate ASSERT(PPM(dip) != NULL);
68110Sstevel@tonic-gate return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req,
68120Sstevel@tonic-gate NULL));
68130Sstevel@tonic-gate } else {
68140Sstevel@tonic-gate #ifdef DEBUG
68150Sstevel@tonic-gate /* pass it to the default handler so we can debug things */
68160Sstevel@tonic-gate power_req.request_type = PMR_PPM_INIT_CHILD;
68170Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
68180Sstevel@tonic-gate (void) pm_ctlops(NULL, dip,
68190Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, NULL);
68200Sstevel@tonic-gate #endif
68210Sstevel@tonic-gate }
68220Sstevel@tonic-gate return (DDI_SUCCESS);
68230Sstevel@tonic-gate }
68240Sstevel@tonic-gate
68250Sstevel@tonic-gate /*
68260Sstevel@tonic-gate * Bring parent of a node that is about to be probed up to full power, and
68270Sstevel@tonic-gate * arrange for it to stay up until pm_post_probe() or pm_post_attach() decide
68280Sstevel@tonic-gate * it is time to let it go down again
68290Sstevel@tonic-gate */
68300Sstevel@tonic-gate void
pm_pre_probe(dev_info_t * dip,pm_ppm_cookie_t * cp)68310Sstevel@tonic-gate pm_pre_probe(dev_info_t *dip, pm_ppm_cookie_t *cp)
68320Sstevel@tonic-gate {
68330Sstevel@tonic-gate int result;
68340Sstevel@tonic-gate power_req_t power_req;
68350Sstevel@tonic-gate
68360Sstevel@tonic-gate bzero(cp, sizeof (*cp));
68370Sstevel@tonic-gate cp->ppc_dip = dip;
68380Sstevel@tonic-gate
68390Sstevel@tonic-gate pm_ppm_claim(dip);
68400Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */
68410Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_PROBE;
68420Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
68430Sstevel@tonic-gate ASSERT(PPM(dip) != NULL);
68440Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip,
68450Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
68460Sstevel@tonic-gate cp->ppc_ppm = PPM(dip);
68470Sstevel@tonic-gate } else {
68480Sstevel@tonic-gate #ifdef DEBUG
68490Sstevel@tonic-gate /* pass it to the default handler so we can debug things */
68500Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_PROBE;
68510Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
68520Sstevel@tonic-gate (void) pm_ctlops(NULL, dip,
68530Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
68540Sstevel@tonic-gate #endif
68550Sstevel@tonic-gate cp->ppc_ppm = NULL;
68560Sstevel@tonic-gate }
68570Sstevel@tonic-gate }
68580Sstevel@tonic-gate
68590Sstevel@tonic-gate int
pm_pre_config(dev_info_t * dip,char * devnm)68600Sstevel@tonic-gate pm_pre_config(dev_info_t *dip, char *devnm)
68610Sstevel@tonic-gate {
68620Sstevel@tonic-gate PMD_FUNC(pmf, "pre_config")
68630Sstevel@tonic-gate int ret;
68640Sstevel@tonic-gate
68650Sstevel@tonic-gate if (MDI_VHCI(dip)) {
68660Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
68670Sstevel@tonic-gate ret = mdi_power(dip, MDI_PM_PRE_CONFIG, NULL, devnm, 0);
68680Sstevel@tonic-gate return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
68690Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip))
68700Sstevel@tonic-gate return (DDI_SUCCESS);
68710Sstevel@tonic-gate
68720Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
68730Sstevel@tonic-gate pm_hold_power(dip);
68740Sstevel@tonic-gate ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK);
68750Sstevel@tonic-gate if (ret != DDI_SUCCESS)
68760Sstevel@tonic-gate pm_rele_power(dip);
68770Sstevel@tonic-gate return (ret);
68780Sstevel@tonic-gate }
68790Sstevel@tonic-gate
68800Sstevel@tonic-gate /*
68810Sstevel@tonic-gate * This routine is called by devfs during its walk to unconfigue a node.
68820Sstevel@tonic-gate * If the call is due to auto mod_unloads and the dip is not at its
68830Sstevel@tonic-gate * full power, we return DDI_FAILURE to terminate the walk, otherwise
68840Sstevel@tonic-gate * return DDI_SUCCESS.
68850Sstevel@tonic-gate */
68860Sstevel@tonic-gate int
pm_pre_unconfig(dev_info_t * dip,int flags,int * held,char * devnm)68870Sstevel@tonic-gate pm_pre_unconfig(dev_info_t *dip, int flags, int *held, char *devnm)
68880Sstevel@tonic-gate {
68890Sstevel@tonic-gate PMD_FUNC(pmf, "pre_unconfig")
68900Sstevel@tonic-gate int ret;
68910Sstevel@tonic-gate
68920Sstevel@tonic-gate if (MDI_VHCI(dip)) {
68930Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf,
68940Sstevel@tonic-gate PM_DEVICE(dip), flags))
68950Sstevel@tonic-gate ret = mdi_power(dip, MDI_PM_PRE_UNCONFIG, held, devnm, flags);
68960Sstevel@tonic-gate return (ret == MDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
68970Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip))
68980Sstevel@tonic-gate return (DDI_SUCCESS);
68990Sstevel@tonic-gate
69000Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), flags=%x\n", pmf, PM_DEVICE(dip),
69010Sstevel@tonic-gate flags))
69020Sstevel@tonic-gate *held = 0;
69030Sstevel@tonic-gate
69040Sstevel@tonic-gate /*
69050Sstevel@tonic-gate * If the dip is a leaf node, don't power it up.
69060Sstevel@tonic-gate */
69070Sstevel@tonic-gate if (!ddi_get_child(dip))
69080Sstevel@tonic-gate return (DDI_SUCCESS);
69090Sstevel@tonic-gate
69100Sstevel@tonic-gate /*
69110Sstevel@tonic-gate * Do not power up the node if it is called due to auto-modunload.
69120Sstevel@tonic-gate */
69130Sstevel@tonic-gate if ((flags & NDI_AUTODETACH) && !pm_all_at_normal(dip))
69140Sstevel@tonic-gate return (DDI_FAILURE);
69150Sstevel@tonic-gate
69160Sstevel@tonic-gate pm_hold_power(dip);
69170Sstevel@tonic-gate *held = 1;
69180Sstevel@tonic-gate ret = pm_all_to_normal(dip, PM_CANBLOCK_BLOCK);
69190Sstevel@tonic-gate if (ret != DDI_SUCCESS) {
69200Sstevel@tonic-gate pm_rele_power(dip);
69210Sstevel@tonic-gate *held = 0;
69220Sstevel@tonic-gate }
69230Sstevel@tonic-gate return (ret);
69240Sstevel@tonic-gate }
69250Sstevel@tonic-gate
69260Sstevel@tonic-gate /*
69270Sstevel@tonic-gate * Notify ppm of attach action. Parent is already held at full power by
69280Sstevel@tonic-gate * probe action.
69290Sstevel@tonic-gate */
69300Sstevel@tonic-gate void
pm_pre_attach(dev_info_t * dip,pm_ppm_cookie_t * cp,ddi_attach_cmd_t cmd)69310Sstevel@tonic-gate pm_pre_attach(dev_info_t *dip, pm_ppm_cookie_t *cp, ddi_attach_cmd_t cmd)
69320Sstevel@tonic-gate {
69330Sstevel@tonic-gate static char *me = "pm_pre_attach";
69340Sstevel@tonic-gate power_req_t power_req;
69350Sstevel@tonic-gate int result;
69360Sstevel@tonic-gate
69370Sstevel@tonic-gate /*
69380Sstevel@tonic-gate * Initialize and fill in the PPM cookie
69390Sstevel@tonic-gate */
69400Sstevel@tonic-gate bzero(cp, sizeof (*cp));
69410Sstevel@tonic-gate cp->ppc_cmd = (int)cmd;
69420Sstevel@tonic-gate cp->ppc_ppm = PPM(dip);
69430Sstevel@tonic-gate cp->ppc_dip = dip;
69440Sstevel@tonic-gate
69450Sstevel@tonic-gate /*
69460Sstevel@tonic-gate * DDI_ATTACH and DDI_RESUME cmds need to call platform specific
69470Sstevel@tonic-gate * Power Management stuff. DDI_RESUME also has to purge it's
69480Sstevel@tonic-gate * powerlevel information.
69490Sstevel@tonic-gate */
69500Sstevel@tonic-gate switch (cmd) {
69510Sstevel@tonic-gate case DDI_ATTACH:
69520Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */
69530Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_ATTACH;
69540Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
69550Sstevel@tonic-gate ASSERT(PPM(dip));
69560Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, dip, DDI_CTLOPS_POWER,
69570Sstevel@tonic-gate &power_req, &result);
69580Sstevel@tonic-gate }
69590Sstevel@tonic-gate #ifdef DEBUG
69600Sstevel@tonic-gate else {
69610Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_ATTACH;
69620Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
69630Sstevel@tonic-gate (void) pm_ctlops(NULL, dip,
69640Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
69650Sstevel@tonic-gate }
69660Sstevel@tonic-gate #endif
69670Sstevel@tonic-gate break;
69680Sstevel@tonic-gate case DDI_RESUME:
69690Sstevel@tonic-gate pm_forget_power_level(dip);
69700Sstevel@tonic-gate
69710Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */
69720Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_RESUME;
69730Sstevel@tonic-gate power_req.req.resume_req.who = cp->ppc_dip;
69740Sstevel@tonic-gate power_req.req.resume_req.cmd =
69750Sstevel@tonic-gate (ddi_attach_cmd_t)cp->ppc_cmd;
69760Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
69770Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
69780Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
69790Sstevel@tonic-gate }
69800Sstevel@tonic-gate #ifdef DEBUG
69810Sstevel@tonic-gate else {
69820Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_RESUME;
69830Sstevel@tonic-gate power_req.req.resume_req.who = cp->ppc_dip;
69840Sstevel@tonic-gate power_req.req.resume_req.cmd =
69850Sstevel@tonic-gate (ddi_attach_cmd_t)cp->ppc_cmd;
69860Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip,
69870Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
69880Sstevel@tonic-gate }
69890Sstevel@tonic-gate #endif
69900Sstevel@tonic-gate break;
69910Sstevel@tonic-gate
69920Sstevel@tonic-gate case DDI_PM_RESUME:
69930Sstevel@tonic-gate break;
69940Sstevel@tonic-gate
69950Sstevel@tonic-gate default:
69960Sstevel@tonic-gate panic(me);
69970Sstevel@tonic-gate }
69980Sstevel@tonic-gate }
69990Sstevel@tonic-gate
70000Sstevel@tonic-gate /*
70010Sstevel@tonic-gate * Nexus drivers call into pm framework to indicate which child driver is
70020Sstevel@tonic-gate * being uninstalled. In some platforms, ppm may need to reconfigure the
70030Sstevel@tonic-gate * hardware since the device driver is no longer installed.
70040Sstevel@tonic-gate */
70050Sstevel@tonic-gate int
pm_uninit_child(dev_info_t * dip)70060Sstevel@tonic-gate pm_uninit_child(dev_info_t *dip)
70070Sstevel@tonic-gate {
70080Sstevel@tonic-gate power_req_t power_req;
70090Sstevel@tonic-gate
70100Sstevel@tonic-gate ASSERT(ddi_binding_name(dip));
70110Sstevel@tonic-gate ASSERT(ddi_get_name_addr(dip));
70120Sstevel@tonic-gate pm_ppm_claim(dip);
70130Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims the node */
70140Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNINIT_CHILD;
70150Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
70160Sstevel@tonic-gate ASSERT(PPM(dip));
70170Sstevel@tonic-gate return (pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER, &power_req,
70180Sstevel@tonic-gate NULL));
70190Sstevel@tonic-gate } else {
70200Sstevel@tonic-gate #ifdef DEBUG
70210Sstevel@tonic-gate /* pass it to the default handler so we can debug things */
70220Sstevel@tonic-gate power_req.request_type = PMR_PPM_UNINIT_CHILD;
70230Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
70240Sstevel@tonic-gate (void) pm_ctlops(NULL, dip, DDI_CTLOPS_POWER, &power_req, NULL);
70250Sstevel@tonic-gate #endif
70260Sstevel@tonic-gate }
70270Sstevel@tonic-gate return (DDI_SUCCESS);
70280Sstevel@tonic-gate }
70290Sstevel@tonic-gate /*
70300Sstevel@tonic-gate * Decrement kidsupcnt so scan can turn the parent back off if it is idle
70310Sstevel@tonic-gate * Also notify ppm of result of probe if there is a ppm that cares
70320Sstevel@tonic-gate */
70330Sstevel@tonic-gate void
pm_post_probe(pm_ppm_cookie_t * cp,int ret,int probe_failed)70340Sstevel@tonic-gate pm_post_probe(pm_ppm_cookie_t *cp, int ret, int probe_failed)
70350Sstevel@tonic-gate {
70360Sstevel@tonic-gate _NOTE(ARGUNUSED(probe_failed))
70370Sstevel@tonic-gate int result;
70380Sstevel@tonic-gate power_req_t power_req;
70390Sstevel@tonic-gate
70400Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */
70410Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_PROBE;
70420Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip;
70430Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret;
70440Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
70450Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip, DDI_CTLOPS_POWER,
70460Sstevel@tonic-gate &power_req, &result);
70470Sstevel@tonic-gate }
70480Sstevel@tonic-gate #ifdef DEBUG
70490Sstevel@tonic-gate else {
70500Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_PROBE;
70510Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip;
70520Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret;
70530Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip, DDI_CTLOPS_POWER,
70540Sstevel@tonic-gate &power_req, &result);
70550Sstevel@tonic-gate }
70560Sstevel@tonic-gate #endif
70570Sstevel@tonic-gate }
70580Sstevel@tonic-gate
70590Sstevel@tonic-gate void
pm_post_config(dev_info_t * dip,char * devnm)70600Sstevel@tonic-gate pm_post_config(dev_info_t *dip, char *devnm)
70610Sstevel@tonic-gate {
70620Sstevel@tonic-gate PMD_FUNC(pmf, "post_config")
70630Sstevel@tonic-gate
70640Sstevel@tonic-gate if (MDI_VHCI(dip)) {
70650Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
70660Sstevel@tonic-gate (void) mdi_power(dip, MDI_PM_POST_CONFIG, NULL, devnm, 0);
70670Sstevel@tonic-gate return;
70680Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip))
70690Sstevel@tonic-gate return;
70700Sstevel@tonic-gate
70710Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
70720Sstevel@tonic-gate pm_rele_power(dip);
70730Sstevel@tonic-gate }
70740Sstevel@tonic-gate
70750Sstevel@tonic-gate void
pm_post_unconfig(dev_info_t * dip,int held,char * devnm)70760Sstevel@tonic-gate pm_post_unconfig(dev_info_t *dip, int held, char *devnm)
70770Sstevel@tonic-gate {
70780Sstevel@tonic-gate PMD_FUNC(pmf, "post_unconfig")
70790Sstevel@tonic-gate
70800Sstevel@tonic-gate if (MDI_VHCI(dip)) {
70810Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf,
70820Sstevel@tonic-gate PM_DEVICE(dip), held))
70830Sstevel@tonic-gate (void) mdi_power(dip, MDI_PM_POST_UNCONFIG, &held, devnm, 0);
70840Sstevel@tonic-gate return;
70850Sstevel@tonic-gate } else if (!PM_GET_PM_INFO(dip))
70860Sstevel@tonic-gate return;
70870Sstevel@tonic-gate
70880Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), held = %d\n", pmf, PM_DEVICE(dip),
70890Sstevel@tonic-gate held))
70900Sstevel@tonic-gate if (!held)
70910Sstevel@tonic-gate return;
70920Sstevel@tonic-gate /*
70930Sstevel@tonic-gate * We have held power in pre_unconfig, release it here.
70940Sstevel@tonic-gate */
70950Sstevel@tonic-gate pm_rele_power(dip);
70960Sstevel@tonic-gate }
70970Sstevel@tonic-gate
70980Sstevel@tonic-gate /*
70990Sstevel@tonic-gate * Notify ppm of result of attach if there is a ppm that cares
71000Sstevel@tonic-gate */
71010Sstevel@tonic-gate void
pm_post_attach(pm_ppm_cookie_t * cp,int ret)71020Sstevel@tonic-gate pm_post_attach(pm_ppm_cookie_t *cp, int ret)
71030Sstevel@tonic-gate {
71040Sstevel@tonic-gate int result;
71050Sstevel@tonic-gate power_req_t power_req;
71060Sstevel@tonic-gate dev_info_t *dip;
71070Sstevel@tonic-gate
71080Sstevel@tonic-gate if (cp->ppc_cmd != DDI_ATTACH)
71090Sstevel@tonic-gate return;
71100Sstevel@tonic-gate
71110Sstevel@tonic-gate dip = cp->ppc_dip;
71120Sstevel@tonic-gate
71130Sstevel@tonic-gate if (ret == DDI_SUCCESS) {
71140Sstevel@tonic-gate /*
71150Sstevel@tonic-gate * Attach succeeded, so proceed to doing post-attach pm tasks
71160Sstevel@tonic-gate */
71170Sstevel@tonic-gate if (PM_GET_PM_INFO(dip) == NULL)
71180Sstevel@tonic-gate (void) pm_start(dip);
71190Sstevel@tonic-gate } else {
71200Sstevel@tonic-gate /*
71210Sstevel@tonic-gate * Attach may have got pm started before failing
71220Sstevel@tonic-gate */
71230Sstevel@tonic-gate pm_stop(dip);
71240Sstevel@tonic-gate }
71250Sstevel@tonic-gate
71260Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */
71270Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_ATTACH;
71280Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip;
71290Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret;
71300Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
71310Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
71320Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
71330Sstevel@tonic-gate }
71340Sstevel@tonic-gate #ifdef DEBUG
71350Sstevel@tonic-gate else {
71360Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_ATTACH;
71370Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip;
71380Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret;
71390Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip,
71400Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
71410Sstevel@tonic-gate }
71420Sstevel@tonic-gate #endif
71430Sstevel@tonic-gate }
71440Sstevel@tonic-gate
71450Sstevel@tonic-gate /*
71460Sstevel@tonic-gate * Notify ppm of attach action. Parent is already held at full power by
71470Sstevel@tonic-gate * probe action.
71480Sstevel@tonic-gate */
71490Sstevel@tonic-gate void
pm_pre_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,pm_ppm_cookie_t * cp)71500Sstevel@tonic-gate pm_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, pm_ppm_cookie_t *cp)
71510Sstevel@tonic-gate {
71520Sstevel@tonic-gate int result;
71530Sstevel@tonic-gate power_req_t power_req;
71540Sstevel@tonic-gate
71550Sstevel@tonic-gate bzero(cp, sizeof (*cp));
71560Sstevel@tonic-gate cp->ppc_dip = dip;
71570Sstevel@tonic-gate cp->ppc_cmd = (int)cmd;
71580Sstevel@tonic-gate
71590Sstevel@tonic-gate switch (cmd) {
71600Sstevel@tonic-gate case DDI_DETACH:
71610Sstevel@tonic-gate pm_detaching(dip); /* suspend pm while detaching */
71620Sstevel@tonic-gate if (pm_ppm_claimed(dip)) { /* if ppm driver claims node */
71630Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_DETACH;
71640Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
71650Sstevel@tonic-gate ASSERT(PPM(dip));
71660Sstevel@tonic-gate (void) pm_ctlops(PPM(dip), dip, DDI_CTLOPS_POWER,
71670Sstevel@tonic-gate &power_req, &result);
71680Sstevel@tonic-gate cp->ppc_ppm = PPM(dip);
71690Sstevel@tonic-gate } else {
71700Sstevel@tonic-gate #ifdef DEBUG
71710Sstevel@tonic-gate /* pass to the default handler so we can debug things */
71720Sstevel@tonic-gate power_req.request_type = PMR_PPM_PRE_DETACH;
71730Sstevel@tonic-gate power_req.req.ppm_config_req.who = dip;
71740Sstevel@tonic-gate (void) pm_ctlops(NULL, dip,
71750Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
71760Sstevel@tonic-gate #endif
71770Sstevel@tonic-gate cp->ppc_ppm = NULL;
71780Sstevel@tonic-gate }
71790Sstevel@tonic-gate break;
71800Sstevel@tonic-gate
71810Sstevel@tonic-gate default:
71820Sstevel@tonic-gate break;
71830Sstevel@tonic-gate }
71840Sstevel@tonic-gate }
71850Sstevel@tonic-gate
71860Sstevel@tonic-gate /*
71870Sstevel@tonic-gate * Dip is either a leaf node that exported "no-involuntary-power-cycles" prop.,
71880Sstevel@tonic-gate * (if devi_pm_noinvol count is 0) or an ancestor of such a node. We need to
71890Sstevel@tonic-gate * make an entry to record the details, which includes certain flag settings.
71900Sstevel@tonic-gate */
71910Sstevel@tonic-gate static void
pm_record_invol_path(char * path,int flags,int noinvolpm,int volpmd,int wasvolpmd,major_t major)71920Sstevel@tonic-gate pm_record_invol_path(char *path, int flags, int noinvolpm, int volpmd,
71930Sstevel@tonic-gate int wasvolpmd, major_t major)
71940Sstevel@tonic-gate {
71950Sstevel@tonic-gate PMD_FUNC(pmf, "record_invol_path")
71960Sstevel@tonic-gate major_t pm_path_to_major(char *);
71970Sstevel@tonic-gate size_t plen;
71980Sstevel@tonic-gate pm_noinvol_t *ip, *np, *pp;
71990Sstevel@tonic-gate pp = NULL;
72000Sstevel@tonic-gate
72010Sstevel@tonic-gate plen = strlen(path) + 1;
72020Sstevel@tonic-gate np = kmem_zalloc(sizeof (*np), KM_SLEEP);
72030Sstevel@tonic-gate np->ni_size = plen;
72040Sstevel@tonic-gate np->ni_path = kmem_alloc(plen, KM_SLEEP);
72050Sstevel@tonic-gate np->ni_noinvolpm = noinvolpm;
72060Sstevel@tonic-gate np->ni_volpmd = volpmd;
72070Sstevel@tonic-gate np->ni_wasvolpmd = wasvolpmd;
72080Sstevel@tonic-gate np->ni_flags = flags;
72090Sstevel@tonic-gate (void) strcpy(np->ni_path, path);
72100Sstevel@tonic-gate /*
72110Sstevel@tonic-gate * If we haven't actually seen the node attached, it is hard to figure
72120Sstevel@tonic-gate * out its major. If we could hold the node by path, we would be much
72130Sstevel@tonic-gate * happier here.
72140Sstevel@tonic-gate */
72157009Scth if (major == DDI_MAJOR_T_NONE) {
72160Sstevel@tonic-gate np->ni_major = pm_path_to_major(path);
72170Sstevel@tonic-gate } else {
72180Sstevel@tonic-gate np->ni_major = major;
72190Sstevel@tonic-gate }
72200Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER);
72210Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
72220Sstevel@tonic-gate int comp = strcmp(path, ip->ni_path);
72230Sstevel@tonic-gate if (comp < 0) {
72240Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s insert before %s\n",
72250Sstevel@tonic-gate pmf, path, ip->ni_path))
72260Sstevel@tonic-gate /* insert before current entry */
72270Sstevel@tonic-gate np->ni_next = ip;
72280Sstevel@tonic-gate if (pp) {
72290Sstevel@tonic-gate pp->ni_next = np;
72300Sstevel@tonic-gate } else {
72310Sstevel@tonic-gate pm_noinvol_head = np;
72320Sstevel@tonic-gate }
72330Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
72340Sstevel@tonic-gate #ifdef DEBUG
72350Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL)
72360Sstevel@tonic-gate pr_noinvol("record_invol_path exit0");
72370Sstevel@tonic-gate #endif
72380Sstevel@tonic-gate return;
72390Sstevel@tonic-gate } else if (comp == 0) {
72400Sstevel@tonic-gate panic("%s already in pm_noinvol list", path);
72410Sstevel@tonic-gate }
72420Sstevel@tonic-gate }
72430Sstevel@tonic-gate /*
72440Sstevel@tonic-gate * If we did not find an entry in the list that this should go before,
72450Sstevel@tonic-gate * then it must go at the end
72460Sstevel@tonic-gate */
72470Sstevel@tonic-gate if (pp) {
72480Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s append after %s\n", pmf, path,
72490Sstevel@tonic-gate pp->ni_path))
72500Sstevel@tonic-gate ASSERT(pp->ni_next == 0);
72510Sstevel@tonic-gate pp->ni_next = np;
72520Sstevel@tonic-gate } else {
72530Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s added to end-of-list\n", pmf, path))
72540Sstevel@tonic-gate ASSERT(!pm_noinvol_head);
72550Sstevel@tonic-gate pm_noinvol_head = np;
72560Sstevel@tonic-gate }
72570Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
72580Sstevel@tonic-gate #ifdef DEBUG
72590Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL)
72600Sstevel@tonic-gate pr_noinvol("record_invol_path exit");
72610Sstevel@tonic-gate #endif
72620Sstevel@tonic-gate }
72630Sstevel@tonic-gate
72640Sstevel@tonic-gate void
pm_record_invol(dev_info_t * dip)72650Sstevel@tonic-gate pm_record_invol(dev_info_t *dip)
72660Sstevel@tonic-gate {
72670Sstevel@tonic-gate char *pathbuf;
72680Sstevel@tonic-gate int pm_all_components_off(dev_info_t *);
72690Sstevel@tonic-gate int volpmd = (PM_NUMCMPTS(dip) > 0) && pm_all_components_off(dip);
72700Sstevel@tonic-gate
72710Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
72720Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
72730Sstevel@tonic-gate
72740Sstevel@tonic-gate pm_record_invol_path(pathbuf, (DEVI(dip)->devi_pm_flags &
72750Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)), DEVI(dip)->devi_pm_noinvolpm,
72760Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, volpmd, PM_MAJOR(dip));
72770Sstevel@tonic-gate
72780Sstevel@tonic-gate /*
72790Sstevel@tonic-gate * If this child's detach will be holding up its ancestors, then we
72800Sstevel@tonic-gate * allow for an exception to that if all children of this type have
72810Sstevel@tonic-gate * gone down voluntarily.
72820Sstevel@tonic-gate * Now walk down the tree incrementing devi_pm_noinvolpm
72830Sstevel@tonic-gate */
72840Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_DETACH, 0, volpmd, pathbuf,
72850Sstevel@tonic-gate dip);
72860Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
72870Sstevel@tonic-gate }
72880Sstevel@tonic-gate
72890Sstevel@tonic-gate void
pm_post_detach(pm_ppm_cookie_t * cp,int ret)72900Sstevel@tonic-gate pm_post_detach(pm_ppm_cookie_t *cp, int ret)
72910Sstevel@tonic-gate {
72920Sstevel@tonic-gate dev_info_t *dip = cp->ppc_dip;
72930Sstevel@tonic-gate int result;
72940Sstevel@tonic-gate power_req_t power_req;
72950Sstevel@tonic-gate
72960Sstevel@tonic-gate switch (cp->ppc_cmd) {
72970Sstevel@tonic-gate case DDI_DETACH:
72980Sstevel@tonic-gate if (cp->ppc_ppm) { /* if ppm driver claims the node */
72990Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_DETACH;
73000Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip;
73010Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret;
73020Sstevel@tonic-gate ASSERT(PPM(cp->ppc_dip) == cp->ppc_ppm);
73030Sstevel@tonic-gate (void) pm_ctlops(cp->ppc_ppm, cp->ppc_dip,
73040Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
73050Sstevel@tonic-gate }
73060Sstevel@tonic-gate #ifdef DEBUG
73070Sstevel@tonic-gate else {
73080Sstevel@tonic-gate power_req.request_type = PMR_PPM_POST_DETACH;
73090Sstevel@tonic-gate power_req.req.ppm_config_req.who = cp->ppc_dip;
73100Sstevel@tonic-gate power_req.req.ppm_config_req.result = ret;
73110Sstevel@tonic-gate (void) pm_ctlops(NULL, cp->ppc_dip,
73120Sstevel@tonic-gate DDI_CTLOPS_POWER, &power_req, &result);
73130Sstevel@tonic-gate }
73140Sstevel@tonic-gate #endif
73150Sstevel@tonic-gate if (ret == DDI_SUCCESS) {
73160Sstevel@tonic-gate /*
73170Sstevel@tonic-gate * For hotplug detach we assume it is *really* gone
73180Sstevel@tonic-gate */
73190Sstevel@tonic-gate if (cp->ppc_cmd == DDI_DETACH &&
73200Sstevel@tonic-gate ((DEVI(dip)->devi_pm_flags &
73210Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
73220Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm))
73230Sstevel@tonic-gate pm_record_invol(dip);
73240Sstevel@tonic-gate DEVI(dip)->devi_pm_flags &=
73250Sstevel@tonic-gate ~(PMC_NO_INVOL | PMC_NOINVOL_DONE);
73260Sstevel@tonic-gate
73270Sstevel@tonic-gate /*
73280Sstevel@tonic-gate * If console fb is detaching, then we don't need to
73290Sstevel@tonic-gate * worry any more about it going off (pm_detaching has
73300Sstevel@tonic-gate * brought up all components)
73310Sstevel@tonic-gate */
73320Sstevel@tonic-gate if (PM_IS_CFB(dip)) {
73330Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
73340Sstevel@tonic-gate ASSERT(cfb_dip_detaching);
73350Sstevel@tonic-gate ASSERT(cfb_dip == NULL);
73360Sstevel@tonic-gate ASSERT(pm_cfb_comps_off == 0);
73370Sstevel@tonic-gate cfb_dip_detaching = NULL;
73380Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
73390Sstevel@tonic-gate }
73400Sstevel@tonic-gate pm_stop(dip); /* make it permanent */
73410Sstevel@tonic-gate } else {
73420Sstevel@tonic-gate if (PM_IS_CFB(dip)) {
73430Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
73440Sstevel@tonic-gate ASSERT(cfb_dip_detaching);
73450Sstevel@tonic-gate ASSERT(cfb_dip == NULL);
73460Sstevel@tonic-gate ASSERT(pm_cfb_comps_off == 0);
73470Sstevel@tonic-gate cfb_dip = cfb_dip_detaching;
73480Sstevel@tonic-gate cfb_dip_detaching = NULL;
73490Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
73500Sstevel@tonic-gate }
73510Sstevel@tonic-gate pm_detach_failed(dip); /* resume power management */
73520Sstevel@tonic-gate }
73530Sstevel@tonic-gate break;
73540Sstevel@tonic-gate case DDI_PM_SUSPEND:
73550Sstevel@tonic-gate break;
73560Sstevel@tonic-gate case DDI_SUSPEND:
73570Sstevel@tonic-gate break; /* legal, but nothing to do */
73580Sstevel@tonic-gate default:
73590Sstevel@tonic-gate #ifdef DEBUG
73600Sstevel@tonic-gate panic("pm_post_detach: unrecognized cmd %d for detach",
73610Sstevel@tonic-gate cp->ppc_cmd);
73620Sstevel@tonic-gate /*NOTREACHED*/
73630Sstevel@tonic-gate #else
73640Sstevel@tonic-gate break;
73650Sstevel@tonic-gate #endif
73660Sstevel@tonic-gate }
73670Sstevel@tonic-gate }
73680Sstevel@tonic-gate
73690Sstevel@tonic-gate /*
73700Sstevel@tonic-gate * Called after vfs_mountroot has got the clock started to fix up timestamps
73710Sstevel@tonic-gate * that were set when root bush drivers attached. hresttime was 0 then, so the
73720Sstevel@tonic-gate * devices look busy but have a 0 busycnt
73730Sstevel@tonic-gate */
73740Sstevel@tonic-gate int
pm_adjust_timestamps(dev_info_t * dip,void * arg)73750Sstevel@tonic-gate pm_adjust_timestamps(dev_info_t *dip, void *arg)
73760Sstevel@tonic-gate {
73770Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
73780Sstevel@tonic-gate
73790Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
73800Sstevel@tonic-gate struct pm_component *cp;
73810Sstevel@tonic-gate int i;
73820Sstevel@tonic-gate
73830Sstevel@tonic-gate if (!info)
73840Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
73850Sstevel@tonic-gate PM_LOCK_BUSY(dip);
73860Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
73870Sstevel@tonic-gate cp = PM_CP(dip, i);
73880Sstevel@tonic-gate if (cp->pmc_timestamp == 0 && cp->pmc_busycount == 0)
73890Sstevel@tonic-gate cp->pmc_timestamp = gethrestime_sec();
73900Sstevel@tonic-gate }
73910Sstevel@tonic-gate PM_UNLOCK_BUSY(dip);
73920Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
73930Sstevel@tonic-gate }
73940Sstevel@tonic-gate
73950Sstevel@tonic-gate /*
73960Sstevel@tonic-gate * Called at attach time to see if the device being attached has a record in
73970Sstevel@tonic-gate * the no involuntary power cycles list. If so, we do some bookkeeping on the
73980Sstevel@tonic-gate * parents and set a flag in the dip
73990Sstevel@tonic-gate */
74000Sstevel@tonic-gate void
pm_noinvol_specd(dev_info_t * dip)74010Sstevel@tonic-gate pm_noinvol_specd(dev_info_t *dip)
74020Sstevel@tonic-gate {
74030Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_specd")
74040Sstevel@tonic-gate char *pathbuf;
74050Sstevel@tonic-gate pm_noinvol_t *ip, *pp = NULL;
74060Sstevel@tonic-gate int wasvolpmd;
74070Sstevel@tonic-gate int found = 0;
74080Sstevel@tonic-gate
74090Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NOINVOL_DONE)
74100Sstevel@tonic-gate return;
74110Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= PMC_NOINVOL_DONE;
74120Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
74130Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
74140Sstevel@tonic-gate
74150Sstevel@tonic-gate PM_LOCK_DIP(dip);
74160Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = 0;
74170Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm = 0;
74180Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER);
74190Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
74200Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n",
74210Sstevel@tonic-gate pmf, pathbuf, ip->ni_path))
74220Sstevel@tonic-gate if (strcmp(pathbuf, ip->ni_path) == 0) {
74230Sstevel@tonic-gate found++;
74240Sstevel@tonic-gate break;
74250Sstevel@tonic-gate }
74260Sstevel@tonic-gate }
74270Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
74280Sstevel@tonic-gate if (!found) {
74290Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
74300Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
74310Sstevel@tonic-gate return;
74320Sstevel@tonic-gate }
74330Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER);
74340Sstevel@tonic-gate pp = NULL;
74350Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
74360Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: comparing '%s' to '%s'\n",
74370Sstevel@tonic-gate pmf, pathbuf, ip->ni_path))
74380Sstevel@tonic-gate if (strcmp(pathbuf, ip->ni_path) == 0) {
74390Sstevel@tonic-gate ip->ni_flags &= ~PMC_DRIVER_REMOVED;
74400Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= ip->ni_flags;
74410Sstevel@tonic-gate /*
74420Sstevel@tonic-gate * Handle special case of console fb
74430Sstevel@tonic-gate */
74440Sstevel@tonic-gate if (PM_IS_CFB(dip)) {
74450Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
74460Sstevel@tonic-gate cfb_dip = dip;
74470Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) setting "
74480Sstevel@tonic-gate "cfb_dip\n", pmf, PM_DEVICE(dip)))
74490Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
74500Sstevel@tonic-gate }
74510Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm = ip->ni_noinvolpm;
74520Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_pm_flags &
74530Sstevel@tonic-gate (PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
74540Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm);
74550Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd = ip->ni_volpmd;
74560Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: noinvol=%d, volpmd=%d, "
74570Sstevel@tonic-gate "wasvolpmd=%d, flags=%x, path=%s\n", pmf,
74580Sstevel@tonic-gate ip->ni_noinvolpm, ip->ni_volpmd,
74590Sstevel@tonic-gate ip->ni_wasvolpmd, ip->ni_flags, ip->ni_path))
74600Sstevel@tonic-gate /*
74610Sstevel@tonic-gate * free the entry in hopes the list will now be empty
74620Sstevel@tonic-gate * and we won't have to search it any more until the
74630Sstevel@tonic-gate * device detaches
74640Sstevel@tonic-gate */
74650Sstevel@tonic-gate if (pp) {
74660Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s, prev %s\n",
74670Sstevel@tonic-gate pmf, ip->ni_path, pp->ni_path))
74680Sstevel@tonic-gate pp->ni_next = ip->ni_next;
74690Sstevel@tonic-gate } else {
74700Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s head\n",
74710Sstevel@tonic-gate pmf, ip->ni_path))
74720Sstevel@tonic-gate ASSERT(pm_noinvol_head == ip);
74730Sstevel@tonic-gate pm_noinvol_head = ip->ni_next;
74740Sstevel@tonic-gate }
74750Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
74760Sstevel@tonic-gate wasvolpmd = ip->ni_wasvolpmd;
74770Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
74780Sstevel@tonic-gate kmem_free(ip->ni_path, ip->ni_size);
74790Sstevel@tonic-gate kmem_free(ip, sizeof (*ip));
74800Sstevel@tonic-gate /*
74810Sstevel@tonic-gate * Now walk up the tree decrementing devi_pm_noinvolpm
74820Sstevel@tonic-gate * (and volpmd if appropriate)
74830Sstevel@tonic-gate */
74840Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_ATTACH, 0,
74850Sstevel@tonic-gate wasvolpmd, pathbuf, dip);
74860Sstevel@tonic-gate #ifdef DEBUG
74870Sstevel@tonic-gate if (pm_debug & PMD_NOINVOL)
74880Sstevel@tonic-gate pr_noinvol("noinvol_specd exit");
74890Sstevel@tonic-gate #endif
74900Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
74910Sstevel@tonic-gate return;
74920Sstevel@tonic-gate }
74930Sstevel@tonic-gate }
74940Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
74950Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
74960Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
74970Sstevel@tonic-gate }
74980Sstevel@tonic-gate
74990Sstevel@tonic-gate int
pm_all_components_off(dev_info_t * dip)75000Sstevel@tonic-gate pm_all_components_off(dev_info_t *dip)
75010Sstevel@tonic-gate {
75020Sstevel@tonic-gate int i;
75030Sstevel@tonic-gate pm_component_t *cp;
75040Sstevel@tonic-gate
75050Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
75060Sstevel@tonic-gate cp = PM_CP(dip, i);
75070Sstevel@tonic-gate if (cp->pmc_cur_pwr == PM_LEVEL_UNKNOWN ||
75080Sstevel@tonic-gate cp->pmc_comp.pmc_lvals[cp->pmc_cur_pwr])
75090Sstevel@tonic-gate return (0);
75100Sstevel@tonic-gate }
75110Sstevel@tonic-gate return (1); /* all off */
75120Sstevel@tonic-gate }
75130Sstevel@tonic-gate
75140Sstevel@tonic-gate /*
75150Sstevel@tonic-gate * Make sure that all "no involuntary power cycles" devices are attached.
75160Sstevel@tonic-gate * Called before doing a cpr suspend to make sure the driver has a say about
75170Sstevel@tonic-gate * the power cycle
75180Sstevel@tonic-gate */
75190Sstevel@tonic-gate int
pm_reattach_noinvol(void)75200Sstevel@tonic-gate pm_reattach_noinvol(void)
75210Sstevel@tonic-gate {
75220Sstevel@tonic-gate PMD_FUNC(pmf, "reattach_noinvol")
75230Sstevel@tonic-gate pm_noinvol_t *ip;
75240Sstevel@tonic-gate char *path;
75250Sstevel@tonic-gate dev_info_t *dip;
75260Sstevel@tonic-gate
75270Sstevel@tonic-gate /*
75280Sstevel@tonic-gate * Prevent the modunload thread from unloading any modules until we
75290Sstevel@tonic-gate * have completely stopped all kernel threads.
75300Sstevel@tonic-gate */
75310Sstevel@tonic-gate modunload_disable();
75320Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
75330Sstevel@tonic-gate /*
75340Sstevel@tonic-gate * Forget we'v ever seen any entry
75350Sstevel@tonic-gate */
75360Sstevel@tonic-gate ip->ni_persistent = 0;
75370Sstevel@tonic-gate }
75380Sstevel@tonic-gate restart:
75390Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER);
75400Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
75413839Skchow #ifdef PMDDEBUG
75420Sstevel@tonic-gate major_t maj;
75430Sstevel@tonic-gate maj = ip->ni_major;
75443839Skchow #endif
75450Sstevel@tonic-gate path = ip->ni_path;
75460Sstevel@tonic-gate if (path != NULL && !(ip->ni_flags & PMC_DRIVER_REMOVED)) {
75470Sstevel@tonic-gate if (ip->ni_persistent) {
75480Sstevel@tonic-gate /*
75490Sstevel@tonic-gate * If we weren't able to make this entry
75500Sstevel@tonic-gate * go away, then we give up, as
75510Sstevel@tonic-gate * holding/attaching the driver ought to have
75520Sstevel@tonic-gate * resulted in this entry being deleted
75530Sstevel@tonic-gate */
75540Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: can't reattach %s "
75550Sstevel@tonic-gate "(%s|%d)\n", pmf, ip->ni_path,
75560Sstevel@tonic-gate ddi_major_to_name(maj), (int)maj))
75570Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable to reattach %s ",
75580Sstevel@tonic-gate ip->ni_path);
75590Sstevel@tonic-gate modunload_enable();
75600Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
75610Sstevel@tonic-gate return (0);
75620Sstevel@tonic-gate }
75630Sstevel@tonic-gate ip->ni_persistent++;
75640Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
75650Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: holding %s\n", pmf, path))
75660Sstevel@tonic-gate dip = e_ddi_hold_devi_by_path(path, 0);
75670Sstevel@tonic-gate if (dip == NULL) {
75680Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: can't hold (%s|%d)\n",
75690Sstevel@tonic-gate pmf, path, (int)maj))
75700Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable to hold %s "
75710Sstevel@tonic-gate "driver", path);
75720Sstevel@tonic-gate modunload_enable();
75730Sstevel@tonic-gate return (0);
75740Sstevel@tonic-gate } else {
75750Sstevel@tonic-gate PMD(PMD_DHR, ("%s: release %s\n", pmf, path))
75760Sstevel@tonic-gate /*
75770Sstevel@tonic-gate * Since the modunload thread is stopped, we
75780Sstevel@tonic-gate * don't have to keep the driver held, which
75790Sstevel@tonic-gate * saves a ton of bookkeeping
75800Sstevel@tonic-gate */
75810Sstevel@tonic-gate ddi_release_devi(dip);
75820Sstevel@tonic-gate goto restart;
75830Sstevel@tonic-gate }
75840Sstevel@tonic-gate } else {
75850Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: skip %s; unknown major\n",
75860Sstevel@tonic-gate pmf, ip->ni_path))
75870Sstevel@tonic-gate continue;
75880Sstevel@tonic-gate }
75890Sstevel@tonic-gate }
75900Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
75910Sstevel@tonic-gate return (1);
75920Sstevel@tonic-gate }
75930Sstevel@tonic-gate
75940Sstevel@tonic-gate void
pm_reattach_noinvol_fini(void)75950Sstevel@tonic-gate pm_reattach_noinvol_fini(void)
75960Sstevel@tonic-gate {
75970Sstevel@tonic-gate modunload_enable();
75980Sstevel@tonic-gate }
75990Sstevel@tonic-gate
76000Sstevel@tonic-gate /*
76010Sstevel@tonic-gate * Display pm support code
76020Sstevel@tonic-gate */
76030Sstevel@tonic-gate
76040Sstevel@tonic-gate
76050Sstevel@tonic-gate /*
76060Sstevel@tonic-gate * console frame-buffer power-mgmt gets enabled when debugging
76070Sstevel@tonic-gate * services are not present or console fbpm override is set
76080Sstevel@tonic-gate */
76090Sstevel@tonic-gate void
pm_cfb_setup(const char * stdout_path)76100Sstevel@tonic-gate pm_cfb_setup(const char *stdout_path)
76110Sstevel@tonic-gate {
76120Sstevel@tonic-gate PMD_FUNC(pmf, "cfb_setup")
76130Sstevel@tonic-gate extern int obpdebug;
76140Sstevel@tonic-gate char *devname;
76150Sstevel@tonic-gate dev_info_t *dip;
76160Sstevel@tonic-gate int devname_len;
76170Sstevel@tonic-gate extern dev_info_t *fbdip;
76180Sstevel@tonic-gate
76190Sstevel@tonic-gate /*
76200Sstevel@tonic-gate * By virtue of this function being called (from consconfig),
76210Sstevel@tonic-gate * we know stdout is a framebuffer.
76220Sstevel@tonic-gate */
76230Sstevel@tonic-gate stdout_is_framebuffer = 1;
76240Sstevel@tonic-gate
76250Sstevel@tonic-gate if (obpdebug || (boothowto & RB_DEBUG)) {
76260Sstevel@tonic-gate if (pm_cfb_override == 0) {
76270Sstevel@tonic-gate /*
76280Sstevel@tonic-gate * Console is frame buffer, but we want to suppress
76290Sstevel@tonic-gate * pm on it because of debugging setup
76300Sstevel@tonic-gate */
76310Sstevel@tonic-gate pm_cfb_enabled = 0;
76320Sstevel@tonic-gate cmn_err(CE_NOTE, "Kernel debugger present: disabling "
76330Sstevel@tonic-gate "console power management.");
76340Sstevel@tonic-gate /*
76350Sstevel@tonic-gate * however, we still need to know which is the console
76360Sstevel@tonic-gate * fb in order to suppress pm on it
76370Sstevel@tonic-gate */
76380Sstevel@tonic-gate } else {
76390Sstevel@tonic-gate cmn_err(CE_WARN, "Kernel debugger present: see "
76400Sstevel@tonic-gate "kmdb(1M) for interaction with power management.");
76410Sstevel@tonic-gate }
76420Sstevel@tonic-gate }
76430Sstevel@tonic-gate #ifdef DEBUG
76440Sstevel@tonic-gate /*
76450Sstevel@tonic-gate * IF console is fb and is power managed, don't do prom_printfs from
76460Sstevel@tonic-gate * pm debug macro
76470Sstevel@tonic-gate */
76485295Srandyf if (pm_cfb_enabled && !pm_debug_to_console) {
76490Sstevel@tonic-gate if (pm_debug)
76500Sstevel@tonic-gate prom_printf("pm debug output will be to log only\n");
76510Sstevel@tonic-gate pm_divertdebug++;
76520Sstevel@tonic-gate }
76530Sstevel@tonic-gate #endif
76540Sstevel@tonic-gate devname = i_ddi_strdup((char *)stdout_path, KM_SLEEP);
76550Sstevel@tonic-gate devname_len = strlen(devname) + 1;
76560Sstevel@tonic-gate PMD(PMD_CFB, ("%s: stripped %s\n", pmf, devname))
76570Sstevel@tonic-gate /* if the driver is attached */
76580Sstevel@tonic-gate if ((dip = fbdip) != NULL) {
76590Sstevel@tonic-gate PMD(PMD_CFB, ("%s: attached: %s@%s(%s#%d)\n", pmf,
76600Sstevel@tonic-gate PM_DEVICE(dip)))
76610Sstevel@tonic-gate /*
76620Sstevel@tonic-gate * We set up here as if the driver were power manageable in case
76630Sstevel@tonic-gate * we get a later attach of a pm'able driver (which would result
76640Sstevel@tonic-gate * in a panic later)
76650Sstevel@tonic-gate */
76660Sstevel@tonic-gate cfb_dip = dip;
76670Sstevel@tonic-gate DEVI(dip)->devi_pm_flags |= (PMC_CONSOLE_FB | PMC_NO_INVOL);
76680Sstevel@tonic-gate PMD(PMD_CFB, ("%s: cfb_dip -> %s@%s(%s#%d)\n", pmf,
76690Sstevel@tonic-gate PM_DEVICE(dip)))
76700Sstevel@tonic-gate #ifdef DEBUG
76710Sstevel@tonic-gate if (!(PM_GET_PM_INFO(dip) != NULL && PM_NUMCMPTS(dip))) {
76720Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d) not power-managed\n",
76730Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
76740Sstevel@tonic-gate }
76750Sstevel@tonic-gate #endif
76760Sstevel@tonic-gate } else {
76770Sstevel@tonic-gate char *ep;
76780Sstevel@tonic-gate PMD(PMD_CFB, ("%s: pntd %s failed\n", pmf, devname))
76790Sstevel@tonic-gate pm_record_invol_path(devname,
76800Sstevel@tonic-gate (PMC_CONSOLE_FB | PMC_NO_INVOL), 1, 0, 0,
76817009Scth DDI_MAJOR_T_NONE);
76820Sstevel@tonic-gate for (ep = strrchr(devname, '/'); ep != devname;
76830Sstevel@tonic-gate ep = strrchr(devname, '/')) {
76840Sstevel@tonic-gate PMD(PMD_CFB, ("%s: devname %s\n", pmf, devname))
76850Sstevel@tonic-gate *ep = '\0';
76860Sstevel@tonic-gate dip = pm_name_to_dip(devname, 0);
76870Sstevel@tonic-gate if (dip != NULL) {
76880Sstevel@tonic-gate /*
76890Sstevel@tonic-gate * Walk up the tree incrementing
76900Sstevel@tonic-gate * devi_pm_noinvolpm
76910Sstevel@tonic-gate */
76920Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_CFB,
76930Sstevel@tonic-gate 0, 0, devname, dip);
76940Sstevel@tonic-gate break;
76950Sstevel@tonic-gate } else {
76960Sstevel@tonic-gate pm_record_invol_path(devname,
76977009Scth PMC_NO_INVOL, 1, 0, 0, DDI_MAJOR_T_NONE);
76980Sstevel@tonic-gate }
76990Sstevel@tonic-gate }
77000Sstevel@tonic-gate }
77010Sstevel@tonic-gate kmem_free(devname, devname_len);
77020Sstevel@tonic-gate }
77030Sstevel@tonic-gate
77040Sstevel@tonic-gate void
pm_cfb_rele(void)77050Sstevel@tonic-gate pm_cfb_rele(void)
77060Sstevel@tonic-gate {
77070Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
77080Sstevel@tonic-gate /*
77090Sstevel@tonic-gate * this call isn't using the console any more, it is ok to take it
77100Sstevel@tonic-gate * down if the count goes to 0
77110Sstevel@tonic-gate */
77120Sstevel@tonic-gate cfb_inuse--;
77130Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
77140Sstevel@tonic-gate }
77150Sstevel@tonic-gate
77160Sstevel@tonic-gate /*
77170Sstevel@tonic-gate * software interrupt handler for fbpm; this function exists because we can't
77180Sstevel@tonic-gate * bring up the frame buffer power from above lock level. So if we need to,
77190Sstevel@tonic-gate * we instead schedule a softint that runs this routine and takes us into
77200Sstevel@tonic-gate * debug_enter (a bit delayed from the original request, but avoiding a panic).
77210Sstevel@tonic-gate */
77220Sstevel@tonic-gate static uint_t
pm_cfb_softint(caddr_t int_handler_arg)77230Sstevel@tonic-gate pm_cfb_softint(caddr_t int_handler_arg)
77240Sstevel@tonic-gate {
77250Sstevel@tonic-gate _NOTE(ARGUNUSED(int_handler_arg))
77260Sstevel@tonic-gate int rval = DDI_INTR_UNCLAIMED;
77270Sstevel@tonic-gate
77280Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
77290Sstevel@tonic-gate if (pm_soft_pending) {
77300Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
77310Sstevel@tonic-gate debug_enter((char *)NULL);
77320Sstevel@tonic-gate /* acquired in debug_enter before calling pm_cfb_trigger */
77330Sstevel@tonic-gate pm_cfb_rele();
77340Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
7735*11066Srafael.vanoni@sun.com pm_soft_pending = B_FALSE;
77360Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
77370Sstevel@tonic-gate rval = DDI_INTR_CLAIMED;
77380Sstevel@tonic-gate } else
77390Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
77400Sstevel@tonic-gate
77410Sstevel@tonic-gate return (rval);
77420Sstevel@tonic-gate }
77430Sstevel@tonic-gate
77440Sstevel@tonic-gate void
pm_cfb_setup_intr(void)77450Sstevel@tonic-gate pm_cfb_setup_intr(void)
77460Sstevel@tonic-gate {
77470Sstevel@tonic-gate PMD_FUNC(pmf, "cfb_setup_intr")
77480Sstevel@tonic-gate extern void prom_set_outfuncs(void (*)(void), void (*)(void));
77490Sstevel@tonic-gate void pm_cfb_check_and_powerup(void);
77500Sstevel@tonic-gate
77515295Srandyf mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
77525295Srandyf #ifdef PMDDEBUG
77535295Srandyf mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
77545295Srandyf #endif
77555295Srandyf
77560Sstevel@tonic-gate if (!stdout_is_framebuffer) {
77570Sstevel@tonic-gate PMD(PMD_CFB, ("%s: console not fb\n", pmf))
77580Sstevel@tonic-gate return;
77590Sstevel@tonic-gate }
77605295Srandyf
77610Sstevel@tonic-gate /*
77620Sstevel@tonic-gate * setup software interrupt handler
77630Sstevel@tonic-gate */
77640Sstevel@tonic-gate if (ddi_add_softintr(ddi_root_node(), DDI_SOFTINT_HIGH, &pm_soft_id,
77650Sstevel@tonic-gate NULL, NULL, pm_cfb_softint, NULL) != DDI_SUCCESS)
77660Sstevel@tonic-gate panic("pm: unable to register soft intr.");
77670Sstevel@tonic-gate
77680Sstevel@tonic-gate prom_set_outfuncs(pm_cfb_check_and_powerup, pm_cfb_rele);
77690Sstevel@tonic-gate }
77700Sstevel@tonic-gate
77710Sstevel@tonic-gate /*
77720Sstevel@tonic-gate * Checks to see if it is safe to write to the console wrt power management
77730Sstevel@tonic-gate * (i.e. if the console is a framebuffer, then it must be at full power)
77740Sstevel@tonic-gate * returns 1 when power is off (power-up is needed)
77750Sstevel@tonic-gate * returns 0 when power is on (power-up not needed)
77760Sstevel@tonic-gate */
77770Sstevel@tonic-gate int
pm_cfb_check_and_hold(void)77780Sstevel@tonic-gate pm_cfb_check_and_hold(void)
77790Sstevel@tonic-gate {
77800Sstevel@tonic-gate /*
77810Sstevel@tonic-gate * cfb_dip is set iff console is a power manageable frame buffer
77820Sstevel@tonic-gate * device
77830Sstevel@tonic-gate */
77840Sstevel@tonic-gate extern int modrootloaded;
77850Sstevel@tonic-gate
77860Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
77870Sstevel@tonic-gate cfb_inuse++;
77880Sstevel@tonic-gate ASSERT(cfb_inuse); /* wrap? */
77890Sstevel@tonic-gate if (modrootloaded && cfb_dip) {
77900Sstevel@tonic-gate /*
77910Sstevel@tonic-gate * don't power down the frame buffer, the prom is using it
77920Sstevel@tonic-gate */
77930Sstevel@tonic-gate if (pm_cfb_comps_off) {
77940Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
77950Sstevel@tonic-gate return (1);
77960Sstevel@tonic-gate }
77970Sstevel@tonic-gate }
77980Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
77990Sstevel@tonic-gate return (0);
78000Sstevel@tonic-gate }
78010Sstevel@tonic-gate
78020Sstevel@tonic-gate /*
78030Sstevel@tonic-gate * turn on cfb power (which is known to be off).
78040Sstevel@tonic-gate * Must be called below lock level!
78050Sstevel@tonic-gate */
78060Sstevel@tonic-gate void
pm_cfb_powerup(void)78070Sstevel@tonic-gate pm_cfb_powerup(void)
78080Sstevel@tonic-gate {
78090Sstevel@tonic-gate pm_info_t *info;
78100Sstevel@tonic-gate int norm;
78110Sstevel@tonic-gate int ccount, ci;
78120Sstevel@tonic-gate int unused;
78130Sstevel@tonic-gate #ifdef DEBUG
78140Sstevel@tonic-gate /*
78150Sstevel@tonic-gate * Can't reenter prom_prekern, so suppress pm debug messages
78160Sstevel@tonic-gate * (still go to circular buffer).
78170Sstevel@tonic-gate */
78180Sstevel@tonic-gate mutex_enter(&pm_debug_lock);
78190Sstevel@tonic-gate pm_divertdebug++;
78200Sstevel@tonic-gate mutex_exit(&pm_debug_lock);
78210Sstevel@tonic-gate #endif
78220Sstevel@tonic-gate info = PM_GET_PM_INFO(cfb_dip);
78230Sstevel@tonic-gate ASSERT(info);
78240Sstevel@tonic-gate
78250Sstevel@tonic-gate ccount = PM_NUMCMPTS(cfb_dip);
78260Sstevel@tonic-gate for (ci = 0; ci < ccount; ci++) {
78270Sstevel@tonic-gate norm = pm_get_normal_power(cfb_dip, ci);
78280Sstevel@tonic-gate (void) pm_set_power(cfb_dip, ci, norm, PM_LEVEL_UPONLY,
78290Sstevel@tonic-gate PM_CANBLOCK_BYPASS, 0, &unused);
78300Sstevel@tonic-gate }
78310Sstevel@tonic-gate #ifdef DEBUG
78320Sstevel@tonic-gate mutex_enter(&pm_debug_lock);
78330Sstevel@tonic-gate pm_divertdebug--;
78340Sstevel@tonic-gate mutex_exit(&pm_debug_lock);
78350Sstevel@tonic-gate #endif
78360Sstevel@tonic-gate }
78370Sstevel@tonic-gate
78380Sstevel@tonic-gate /*
78390Sstevel@tonic-gate * Check if the console framebuffer is powered up. If not power it up.
78400Sstevel@tonic-gate * Note: Calling pm_cfb_check_and_hold has put a hold on the power state which
78410Sstevel@tonic-gate * must be released by calling pm_cfb_rele when the console fb operation
78420Sstevel@tonic-gate * is completed.
78430Sstevel@tonic-gate */
78440Sstevel@tonic-gate void
pm_cfb_check_and_powerup(void)78450Sstevel@tonic-gate pm_cfb_check_and_powerup(void)
78460Sstevel@tonic-gate {
78470Sstevel@tonic-gate if (pm_cfb_check_and_hold())
78480Sstevel@tonic-gate pm_cfb_powerup();
78490Sstevel@tonic-gate }
78500Sstevel@tonic-gate
78510Sstevel@tonic-gate /*
78520Sstevel@tonic-gate * Trigger a low level interrupt to power up console frame buffer.
78530Sstevel@tonic-gate */
78540Sstevel@tonic-gate void
pm_cfb_trigger(void)78550Sstevel@tonic-gate pm_cfb_trigger(void)
78560Sstevel@tonic-gate {
78570Sstevel@tonic-gate if (cfb_dip == NULL)
78580Sstevel@tonic-gate return;
78590Sstevel@tonic-gate
78600Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
78610Sstevel@tonic-gate /*
7862*11066Srafael.vanoni@sun.com * If the machine appears to be hung, pulling the keyboard connector of
78630Sstevel@tonic-gate * the console will cause a high level interrupt and go to debug_enter.
78640Sstevel@tonic-gate * But, if the fb is powered down, this routine will be called to bring
7865*11066Srafael.vanoni@sun.com * it up (by generating a softint to do the work). If a second attempt
7866*11066Srafael.vanoni@sun.com * at triggering this softint happens before the first one completes,
7867*11066Srafael.vanoni@sun.com * we panic as softints are most likely not being handled.
78680Sstevel@tonic-gate */
7869*11066Srafael.vanoni@sun.com if (pm_soft_pending) {
7870*11066Srafael.vanoni@sun.com panicstr = "pm_cfb_trigger: failed to enter the debugger";
78710Sstevel@tonic-gate panic(panicstr); /* does a power up at any intr level */
78720Sstevel@tonic-gate /* NOTREACHED */
78730Sstevel@tonic-gate }
7874*11066Srafael.vanoni@sun.com pm_soft_pending = B_TRUE;
78750Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
78760Sstevel@tonic-gate ddi_trigger_softintr(pm_soft_id);
78770Sstevel@tonic-gate }
78780Sstevel@tonic-gate
78799694SScott.Rotondo@Sun.COM static major_t i_path_to_major(char *, char *);
78809694SScott.Rotondo@Sun.COM
78810Sstevel@tonic-gate major_t
pm_path_to_major(char * path)78820Sstevel@tonic-gate pm_path_to_major(char *path)
78830Sstevel@tonic-gate {
78840Sstevel@tonic-gate PMD_FUNC(pmf, "path_to_major")
78850Sstevel@tonic-gate char *np, *ap, *bp;
78860Sstevel@tonic-gate major_t ret;
78870Sstevel@tonic-gate size_t len;
78880Sstevel@tonic-gate
78890Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s\n", pmf, path))
78900Sstevel@tonic-gate
78910Sstevel@tonic-gate np = strrchr(path, '/');
78920Sstevel@tonic-gate if (np != NULL)
78930Sstevel@tonic-gate np++;
78940Sstevel@tonic-gate else
78950Sstevel@tonic-gate np = path;
78960Sstevel@tonic-gate len = strlen(np) + 1;
78970Sstevel@tonic-gate bp = kmem_alloc(len, KM_SLEEP);
78980Sstevel@tonic-gate (void) strcpy(bp, np);
78990Sstevel@tonic-gate if ((ap = strchr(bp, '@')) != NULL) {
79000Sstevel@tonic-gate *ap = '\0';
79010Sstevel@tonic-gate }
79020Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %d\n", pmf, ddi_name_to_major(np)))
79030Sstevel@tonic-gate ret = i_path_to_major(path, np);
79040Sstevel@tonic-gate kmem_free(bp, len);
79050Sstevel@tonic-gate return (ret);
79060Sstevel@tonic-gate }
79070Sstevel@tonic-gate
79080Sstevel@tonic-gate #ifdef DEBUG
79095295Srandyf #ifndef sparc
79105295Srandyf clock_t pt_sleep = 1;
79115295Srandyf #endif
79125295Srandyf
79135295Srandyf char *pm_msgp;
79145295Srandyf char *pm_bufend;
79155295Srandyf char *pm_msgbuf = NULL;
79165295Srandyf int pm_logpages = 0x100;
79175295Srandyf #include <sys/sunldi.h>
79185295Srandyf #include <sys/uio.h>
79195295Srandyf clock_t pm_log_sleep = 1000;
79205295Srandyf int pm_extra_cr = 1;
79215295Srandyf volatile int pm_tty = 1;
79220Sstevel@tonic-gate
79230Sstevel@tonic-gate #define PMLOGPGS pm_logpages
79240Sstevel@tonic-gate
79255295Srandyf #if defined(__x86)
79265295Srandyf void pm_printf(char *s);
79275295Srandyf #endif
79285295Srandyf
79290Sstevel@tonic-gate /*PRINTFLIKE1*/
79300Sstevel@tonic-gate void
pm_log(const char * fmt,...)79310Sstevel@tonic-gate pm_log(const char *fmt, ...)
79320Sstevel@tonic-gate {
79330Sstevel@tonic-gate va_list adx;
79340Sstevel@tonic-gate size_t size;
79350Sstevel@tonic-gate
79360Sstevel@tonic-gate mutex_enter(&pm_debug_lock);
79370Sstevel@tonic-gate if (pm_msgbuf == NULL) {
79380Sstevel@tonic-gate pm_msgbuf = kmem_zalloc(mmu_ptob(PMLOGPGS), KM_SLEEP);
79390Sstevel@tonic-gate pm_bufend = pm_msgbuf + mmu_ptob(PMLOGPGS) - 1;
79400Sstevel@tonic-gate pm_msgp = pm_msgbuf;
79410Sstevel@tonic-gate }
79420Sstevel@tonic-gate va_start(adx, fmt);
79430Sstevel@tonic-gate size = vsnprintf(NULL, 0, fmt, adx) + 1;
79440Sstevel@tonic-gate va_end(adx);
79450Sstevel@tonic-gate va_start(adx, fmt);
79460Sstevel@tonic-gate if (size > (pm_bufend - pm_msgp)) { /* wraps */
79470Sstevel@tonic-gate bzero(pm_msgp, pm_bufend - pm_msgp);
79480Sstevel@tonic-gate (void) vsnprintf(pm_msgbuf, size, fmt, adx);
79490Sstevel@tonic-gate if (!pm_divertdebug)
79500Sstevel@tonic-gate prom_printf("%s", pm_msgp);
79515295Srandyf #if defined(__x86)
79525295Srandyf if (pm_tty) {
79535295Srandyf pm_printf(pm_msgp);
79545295Srandyf if (pm_extra_cr)
79555295Srandyf pm_printf("\r");
79565295Srandyf }
79575295Srandyf #endif
79580Sstevel@tonic-gate pm_msgp = pm_msgbuf + size;
79590Sstevel@tonic-gate } else {
79600Sstevel@tonic-gate (void) vsnprintf(pm_msgp, size, fmt, adx);
79615295Srandyf #if defined(__x86)
79625295Srandyf if (pm_tty) {
79635295Srandyf pm_printf(pm_msgp);
79645295Srandyf if (pm_extra_cr)
79655295Srandyf pm_printf("\r");
79665295Srandyf }
79675295Srandyf #endif
79680Sstevel@tonic-gate if (!pm_divertdebug)
79690Sstevel@tonic-gate prom_printf("%s", pm_msgp);
79700Sstevel@tonic-gate pm_msgp += size;
79710Sstevel@tonic-gate }
79720Sstevel@tonic-gate va_end(adx);
79730Sstevel@tonic-gate mutex_exit(&pm_debug_lock);
79745295Srandyf drv_usecwait((clock_t)pm_log_sleep);
79750Sstevel@tonic-gate }
79760Sstevel@tonic-gate #endif /* DEBUG */
79770Sstevel@tonic-gate
79780Sstevel@tonic-gate /*
79790Sstevel@tonic-gate * We want to save the state of any directly pm'd devices over the suspend/
79800Sstevel@tonic-gate * resume process so that we can put them back the way the controlling
79810Sstevel@tonic-gate * process left them.
79820Sstevel@tonic-gate */
79830Sstevel@tonic-gate void
pm_save_direct_levels(void)79840Sstevel@tonic-gate pm_save_direct_levels(void)
79850Sstevel@tonic-gate {
79860Sstevel@tonic-gate pm_processes_stopped = 1;
79870Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_save_direct_lvl_walk, 0);
79880Sstevel@tonic-gate }
79890Sstevel@tonic-gate
79900Sstevel@tonic-gate static int
pm_save_direct_lvl_walk(dev_info_t * dip,void * arg)79910Sstevel@tonic-gate pm_save_direct_lvl_walk(dev_info_t *dip, void *arg)
79920Sstevel@tonic-gate {
79930Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
79940Sstevel@tonic-gate int i;
79950Sstevel@tonic-gate int *ip;
79960Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
79970Sstevel@tonic-gate
79980Sstevel@tonic-gate if (!info)
79990Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
80000Sstevel@tonic-gate
80010Sstevel@tonic-gate if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) {
80020Sstevel@tonic-gate if (PM_NUMCMPTS(dip) > 2) {
80030Sstevel@tonic-gate info->pmi_lp = kmem_alloc(PM_NUMCMPTS(dip) *
80040Sstevel@tonic-gate sizeof (int), KM_SLEEP);
80050Sstevel@tonic-gate ip = info->pmi_lp;
80060Sstevel@tonic-gate } else {
80070Sstevel@tonic-gate ip = info->pmi_levels;
80080Sstevel@tonic-gate }
80090Sstevel@tonic-gate /* autopm and processes are stopped, ok not to lock power */
80100Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++)
80110Sstevel@tonic-gate *ip++ = PM_CURPOWER(dip, i);
80120Sstevel@tonic-gate /*
80130Sstevel@tonic-gate * There is a small window between stopping the
80140Sstevel@tonic-gate * processes and setting pm_processes_stopped where
80150Sstevel@tonic-gate * a driver could get hung up in a pm_raise_power()
80160Sstevel@tonic-gate * call. Free any such driver now.
80170Sstevel@tonic-gate */
80180Sstevel@tonic-gate pm_proceed(dip, PMP_RELEASE, -1, -1);
80190Sstevel@tonic-gate }
80200Sstevel@tonic-gate
80210Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
80220Sstevel@tonic-gate }
80230Sstevel@tonic-gate
80240Sstevel@tonic-gate void
pm_restore_direct_levels(void)80250Sstevel@tonic-gate pm_restore_direct_levels(void)
80260Sstevel@tonic-gate {
80270Sstevel@tonic-gate /*
80280Sstevel@tonic-gate * If cpr didn't call pm_save_direct_levels, (because stopping user
80290Sstevel@tonic-gate * threads failed) then we don't want to try to restore them
80300Sstevel@tonic-gate */
80310Sstevel@tonic-gate if (!pm_processes_stopped)
80320Sstevel@tonic-gate return;
80330Sstevel@tonic-gate
80340Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), pm_restore_direct_lvl_walk, 0);
80350Sstevel@tonic-gate pm_processes_stopped = 0;
80360Sstevel@tonic-gate }
80370Sstevel@tonic-gate
80380Sstevel@tonic-gate static int
pm_restore_direct_lvl_walk(dev_info_t * dip,void * arg)80390Sstevel@tonic-gate pm_restore_direct_lvl_walk(dev_info_t *dip, void *arg)
80400Sstevel@tonic-gate {
80410Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
80420Sstevel@tonic-gate PMD_FUNC(pmf, "restore_direct_lvl_walk")
80430Sstevel@tonic-gate int i, nc, result;
80440Sstevel@tonic-gate int *ip;
80450Sstevel@tonic-gate
80460Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
80470Sstevel@tonic-gate if (!info)
80480Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
80490Sstevel@tonic-gate
80500Sstevel@tonic-gate if (PM_ISDIRECT(dip) && !PM_ISBC(dip)) {
80510Sstevel@tonic-gate if ((nc = PM_NUMCMPTS(dip)) > 2) {
80520Sstevel@tonic-gate ip = &info->pmi_lp[nc - 1];
80530Sstevel@tonic-gate } else {
80540Sstevel@tonic-gate ip = &info->pmi_levels[nc - 1];
80550Sstevel@tonic-gate }
80560Sstevel@tonic-gate /*
80570Sstevel@tonic-gate * Because fb drivers fail attempts to turn off the
80580Sstevel@tonic-gate * fb when the monitor is on, but treat a request to
80590Sstevel@tonic-gate * turn on the monitor as a request to turn on the
80600Sstevel@tonic-gate * fb too, we process components in descending order
80610Sstevel@tonic-gate * Because autopm is disabled and processes aren't
80620Sstevel@tonic-gate * running, it is ok to examine current power outside
80630Sstevel@tonic-gate * of the power lock
80640Sstevel@tonic-gate */
80650Sstevel@tonic-gate for (i = nc - 1; i >= 0; i--, ip--) {
80660Sstevel@tonic-gate if (PM_CURPOWER(dip, i) == *ip)
80670Sstevel@tonic-gate continue;
80680Sstevel@tonic-gate if (pm_set_power(dip, i, *ip, PM_LEVEL_EXACT,
80694780Smh27603 PM_CANBLOCK_BYPASS, 0, &result) != DDI_SUCCESS) {
80700Sstevel@tonic-gate cmn_err(CE_WARN, "cpr: unable "
80710Sstevel@tonic-gate "to restore power level of "
80720Sstevel@tonic-gate "component %d of directly "
80730Sstevel@tonic-gate "power manged device %s@%s"
80740Sstevel@tonic-gate " to %d",
80750Sstevel@tonic-gate i, PM_NAME(dip),
80760Sstevel@tonic-gate PM_ADDR(dip), *ip);
80770Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to restore "
80780Sstevel@tonic-gate "%s@%s(%s#%d)[%d] exact(%d)->%d, "
80790Sstevel@tonic-gate "errno %d\n", pmf, PM_DEVICE(dip), i,
80800Sstevel@tonic-gate PM_CURPOWER(dip, i), *ip, result))
80810Sstevel@tonic-gate }
80820Sstevel@tonic-gate }
80830Sstevel@tonic-gate if (nc > 2) {
80840Sstevel@tonic-gate kmem_free(info->pmi_lp, nc * sizeof (int));
80850Sstevel@tonic-gate info->pmi_lp = NULL;
80860Sstevel@tonic-gate }
80870Sstevel@tonic-gate }
80880Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
80890Sstevel@tonic-gate }
80900Sstevel@tonic-gate
80910Sstevel@tonic-gate /*
80920Sstevel@tonic-gate * Stolen from the bootdev module
80930Sstevel@tonic-gate * attempt to convert a path to a major number
80940Sstevel@tonic-gate */
80950Sstevel@tonic-gate static major_t
i_path_to_major(char * path,char * leaf_name)80960Sstevel@tonic-gate i_path_to_major(char *path, char *leaf_name)
80970Sstevel@tonic-gate {
80980Sstevel@tonic-gate extern major_t path_to_major(char *pathname);
80990Sstevel@tonic-gate major_t maj;
81000Sstevel@tonic-gate
81017009Scth if ((maj = path_to_major(path)) == DDI_MAJOR_T_NONE) {
81020Sstevel@tonic-gate maj = ddi_name_to_major(leaf_name);
81030Sstevel@tonic-gate }
81040Sstevel@tonic-gate
81050Sstevel@tonic-gate return (maj);
81060Sstevel@tonic-gate }
81070Sstevel@tonic-gate
81089694SScott.Rotondo@Sun.COM static void i_pm_driver_removed(major_t major);
81099694SScott.Rotondo@Sun.COM
81100Sstevel@tonic-gate /*
81110Sstevel@tonic-gate * When user calls rem_drv, we need to forget no-involuntary-power-cycles state
81120Sstevel@tonic-gate * An entry in the list means that the device is detached, so we need to
81130Sstevel@tonic-gate * adjust its ancestors as if they had just seen this attach, and any detached
81140Sstevel@tonic-gate * ancestors need to have their list entries adjusted.
81150Sstevel@tonic-gate */
81160Sstevel@tonic-gate void
pm_driver_removed(major_t major)81170Sstevel@tonic-gate pm_driver_removed(major_t major)
81180Sstevel@tonic-gate {
81190Sstevel@tonic-gate
81200Sstevel@tonic-gate /*
81210Sstevel@tonic-gate * Serialize removal of drivers. This is to keep ancestors of
81220Sstevel@tonic-gate * a node that is being deleted from getting deleted and added back
81230Sstevel@tonic-gate * with different counters.
81240Sstevel@tonic-gate */
81250Sstevel@tonic-gate mutex_enter(&pm_remdrv_lock);
81260Sstevel@tonic-gate i_pm_driver_removed(major);
81270Sstevel@tonic-gate mutex_exit(&pm_remdrv_lock);
81280Sstevel@tonic-gate }
81290Sstevel@tonic-gate
81309694SScott.Rotondo@Sun.COM static void adjust_ancestors(char *, int);
81319694SScott.Rotondo@Sun.COM static int pm_is_noinvol_ancestor(pm_noinvol_t *);
81329694SScott.Rotondo@Sun.COM static void pm_noinvol_process_ancestors(char *);
81339694SScott.Rotondo@Sun.COM
81340Sstevel@tonic-gate /*
81350Sstevel@tonic-gate * This routine is called recursively by pm_noinvol_process_ancestors()
81360Sstevel@tonic-gate */
81370Sstevel@tonic-gate static void
i_pm_driver_removed(major_t major)81380Sstevel@tonic-gate i_pm_driver_removed(major_t major)
81390Sstevel@tonic-gate {
81400Sstevel@tonic-gate PMD_FUNC(pmf, "driver_removed")
81410Sstevel@tonic-gate pm_noinvol_t *ip, *pp = NULL;
81420Sstevel@tonic-gate int wasvolpmd;
81437009Scth ASSERT(major != DDI_MAJOR_T_NONE);
81440Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s\n", pmf, ddi_major_to_name(major)))
81450Sstevel@tonic-gate again:
81460Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER);
81470Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; pp = ip, ip = ip->ni_next) {
81480Sstevel@tonic-gate if (major != ip->ni_major)
81490Sstevel@tonic-gate continue;
81500Sstevel@tonic-gate /*
81510Sstevel@tonic-gate * If it is an ancestor of no-invol node, which is
81520Sstevel@tonic-gate * not removed, skip it. This is to cover the case of
81530Sstevel@tonic-gate * ancestor removed without removing its descendants.
81540Sstevel@tonic-gate */
81550Sstevel@tonic-gate if (pm_is_noinvol_ancestor(ip)) {
81560Sstevel@tonic-gate ip->ni_flags |= PMC_DRIVER_REMOVED;
81570Sstevel@tonic-gate continue;
81580Sstevel@tonic-gate }
81590Sstevel@tonic-gate wasvolpmd = ip->ni_wasvolpmd;
81600Sstevel@tonic-gate /*
81610Sstevel@tonic-gate * remove the entry from the list
81620Sstevel@tonic-gate */
81630Sstevel@tonic-gate if (pp) {
81640Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: freeing %s, prev is %s\n",
81650Sstevel@tonic-gate pmf, ip->ni_path, pp->ni_path))
81660Sstevel@tonic-gate pp->ni_next = ip->ni_next;
81670Sstevel@tonic-gate } else {
81680Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s head\n", pmf,
81690Sstevel@tonic-gate ip->ni_path))
81700Sstevel@tonic-gate ASSERT(pm_noinvol_head == ip);
81710Sstevel@tonic-gate pm_noinvol_head = ip->ni_next;
81720Sstevel@tonic-gate }
81730Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
81740Sstevel@tonic-gate adjust_ancestors(ip->ni_path, wasvolpmd);
81750Sstevel@tonic-gate /*
81760Sstevel@tonic-gate * Had an ancestor been removed before this node, it would have
81770Sstevel@tonic-gate * been skipped. Adjust the no-invol counters for such skipped
81780Sstevel@tonic-gate * ancestors.
81790Sstevel@tonic-gate */
81800Sstevel@tonic-gate pm_noinvol_process_ancestors(ip->ni_path);
81810Sstevel@tonic-gate kmem_free(ip->ni_path, ip->ni_size);
81820Sstevel@tonic-gate kmem_free(ip, sizeof (*ip));
81830Sstevel@tonic-gate goto again;
81840Sstevel@tonic-gate }
81850Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
81860Sstevel@tonic-gate }
81870Sstevel@tonic-gate
81880Sstevel@tonic-gate /*
81890Sstevel@tonic-gate * returns 1, if *aip is a ancestor of a no-invol node
81900Sstevel@tonic-gate * 0, otherwise
81910Sstevel@tonic-gate */
81920Sstevel@tonic-gate static int
pm_is_noinvol_ancestor(pm_noinvol_t * aip)81930Sstevel@tonic-gate pm_is_noinvol_ancestor(pm_noinvol_t *aip)
81940Sstevel@tonic-gate {
81950Sstevel@tonic-gate pm_noinvol_t *ip;
81960Sstevel@tonic-gate
81970Sstevel@tonic-gate ASSERT(strlen(aip->ni_path) != 0);
81980Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
81990Sstevel@tonic-gate if (ip == aip)
82000Sstevel@tonic-gate continue;
82010Sstevel@tonic-gate /*
82020Sstevel@tonic-gate * To be an ancestor, the path must be an initial substring of
82030Sstevel@tonic-gate * the descendent, and end just before a '/' in the
82040Sstevel@tonic-gate * descendent's path.
82050Sstevel@tonic-gate */
82060Sstevel@tonic-gate if ((strstr(ip->ni_path, aip->ni_path) == ip->ni_path) &&
82070Sstevel@tonic-gate (ip->ni_path[strlen(aip->ni_path)] == '/'))
82080Sstevel@tonic-gate return (1);
82090Sstevel@tonic-gate }
82100Sstevel@tonic-gate return (0);
82110Sstevel@tonic-gate }
82120Sstevel@tonic-gate
82130Sstevel@tonic-gate /*
82140Sstevel@tonic-gate * scan through the pm_noinvolpm list adjusting ancestors of the current
82150Sstevel@tonic-gate * node; Modifies string *path.
82160Sstevel@tonic-gate */
82170Sstevel@tonic-gate static void
adjust_ancestors(char * path,int wasvolpmd)82180Sstevel@tonic-gate adjust_ancestors(char *path, int wasvolpmd)
82190Sstevel@tonic-gate {
82200Sstevel@tonic-gate PMD_FUNC(pmf, "adjust_ancestors")
82210Sstevel@tonic-gate char *cp;
82220Sstevel@tonic-gate pm_noinvol_t *lp;
82230Sstevel@tonic-gate pm_noinvol_t *pp = NULL;
82247009Scth major_t locked = DDI_MAJOR_T_NONE;
82250Sstevel@tonic-gate dev_info_t *dip;
82260Sstevel@tonic-gate char *pathbuf;
82270Sstevel@tonic-gate size_t pathbuflen = strlen(path) + 1;
82280Sstevel@tonic-gate
82290Sstevel@tonic-gate /*
82300Sstevel@tonic-gate * First we look up the ancestor's dip. If we find it, then we
82310Sstevel@tonic-gate * adjust counts up the tree
82320Sstevel@tonic-gate */
82330Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s wasvolpmd %d\n", pmf, path, wasvolpmd))
82340Sstevel@tonic-gate pathbuf = kmem_alloc(pathbuflen, KM_SLEEP);
82350Sstevel@tonic-gate (void) strcpy(pathbuf, path);
82360Sstevel@tonic-gate cp = strrchr(pathbuf, '/');
82370Sstevel@tonic-gate if (cp == NULL) {
82380Sstevel@tonic-gate /* if no ancestors, then nothing to do */
82390Sstevel@tonic-gate kmem_free(pathbuf, pathbuflen);
82400Sstevel@tonic-gate return;
82410Sstevel@tonic-gate }
82420Sstevel@tonic-gate *cp = '\0';
82430Sstevel@tonic-gate dip = pm_name_to_dip(pathbuf, 1);
82440Sstevel@tonic-gate if (dip != NULL) {
82450Sstevel@tonic-gate locked = PM_MAJOR(dip);
82460Sstevel@tonic-gate
82470Sstevel@tonic-gate (void) pm_noinvol_update(PM_BP_NOINVOL_REMDRV, 0, wasvolpmd,
82480Sstevel@tonic-gate path, dip);
82490Sstevel@tonic-gate
82507009Scth if (locked != DDI_MAJOR_T_NONE)
82510Sstevel@tonic-gate ddi_release_devi(dip);
82520Sstevel@tonic-gate } else {
82530Sstevel@tonic-gate char *apath;
82540Sstevel@tonic-gate size_t len = strlen(pathbuf) + 1;
82550Sstevel@tonic-gate int lock_held = 1;
82560Sstevel@tonic-gate
82570Sstevel@tonic-gate /*
82580Sstevel@tonic-gate * Now check for ancestors that exist only in the list
82590Sstevel@tonic-gate */
82600Sstevel@tonic-gate apath = kmem_alloc(len, KM_SLEEP);
82610Sstevel@tonic-gate (void) strcpy(apath, pathbuf);
82620Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_WRITER);
82630Sstevel@tonic-gate for (lp = pm_noinvol_head; lp; pp = lp, lp = lp->ni_next) {
82640Sstevel@tonic-gate /*
82650Sstevel@tonic-gate * This can only happen once. Since we have to drop
82660Sstevel@tonic-gate * the lock, we need to extract the relevant info.
82670Sstevel@tonic-gate */
82680Sstevel@tonic-gate if (strcmp(pathbuf, lp->ni_path) == 0) {
82690Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s no %d -> %d\n", pmf,
82700Sstevel@tonic-gate lp->ni_path, lp->ni_noinvolpm,
82710Sstevel@tonic-gate lp->ni_noinvolpm - 1))
82720Sstevel@tonic-gate lp->ni_noinvolpm--;
82730Sstevel@tonic-gate if (wasvolpmd && lp->ni_volpmd) {
82740Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s vol %d -> "
82750Sstevel@tonic-gate "%d\n", pmf, lp->ni_path,
82760Sstevel@tonic-gate lp->ni_volpmd, lp->ni_volpmd - 1))
82770Sstevel@tonic-gate lp->ni_volpmd--;
82780Sstevel@tonic-gate }
82790Sstevel@tonic-gate /*
82800Sstevel@tonic-gate * remove the entry from the list, if there
82810Sstevel@tonic-gate * are no more no-invol descendants and node
82820Sstevel@tonic-gate * itself is not a no-invol node.
82830Sstevel@tonic-gate */
82840Sstevel@tonic-gate if (!(lp->ni_noinvolpm ||
82850Sstevel@tonic-gate (lp->ni_flags & PMC_NO_INVOL))) {
82860Sstevel@tonic-gate ASSERT(lp->ni_volpmd == 0);
82870Sstevel@tonic-gate if (pp) {
82880Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: freeing "
82890Sstevel@tonic-gate "%s, prev is %s\n", pmf,
82900Sstevel@tonic-gate lp->ni_path, pp->ni_path))
82910Sstevel@tonic-gate pp->ni_next = lp->ni_next;
82920Sstevel@tonic-gate } else {
82930Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: free %s "
82940Sstevel@tonic-gate "head\n", pmf, lp->ni_path))
82950Sstevel@tonic-gate ASSERT(pm_noinvol_head == lp);
82960Sstevel@tonic-gate pm_noinvol_head = lp->ni_next;
82970Sstevel@tonic-gate }
82980Sstevel@tonic-gate lock_held = 0;
82990Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
83000Sstevel@tonic-gate adjust_ancestors(apath, wasvolpmd);
83010Sstevel@tonic-gate /* restore apath */
83020Sstevel@tonic-gate (void) strcpy(apath, pathbuf);
83030Sstevel@tonic-gate kmem_free(lp->ni_path, lp->ni_size);
83040Sstevel@tonic-gate kmem_free(lp, sizeof (*lp));
83050Sstevel@tonic-gate }
83060Sstevel@tonic-gate break;
83070Sstevel@tonic-gate }
83080Sstevel@tonic-gate }
83090Sstevel@tonic-gate if (lock_held)
83100Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
83110Sstevel@tonic-gate adjust_ancestors(apath, wasvolpmd);
83120Sstevel@tonic-gate kmem_free(apath, len);
83130Sstevel@tonic-gate }
83140Sstevel@tonic-gate kmem_free(pathbuf, pathbuflen);
83150Sstevel@tonic-gate }
83160Sstevel@tonic-gate
83170Sstevel@tonic-gate /*
83180Sstevel@tonic-gate * Do no-invol processing for any ancestors i.e. adjust counters of ancestors,
83190Sstevel@tonic-gate * which were skipped even though their drivers were removed.
83200Sstevel@tonic-gate */
83210Sstevel@tonic-gate static void
pm_noinvol_process_ancestors(char * path)83220Sstevel@tonic-gate pm_noinvol_process_ancestors(char *path)
83230Sstevel@tonic-gate {
83240Sstevel@tonic-gate pm_noinvol_t *lp;
83250Sstevel@tonic-gate
83260Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER);
83270Sstevel@tonic-gate for (lp = pm_noinvol_head; lp; lp = lp->ni_next) {
83280Sstevel@tonic-gate if (strstr(path, lp->ni_path) &&
83290Sstevel@tonic-gate (lp->ni_flags & PMC_DRIVER_REMOVED)) {
83300Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
83310Sstevel@tonic-gate i_pm_driver_removed(lp->ni_major);
83320Sstevel@tonic-gate return;
83330Sstevel@tonic-gate }
83340Sstevel@tonic-gate }
83350Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
83360Sstevel@tonic-gate }
83370Sstevel@tonic-gate
83380Sstevel@tonic-gate /*
83390Sstevel@tonic-gate * Returns true if (detached) device needs to be kept up because it exported the
83400Sstevel@tonic-gate * "no-involuntary-power-cycles" property or we're pretending it did (console
83410Sstevel@tonic-gate * fb case) or it is an ancestor of such a device and has used up the "one
83420Sstevel@tonic-gate * free cycle" allowed when all such leaf nodes have voluntarily powered down
83430Sstevel@tonic-gate * upon detach. In any event, we need an exact hit on the path or we return
83440Sstevel@tonic-gate * false.
83450Sstevel@tonic-gate */
83460Sstevel@tonic-gate int
pm_noinvol_detached(char * path)83470Sstevel@tonic-gate pm_noinvol_detached(char *path)
83480Sstevel@tonic-gate {
83490Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_detached")
83500Sstevel@tonic-gate pm_noinvol_t *ip;
83510Sstevel@tonic-gate int ret = 0;
83520Sstevel@tonic-gate
83530Sstevel@tonic-gate rw_enter(&pm_noinvol_rwlock, RW_READER);
83540Sstevel@tonic-gate for (ip = pm_noinvol_head; ip; ip = ip->ni_next) {
83550Sstevel@tonic-gate if (strcmp(path, ip->ni_path) == 0) {
83560Sstevel@tonic-gate if (ip->ni_flags & PMC_CONSOLE_FB) {
83570Sstevel@tonic-gate PMD(PMD_NOINVOL | PMD_CFB, ("%s: inhibits CFB "
83580Sstevel@tonic-gate "%s\n", pmf, path))
83590Sstevel@tonic-gate ret = 1;
83600Sstevel@tonic-gate break;
83610Sstevel@tonic-gate }
83620Sstevel@tonic-gate #ifdef DEBUG
83630Sstevel@tonic-gate if (ip->ni_noinvolpm != ip->ni_volpmd)
83640Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: (%d != %d) inhibits %s"
83650Sstevel@tonic-gate "\n", pmf, ip->ni_noinvolpm, ip->ni_volpmd,
83660Sstevel@tonic-gate path))
83670Sstevel@tonic-gate #endif
83680Sstevel@tonic-gate ret = (ip->ni_noinvolpm != ip->ni_volpmd);
83690Sstevel@tonic-gate break;
83700Sstevel@tonic-gate }
83710Sstevel@tonic-gate }
83720Sstevel@tonic-gate rw_exit(&pm_noinvol_rwlock);
83730Sstevel@tonic-gate return (ret);
83740Sstevel@tonic-gate }
83750Sstevel@tonic-gate
83760Sstevel@tonic-gate int
pm_is_cfb(dev_info_t * dip)83770Sstevel@tonic-gate pm_is_cfb(dev_info_t *dip)
83780Sstevel@tonic-gate {
83790Sstevel@tonic-gate return (dip == cfb_dip);
83800Sstevel@tonic-gate }
83810Sstevel@tonic-gate
83820Sstevel@tonic-gate #ifdef DEBUG
83830Sstevel@tonic-gate /*
83840Sstevel@tonic-gate * Return true if all components of the console frame buffer are at
83850Sstevel@tonic-gate * "normal" power, i.e., fully on. For the case where the console is not
83860Sstevel@tonic-gate * a framebuffer, we also return true
83870Sstevel@tonic-gate */
83880Sstevel@tonic-gate int
pm_cfb_is_up(void)83890Sstevel@tonic-gate pm_cfb_is_up(void)
83900Sstevel@tonic-gate {
83910Sstevel@tonic-gate return (pm_cfb_comps_off == 0);
83920Sstevel@tonic-gate }
83930Sstevel@tonic-gate #endif
83940Sstevel@tonic-gate
83950Sstevel@tonic-gate /*
83960Sstevel@tonic-gate * Preventing scan from powering down the node by incrementing the
83970Sstevel@tonic-gate * kidsupcnt.
83980Sstevel@tonic-gate */
83990Sstevel@tonic-gate void
pm_hold_power(dev_info_t * dip)84000Sstevel@tonic-gate pm_hold_power(dev_info_t *dip)
84010Sstevel@tonic-gate {
84020Sstevel@tonic-gate e_pm_hold_rele_power(dip, 1);
84030Sstevel@tonic-gate }
84040Sstevel@tonic-gate
84050Sstevel@tonic-gate /*
84060Sstevel@tonic-gate * Releasing the hold by decrementing the kidsupcnt allowing scan
84070Sstevel@tonic-gate * to power down the node if all conditions are met.
84080Sstevel@tonic-gate */
84090Sstevel@tonic-gate void
pm_rele_power(dev_info_t * dip)84100Sstevel@tonic-gate pm_rele_power(dev_info_t *dip)
84110Sstevel@tonic-gate {
84120Sstevel@tonic-gate e_pm_hold_rele_power(dip, -1);
84130Sstevel@tonic-gate }
84140Sstevel@tonic-gate
84150Sstevel@tonic-gate /*
84160Sstevel@tonic-gate * A wrapper of pm_all_to_normal() to power up a dip
84170Sstevel@tonic-gate * to its normal level
84180Sstevel@tonic-gate */
84190Sstevel@tonic-gate int
pm_powerup(dev_info_t * dip)84200Sstevel@tonic-gate pm_powerup(dev_info_t *dip)
84210Sstevel@tonic-gate {
84220Sstevel@tonic-gate PMD_FUNC(pmf, "pm_powerup")
84230Sstevel@tonic-gate
84240Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
84250Sstevel@tonic-gate ASSERT(!(servicing_interrupt()));
84260Sstevel@tonic-gate
84270Sstevel@tonic-gate /*
84280Sstevel@tonic-gate * in case this node is not already participating pm
84290Sstevel@tonic-gate */
84300Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip)) {
84310Sstevel@tonic-gate if (!DEVI_IS_ATTACHING(dip))
84320Sstevel@tonic-gate return (DDI_SUCCESS);
84330Sstevel@tonic-gate if (pm_start(dip) != DDI_SUCCESS)
84340Sstevel@tonic-gate return (DDI_FAILURE);
84350Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip))
84360Sstevel@tonic-gate return (DDI_SUCCESS);
84370Sstevel@tonic-gate }
84380Sstevel@tonic-gate
84390Sstevel@tonic-gate return (pm_all_to_normal(dip, PM_CANBLOCK_BLOCK));
84400Sstevel@tonic-gate }
84410Sstevel@tonic-gate
84420Sstevel@tonic-gate int
pm_rescan_walk(dev_info_t * dip,void * arg)84430Sstevel@tonic-gate pm_rescan_walk(dev_info_t *dip, void *arg)
84440Sstevel@tonic-gate {
84450Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
84460Sstevel@tonic-gate
84470Sstevel@tonic-gate if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
84480Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
84490Sstevel@tonic-gate
84500Sstevel@tonic-gate /*
84510Sstevel@tonic-gate * Currently pm_cpr_callb/resume code is the only caller
84520Sstevel@tonic-gate * and it needs to make sure that stopped scan get
84530Sstevel@tonic-gate * reactivated. Otherwise, rescan walk needn't reactive
84540Sstevel@tonic-gate * stopped scan.
84550Sstevel@tonic-gate */
84560Sstevel@tonic-gate pm_scan_init(dip);
84570Sstevel@tonic-gate
84580Sstevel@tonic-gate (void) pm_rescan(dip);
84590Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
84600Sstevel@tonic-gate }
84610Sstevel@tonic-gate
84620Sstevel@tonic-gate static dev_info_t *
pm_get_next_descendent(dev_info_t * dip,dev_info_t * tdip)84630Sstevel@tonic-gate pm_get_next_descendent(dev_info_t *dip, dev_info_t *tdip)
84640Sstevel@tonic-gate {
84650Sstevel@tonic-gate dev_info_t *wdip, *pdip;
84660Sstevel@tonic-gate
84670Sstevel@tonic-gate for (wdip = tdip; wdip != dip; wdip = pdip) {
84680Sstevel@tonic-gate pdip = ddi_get_parent(wdip);
84690Sstevel@tonic-gate if (pdip == dip)
84700Sstevel@tonic-gate return (wdip);
84710Sstevel@tonic-gate }
84720Sstevel@tonic-gate return (NULL);
84730Sstevel@tonic-gate }
84740Sstevel@tonic-gate
84750Sstevel@tonic-gate int
pm_busop_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)84760Sstevel@tonic-gate pm_busop_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
84770Sstevel@tonic-gate void *arg, void *result)
84780Sstevel@tonic-gate {
84790Sstevel@tonic-gate PMD_FUNC(pmf, "bp_bus_power")
84800Sstevel@tonic-gate dev_info_t *cdip;
84810Sstevel@tonic-gate pm_info_t *cinfo;
84820Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc;
84830Sstevel@tonic-gate pm_sp_misc_t *pspm;
84840Sstevel@tonic-gate pm_bp_nexus_pwrup_t *bpn;
84850Sstevel@tonic-gate pm_bp_child_pwrchg_t new_bpc;
84860Sstevel@tonic-gate pm_bp_noinvol_t *bpi;
84870Sstevel@tonic-gate dev_info_t *tdip;
84880Sstevel@tonic-gate char *pathbuf;
84890Sstevel@tonic-gate int ret = DDI_SUCCESS;
84900Sstevel@tonic-gate int errno = 0;
84910Sstevel@tonic-gate pm_component_t *cp;
84920Sstevel@tonic-gate
84930Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip),
84940Sstevel@tonic-gate pm_decode_op(op)))
84950Sstevel@tonic-gate switch (op) {
84960Sstevel@tonic-gate case BUS_POWER_CHILD_PWRCHG:
84970Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg;
84980Sstevel@tonic-gate pspm = (pm_sp_misc_t *)bpc->bpc_private;
84990Sstevel@tonic-gate tdip = bpc->bpc_dip;
85000Sstevel@tonic-gate cdip = pm_get_next_descendent(dip, tdip);
85010Sstevel@tonic-gate cinfo = PM_GET_PM_INFO(cdip);
85020Sstevel@tonic-gate if (cdip != tdip) {
85030Sstevel@tonic-gate /*
85040Sstevel@tonic-gate * If the node is an involved parent, it needs to
85050Sstevel@tonic-gate * power up the node as it is needed. There is nothing
85060Sstevel@tonic-gate * else the framework can do here.
85070Sstevel@tonic-gate */
85080Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(cdip)) {
85090Sstevel@tonic-gate PMD(PMD_SET, ("%s: call bus_power for "
85100Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(cdip)))
85110Sstevel@tonic-gate return ((*PM_BUS_POWER_FUNC(cdip))(cdip,
85120Sstevel@tonic-gate impl_arg, op, arg, result));
85130Sstevel@tonic-gate }
85140Sstevel@tonic-gate ASSERT(pspm->pspm_direction == PM_LEVEL_UPONLY ||
85150Sstevel@tonic-gate pspm->pspm_direction == PM_LEVEL_DOWNONLY ||
85160Sstevel@tonic-gate pspm->pspm_direction == PM_LEVEL_EXACT);
85170Sstevel@tonic-gate /*
85180Sstevel@tonic-gate * we presume that the parent needs to be up in
85190Sstevel@tonic-gate * order for the child to change state (either
85200Sstevel@tonic-gate * because it must already be on if the child is on
85210Sstevel@tonic-gate * (and the pm_all_to_normal_nexus() will be a nop)
85220Sstevel@tonic-gate * or because it will need to be on for the child
85230Sstevel@tonic-gate * to come on; so we make the call regardless
85240Sstevel@tonic-gate */
85250Sstevel@tonic-gate pm_hold_power(cdip);
85260Sstevel@tonic-gate if (cinfo) {
85270Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock;
85280Sstevel@tonic-gate ret = pm_all_to_normal_nexus(cdip, canblock);
85290Sstevel@tonic-gate if (ret != DDI_SUCCESS) {
85300Sstevel@tonic-gate pm_rele_power(cdip);
85310Sstevel@tonic-gate return (ret);
85320Sstevel@tonic-gate }
85330Sstevel@tonic-gate }
85340Sstevel@tonic-gate PMD(PMD_SET, ("%s: walk down to %s@%s(%s#%d)\n", pmf,
85350Sstevel@tonic-gate PM_DEVICE(cdip)))
85360Sstevel@tonic-gate ret = pm_busop_bus_power(cdip, impl_arg, op, arg,
85370Sstevel@tonic-gate result);
85380Sstevel@tonic-gate pm_rele_power(cdip);
85390Sstevel@tonic-gate } else {
85400Sstevel@tonic-gate ret = pm_busop_set_power(cdip, impl_arg, op, arg,
85410Sstevel@tonic-gate result);
85420Sstevel@tonic-gate }
85430Sstevel@tonic-gate return (ret);
85440Sstevel@tonic-gate
85450Sstevel@tonic-gate case BUS_POWER_NEXUS_PWRUP:
85460Sstevel@tonic-gate bpn = (pm_bp_nexus_pwrup_t *)arg;
85470Sstevel@tonic-gate pspm = (pm_sp_misc_t *)bpn->bpn_private;
85480Sstevel@tonic-gate
85490Sstevel@tonic-gate if (!e_pm_valid_info(dip, NULL) ||
85500Sstevel@tonic-gate !e_pm_valid_comp(dip, bpn->bpn_comp, &cp) ||
85510Sstevel@tonic-gate !e_pm_valid_power(dip, bpn->bpn_comp, bpn->bpn_level)) {
85520Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) has no pm info; EIO\n",
85530Sstevel@tonic-gate pmf, PM_DEVICE(dip)))
85540Sstevel@tonic-gate *pspm->pspm_errnop = EIO;
85550Sstevel@tonic-gate *(int *)result = DDI_FAILURE;
85560Sstevel@tonic-gate return (DDI_FAILURE);
85570Sstevel@tonic-gate }
85580Sstevel@tonic-gate
85590Sstevel@tonic-gate ASSERT(bpn->bpn_dip == dip);
85600Sstevel@tonic-gate PMD(PMD_SET, ("%s: nexus powerup for %s@%s(%s#%d)\n", pmf,
85610Sstevel@tonic-gate PM_DEVICE(dip)))
85620Sstevel@tonic-gate new_bpc.bpc_dip = dip;
85630Sstevel@tonic-gate pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
85640Sstevel@tonic-gate new_bpc.bpc_path = ddi_pathname(dip, pathbuf);
85650Sstevel@tonic-gate new_bpc.bpc_comp = bpn->bpn_comp;
85660Sstevel@tonic-gate new_bpc.bpc_olevel = PM_CURPOWER(dip, bpn->bpn_comp);
85670Sstevel@tonic-gate new_bpc.bpc_nlevel = bpn->bpn_level;
85680Sstevel@tonic-gate new_bpc.bpc_private = bpn->bpn_private;
85690Sstevel@tonic-gate ((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_direction =
85700Sstevel@tonic-gate PM_LEVEL_UPONLY;
85710Sstevel@tonic-gate ((pm_sp_misc_t *)(new_bpc.bpc_private))->pspm_errnop =
85720Sstevel@tonic-gate &errno;
85730Sstevel@tonic-gate ret = pm_busop_set_power(dip, impl_arg, BUS_POWER_CHILD_PWRCHG,
85740Sstevel@tonic-gate (void *)&new_bpc, result);
85750Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
85760Sstevel@tonic-gate return (ret);
85770Sstevel@tonic-gate
85780Sstevel@tonic-gate case BUS_POWER_NOINVOL:
85790Sstevel@tonic-gate bpi = (pm_bp_noinvol_t *)arg;
85800Sstevel@tonic-gate tdip = bpi->bpni_dip;
85810Sstevel@tonic-gate cdip = pm_get_next_descendent(dip, tdip);
85820Sstevel@tonic-gate
85830Sstevel@tonic-gate /* In case of rem_drv, the leaf node has been removed */
85840Sstevel@tonic-gate if (cdip == NULL)
85850Sstevel@tonic-gate return (DDI_SUCCESS);
85860Sstevel@tonic-gate
85870Sstevel@tonic-gate cinfo = PM_GET_PM_INFO(cdip);
85880Sstevel@tonic-gate if (cdip != tdip) {
85890Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(cdip)) {
85900Sstevel@tonic-gate PMD(PMD_NOINVOL,
85910Sstevel@tonic-gate ("%s: call bus_power for %s@%s(%s#%d)\n",
85920Sstevel@tonic-gate pmf, PM_DEVICE(cdip)))
85930Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(cdip))
85940Sstevel@tonic-gate (cdip, NULL, op, arg, result);
85950Sstevel@tonic-gate if ((cinfo) && (ret == DDI_SUCCESS))
85960Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip,
85970Sstevel@tonic-gate bpi);
85980Sstevel@tonic-gate return (ret);
85990Sstevel@tonic-gate } else {
86000Sstevel@tonic-gate PMD(PMD_NOINVOL,
86010Sstevel@tonic-gate ("%s: walk down to %s@%s(%s#%d)\n", pmf,
86020Sstevel@tonic-gate PM_DEVICE(cdip)))
86030Sstevel@tonic-gate ret = pm_busop_bus_power(cdip, NULL, op,
86040Sstevel@tonic-gate arg, result);
86050Sstevel@tonic-gate /*
86060Sstevel@tonic-gate * Update the current node.
86070Sstevel@tonic-gate */
86080Sstevel@tonic-gate if ((cinfo) && (ret == DDI_SUCCESS))
86090Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip,
86100Sstevel@tonic-gate bpi);
86110Sstevel@tonic-gate return (ret);
86120Sstevel@tonic-gate }
86130Sstevel@tonic-gate } else {
86140Sstevel@tonic-gate /*
86150Sstevel@tonic-gate * For attach, detach, power up:
86160Sstevel@tonic-gate * Do nothing for leaf node since its
86170Sstevel@tonic-gate * counts are already updated.
86180Sstevel@tonic-gate * For CFB and driver removal, since the
86190Sstevel@tonic-gate * path and the target dip passed in is up to and incl.
86200Sstevel@tonic-gate * the immediate ancestor, need to do the update.
86210Sstevel@tonic-gate */
86220Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: target %s@%s(%s#%d) is "
86230Sstevel@tonic-gate "reached\n", pmf, PM_DEVICE(cdip)))
86240Sstevel@tonic-gate if (cinfo && ((bpi->bpni_cmd == PM_BP_NOINVOL_REMDRV) ||
86250Sstevel@tonic-gate (bpi->bpni_cmd == PM_BP_NOINVOL_CFB)))
86260Sstevel@tonic-gate (void) pm_noinvol_update_node(cdip, bpi);
86270Sstevel@tonic-gate return (DDI_SUCCESS);
86280Sstevel@tonic-gate }
86290Sstevel@tonic-gate
86300Sstevel@tonic-gate default:
86310Sstevel@tonic-gate PMD(PMD_SET, ("%s: operation %d is not supported!\n", pmf, op))
86320Sstevel@tonic-gate return (DDI_FAILURE);
86330Sstevel@tonic-gate }
86340Sstevel@tonic-gate }
86350Sstevel@tonic-gate
86360Sstevel@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)86370Sstevel@tonic-gate pm_busop_set_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
86380Sstevel@tonic-gate void *arg, void *resultp)
86390Sstevel@tonic-gate {
86400Sstevel@tonic-gate _NOTE(ARGUNUSED(impl_arg))
86410Sstevel@tonic-gate PMD_FUNC(pmf, "bp_set_power")
86425020Smh27603 pm_ppm_devlist_t *devl = NULL;
86430Sstevel@tonic-gate int clevel, circ;
86440Sstevel@tonic-gate #ifdef DEBUG
86450Sstevel@tonic-gate int circ_db, ccirc_db;
86460Sstevel@tonic-gate #endif
86470Sstevel@tonic-gate int ret = DDI_SUCCESS;
86480Sstevel@tonic-gate dev_info_t *cdip;
86490Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
86500Sstevel@tonic-gate pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private;
86510Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock;
86520Sstevel@tonic-gate int scan = pspm->pspm_scan;
86530Sstevel@tonic-gate int comp = bpc->bpc_comp;
86540Sstevel@tonic-gate int olevel = bpc->bpc_olevel;
86550Sstevel@tonic-gate int nlevel = bpc->bpc_nlevel;
86560Sstevel@tonic-gate int comps_off_incr = 0;
86570Sstevel@tonic-gate dev_info_t *pdip = ddi_get_parent(dip);
86580Sstevel@tonic-gate int dodeps;
86590Sstevel@tonic-gate int direction = pspm->pspm_direction;
86600Sstevel@tonic-gate int *errnop = pspm->pspm_errnop;
86613839Skchow #ifdef PMDDEBUG
86620Sstevel@tonic-gate char *dir = pm_decode_direction(direction);
86633839Skchow #endif
86640Sstevel@tonic-gate int *iresp = (int *)resultp;
86650Sstevel@tonic-gate time_t idletime, thresh;
86660Sstevel@tonic-gate pm_component_t *cp = PM_CP(dip, comp);
86670Sstevel@tonic-gate int work_type;
86680Sstevel@tonic-gate
86690Sstevel@tonic-gate *iresp = DDI_SUCCESS;
86700Sstevel@tonic-gate *errnop = 0;
86710Sstevel@tonic-gate ASSERT(op == BUS_POWER_CHILD_PWRCHG);
86720Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) %s\n", pmf, PM_DEVICE(dip),
86730Sstevel@tonic-gate pm_decode_op(op)))
86740Sstevel@tonic-gate
86750Sstevel@tonic-gate /*
86760Sstevel@tonic-gate * The following set of conditions indicate we are here to handle a
86770Sstevel@tonic-gate * driver's pm_[raise|lower]_power request, but the device is being
86780Sstevel@tonic-gate * power managed (PM_DIRECT_PM) by a user process. For that case
86790Sstevel@tonic-gate * we want to pm_block and pass a status back to the caller based
86800Sstevel@tonic-gate * on whether the controlling process's next activity on the device
86810Sstevel@tonic-gate * matches the current request or not. This distinction tells
86820Sstevel@tonic-gate * downstream functions to avoid calling into a driver or changing
86830Sstevel@tonic-gate * the framework's power state. To actually block, we need:
86840Sstevel@tonic-gate *
86850Sstevel@tonic-gate * PM_ISDIRECT(dip)
86860Sstevel@tonic-gate * no reason to block unless a process is directly controlling dev
86870Sstevel@tonic-gate * direction != PM_LEVEL_EXACT
86880Sstevel@tonic-gate * EXACT is used by controlling proc's PM_SET_CURRENT_POWER ioctl
86890Sstevel@tonic-gate * !pm_processes_stopped
86900Sstevel@tonic-gate * don't block if controlling proc already be stopped for cpr
86910Sstevel@tonic-gate * canblock != PM_CANBLOCK_BYPASS
86920Sstevel@tonic-gate * our caller must not have explicitly prevented blocking
86930Sstevel@tonic-gate */
86940Sstevel@tonic-gate if (direction != PM_LEVEL_EXACT && canblock != PM_CANBLOCK_BYPASS) {
86950Sstevel@tonic-gate PM_LOCK_DIP(dip);
86960Sstevel@tonic-gate while (PM_ISDIRECT(dip) && !pm_processes_stopped) {
86970Sstevel@tonic-gate /* releases dip lock */
86980Sstevel@tonic-gate ret = pm_busop_match_request(dip, bpc);
86990Sstevel@tonic-gate if (ret == EAGAIN) {
87000Sstevel@tonic-gate PM_LOCK_DIP(dip);
87010Sstevel@tonic-gate continue;
87020Sstevel@tonic-gate }
87030Sstevel@tonic-gate return (*iresp = ret);
87040Sstevel@tonic-gate }
87050Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
87060Sstevel@tonic-gate }
87070Sstevel@tonic-gate /* BC device is never scanned, so power will stick until we are done */
87080Sstevel@tonic-gate if (PM_ISBC(dip) && comp != 0 && nlevel != 0 &&
87090Sstevel@tonic-gate direction != PM_LEVEL_DOWNONLY) {
87100Sstevel@tonic-gate int nrmpwr0 = pm_get_normal_power(dip, 0);
87110Sstevel@tonic-gate if (pm_set_power(dip, 0, nrmpwr0, direction,
87120Sstevel@tonic-gate canblock, 0, resultp) != DDI_SUCCESS) {
87130Sstevel@tonic-gate /* *resultp set by pm_set_power */
87140Sstevel@tonic-gate return (DDI_FAILURE);
87150Sstevel@tonic-gate }
87160Sstevel@tonic-gate }
87170Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(pdip)) {
87180Sstevel@tonic-gate PMD(PMD_SET, ("%s: pre_notify %s@%s(%s#%d) for child "
87190Sstevel@tonic-gate "%s@%s(%s#%d)\n", pmf, PM_DEVICE(pdip), PM_DEVICE(dip)))
87200Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
87210Sstevel@tonic-gate BUS_POWER_PRE_NOTIFICATION, bpc, resultp);
87220Sstevel@tonic-gate if (ret != DDI_SUCCESS) {
87230Sstevel@tonic-gate PMD(PMD_SET, ("%s: failed to pre_notify %s@%s(%s#%d)\n",
87240Sstevel@tonic-gate pmf, PM_DEVICE(pdip)))
87250Sstevel@tonic-gate return (DDI_FAILURE);
87260Sstevel@tonic-gate }
87270Sstevel@tonic-gate } else {
87280Sstevel@tonic-gate /*
87290Sstevel@tonic-gate * Since we don't know what the actual power level is,
87300Sstevel@tonic-gate * we place a power hold on the parent no matter what
87310Sstevel@tonic-gate * component and level is changing.
87320Sstevel@tonic-gate */
87330Sstevel@tonic-gate pm_hold_power(pdip);
87340Sstevel@tonic-gate }
87350Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
87364780Smh27603 clevel = PM_CURPOWER(dip, comp);
87374667Smh27603 /*
87384667Smh27603 * It's possible that a call was made to pm_update_maxpower()
87394667Smh27603 * on another thread before we took the lock above. So, we need to
87404667Smh27603 * make sure that this request isn't processed after the
87414667Smh27603 * change of power executed on behalf of pm_update_maxpower().
87424667Smh27603 */
87434667Smh27603 if (nlevel > pm_get_normal_power(dip, comp)) {
87444667Smh27603 PMD(PMD_SET, ("%s: requested level is higher than normal.\n",
87454667Smh27603 pmf))
87464667Smh27603 ret = DDI_FAILURE;
87474667Smh27603 *iresp = DDI_FAILURE;
87484667Smh27603 goto post_notify;
87494667Smh27603 }
87500Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, olvl=%d, nlvl=%d, clvl=%d, "
87510Sstevel@tonic-gate "dir=%s\n", pmf, PM_DEVICE(dip), comp, bpc->bpc_olevel, nlevel,
87520Sstevel@tonic-gate clevel, dir))
87530Sstevel@tonic-gate switch (direction) {
87540Sstevel@tonic-gate case PM_LEVEL_UPONLY:
87550Sstevel@tonic-gate /* Powering up */
87560Sstevel@tonic-gate if (clevel >= nlevel) {
87570Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already "
87580Sstevel@tonic-gate "at or above the requested level.\n", pmf))
87590Sstevel@tonic-gate *iresp = DDI_SUCCESS;
87600Sstevel@tonic-gate ret = DDI_SUCCESS;
87610Sstevel@tonic-gate goto post_notify;
87620Sstevel@tonic-gate }
87630Sstevel@tonic-gate break;
87640Sstevel@tonic-gate case PM_LEVEL_EXACT:
87650Sstevel@tonic-gate /* specific level request */
87660Sstevel@tonic-gate if (clevel == nlevel && !PM_ISBC(dip)) {
87670Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already "
87680Sstevel@tonic-gate "at the requested level.\n", pmf))
87690Sstevel@tonic-gate *iresp = DDI_SUCCESS;
87700Sstevel@tonic-gate ret = DDI_SUCCESS;
87710Sstevel@tonic-gate goto post_notify;
87720Sstevel@tonic-gate } else if (PM_IS_CFB(dip) && (nlevel < clevel)) {
87730Sstevel@tonic-gate PMD(PMD_CFB, ("%s: powerdown of console\n", pmf))
87740Sstevel@tonic-gate if (!pm_cfb_enabled) {
87750Sstevel@tonic-gate PMD(PMD_ERROR | PMD_CFB,
87760Sstevel@tonic-gate ("%s: !pm_cfb_enabled, fails\n", pmf))
87770Sstevel@tonic-gate *errnop = EINVAL;
87780Sstevel@tonic-gate *iresp = DDI_FAILURE;
87790Sstevel@tonic-gate ret = DDI_FAILURE;
87800Sstevel@tonic-gate goto post_notify;
87810Sstevel@tonic-gate }
87820Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
87830Sstevel@tonic-gate while (cfb_inuse) {
87840Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
87850Sstevel@tonic-gate if (delay_sig(1) == EINTR) {
87860Sstevel@tonic-gate ret = DDI_FAILURE;
87870Sstevel@tonic-gate *iresp = DDI_FAILURE;
87880Sstevel@tonic-gate *errnop = EINTR;
87890Sstevel@tonic-gate goto post_notify;
87900Sstevel@tonic-gate }
87910Sstevel@tonic-gate mutex_enter(&pm_cfb_lock);
87920Sstevel@tonic-gate }
87930Sstevel@tonic-gate mutex_exit(&pm_cfb_lock);
87940Sstevel@tonic-gate }
87950Sstevel@tonic-gate break;
87960Sstevel@tonic-gate case PM_LEVEL_DOWNONLY:
87970Sstevel@tonic-gate /* Powering down */
87980Sstevel@tonic-gate thresh = cur_threshold(dip, comp);
87990Sstevel@tonic-gate idletime = gethrestime_sec() - cp->pmc_timestamp;
88000Sstevel@tonic-gate if (scan && ((PM_KUC(dip) != 0) ||
88011316Sosaeed (cp->pmc_busycount > 0) ||
88021316Sosaeed ((idletime < thresh) && !PM_IS_PID(dip)))) {
88030Sstevel@tonic-gate #ifdef DEBUG
88040Sstevel@tonic-gate if (DEVI(dip)->devi_pm_kidsupcnt != 0)
88050Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: "
88060Sstevel@tonic-gate "kidsupcnt != 0\n", pmf))
88070Sstevel@tonic-gate if (cp->pmc_busycount > 0)
88080Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: "
88090Sstevel@tonic-gate "device become busy\n", pmf))
88100Sstevel@tonic-gate if (idletime < thresh)
88110Sstevel@tonic-gate PMD(PMD_SET, ("%s: scan failed: device "
88120Sstevel@tonic-gate "hasn't been idle long enough\n", pmf))
88130Sstevel@tonic-gate #endif
88140Sstevel@tonic-gate *iresp = DDI_FAILURE;
88150Sstevel@tonic-gate *errnop = EBUSY;
88160Sstevel@tonic-gate ret = DDI_FAILURE;
88170Sstevel@tonic-gate goto post_notify;
88180Sstevel@tonic-gate } else if (clevel != PM_LEVEL_UNKNOWN && clevel <= nlevel) {
88190Sstevel@tonic-gate PMD(PMD_SET, ("%s: current level is already at "
88200Sstevel@tonic-gate "or below the requested level.\n", pmf))
88210Sstevel@tonic-gate *iresp = DDI_SUCCESS;
88220Sstevel@tonic-gate ret = DDI_SUCCESS;
88230Sstevel@tonic-gate goto post_notify;
88240Sstevel@tonic-gate }
88250Sstevel@tonic-gate break;
88260Sstevel@tonic-gate }
88270Sstevel@tonic-gate
88280Sstevel@tonic-gate if (PM_IS_CFB(dip) && (comps_off_incr =
88290Sstevel@tonic-gate calc_cfb_comps_incr(dip, comp, clevel, nlevel)) > 0) {
88300Sstevel@tonic-gate /*
88310Sstevel@tonic-gate * Pre-adjust pm_cfb_comps_off if lowering a console fb
88320Sstevel@tonic-gate * component from full power. Remember that we tried to
88330Sstevel@tonic-gate * lower power in case it fails and we need to back out
88340Sstevel@tonic-gate * the adjustment.
88350Sstevel@tonic-gate */
88360Sstevel@tonic-gate update_comps_off(comps_off_incr, dip);
88370Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d cfb_comps_off->%d\n",
88380Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, clevel, nlevel,
88390Sstevel@tonic-gate pm_cfb_comps_off))
88400Sstevel@tonic-gate }
88410Sstevel@tonic-gate
88420Sstevel@tonic-gate if ((*iresp = power_dev(dip,
88430Sstevel@tonic-gate comp, nlevel, clevel, canblock, &devl)) == DDI_SUCCESS) {
88440Sstevel@tonic-gate #ifdef DEBUG
88450Sstevel@tonic-gate /*
88460Sstevel@tonic-gate * All descendents of this node should already be powered off.
88470Sstevel@tonic-gate */
88480Sstevel@tonic-gate if (PM_CURPOWER(dip, comp) == 0) {
88490Sstevel@tonic-gate pm_desc_pwrchk_t pdpchk;
88500Sstevel@tonic-gate pdpchk.pdpc_dip = dip;
88510Sstevel@tonic-gate pdpchk.pdpc_par_involved = PM_WANTS_NOTIFICATION(dip);
88520Sstevel@tonic-gate ndi_devi_enter(dip, &circ_db);
88530Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip != NULL;
88540Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) {
88550Sstevel@tonic-gate ndi_devi_enter(cdip, &ccirc_db);
88560Sstevel@tonic-gate ddi_walk_devs(cdip, pm_desc_pwrchk_walk,
88570Sstevel@tonic-gate (void *)&pdpchk);
88580Sstevel@tonic-gate ndi_devi_exit(cdip, ccirc_db);
88590Sstevel@tonic-gate }
88600Sstevel@tonic-gate ndi_devi_exit(dip, circ_db);
88610Sstevel@tonic-gate }
88620Sstevel@tonic-gate #endif
88630Sstevel@tonic-gate /*
88640Sstevel@tonic-gate * Post-adjust pm_cfb_comps_off if we brought an fb component
88650Sstevel@tonic-gate * back up to full power.
88660Sstevel@tonic-gate */
88670Sstevel@tonic-gate if (PM_IS_CFB(dip) && comps_off_incr < 0) {
88680Sstevel@tonic-gate update_comps_off(comps_off_incr, dip);
88690Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d "
88700Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
88710Sstevel@tonic-gate comp, clevel, nlevel, pm_cfb_comps_off))
88720Sstevel@tonic-gate }
88730Sstevel@tonic-gate dodeps = 0;
88740Sstevel@tonic-gate if (POWERING_OFF(clevel, nlevel)) {
88750Sstevel@tonic-gate if (PM_ISBC(dip)) {
88760Sstevel@tonic-gate dodeps = (comp == 0);
88770Sstevel@tonic-gate } else {
88780Sstevel@tonic-gate int i;
88790Sstevel@tonic-gate dodeps = 1;
88800Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
88810Sstevel@tonic-gate /* if some component still on */
88820Sstevel@tonic-gate if (PM_CURPOWER(dip, i)) {
88830Sstevel@tonic-gate dodeps = 0;
88840Sstevel@tonic-gate break;
88850Sstevel@tonic-gate }
88860Sstevel@tonic-gate }
88870Sstevel@tonic-gate }
88880Sstevel@tonic-gate if (dodeps)
88890Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_OFF;
88900Sstevel@tonic-gate } else if (POWERING_ON(clevel, nlevel)) {
88910Sstevel@tonic-gate if (PM_ISBC(dip)) {
88920Sstevel@tonic-gate dodeps = (comp == 0);
88930Sstevel@tonic-gate } else {
88940Sstevel@tonic-gate int i;
88950Sstevel@tonic-gate dodeps = 1;
88960Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
88970Sstevel@tonic-gate if (i == comp)
88980Sstevel@tonic-gate continue;
88990Sstevel@tonic-gate if (PM_CURPOWER(dip, i) > 0) {
89000Sstevel@tonic-gate dodeps = 0;
89010Sstevel@tonic-gate break;
89020Sstevel@tonic-gate }
89030Sstevel@tonic-gate }
89040Sstevel@tonic-gate }
89050Sstevel@tonic-gate if (dodeps)
89060Sstevel@tonic-gate work_type = PM_DEP_WK_POWER_ON;
89070Sstevel@tonic-gate }
89080Sstevel@tonic-gate
89090Sstevel@tonic-gate if (dodeps) {
89100Sstevel@tonic-gate char *pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
89110Sstevel@tonic-gate
89120Sstevel@tonic-gate (void) ddi_pathname(dip, pathbuf);
89130Sstevel@tonic-gate pm_dispatch_to_dep_thread(work_type, pathbuf, NULL,
89140Sstevel@tonic-gate PM_DEP_NOWAIT, NULL, 0);
89150Sstevel@tonic-gate kmem_free(pathbuf, MAXPATHLEN);
89160Sstevel@tonic-gate }
89170Sstevel@tonic-gate if ((PM_CURPOWER(dip, comp) == nlevel) && pm_watchers()) {
89180Sstevel@tonic-gate int old;
89190Sstevel@tonic-gate
89200Sstevel@tonic-gate /* If old power cached during deadlock, use it. */
89210Sstevel@tonic-gate old = (cp->pmc_flags & PM_PHC_WHILE_SET_POWER ?
89220Sstevel@tonic-gate cp->pmc_phc_pwr : olevel);
89230Sstevel@tonic-gate mutex_enter(&pm_rsvp_lock);
89240Sstevel@tonic-gate pm_enqueue_notify(PSC_HAS_CHANGED, dip, comp, nlevel,
89250Sstevel@tonic-gate old, canblock);
89260Sstevel@tonic-gate pm_enqueue_notify_others(&devl, canblock);
89270Sstevel@tonic-gate mutex_exit(&pm_rsvp_lock);
89285020Smh27603 } else {
89295020Smh27603 pm_ppm_devlist_t *p;
89305020Smh27603 pm_ppm_devlist_t *next;
89315020Smh27603 for (p = devl; p != NULL; p = next) {
89325020Smh27603 next = p->ppd_next;
89335020Smh27603 kmem_free(p, sizeof (pm_ppm_devlist_t));
89345020Smh27603 }
89355020Smh27603 devl = NULL;
89360Sstevel@tonic-gate }
89370Sstevel@tonic-gate
89380Sstevel@tonic-gate /*
89390Sstevel@tonic-gate * If we are coming from a scan, don't do it again,
89400Sstevel@tonic-gate * else we can have infinite loops.
89410Sstevel@tonic-gate */
89420Sstevel@tonic-gate if (!scan)
89430Sstevel@tonic-gate pm_rescan(dip);
89440Sstevel@tonic-gate } else {
89450Sstevel@tonic-gate /* if we incremented pm_comps_off_count, but failed */
89460Sstevel@tonic-gate if (comps_off_incr > 0) {
89470Sstevel@tonic-gate update_comps_off(-comps_off_incr, dip);
89480Sstevel@tonic-gate PMD(PMD_CFB, ("%s: %s@%s(%s#%d)[%d] %d->%d "
89490Sstevel@tonic-gate "cfb_comps_off->%d\n", pmf, PM_DEVICE(dip),
89500Sstevel@tonic-gate comp, clevel, nlevel, pm_cfb_comps_off))
89510Sstevel@tonic-gate }
89520Sstevel@tonic-gate *errnop = EIO;
89530Sstevel@tonic-gate }
89540Sstevel@tonic-gate
89550Sstevel@tonic-gate post_notify:
89560Sstevel@tonic-gate /*
89570Sstevel@tonic-gate * This thread may have been in deadlock with pm_power_has_changed.
89580Sstevel@tonic-gate * Before releasing power lock, clear the flag which marks this
89590Sstevel@tonic-gate * condition.
89600Sstevel@tonic-gate */
89610Sstevel@tonic-gate cp->pmc_flags &= ~PM_PHC_WHILE_SET_POWER;
89620Sstevel@tonic-gate
89630Sstevel@tonic-gate /*
89640Sstevel@tonic-gate * Update the old power level in the bus power structure with the
89650Sstevel@tonic-gate * actual power level before the transition was made to the new level.
89660Sstevel@tonic-gate * Some involved parents depend on this information to keep track of
89670Sstevel@tonic-gate * their children's power transition.
89680Sstevel@tonic-gate */
89690Sstevel@tonic-gate if (*iresp != DDI_FAILURE)
89700Sstevel@tonic-gate bpc->bpc_olevel = clevel;
89710Sstevel@tonic-gate
89720Sstevel@tonic-gate if (PM_WANTS_NOTIFICATION(pdip)) {
89730Sstevel@tonic-gate ret = (*PM_BUS_POWER_FUNC(pdip))(pdip, NULL,
89740Sstevel@tonic-gate BUS_POWER_POST_NOTIFICATION, bpc, resultp);
89750Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
89760Sstevel@tonic-gate PMD(PMD_SET, ("%s: post_notify %s@%s(%s#%d) for "
89770Sstevel@tonic-gate "child %s@%s(%s#%d), ret=%d\n", pmf, PM_DEVICE(pdip),
89780Sstevel@tonic-gate PM_DEVICE(dip), ret))
89790Sstevel@tonic-gate } else {
89800Sstevel@tonic-gate nlevel = cur_power(cp); /* in case phc deadlock updated pwr */
89810Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
89820Sstevel@tonic-gate /*
89830Sstevel@tonic-gate * Now that we know what power transition has occurred
89840Sstevel@tonic-gate * (if any), release the power hold. Leave the hold
89850Sstevel@tonic-gate * in effect in the case of OFF->ON transition.
89860Sstevel@tonic-gate */
89870Sstevel@tonic-gate if (!(clevel == 0 && nlevel > 0 &&
89880Sstevel@tonic-gate (!PM_ISBC(dip) || comp == 0)))
89890Sstevel@tonic-gate pm_rele_power(pdip);
89900Sstevel@tonic-gate /*
89910Sstevel@tonic-gate * If the power transition was an ON->OFF transition,
89920Sstevel@tonic-gate * remove the power hold from the parent.
89930Sstevel@tonic-gate */
89940Sstevel@tonic-gate if ((clevel > 0 || clevel == PM_LEVEL_UNKNOWN) &&
89950Sstevel@tonic-gate nlevel == 0 && (!PM_ISBC(dip) || comp == 0))
89960Sstevel@tonic-gate pm_rele_power(pdip);
89970Sstevel@tonic-gate }
89980Sstevel@tonic-gate if (*iresp != DDI_SUCCESS || ret != DDI_SUCCESS)
89990Sstevel@tonic-gate return (DDI_FAILURE);
90000Sstevel@tonic-gate else
90010Sstevel@tonic-gate return (DDI_SUCCESS);
90020Sstevel@tonic-gate }
90030Sstevel@tonic-gate
90040Sstevel@tonic-gate /*
90050Sstevel@tonic-gate * If an app (SunVTS or Xsun) has taken control, then block until it
90060Sstevel@tonic-gate * gives it up or makes the requested power level change, unless
90070Sstevel@tonic-gate * we have other instructions about blocking. Returns DDI_SUCCESS,
90080Sstevel@tonic-gate * DDI_FAILURE or EAGAIN (owner released device from directpm).
90090Sstevel@tonic-gate */
90100Sstevel@tonic-gate static int
pm_busop_match_request(dev_info_t * dip,void * arg)90110Sstevel@tonic-gate pm_busop_match_request(dev_info_t *dip, void *arg)
90120Sstevel@tonic-gate {
90130Sstevel@tonic-gate PMD_FUNC(pmf, "bp_match_request")
90140Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc = (pm_bp_child_pwrchg_t *)arg;
90150Sstevel@tonic-gate pm_sp_misc_t *pspm = (pm_sp_misc_t *)bpc->bpc_private;
90160Sstevel@tonic-gate int comp = bpc->bpc_comp;
90170Sstevel@tonic-gate int nlevel = bpc->bpc_nlevel;
90180Sstevel@tonic-gate pm_canblock_t canblock = pspm->pspm_canblock;
90190Sstevel@tonic-gate int direction = pspm->pspm_direction;
90200Sstevel@tonic-gate int clevel, circ;
90210Sstevel@tonic-gate
90220Sstevel@tonic-gate ASSERT(PM_IAM_LOCKING_DIP(dip));
90230Sstevel@tonic-gate PM_LOCK_POWER(dip, &circ);
90240Sstevel@tonic-gate clevel = PM_CURPOWER(dip, comp);
90250Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d), cmp=%d, nlvl=%d, clvl=%d\n",
90260Sstevel@tonic-gate pmf, PM_DEVICE(dip), comp, nlevel, clevel))
90270Sstevel@tonic-gate if (direction == PM_LEVEL_UPONLY) {
90280Sstevel@tonic-gate if (clevel >= nlevel) {
90290Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
90300Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
90310Sstevel@tonic-gate return (DDI_SUCCESS);
90320Sstevel@tonic-gate }
90330Sstevel@tonic-gate } else if (clevel == nlevel) {
90340Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
90350Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
90360Sstevel@tonic-gate return (DDI_SUCCESS);
90370Sstevel@tonic-gate }
90380Sstevel@tonic-gate if (canblock == PM_CANBLOCK_FAIL) {
90390Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
90400Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
90410Sstevel@tonic-gate return (DDI_FAILURE);
90420Sstevel@tonic-gate }
90430Sstevel@tonic-gate if (canblock == PM_CANBLOCK_BLOCK) {
90440Sstevel@tonic-gate /*
90450Sstevel@tonic-gate * To avoid a deadlock, we must not hold the
90460Sstevel@tonic-gate * power lock when we pm_block.
90470Sstevel@tonic-gate */
90480Sstevel@tonic-gate PM_UNLOCK_POWER(dip, circ);
90490Sstevel@tonic-gate PMD(PMD_SET, ("%s: blocking\n", pmf))
90504780Smh27603 /* pm_block releases dip lock */
90514780Smh27603 switch (pm_block(dip, comp, nlevel, clevel)) {
90524780Smh27603 case PMP_RELEASE:
90534780Smh27603 return (EAGAIN);
90544780Smh27603 case PMP_SUCCEED:
90554780Smh27603 return (DDI_SUCCESS);
90564780Smh27603 case PMP_FAIL:
90574780Smh27603 return (DDI_FAILURE);
90584780Smh27603 }
90590Sstevel@tonic-gate } else {
90600Sstevel@tonic-gate ASSERT(0);
90610Sstevel@tonic-gate }
90620Sstevel@tonic-gate _NOTE(NOTREACHED);
90630Sstevel@tonic-gate return (DDI_FAILURE); /* keep gcc happy */
90640Sstevel@tonic-gate }
90650Sstevel@tonic-gate
90660Sstevel@tonic-gate static int
pm_all_to_normal_nexus(dev_info_t * dip,pm_canblock_t canblock)90670Sstevel@tonic-gate pm_all_to_normal_nexus(dev_info_t *dip, pm_canblock_t canblock)
90680Sstevel@tonic-gate {
90690Sstevel@tonic-gate PMD_FUNC(pmf, "all_to_normal_nexus")
90700Sstevel@tonic-gate int *normal;
90710Sstevel@tonic-gate int i, ncomps;
90720Sstevel@tonic-gate size_t size;
90730Sstevel@tonic-gate int changefailed = 0;
90740Sstevel@tonic-gate int ret, result = DDI_SUCCESS;
90750Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn;
90760Sstevel@tonic-gate pm_sp_misc_t pspm;
90770Sstevel@tonic-gate
90780Sstevel@tonic-gate ASSERT(PM_GET_PM_INFO(dip));
90790Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
90800Sstevel@tonic-gate if (pm_get_norm_pwrs(dip, &normal, &size) != DDI_SUCCESS) {
90810Sstevel@tonic-gate PMD(PMD_ALLNORM, ("%s: can't get norm pwrs\n", pmf))
90820Sstevel@tonic-gate return (DDI_FAILURE);
90830Sstevel@tonic-gate }
90840Sstevel@tonic-gate ncomps = PM_NUMCMPTS(dip);
90850Sstevel@tonic-gate for (i = 0; i < ncomps; i++) {
90860Sstevel@tonic-gate bpn.bpn_dip = dip;
90870Sstevel@tonic-gate bpn.bpn_comp = i;
90880Sstevel@tonic-gate bpn.bpn_level = normal[i];
90890Sstevel@tonic-gate pspm.pspm_canblock = canblock;
90900Sstevel@tonic-gate pspm.pspm_scan = 0;
90910Sstevel@tonic-gate bpn.bpn_private = &pspm;
90920Sstevel@tonic-gate ret = pm_busop_bus_power(dip, NULL, BUS_POWER_NEXUS_PWRUP,
90930Sstevel@tonic-gate (void *)&bpn, (void *)&result);
90940Sstevel@tonic-gate if (ret != DDI_SUCCESS || result != DDI_SUCCESS) {
90950Sstevel@tonic-gate PMD(PMD_FAIL | PMD_ALLNORM, ("%s: %s@%s(%s#%d)[%d] "
90960Sstevel@tonic-gate "->%d failure result %d\n", pmf, PM_DEVICE(dip),
90970Sstevel@tonic-gate i, normal[i], result))
90980Sstevel@tonic-gate changefailed++;
90990Sstevel@tonic-gate }
91000Sstevel@tonic-gate }
91010Sstevel@tonic-gate kmem_free(normal, size);
91020Sstevel@tonic-gate if (changefailed) {
91030Sstevel@tonic-gate PMD(PMD_FAIL, ("%s: failed to set %d comps %s@%s(%s#%d) "
91040Sstevel@tonic-gate "full power\n", pmf, changefailed, PM_DEVICE(dip)))
91050Sstevel@tonic-gate return (DDI_FAILURE);
91060Sstevel@tonic-gate }
91070Sstevel@tonic-gate return (DDI_SUCCESS);
91080Sstevel@tonic-gate }
91090Sstevel@tonic-gate
91100Sstevel@tonic-gate int
pm_noinvol_update(int subcmd,int volpmd,int wasvolpmd,char * path,dev_info_t * tdip)91110Sstevel@tonic-gate pm_noinvol_update(int subcmd, int volpmd, int wasvolpmd, char *path,
91120Sstevel@tonic-gate dev_info_t *tdip)
91130Sstevel@tonic-gate {
91140Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_update")
91150Sstevel@tonic-gate pm_bp_noinvol_t args;
91160Sstevel@tonic-gate int ret;
91170Sstevel@tonic-gate int result = DDI_SUCCESS;
91180Sstevel@tonic-gate
91190Sstevel@tonic-gate args.bpni_path = path;
91200Sstevel@tonic-gate args.bpni_dip = tdip;
91210Sstevel@tonic-gate args.bpni_cmd = subcmd;
91220Sstevel@tonic-gate args.bpni_wasvolpmd = wasvolpmd;
91230Sstevel@tonic-gate args.bpni_volpmd = volpmd;
91240Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: update for path %s tdip %p subcmd %d "
91250Sstevel@tonic-gate "volpmd %d wasvolpmd %d\n", pmf,
91260Sstevel@tonic-gate path, (void *)tdip, subcmd, wasvolpmd, volpmd))
91270Sstevel@tonic-gate ret = pm_busop_bus_power(ddi_root_node(), NULL, BUS_POWER_NOINVOL,
91280Sstevel@tonic-gate &args, &result);
91290Sstevel@tonic-gate return (ret);
91300Sstevel@tonic-gate }
91310Sstevel@tonic-gate
91320Sstevel@tonic-gate void
pm_noinvol_update_node(dev_info_t * dip,pm_bp_noinvol_t * req)91330Sstevel@tonic-gate pm_noinvol_update_node(dev_info_t *dip, pm_bp_noinvol_t *req)
91340Sstevel@tonic-gate {
91350Sstevel@tonic-gate PMD_FUNC(pmf, "noinvol_update_node")
91360Sstevel@tonic-gate
91370Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
91380Sstevel@tonic-gate switch (req->bpni_cmd) {
91390Sstevel@tonic-gate case PM_BP_NOINVOL_ATTACH:
91400Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_PB_NOINVOL_ATTACH %s@%s(%s#%d) "
91410Sstevel@tonic-gate "noinvol %d->%d\n", pmf, PM_DEVICE(dip),
91420Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm,
91430Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm - 1))
91440Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_noinvolpm);
91450Sstevel@tonic-gate PM_LOCK_DIP(dip);
91460Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm--;
91470Sstevel@tonic-gate if (req->bpni_wasvolpmd) {
91480Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_ATTACH "
91490Sstevel@tonic-gate "%s@%s(%s#%d) volpmd %d->%d\n", pmf,
91500Sstevel@tonic-gate PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd,
91510Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd - 1))
91520Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd)
91530Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd--;
91540Sstevel@tonic-gate }
91550Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
91560Sstevel@tonic-gate break;
91570Sstevel@tonic-gate
91580Sstevel@tonic-gate case PM_BP_NOINVOL_DETACH:
91590Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH %s@%s(%s#%d) "
91600Sstevel@tonic-gate "noinvolpm %d->%d\n", pmf, PM_DEVICE(dip),
91610Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm,
91620Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm + 1))
91630Sstevel@tonic-gate PM_LOCK_DIP(dip);
91640Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm++;
91650Sstevel@tonic-gate if (req->bpni_wasvolpmd) {
91660Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_DETACH "
91670Sstevel@tonic-gate "%s@%s(%s#%d) volpmd %d->%d\n", pmf,
91680Sstevel@tonic-gate PM_DEVICE(dip), DEVI(dip)->devi_pm_volpmd,
91690Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd + 1))
91700Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd++;
91710Sstevel@tonic-gate }
91720Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
91730Sstevel@tonic-gate break;
91740Sstevel@tonic-gate
91750Sstevel@tonic-gate case PM_BP_NOINVOL_REMDRV:
91760Sstevel@tonic-gate PMD(PMD_NOINVOL, ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
91770Sstevel@tonic-gate "noinvol %d->%d\n", pmf, PM_DEVICE(dip),
91780Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm,
91790Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm - 1))
91800Sstevel@tonic-gate ASSERT(DEVI(dip)->devi_pm_noinvolpm);
91810Sstevel@tonic-gate PM_LOCK_DIP(dip);
91820Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm--;
91830Sstevel@tonic-gate if (req->bpni_wasvolpmd) {
91840Sstevel@tonic-gate PMD(PMD_NOINVOL,
91850Sstevel@tonic-gate ("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
91860Sstevel@tonic-gate "volpmd %d->%d\n", pmf, PM_DEVICE(dip),
91870Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd,
91880Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd - 1))
91890Sstevel@tonic-gate /*
91900Sstevel@tonic-gate * A power up could come in between and
91910Sstevel@tonic-gate * clear the volpmd, if that's the case,
91920Sstevel@tonic-gate * volpmd would be clear.
91930Sstevel@tonic-gate */
91940Sstevel@tonic-gate if (DEVI(dip)->devi_pm_volpmd)
91950Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd--;
91960Sstevel@tonic-gate }
91970Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
91980Sstevel@tonic-gate break;
91990Sstevel@tonic-gate
92000Sstevel@tonic-gate case PM_BP_NOINVOL_CFB:
92010Sstevel@tonic-gate PMD(PMD_NOINVOL,
92020Sstevel@tonic-gate ("%s: PM_BP_NOIVOL_CFB %s@%s(%s#%d) noinvol %d->%d\n",
92030Sstevel@tonic-gate pmf, PM_DEVICE(dip), DEVI(dip)->devi_pm_noinvolpm,
92040Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm + 1))
92050Sstevel@tonic-gate PM_LOCK_DIP(dip);
92060Sstevel@tonic-gate DEVI(dip)->devi_pm_noinvolpm++;
92070Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
92080Sstevel@tonic-gate break;
92090Sstevel@tonic-gate
92100Sstevel@tonic-gate case PM_BP_NOINVOL_POWER:
92110Sstevel@tonic-gate PMD(PMD_NOINVOL,
92120Sstevel@tonic-gate ("%s: PM_BP_NOIVOL_PWR %s@%s(%s#%d) volpmd %d->%d\n",
92130Sstevel@tonic-gate pmf, PM_DEVICE(dip),
92140Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd, DEVI(dip)->devi_pm_volpmd -
92150Sstevel@tonic-gate req->bpni_volpmd))
92160Sstevel@tonic-gate PM_LOCK_DIP(dip);
92170Sstevel@tonic-gate DEVI(dip)->devi_pm_volpmd -= req->bpni_volpmd;
92180Sstevel@tonic-gate PM_UNLOCK_DIP(dip);
92190Sstevel@tonic-gate break;
92200Sstevel@tonic-gate
92210Sstevel@tonic-gate default:
92220Sstevel@tonic-gate break;
92230Sstevel@tonic-gate }
92240Sstevel@tonic-gate
92250Sstevel@tonic-gate }
92260Sstevel@tonic-gate
92270Sstevel@tonic-gate #ifdef DEBUG
92280Sstevel@tonic-gate static int
pm_desc_pwrchk_walk(dev_info_t * dip,void * arg)92290Sstevel@tonic-gate pm_desc_pwrchk_walk(dev_info_t *dip, void *arg)
92300Sstevel@tonic-gate {
92310Sstevel@tonic-gate PMD_FUNC(pmf, "desc_pwrchk")
92320Sstevel@tonic-gate pm_desc_pwrchk_t *pdpchk = (pm_desc_pwrchk_t *)arg;
92330Sstevel@tonic-gate pm_info_t *info = PM_GET_PM_INFO(dip);
92345295Srandyf int i;
92355295Srandyf /* LINTED */
92365295Srandyf int curpwr, ce_level;
92370Sstevel@tonic-gate
92380Sstevel@tonic-gate if (!info)
92390Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
92400Sstevel@tonic-gate
92410Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
92420Sstevel@tonic-gate for (i = 0; i < PM_NUMCMPTS(dip); i++) {
92435295Srandyf /* LINTED */
92445295Srandyf if ((curpwr = PM_CURPOWER(dip, i)) == 0)
92450Sstevel@tonic-gate continue;
92465295Srandyf /* E_FUNC_SET_NOT_USED */
92470Sstevel@tonic-gate ce_level = (pdpchk->pdpc_par_involved == 0) ? CE_PANIC :
92480Sstevel@tonic-gate CE_WARN;
92490Sstevel@tonic-gate PMD(PMD_SET, ("%s: %s@%s(%s#%d) is powered off while desc "
92500Sstevel@tonic-gate "%s@%s(%s#%d)[%d] is at %d\n", pmf,
92510Sstevel@tonic-gate PM_DEVICE(pdpchk->pdpc_dip), PM_DEVICE(dip), i, curpwr))
92520Sstevel@tonic-gate cmn_err(ce_level, "!device %s@%s(%s#%d) is powered on, "
92530Sstevel@tonic-gate "while its ancestor, %s@%s(%s#%d), is powering off!",
92540Sstevel@tonic-gate PM_DEVICE(dip), PM_DEVICE(pdpchk->pdpc_dip));
92550Sstevel@tonic-gate }
92560Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
92570Sstevel@tonic-gate }
92580Sstevel@tonic-gate #endif
92590Sstevel@tonic-gate
92600Sstevel@tonic-gate /*
92610Sstevel@tonic-gate * Record the fact that one thread is borrowing the lock on a device node.
92620Sstevel@tonic-gate * Use is restricted to the case where the lending thread will block until
92630Sstevel@tonic-gate * the borrowing thread (always curthread) completes.
92640Sstevel@tonic-gate */
92650Sstevel@tonic-gate void
pm_borrow_lock(kthread_t * lender)92660Sstevel@tonic-gate pm_borrow_lock(kthread_t *lender)
92670Sstevel@tonic-gate {
92680Sstevel@tonic-gate lock_loan_t *prev = &lock_loan_head;
92690Sstevel@tonic-gate lock_loan_t *cur = (lock_loan_t *)kmem_zalloc(sizeof (*cur), KM_SLEEP);
92700Sstevel@tonic-gate
92710Sstevel@tonic-gate cur->pmlk_borrower = curthread;
92720Sstevel@tonic-gate cur->pmlk_lender = lender;
92730Sstevel@tonic-gate mutex_enter(&pm_loan_lock);
92740Sstevel@tonic-gate cur->pmlk_next = prev->pmlk_next;
92750Sstevel@tonic-gate prev->pmlk_next = cur;
92760Sstevel@tonic-gate mutex_exit(&pm_loan_lock);
92770Sstevel@tonic-gate }
92780Sstevel@tonic-gate
92790Sstevel@tonic-gate /*
92800Sstevel@tonic-gate * Return the borrowed lock. A thread can borrow only one.
92810Sstevel@tonic-gate */
92820Sstevel@tonic-gate void
pm_return_lock(void)92830Sstevel@tonic-gate pm_return_lock(void)
92840Sstevel@tonic-gate {
92850Sstevel@tonic-gate lock_loan_t *cur;
92860Sstevel@tonic-gate lock_loan_t *prev = &lock_loan_head;
92870Sstevel@tonic-gate
92880Sstevel@tonic-gate mutex_enter(&pm_loan_lock);
92890Sstevel@tonic-gate ASSERT(prev->pmlk_next != NULL);
92900Sstevel@tonic-gate for (cur = prev->pmlk_next; cur; prev = cur, cur = cur->pmlk_next)
92910Sstevel@tonic-gate if (cur->pmlk_borrower == curthread)
92920Sstevel@tonic-gate break;
92930Sstevel@tonic-gate
92940Sstevel@tonic-gate ASSERT(cur != NULL);
92950Sstevel@tonic-gate prev->pmlk_next = cur->pmlk_next;
92960Sstevel@tonic-gate mutex_exit(&pm_loan_lock);
92970Sstevel@tonic-gate kmem_free(cur, sizeof (*cur));
92980Sstevel@tonic-gate }
92995295Srandyf
93005295Srandyf #if defined(__x86)
93015295Srandyf
93025295Srandyf #define CPR_RXR 0x1
93035295Srandyf #define CPR_TXR 0x20
93045295Srandyf #define CPR_DATAREG 0x3f8
93055295Srandyf #define CPR_LSTAT 0x3fd
93065295Srandyf #define CPR_INTRCTL 0x3f9
93075295Srandyf
93085295Srandyf char
pm_getchar(void)93095295Srandyf pm_getchar(void)
93105295Srandyf {
93115295Srandyf while ((inb(CPR_LSTAT) & CPR_RXR) != CPR_RXR)
93125295Srandyf drv_usecwait(10);
93135295Srandyf
93145295Srandyf return (inb(CPR_DATAREG));
93155295Srandyf
93165295Srandyf }
93175295Srandyf
93185295Srandyf void
pm_putchar(char c)93195295Srandyf pm_putchar(char c)
93205295Srandyf {
93215295Srandyf while ((inb(CPR_LSTAT) & CPR_TXR) == 0)
93225295Srandyf drv_usecwait(10);
93235295Srandyf
93245295Srandyf outb(CPR_DATAREG, c);
93255295Srandyf }
93265295Srandyf
93275295Srandyf void
pm_printf(char * s)93285295Srandyf pm_printf(char *s)
93295295Srandyf {
93305295Srandyf while (*s) {
93315295Srandyf pm_putchar(*s++);
93325295Srandyf }
93335295Srandyf }
93345295Srandyf
93355295Srandyf #endif
93365295Srandyf
93375295Srandyf int
pm_ppm_searchlist(pm_searchargs_t * sp)93385295Srandyf pm_ppm_searchlist(pm_searchargs_t *sp)
93395295Srandyf {
93405295Srandyf power_req_t power_req;
93415295Srandyf int result = 0;
93425295Srandyf /* LINTED */
93435295Srandyf int ret;
93445295Srandyf
93455295Srandyf power_req.request_type = PMR_PPM_SEARCH_LIST;
93465295Srandyf power_req.req.ppm_search_list_req.searchlist = sp;
93475295Srandyf ASSERT(DEVI(ddi_root_node())->devi_pm_ppm);
93485295Srandyf ret = pm_ctlops((dev_info_t *)DEVI(ddi_root_node())->devi_pm_ppm,
93495295Srandyf ddi_root_node(), DDI_CTLOPS_POWER, &power_req, &result);
93505295Srandyf PMD(PMD_SX, ("pm_ppm_searchlist returns %d, result %d\n",
93515295Srandyf ret, result))
93525295Srandyf return (result);
93535295Srandyf }
9354