/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
*/
/*
* sunpm.c builds sunpm.o "power management framework"
* kernel-resident power management code. Implements power management
* policy
* Assumes: all backwards compat. device components wake up on &
* the pm_info pointer in dev_info is initially NULL
*
* PM - (device) Power Management
*
* Each device may have 0 or more components. If a device has no components,
* then it can't be power managed. Each component has 2 or more
* power states.
*
* "Backwards Compatible" (bc) devices:
* There are two different types of devices from the point of view of this
* code. The original type, left over from the original PM implementation on
* the voyager platform are known in this code as "backwards compatible"
* devices (PM_ISBC(dip) returns true).
* They are recognized by the pm code by the lack of a pm-components property
* and a call made by the driver to pm_create_components(9F).
* For these devices, component 0 is special, and represents the power state
* of the device. If component 0 is to be set to power level 0 (off), then
* the framework must first call into the driver's detach(9E) routine with
* DDI_PM_SUSPEND, to get the driver to save the hardware state of the device.
* After setting component 0 from 0 to a non-zero power level, a call must be
* made into the driver's attach(9E) routine with DDI_PM_RESUME.
*
* Currently, the only way to get a bc device power managed is via a set of
*
* For non-bc devices, the driver describes the components by exporting a
* pm-components(9P) property that tells how many components there are,
* tells what each component's power state values are, and provides human
* readable strings (currently unused) for each component name and power state.
* Devices which export pm-components(9P) are automatically power managed
* whenever autopm is enabled (via PM_START_PM ioctl issued by pmconfig(1M)
* after parsing power.conf(4)). The exception to this rule is that power
* manageable CPU devices may be automatically managed independently of autopm
* by either enabling or disabling (via PM_START_CPUPM and PM_STOP_CPUPM
* ioctls) cpupm. If the CPU devices are not managed independently, then they
* are managed by autopm. In either case, for automatically power managed
* devices, all components are considered independent of each other, and it is
* up to the driver to decide when a transition requires saving or restoring
* hardware state.
*
* Each device component also has a threshold time associated with each power
* transition (see power.conf(4)), and a busy/idle state maintained by the
* driver calling pm_idle_component(9F) and pm_busy_component(9F).
* Components are created idle.
*
* The PM framework provides several functions:
* -implement PM policy as described in power.conf(4)
* Policy is set by pmconfig(1M) issuing pm ioctls based on power.conf(4).
* Policies consist of:
* -set threshold values (defaults if none provided by pmconfig)
* -set dependencies among devices
* -turn down idle components based on thresholds (if autopm or cpupm is
* enabled) (aka scanning)
* -maintain power states based on dependencies among devices
* -upon request, or when the frame buffer powers off, attempt to turn off
* all components that are idle or become idle over the next (10 sec)
* period in an attempt to get down to an EnergyStar compliant state
* -prevent powering off of a device which exported the
* pm-no-involuntary-power-cycles property without active involvement of
* the device's driver (so no removing power when the device driver is
* not attached)
* -provide a mechanism for a device driver to request that a device's component
* be brought back to the power level necessary for the use of the device
* -allow a process to directly control the power levels of device components
* -ensure that the console frame buffer is powered up before being referenced
* via prom_printf() or other prom calls that might generate console output
* -maintain implicit dependencies (e.g. parent must be powered up if child is)
* -provide "backwards compatible" behavior for devices without pm-components
* property
*
* Scanning:
* Whenever autopm or cpupm is enabled, the framework attempts to bring each
* component of each managed device to its lowest power based on the threshold
* component.
*
* The actual work of this is done by pm_scan_dev(), which cycles through each
* component of a device, checking its idleness against its current threshold,
* and calling pm_set_power() as appropriate to change the power level.
* This function also indicates when it would next be profitable to scan the
* device again, and a new scan is scheduled after that time.
*
* Dependencies:
* It is possible to establish a dependency between the power states of two
* otherwise unrelated devices. This is currently done to ensure that the
* cdrom is always up whenever the console framebuffer is up, so that the user
* can insert a cdrom and see a popup as a result.
*
* The dependency terminology used in power.conf(4) is not easy to understand,
* so we've adopted a different terminology in the implementation. We write
* of a "keeps up" and a "kept up" device. A relationship can be established
* where one device keeps up another. That means that if the keepsup device
* has any component that is at a non-zero power level, all components of the
* "kept up" device must be brought to full power. This relationship is
* asynchronous. When the keeping device is powered up, a request is queued
* to a worker thread to bring up the kept device. The caller does not wait.
* Scan will not turn down a kept up device.
*
* Direct PM:
* A device may be directly power managed by a process. If a device is
* directly pm'd, then it will not be scanned, and dependencies will not be
* enforced. * If a directly pm'd device's driver requests a power change (via
* pm_raise_power(9F)), then the request is blocked and notification is sent
* to the controlling process, which must issue the requested power change for
* the driver to proceed.
*
*/
#include <sys/archsystm.h>
#include <sys/bootconf.h>
/*
* PM LOCKING
* The list of locks:
* Global pm mutex locks.
*
* pm_scan_lock:
* It protects the timeout id of the scan thread, and the value
* of autopm_enabled and cpupm. This lock is not held
* concurrently with any other PM locks.
*
* pm_clone_lock: Protects the clone list and count of poll events
* pending for the pm driver.
* Lock ordering:
* pm_clone_lock -> pm_pscc_interest_rwlock,
* pm_clone_lock -> pm_pscc_direct_rwlock.
*
* pm_rsvp_lock:
* Used to synchronize the data structures used for processes
* to rendezvous with state change information when doing
* direct PM.
* Lock ordering:
* pm_rsvp_lock -> pm_pscc_interest_rwlock,
* pm_rsvp_lock -> pm_pscc_direct_rwlock,
* pm_rsvp_lock -> pm_clone_lock.
*
* ppm_lock: protects the list of registered ppm drivers
* Lock ordering:
* ppm_lock -> ppm driver unit_lock
*
* pm_compcnt_lock:
* Protects count of components that are not at their lowest
* power level.
* Lock ordering:
* pm_compcnt_lock -> ppm_lock.
*
* pm_dep_thread_lock:
* Protects work list for pm_dep_thread. Not taken concurrently
* with any other pm lock.
*
* pm_remdrv_lock:
* Serializes the operation of removing noinvol data structure
* entries for a branch of the tree when a driver has been
* removed from the system (modctl_rem_major).
* Lock ordering:
* pm_remdrv_lock -> pm_noinvol_rwlock.
*
* pm_cfb_lock: (High level spin lock)
* Protects the count of how many components of the console
* frame buffer are off (so we know if we have to bring up the
* console as a result of a prom_printf, etc.
* No other locks are taken while holding this lock.
*
* pm_loan_lock:
* Protects the lock_loan list. List is used to record that one
* thread has acquired a power lock but has launched another thread
* to complete its processing. An entry in the list indicates that
* the worker thread can borrow the lock held by the other thread,
* which must block on the completion of the worker. Use is
* specific to module loading.
* No other locks are taken while holding this lock.
*
* Global PM rwlocks
*
* pm_thresh_rwlock:
* Protects the list of thresholds recorded for future use (when
* devices attach).
* Lock ordering:
* pm_thresh_rwlock -> devi_pm_lock
*
* pm_noinvol_rwlock:
* Protects list of detached nodes that had noinvol registered.
* No other PM locks are taken while holding pm_noinvol_rwlock.
*
* pm_pscc_direct_rwlock:
* Protects the list that maps devices being directly power
* managed to the processes that manage them.
* Lock ordering:
* pm_pscc_direct_rwlock -> psce_lock
*
* pm_pscc_interest_rwlock;
* Protects the list that maps state change events to processes
* that want to know about them.
* Lock ordering:
* pm_pscc_interest_rwlock -> psce_lock
*
* per-dip locks:
*
* Each node has these per-dip locks, which are only used if the device is
* a candidate for power management (e.g. has pm components)
*
* devi_pm_lock:
* Protects all power management state of the node except for
* power level, which is protected by ndi_devi_enter().
* Encapsulated in macros PM_LOCK_DIP()/PM_UNLOCK_DIP().
* Lock ordering:
* devi_pm_lock -> pm_rsvp_lock,
* devi_pm_lock -> pm_dep_thread_lock,
* devi_pm_lock -> pm_noinvol_rwlock,
* devi_pm_lock -> power lock
*
* power lock (ndi_devi_enter()):
* Since changing power level is possibly a slow operation (30
* seconds to spin up a disk drive), this is locked separately.
* Since a call into the driver to change the power level of one
* component may result in a call back into the framework to change
* the power level of another, this lock allows re-entrancy by
* the same thread (ndi_devi_enter is used for this because
* the USB framework uses ndi_devi_enter in its power entry point,
* and use of any other lock would produce a deadlock.
*
* devi_pm_busy_lock:
* This lock protects the integrity of the busy count. It is
* only taken by pm_busy_component() and pm_idle_component and
* some code that adjust the busy time after the timer gets set
* up or after a CPR operation. It is per-dip to keep from
* single-threading all the disk drivers on a system.
* It could be per component instead, but most devices have
* only one component.
* No other PM locks are taken while holding this lock.
*
*/
static int stdout_is_framebuffer;
static int pm_busop_set_power(dev_info_t *,
void *, pm_bus_power_op_t, void *, void *);
static int pm_busop_match_request(dev_info_t *, void *);
static void e_pm_set_max_power(dev_info_t *, int, int);
static int e_pm_get_max_power(dev_info_t *, int);
/*
* Dependency Processing is done thru a seperate thread.
*/
/*
* Autopm must be turned on by a PM_START_PM ioctl, so we don't end up
* power managing things in single user mode that have been suppressed via
* power.conf entries. Protected by pm_scan_lock.
*/
int autopm_enabled;
/*
* cpupm is turned on and off, by the PM_START_CPUPM and PM_STOP_CPUPM ioctls,
* to define the power management behavior of CPU devices separate from
* autopm. Protected by pm_scan_lock.
*/
/*
* Defines the default mode of operation for CPU power management,
* either the polling implementation, or the event based dispatcher driven
* implementation.
*/
/*
* AutoS3 depends on autopm being enabled, and must be enabled by
* PM_START_AUTOS3 command.
*/
int autoS3_enabled;
#if !defined(__sparc)
/*
* on sparc these live in fillsysinfo.c
*
* If this variable is non-zero, cpr should return "not supported" when
* it is queried even though it would normally be supported on this platform.
*/
/*
* Some platforms may need to support CPR even in the absence of
* having the correct platform id information. If this
* variable is non-zero, cpr should proceed even in the absence
* of otherwise being qualified.
*/
int cpr_platform_enable = 0;
#endif
/*
* pm_S3_enabled indicates that we believe the platform can support S3,
* which we get from pmconfig(1M)
*/
int pm_S3_enabled;
/*
* This flag is true while processes are stopped for a checkpoint/resume.
* Controlling processes of direct pm'd devices are not available to
* participate in power level changes, so we bypass them when this is set.
*/
static int pm_processes_stopped;
#ifdef DEBUG
/*
*/
/*
* If pm_divertdebug is set, then no prom_printf calls will be made by
* PMD(), which will prevent debug output from bringing up the console
* frame buffer. Clearing this variable before setting pm_debug will result
* in PMD output going to the console.
*
* pm_divertdebug is incremented in pm_set_power() if dip == cfb_dip to avoid
* deadlocks and decremented at the end of pm_set_power()
*/
void prdeps(char *);
#endif
/* Globals */
/*
* List of recorded thresholds and dependencies
*/
static int pm_unresolved_deps = 0;
static int pm_prop_deps = 0;
/*
* List of devices that exported no-involuntary-power-cycles property
*/
/*
* Locks used in noinvol processing
*/
/*
* By default nexus has 0 threshold, and depends on its children to keep it up
*/
int pm_default_nexus_threshold = 0;
/*
*/
/*
*/
extern int hz;
extern char *platform_module_list[];
/*
* Wrappers for use in ddi_walk_devs
*/
static int pm_set_dev_thr_walk(dev_info_t *, void *);
static int pm_restore_direct_lvl_walk(dev_info_t *, void *);
static int pm_save_direct_lvl_walk(dev_info_t *, void *);
static int pm_discard_dep_walk(dev_info_t *, void *);
#ifdef DEBUG
static int pm_desc_pwrchk_walk(dev_info_t *, void *);
#endif
/*
* Routines for managing noinvol devices
*/
int pm_noinvol_update(int, int, int, char *, dev_info_t *);
void pm_noinvol_update_node(dev_info_t *,
if (!PM_IS_NEXUS(dip) || \
if (pm_comps_notlowest == 0) \
pm_comps_notlowest++; \
} \
}
if (!PM_IS_NEXUS(dip) || \
pm_comps_notlowest--; \
if (pm_comps_notlowest == 0) \
} \
}
/*
* console frame-buffer power-management is not enabled when
* debugging services are present. to override, set pm_cfb_override
* to non-zero.
*/
#ifdef DEBUG
#else
#endif
int pm_scans_disabled = 0;
/*
* A structure to record the fact that one thread has borrowed a lock held
* by another thread. The context requires that the lender block on the
* completion of the borrower.
*/
typedef struct lock_loan {
} lock_loan_t;
#ifdef DEBUG
#ifdef PMDDEBUG
#else /* !PMDDEBUG */
#endif /* PMDDEBUG */
#else /* !DEBUG */
#endif /* DEBUG */
/*
* Must be called before first device (including pseudo) attach
*/
void
pm_init_locks(void)
{
}
static int pm_reset_timestamps(dev_info_t *, void *);
static boolean_t
{
static int auto_save;
switch (code) {
case CB_CODE_CPR_CHKPT:
/*
* Cancel scan or wait for scan in progress to finish
* Other threads may be trying to restart the scan, so we
* have to keep at it unil it sticks
*/
pm_scans_disabled = 1;
autopm_enabled = 0;
cpupm_save = cpupm;
break;
case CB_CODE_CPR_RESUME:
pm_scans_disabled = 0;
/*
* Call pm_reset_timestamps to reset timestamps of each
* device to the time when the system is resumed so that their
* idleness can be re-calculated. That's to avoid devices from
* being powered down right after resume if the system was in
* suspended mode long enough.
*/
cpupm = cpupm_save;
/*
* If there is any auto-pm device, get the scanning
* going. Otherwise don't bother.
*/
break;
}
return (B_TRUE);
}
/*
* This callback routine is called when there is a system panic. This function
* exists for prototype matching.
*/
static boolean_t
{
void pm_cfb_check_and_powerup(void);
return (B_TRUE);
}
static boolean_t
{
return (B_TRUE);
}
static void pm_dep_thread(void);
/*
* This needs to be called after the root and platform drivers are loaded
*/
void
pm_init(void)
{
char **mod;
extern pri_t minclsyspri;
pm_comps_notlowest = 0;
CB_CL_CPR_PM, "pm_cpr");
CB_CL_PANIC, "pm_panic");
CB_CL_HALT, "pm_halt");
/*
* Create a thread to do dependency processing.
*/
/*
* loadrootmodules already loaded these ppm drivers, now get them
* attached so they can claim the root drivers as they attach
*/
*mod);
} else {
}
}
}
/*
* pm_scan_init - create pm scan data structure. Called (if autopm or cpupm
* enabled) when device becomes power managed or after a failed detach and
* when autopm is started via PM_START_PM or PM_START_CPUPM ioctls, and after
* a CPR resume to get all the devices scanning again.
*/
void
{
if (!scanp) {
}
}
/*
* pm_scan_fini - remove pm scan data structure when stopping pm on the device
*/
void
{
if (!scanp) {
return;
}
}
/*
* Given a pointer to a component struct, return the current power level
* (struct contains index unless it is a continuous level).
* Located here in hopes of getting both this and dev_is_needed into the
* cache together
*/
static int
{
return (cp->pmc_cur_pwr);
}
static char *
{
switch (direction) {
case PM_LEVEL_UPONLY:
return ("up");
case PM_LEVEL_EXACT:
return ("exact");
case PM_LEVEL_DOWNONLY:
return ("down");
default:
return ("INVALID DIRECTION");
}
}
char *
{
switch (op) {
case BUS_POWER_CHILD_PWRCHG:
return ("CHILD_PWRCHG");
case BUS_POWER_NEXUS_PWRUP:
return ("NEXUS_PWRUP");
return ("PRE_NOTIFICATION");
return ("POST_NOTIFICATION");
case BUS_POWER_HAS_CHANGED:
return ("HAS_CHANGED");
case BUS_POWER_NOINVOL:
return ("NOINVOL");
default:
return ("UNKNOWN OP");
}
}
/*
* Returns true if level is a possible (valid) power level for component
*/
int
{
int i;
if (level < 0)
return (0);
for (i = 0; i < limit; i++) {
return (1);
}
#ifdef DEBUG
for (i = 0; i < limit; i++)
}
#endif
return (0);
}
/*
* Returns true if device is pm'd (after calling pm_start if need be)
*/
int
{
/*
* Check if the device is power managed if not.
* To make the common case (device is power managed already)
* fast, we check without the lock. If device is not already
* power managed, then we take the lock and the long route through
* go get it managed. Devices never go unmanaged until they
* detach.
*/
if (!info) {
if (!DEVI_IS_ATTACHING(dip)) {
return (0);
}
return (0);
}
}
return (1);
}
int
{
return (1);
} else {
return (0);
}
}
/*
* Internal guts of ddi_dev_is_needed and pm_raise/lower_power
*/
static int
{
char *pathbuf;
int result;
return (DDI_FAILURE);
if (direction == PM_LEVEL_UPONLY) {
pathbuf);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* We can get multiple pm_rescan() threads, if one of them discovers
* that no scan is running at the moment, it kicks it into action.
* Otherwise, it tells the current scanning thread to scan again when
* it is done by asserting the PM_SCAN_AGAIN flag. The PM_SCANNING and
* PM_SCAN_AGAIN flags are used to regulate scan, to make sure only one
* thread at a time runs the pm_scan_dev() code.
*/
void
{
return;
}
return;
} else if (scanp->ps_scan_id) {
scanp->ps_scan_id = 0;
}
/*
* Dispatching pm_scan during attach time is risky due to the fact that
* attach might soon fail and dip dissolved, and panic may happen while
* attempting to stop scan. So schedule a pm_rescan instead.
* (Note that if either of the first two terms are true, taskq_dispatch
* will not be invoked).
*
* Multiple pm_scan dispatching is unecessary and costly to keep track
* of. The PM_SCAN_DISPATCHED flag is used between pm_rescan and pm_scan
* to regulate the dispatching.
*
* Scan is stopped before the device is detached (in pm_detaching())
* but it may get re-started during the post_detach processing if the
* driver fails to detach.
*/
if (DEVI_IS_ATTACHING(dip) ||
if (scanp->ps_scan_id) {
scanp->ps_scan_id = 0;
if (scanp->ps_scan_id) {
"thread scheduled pm_rescan, scanid %lx\n",
return;
}
}
} else {
}
}
void
{
return;
}
if (scanp->ps_idle_down) {
/*
* make sure we remember idledown was in affect until
* we've completed the scan
*/
}
/* possible having two threads running pm_scan() */
return;
}
do {
if (scanp->ps_idle_down) {
}
/* schedule for next idle check */
if (scanp->ps_scan_id) {
"another rescan scheduled scanid(%lx)\n", pmf,
return;
}
}
}
void
{
int i;
ASSERT(components > 0);
for (i = 0; i < components; i++) {
}
}
/*
* Returns true if device needs to be kept up because it exported the
* "no-involuntary-power-cycles" property or we're pretending it did (console
* fb case) or it is an ancestor of such a device and has used up the "one
* free cycle" allowed when all such leaf nodes have voluntarily powered down
* upon detach
*/
int
{
/*
* This doesn't change over the life of a driver, so no locking needed
*/
return (1);
}
/*
* Not an issue if no such kids
*/
#ifdef DEBUG
do {
} while (pdip);
}
#endif
return (0);
}
/*
* Since we now maintain the counts correct at every node, we no longer
* need to look up the tree. An ancestor cannot use up the free cycle
* without the children getting their counts adjusted.
*/
#ifdef DEBUG
#endif
}
static int cur_threshold(dev_info_t *, int);
static int pm_next_lower_power(pm_component_t *, int);
/*
* This function performs the actual scanning of the device.
* It attempts to power off the indicated device's components if they have
* been idle and other restrictions are met.
* pm_scan_dev calculates and returns when the next scan should happen for
* this device.
*/
{
#ifdef PMDDEBUG
int curpwr;
#endif
int circ;
/*
* skip attaching device
*/
if (DEVI_IS_ATTACHING(dip)) {
return (min_scan);
}
/* no scan under the following conditions */
"scan_disabled(%d), apm_enabled(%d), cpupm(%d), "
"kuc(%d), %s directpm, %s pm_noinvol\n",
return (LONG_MAX);
}
return ((time_t)1);
}
now = gethrestime_sec();
/*
* Since we removed support for backwards compatible devices,
* (see big comment at top of file)
* it is no longer required to deal with component 0 last.
*/
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
/*
* If already off (an optimization, perhaps)
*/
#ifdef PMDDEBUG
#endif
if (pwrndx == 0) {
/* skip device if off or at its lowest */
continue;
}
/* were busy or newly became busy by another thread */
if (timeleft == 0)
else
continue;
}
continue;
} else {
"%d->%d, GOOD curpwr %d\n", pmf,
if (nxtpwr == 0) /* component went off */
continue;
/*
* scan to next lower level
*/
if (timeleft == 0)
else
i, timeleft))
}
} else { /* comp not idle long enough */
if (timeleft == 0)
else
}
}
/*
* if components are already at lowest level, timeleft is left 0
*/
}
/*
* pm_scan_stop - cancel scheduled pm_rescan,
* wait for termination of dispatched pm_scan thread
* and active pm_scan_dev thread.
*/
void
{
if (!scanp) {
return;
}
/* cancel scheduled scan taskq */
while (scanp->ps_scan_id) {
scanp->ps_scan_id = 0;
}
delay(1);
}
}
int
{
if (!PM_GET_PM_SCAN(dip))
return (DDI_WALK_CONTINUE);
return (DDI_WALK_CONTINUE);
}
/*
* Converts a power level value to its index
*/
static int
{
val != PM_LEVEL_EXACT);
/* convert power value into index (i) */
for (i = 0; i < limit; i++)
return (i);
return (-1);
}
/*
* Converts a numeric power level to a printable string
*/
static char *
{
int index;
if (val == PM_LEVEL_UPONLY)
return ("<UPONLY>");
if (val == PM_LEVEL_UNKNOWN ||
return ("<LEVEL_UNKNOWN>");
}
/*
* Return true if this node has been claimed by a ppm.
*/
static int
{
}
/*
* A node which was voluntarily power managed has just used up its "free cycle"
* and need is volpmd field cleared, and the same done to all its descendents
*/
static void
{
return;
}
}
/*
* A node which was voluntarily power managed has used up the "free cycles"
* for the subtree that it is the root of. Scan through the list of detached
* nodes and adjust the counts of any that are descendents of the node.
*/
static void
{
char *pathbuf;
ip->ni_wasvolpmd = 0;
}
}
}
/*
* Powers a device, suspending or resuming the driver if it is a backward
* compatible device, calling into ppm to change power level.
* Called with the component's power lock held.
*/
static int
{
int resume_needed = 0;
int suspended = 0;
int result;
#ifdef PMDDEBUG
#endif
int pm_all_components_off(dev_info_t *);
int clearvolpmd = 0;
#ifdef PMDDEBUG
#endif
/*
* If this is comp 0 of a backwards compat device and we are
* going to take the power away, we need to detach it with
* DDI_PM_SUSPEND command.
*/
/* We could not suspend before turning cmpt zero off */
return (DDI_FAILURE);
} else {
suspended++;
}
}
#ifdef PMDDEBUG
if (pm_ppm_claimed(dip)) {
} else {
ppmname = "noppm";
ppmaddr = "0";
}
#endif
/*
* If non-bc noinvolpm device is turning first comp on, or noinvolpm
* bc device comp 0 is powering on, then we count it as a power cycle
* against its voluntary count.
*/
clearvolpmd = 1;
/*
* Now do involuntary pm accounting; If we've just cycled power
* on a voluntarily pm'd node, and by inference on its entire
* subtree, we need to set the subtree (including those nodes
* already detached) volpmd counts to 0, and subtract out the
* value of the current node's volpmd count from the ancestors
*/
if (clearvolpmd) {
if (volpmd) {
(void) pm_noinvol_update(PM_BP_NOINVOL_POWER,
}
}
} else {
}
/*
* If some other devices were also powered up (e.g. other cpus in
* the same domain) return a pointer to that list
*/
if (devlist) {
*devlist = (pm_ppm_devlist_t *)
}
/*
* We will have to resume the device if the device is backwards compat
* device and either of the following is true:
* -This is comp 0 and we have successfully powered it up
* -This is comp 0 and we have failed to power it down. Resume is
* needed because we have suspended it above
*/
if (power_op_ret == DDI_SUCCESS) {
/*
* It must be either suspended or resumed
* via pm_power_has_changed path
*/
PMC_SUSPENDED) ||
}
} else {
/*
* It must be either suspended or resumed
* via pm_power_has_changed path
*/
PMC_SUSPENDED) ||
}
}
}
if (resume_needed) {
/* ppm is not interested in DDI_PM_RESUME */
DDI_SUCCESS) {
} else
}
return (power_op_ret);
}
/*
* Return true if we are the owner or a borrower of the devi lock. See
* pm_lock_power_single() about borrowing the lock.
*/
static int
{
if (DEVI_BUSY_OWNED(dip))
return (1);
/* return false if no locks borrowed */
return (0);
/* see if our thread is registered as a lock borrower. */
break;
}
/*
* pm_set_power: adjusts power level of device. Assumes device is power
* manageable & component exists.
*
* Cases which require us to bring up devices we keep up ("wekeepups") for
* backwards compatible devices:
* component 0 is off and we're bringing it up from 0
* bring up wekeepup first
* and recursively when component 0 is off and we bring some other
* component up from 0
* For devices which are not backward compatible, our dependency notion is much
* simpler. Unless all components are off, then wekeeps must be on.
* We don't treat component 0 differently.
* Canblock tells how to deal with a direct pm'd device.
* Scan arg tells us if we were called from scan, in which case we don't need
* to go back to the root node and walk down to change power.
*/
int
{
char *pathbuf;
#ifdef DEBUG
int diverted = 0;
/*
* This prevents operations on the console from calling prom_printf and
* either deadlocking or bringing up the console because of debug
* output
*/
diverted++;
}
#endif
direction == PM_LEVEL_EXACT);
/*
* If a config operation is being done (we've locked the parent) or
* we already hold the power lock (we've locked the node)
* then we can operate directly on the node because we have already
* brought up all the ancestors, otherwise, we have to go back to the
* top of the tree.
*/
else
#ifdef DEBUG
}
if (diverted) {
}
#endif
return (ret);
}
/*
* If holddip is set, then if a dip is found we return with the node held.
*
* This code uses the same locking scheme as e_ddi_hold_devi_by_path
* (resolve_pathname), but it does not drive attach.
*/
{
char *component;
int circ;
return (NULL);
/* setup pathname and allocate component */
return (NULL);
/* start at top, process '/' component */
pn_skipslash(&pn);
/* process components of pathname */
while (pn_pathleft(&pn)) {
/* enter parent and search for component child */
goto out;
}
/* attached child found, hold child and release parent */
/* child becomes parent, and process next component */
pn_skipslash(&pn);
/* loop with active ndi_devi_hold of child->parent */
}
out:
/* if we are not asked to return with hold, drop current hold */
return (child);
}
/*
* Search for a dependency and mark it unsatisfied
*/
static void
{
if (!dp->pdr_isprop) {
(dp->pdr_kept_count > 0) &&
if (dp->pdr_satisfied) {
dp->pdr_satisfied = 0;
"pm_unresolved_deps now %d\n", pmf,
}
}
}
}
}
/*
* Device dip is being un power managed, it keeps up count other devices.
* We need to release any hold we have on the kept devices, and also
* mark the dependency no longer satisfied.
*/
static void
{
int i, j;
/*
* Try to grab keeper. Keeper may have gone away by now,
* in this case, used the passed in value pwr
*/
for (i = 0; i < count; i++) {
/* Release power hold */
if (kept) {
/*
* We need to check if we skipped a bringup here
* because we could have failed the bringup
* (ie DIRECT PM device) and have
* not increment the count.
*/
keeper_on = 0;
for (j = 0; j < PM_NUMCMPTS(dip); j++) {
keeper_on++;
break;
}
}
&= ~PMC_SKIP_BRINGUP;
}
} else if (pwr) {
&= ~PMC_SKIP_BRINGUP;
}
}
}
/*
* mark this dependency not satisfied
*/
}
if (dip)
}
/*
* Device kept is being un power managed, it is kept up by keeper.
* We need to mark the dependency no longer satisfied.
*/
static void
{
/*
* mark this dependency not satisfied
*/
}
/*
* Removes dependency information and hold on the kepts, if the path is a
* path of a keeper.
*/
static void
{
int i;
continue;
/*
* Remove all our kept holds and the dependency records,
* then free up the kept lists.
*/
if (dp->pdr_kept_count) {
for (i = 0; i < dp->pdr_kept_count; i++) {
}
dp->pdr_kept_count * sizeof (char **));
dp->pdr_kept_count = 0;
}
}
}
/*
* Removes the device represented by path from the list of kepts, if the
* path is a path of a kept
*/
static void
{
int i;
int j, count;
char **paths;
if (dp->pdr_kept_count == 0)
continue;
/* Remove this device from the kept path lists */
for (i = 0; i < count; i++) {
dp->pdr_kept_count--;
}
}
/* Compact the kept paths array */
if (dp->pdr_kept_count) {
j = 0;
for (i = 0; i < count; i++) {
j++;
}
}
}
/* Now free the old array and point to the new one */
if (dp->pdr_kept_count)
else
}
}
/*
* Free the dependency information for a device.
*/
void
{
#ifdef DEBUG
int doprdeps = 0;
void prdeps(char *);
doprdeps = 1;
prdeps("pm_free_keeps before");
}
#endif
/*
* First assume we are a keeper and remove all our kepts.
*/
/*
* Now assume we a kept device, and remove all our records.
*/
#ifdef DEBUG
if (doprdeps) {
prdeps("pm_free_keeps after");
}
#endif
}
static int
{
int i;
if (dp->pdr_kept_count == 0)
continue;
for (i = 0; i < dp->pdr_kept_count; i++) {
return (1);
}
}
return (0);
}
static void
{
int circ;
return;
}
struct ppm_callbacks {
/*
* This routine calls into all the registered ppms to notify them
* that either all components of power-managed devices are at their
* lowest levels or no longer all are at their lowest levels.
*/
static void
{
int result = 0;
if (mode == PM_ALL_LOWEST) {
if (autoS3_enabled) {
"autos3\n"))
if (srn_signal) {
srn_inuse++;
srn_inuse--;
} else {
}
} else {
"disabled\n"));
}
}
}
static void
{
}
/*
* Look up an entry in the blocked list by dip and component
*/
static pm_rsvp_t *
{
pm_rsvp_t *p;
for (p = pm_blocked_list; p; p = p->pr_next)
return (p);
}
return (NULL);
}
/*
* Called when a device which is direct power managed (or the parent or
* dependent of such a device) changes power, or when a pm clone is closed
* that was direct power managing a device. This call results in pm_blocked()
* (below) returning.
*/
void
{
pm_rsvp_t *p;
switch (cmd) {
/*
* we're giving up control, let any pending op continue
*/
case PMP_RELEASE:
for (p = pm_blocked_list; p; p = p->pr_next) {
p->pr_retval = PMP_RELEASE;
}
}
break;
/*
* process has done PM_SET_CURRENT_POWER; let a matching request
* succeed and a non-matching request for the same device fail
*/
case PMP_SETPOWER:
if (!found) /* if driver not waiting */
break;
/*
* This cannot be pm_lower_power, since that can only happen
* during detach or probe
*/
} else {
}
break;
default:
}
}
/*
* This routine dispatches new work to the dependency thread. Caller must
* be prepared to block for memory if necessary.
*/
void
int *res, int cached_pwr)
{
KM_SLEEP);
}
}
if (pm_dep_thread_workq == NULL) {
} else {
}
/* If caller asked for it, wait till it is done. */
if (wait) {
/*
* Pass return status, if any, back.
*/
/*
* If we asked to wait, it is our job to free the request
* structure.
*/
if (new_work->pdw_keeper)
}
}
/*
* Release the pm resource for this device.
*/
void
{
int i, count = 0;
char *pathbuf;
if (PM_ISDIRECT(dip)) {
}
/*
* Now adjust parent's kidsupcnt. BC nodes we check only comp 0,
* Others we check all components. BC node that has already
* called pm_destroy_components() has zero component count.
* Parents that get notification are not adjusted because their
* kidsupcnt is always 0 (or 1 during configuration).
*/
/* node is detached, so we can examine power without locking */
} else {
for (i = 0; i < PM_NUMCMPTS(dip); i++)
}
/* Schedule a request to clean up dependency records */
/*
* Adjust the pm_comps_notlowest count since this device is
* not being power-managed anymore.
*/
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
if (cp->pmc_cur_pwr != 0)
}
/*
* Once we clear the info pointer, it looks like it is not power
* managed to everybody else.
*/
}
int
{
int *bufp;
int i;
if (components <= 0) {
return (DDI_FAILURE);
} else {
size = components * sizeof (int);
for (i = 0; i < components; i++) {
}
}
return (DDI_SUCCESS);
}
static int
{
int components;
int i;
if (!PM_GET_PM_INFO(dip))
return (DDI_WALK_CONTINUE);
ASSERT(components > 0);
for (i = 0; i < components; i++) {
/*
* If the component was not marked as busy,
* reset its timestamp to now.
*/
if (cp->pmc_timestamp)
}
return (DDI_WALK_CONTINUE);
}
/*
* Convert a power level to an index into the levels array (or
* just PM_LEVEL_UNKNOWN in that special case).
*/
static int
{
int i;
if (level == PM_LEVEL_UNKNOWN)
return (level);
for (i = 0; i < limit; i++) {
return (i);
}
}
panic("pm_level_to_index: level %d not found for device "
/*NOTREACHED*/
}
/*
* Internal function to set current power level
*/
static void
{
/*
* Nothing to adjust if current & new levels are the same.
*/
if (curpwr != PM_LEVEL_UNKNOWN &&
return;
/*
* level.
*/
if (curpwr == 0) {
}
}
static int pm_phc_impl(dev_info_t *, int, int, int);
/*
* This is the default method of setting the power of a device if no ppm
* driver has claimed it.
*/
int
{
int (*fn)(dev_info_t *, int, int);
int retval;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
if (retval == DDI_SUCCESS) {
return (DDI_SUCCESS);
}
/*
* If pm_power_has_changed() detected a deadlock with pm_power() it
* updated only the power level of the component. If our attempt to
* set the device new to a power level above has failed we sync the
* total power state via phc code now.
*/
int phc_lvl =
}
return (DDI_FAILURE);
}
int
{
if (pm_ppm_claimed(dip))
#ifdef DEBUG
else
#endif
return (retval);
}
int
{
if (level < 0)
return (DDI_FAILURE);
return (DDI_FAILURE);
}
int
{
return (DDI_FAILURE);
}
if (!DEVI_IS_DETACHING(dip)) {
return (DDI_FAILURE);
}
/*
* If we don't care about saving power, or we're treating this node
* specially, then this is a no-op
*/
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Find the entries struct for a given dip in the blocked list, return it locked
*/
static psce_t *
{
pscc_t *p;
for (p = pm_pscc_direct; p; p = p->pscc_next) {
*psccp = p;
psce = p->pscc_entries;
return (psce);
}
}
/*NOTREACHED*/
}
/*
* Write an entry indicating a power level change (to be passed to a process
* later) in the given psce.
* If we were called in the path that brings up the console fb in the
* case of entering the prom, we don't want to sleep. If the alloc fails, then
* we create a record that has a size of -1, a physaddr of NULL, and that
* has the overflow flag set.
*/
static int
{
int overrun = 0;
if (canblock == PM_CANBLOCK_BYPASS) {
/*
* mark current entry as overrun
*/
p->flags |= PSC_EVENT_LOST;
}
} else
if (p->size) { /* overflow; mark the next entry */
} else {
}
overrun++;
p->flags |= PSC_EVENT_LOST;
p->size = 0;
}
if (which == PSC_INTEREST) {
if (pm_comps_notlowest == 0)
p->flags |= PSC_ALL_LOWEST;
else
p->flags &= ~PSC_ALL_LOWEST;
}
p->timestamp = gethrestime_sec();
else
return (overrun);
}
/*
* Find the next entry on the interest list. We keep a pointer to the item we
* last returned in the user's cooke. Returns a locked entries struct.
*/
static psce_t *
{
else
if (pscc) {
return (pscc->pscc_entries);
} else {
return (NULL);
}
}
/*
* Create an entry for a process to pick up indicating a power level change.
*/
static void
{
int overrun;
switch (cmd) {
case PSC_PENDING_CHANGE: /* only for controlling process */
if (!overrun)
break;
case PSC_HAS_CHANGED:
if (!overrun)
}
}
break;
#ifdef DEBUG
default:
ASSERT(0);
#endif
}
}
static void
{
if (listp) {
canblock);
kmem_free(p, sizeof (pm_ppm_devlist_t));
}
}
}
/*
* Try to get the power locks of the parent node and target (child)
* node. Return true if successful (with both locks held) or false
* (with no locks held).
*/
static int
{
return (1);
} else {
}
return (0);
}
/*
* Determine if the power lock owner is blocked by current thread.
* returns :
* 1 - If the thread owning the effective power lock (the first lock on
* which a thread blocks when it does PM_LOCK_POWER) is blocked by
* a mutex held by the current thread.
*
* 0 - otherwise
*
* Note : This function is called by pm_power_has_changed to determine whether
* it is executing in parallel with pm_set_power.
*/
static int
{
int result;
DDI_SUCCESS) {
/*
* It is assumed that if the device is claimed by ppm, ppm
* will always implement this request type and it'll always
* return success. We panic here, if it fails.
*/
panic("pm: Can't determine power lock owner of %s@%s(%s#%d)\n",
/*NOTREACHED*/
}
owner->t_sobj_ops &&
return (1);
return (0);
}
/*
* Notify parent which wants to hear about a child's power changes.
*/
static void
{
}
/*
* Check if we need to resume a BC device, and make the attach call as required.
*/
static int
{
/* ppm is not interested in DDI_PM_RESUME */
/* XXX Should we mark it resumed, */
/* even though it failed? */
}
return (ret);
}
/*
* Tests outside the lock to see if we should bother to enqueue an entry
* for any watching process. If yes, then caller will take the lock and
* do the full protocol
*/
static int
{
if (pm_processes_stopped)
return (0);
return (pm_pscc_direct || pm_pscc_interest);
}
static int pm_phc_impl(dev_info_t *, int, int, int);
/*
* A driver is reporting that the power of one of its device's components
* has changed. Update the power state accordingly.
*/
int
{
int ret;
if (level < 0) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/*
* A driver thread calling pm_power_has_changed and another thread
* calling pm_set_power can deadlock. The problem is not resolvable
* by changing lock order, so we use pm_blocked_by_us() to detect
* this specific deadlock. If we can't get the lock immediately
* and we are deadlocked, just update the component's level, do
* notifications, and return. We intend to update the total power
* state later (if the other thread fails to set power to the
* desired level). If we were called because of a power change on a
* component that isn't involved in a set_power op, update all state
* immediately.
*/
if (pm_watchers()) {
}
(void) pm_check_and_resume(dip,
/*
* Stash the old power index, update curpwr, and flag
* that the total power state needs to be synched.
*/
/*
* Several pm_power_has_changed calls could arrive
* while the set power path remains blocked. Keep the
* oldest old power and the newest new power of any
* sequence of phc calls which arrive during deadlock.
*/
cp->pmc_cur_pwr =
return (DDI_SUCCESS);
} else
if (blocked) { /* blocked, but different cmpt? */
"!pm: parent kuc not updated due "
"to possible deadlock.\n");
return (pm_phc_impl(dip,
}
}
/* child lock not held: deadlock */
return (ret);
}
delay(1);
}
/* non-deadlock case */
}
return (ret);
}
/*
* Account for power changes to a component of the the console frame buffer.
* If lowering power from full (or "unkown", which is treatd as full)
* we will increment the "components off" count of the fb device.
* Subsequent lowering of the same component doesn't affect the count. If
* raising a component back to full power, we will decrement the count.
*
* Return: the increment value for pm_cfb_comps_off (-1, 0, or 1)
*/
static int
{
int incr = 0;
if (on && !want_normal)
incr = 1;
else if (!on && want_normal)
incr = -1;
return (incr);
}
/*
* Adjust the count of console frame buffer components < full power.
*/
static void
{
pm_cfb_comps_off += incr;
}
/*
* Update the power state in the framework (via the ppm). The 'notify'
* argument tells whether to notify watchers. Power lock is already held.
*/
static int
{
int i, dodeps = 0;
int result;
int old_level;
int incr = 0;
int work_type = 0;
char *pathbuf;
/* Must use "official" power level for this test. */
if (old_level != PM_LEVEL_UNKNOWN)
return (DDI_SUCCESS);
}
/*
* Tell ppm about this.
*/
&result) == DDI_FAILURE) {
return (DDI_FAILURE);
}
if (incr) {
}
}
if (notify) {
}
/*
* Decrement the dependency kidsup count if we turn a device
* off.
*/
dodeps = 1;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
dodeps = 0;
break;
}
}
if (dodeps)
}
/*
* Increment if we turn it on. Check to see
* if other comps are already on, if so,
* dont increment.
*/
dodeps = 1;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
if (comp == i)
continue;
/* -1 also treated as 0 in this case */
dodeps = 0;
break;
}
}
if (dodeps)
}
if (dodeps) {
PM_DEP_NOWAIT, NULL, 0);
}
}
return (DDI_SUCCESS);
}
/*
* This function is called at startup time to notify pm of the existence
* of any platform power managers for this platform. As a result of
* this registration, each function provided will be called each time
* a device node is attached, until one returns true, and it must claim the
* device node (by returning non-zero) if it wants to be involved in the
* node's power management. If it does claim the node, then it will
* subsequently be notified of attach and detach events.
*
*/
int
{
void pm_ppm_claim(dev_info_t *);
for (i = 0; i < MAX_PPM_HANDLERS; i++, ppmcp++) {
break;
}
}
if (i >= MAX_PPM_HANDLERS)
return (DDI_FAILURE);
continue;
/* don't bother with the not power-manageable nodes */
/*
* Tell ppm about this.
*/
p->old_level = PM_LEVEL_UNKNOWN;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
if (pwr != PM_LEVEL_UNKNOWN) {
p->cmpt = i;
p->old_level = PM_LEVEL_UNKNOWN;
&result) == DDI_FAILURE) {
"%s@%s(%s#%d) to %d "
"fails\n", pmf,
}
}
}
}
}
return (DDI_SUCCESS);
}
/*
* Call the ppm's that have registered and adjust the devinfo struct as
* appropriate. First one to claim it gets it. The sets of devices claimed
* by each ppm are assumed to be disjoint.
*/
void
{
return;
}
return;
}
}
}
/*
* Node is being detached so stop autopm until we see if it succeeds, in which
* case pm_stop will be called. For backwards compatible devices we bring the
* device up to full power on the assumption the detach will succeed.
*/
void
{
int iscons;
PM_NUMCMPTS(dip)))
return;
/*
* console and old-style devices get brought up when detaching.
*/
if (iscons) {
while (cfb_inuse) {
delay(1);
}
}
}
}
/*
* Node failed to detach. If it used to be autopm'd, make it so again.
*/
void
{
int pm_all_at_normal(dev_info_t *);
return;
/* Make sure the operation is still needed */
if (!pm_all_at_normal(dip)) {
if (pm_all_to_normal(dip,
PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
"%s@%s(%s#%d) to normal\n", pmf,
}
}
}
}
if (PM_SCANABLE(dip))
}
}
/* generic Backwards Compatible component */
static void
{
}
static void
{
int i;
for (i = 0; i < cmpts; i++, p++) {
KM_SLEEP);
KM_SLEEP);
}
}
/*
* Called from functions that require components to exist already to allow
* for their creation by parsing the pm-components property.
* Device will not be power managed as a result of this call
* No locking needed because we're single threaded by the ndi_devi_enter
* done while attaching, and the device isn't visible until after it has
* attached
*/
int
{
/*
* If this dip has already been processed, don't mess with it
*/
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* Look up pm-components property and create components accordingly
* If that fails, fall back to backwards compatibility
*/
/*
* If error is set, the property existed but was not well formed
*/
return (DDI_FAILURE);
}
/*
* If they don't have the pm-components property, then we
* want the old "no pm until PM_SET_DEVICE_THRESHOLDS ioctl"
* behavior driver must have called pm_create_components, and
* we need to flesh out dummy components
*/
/*
* Not really failure, but we don't want the
* caller to treat it as success
*/
return (DDI_FAILURE);
}
for (i = 0; i < cmpts; i++) {
/*
* if normal power not set yet, we don't really know
* what *ANY* of the power values are. If normal
* power is set, then we assume for this backwards
* compatible case that the values are 0, normal power.
*/
return (DDI_FAILURE);
}
/*
* Components of BC devices start at their normal power,
* so count them to be not at their lowest power.
*/
}
} else {
/*
* e_pm_create_components was called from pm_autoconfig(), it
* creates components with no descriptions (or known levels)
*/
for (i = 0; i < cmpts; i++, p++) {
}
else
}
return (DDI_SUCCESS);
}
/*
* Called from during or after the device's attach to let us know it is ready
* to play autopm. Look up the pm model and manage the device accordingly.
* Returns system call errno value.
* If DDI_ATTACH and DDI_DETACH were in same namespace, this would be
* a little cleaner
*
* Called with dip lock held, return with dip lock unheld.
*/
int
{
int pm_thresh_specd(dev_info_t *);
int count;
char *pathbuf;
return (DDI_FAILURE);
}
/*
* Now set up parent's kidsupcnt. BC nodes are assumed to start
* out at their normal power, so they are "up", others start out
* unknown, which is effectively "up". Parent which want notification
* get kidsupcnt of 0 always.
*/
/*
* Apply any recorded thresholds
*/
(void) pm_thresh_specd(dip);
/*
* Do dependency processing.
*/
PM_DEP_NOWAIT, NULL, 0);
if (PM_SCANABLE(dip)) {
} else {
}
}
return (0);
}
/*
* This is the obsolete exported interface for a driver to find out its
* "normal" (max) power.
* We only get components destroyed while no power management is
* going on (and the device is detached), so we don't need a mutex here
*/
int
{
}
return (DDI_FAILURE);
}
/*
* Fetches the current power level. Return DDI_SUCCESS or DDI_FAILURE.
*/
int
{
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* Returns current threshold of indicated component
*/
static int
{
int pwr;
/*
* backwards compatible nodes only have one threshold
*/
}
if (pwr == PM_LEVEL_UNKNOWN) {
int thresh;
else
return (thresh);
}
}
/*
* Compute next lower component power level given power index.
*/
static int
{
int nxt_pwr;
if (pwrndx == PM_LEVEL_UNKNOWN) {
} else {
pwrndx--;
}
return (nxt_pwr);
}
/*
* Update the maxpower (normal) power of a component. Note that the
* component's power level is only changed if it's current power level
* is higher than the new max power.
*/
int
{
int old;
int result;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Bring all components of device to normal power
*/
int
{
int *normal;
int changefailed = 0;
return (DDI_FAILURE);
}
for (i = 0; i < ncomps; i++) {
changefailed++;
"%s@%s(%s#%d)[%d] to %d, errno %d\n", pmf,
}
}
if (changefailed) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Returns true if all components of device are at normal power
*/
int
{
int *normal;
int i;
return (DDI_FAILURE);
}
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
break;
}
}
if (i != PM_NUMCMPTS(dip)) {
return (0);
}
return (1);
}
static void bring_pmdep_up(dev_info_t *, int);
static void
{
int i;
char *kept_path;
if (panicstr) {
return;
}
/*
* We process the request even if the keeper detaches because
* detach processing expects this to increment kidsupcnt of kept.
*/
continue;
for (i = 0; i < dp->pdr_kept_count; i++) {
continue;
continue;
if (kept)
continue;
}
/*
* Don't mess with it if it is being detached, it isn't
* safe to call its power entry point
*/
if (kept)
continue;
}
}
}
}
/*
* Bring up the 'kept' device passed as argument
*/
static void
{
int is_all_at_normal = 0;
/*
* If the kept device has been unmanaged, do nothing.
*/
if (!PM_GET_PM_INFO(kept_dip))
return;
/* Just ignore DIRECT PM device till they are released. */
"controlling process did something else\n", pmf,
return;
}
/* if we got here the keeper had a transition from OFF->ON */
if (hold)
if (!is_all_at_normal)
}
/*
* A bunch of stuff that belongs only to the next routine (or two)
*/
struct pm_comp_pkg {
};
/*
* Rather than duplicate this code ...
* (this code excerpted from the function that follows it)
*/
#define FINISH_COMP { \
/* copy string out of prop array into buffer */ \
for (j = 0; j < level; j++) { \
} \
}
/*
* Create (empty) component data structures.
*/
static void
{
int i, size = 0;
for (i = 0; i < num_components; i++) {
compp++;
}
}
/*
* Parse hex or decimal value from char string
*/
static char *
{
int value = 0;
if (ch == '0') {
}
*np = 0;
cp--;
goto hexval;
} else {
goto digit;
}
} else {
}
*np = 0;
cp--;
goto decval;
}
} else
return (NULL);
offset = '0';
value *= 16;
}
return (cp);
offset = '0';
value *= 10;
}
return (cp);
}
/*
* Set max (previously documented as "normal") power.
*/
static void
{
}
/*
* Get max (previously documented as "normal") power.
*/
static int
{
}
/*
* Internal routine for destroying components
* It is called even when there might not be any, so it must be forgiving.
*/
static void
{
int i;
if (PM_NUMCMPTS(dip) == 0)
return;
/*
* For BC nodes, the rest is static in bc_comp, so skip it
*/
continue;
}
}
/*
* Read the pm-components property (if there is one) and use it to set up
* components. Returns a pointer to an array of component structures if
* pm-components found and successfully parsed, else returns NULL.
* Sets error return *errp to true to indicate a failure (as opposed to no
* property being present).
*/
{
char **pp;
int npi = 0;
*errp = 0; /* assume success */
return (NULL);
}
goto errout;
}
/*
* pm_create_components is no longer allowed
*/
if (PM_NUMCMPTS(dip) != 0) {
goto errout;
}
level = 0;
for (i = 0; i < nelems; i++) {
goto errout;
}
if (i != 0) {
if (level == 0) { /* no level spec'd */
pmf))
goto errout;
}
/* finish up previous component levels */
}
if (!*cp) {
goto errout;
}
} else {
ptail = p;
}
KM_SLEEP);
KM_SLEEP);
components++;
level = 0;
} else { /* better be power level <num>=<name> */
#ifdef DEBUG
#endif
if (i == 0 ||
goto errout;
}
#ifdef DEBUG
#endif
goto errout;
}
level++;
}
}
if (level == 0) { /* ended with a name */
goto errout;
}
/*
* Now we have a list of components--we have to return instead an
* array of them, but we can just copy the top level and leave
* the rest as is
*/
for (i = 0; i < components; i++)
for (i = 0, p = phead; i < components; i++) {
ASSERT(p);
/*
* Now sanity-check values: levels must be monotonically
* increasing
*/
"levels\n", pmf,
p->comp->pmc_numlevels))
goto errout;
}
for (j = 0; j < p->comp->pmc_numlevels; j++) {
"not mono. incr, %d follows %d\n", pmf,
goto errout;
}
}
for (j = 0; j < i; j++) {
/*
* Test for unique component names
*/
goto errout;
}
}
ptail = p;
p = p->next;
phead = p; /* errout depends on phead making sense */
}
out:
if (lvals)
if (lszs)
if (lnames)
if (np)
return (ret);
for (i = 0; i < nelems - 1; i++)
if (nelems != 0)
for (p = phead; p; ) {
int n;
ptail = p;
/*
* Free component data structures
*/
n = pp->pmc_numlevels;
if (pp->pmc_name_sz) {
}
if (pp->pmc_lnames_sz) {
}
if (pp->pmc_lnames) {
}
if (pp->pmc_thresh) {
}
}
}
goto out;
}
/*
* Set threshold values for a devices components by dividing the target
* threshold (base) by the number of transitions and assign each transition
* that threshold. This will get the entire device down in the target time if
* all components are idle and even if there are dependencies among components.
*
* Devices may well get powered all the way down before the target time, but
* at least the EPA will be happy.
*/
void
{
int transitions = 0;
int thresh;
int remainder;
int i, circ;
/*
* First we handle the easy one. If we're setting the default
* threshold for a node with children, then we set it to the
* default nexus threshold (currently 0) and mark it as default
* nexus threshold instead
*/
if (PM_IS_NEXUS(dip)) {
if (flag == PMC_DEF_THRESH) {
level++) {
}
}
/*
* If the nexus node is being reconfigured back to
* the default threshold, adjust the notlowest count.
*/
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
if (PM_CURPOWER(dip, i) == 0)
continue;
"notlowest to %d\n", pmf,
if (pm_comps_notlowest == 0)
}
}
return;
/*
* If the nexus node is being configured for a
* non-default threshold, include that node in
* the notlowest accounting.
*/
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
if (PM_CURPOWER(dip, i) == 0)
continue;
if (pm_comps_notlowest == 0)
"notlowest to %d\n", pmf,
}
}
}
/*
* Compute the total number of transitions for all components
* of the device. Distribute the threshold evenly over them
*/
}
}
}
#ifdef DEBUG
}
}
#endif
/*
* Distribute any remainder till they are all gone
*/
level = 1;
#ifdef DEBUG
#endif
while (remainder > 0) {
comp = 0;
remainder--;
}
comp++;
}
level++;
}
#ifdef DEBUG
}
}
#endif
}
/*
* Called when there is no old-style platform power management driver
*/
static int
{
return (DDI_FAILURE);
}
/*
* This function calls the entry point supplied by the platform-specific
* pm driver to bring the device component 'pm_cmpt' to power level 'pm_level'.
* The use of global for getting the function name from platform-specific
* pm driver is not ideal, but it is simple and efficient.
* The previous property lookup was being done in the idle loop on swift
* systems without pmc chips and hurt deskbench performance as well as
* violating scheduler locking rules
*/
/*
* Old obsolete interface for a device to request a power change (but only
* an increase in power)
*/
int
{
}
/*
* The old obsolete interface to platform power management. Only used by
* Gypsy platform and APM on X86.
*/
int
{
}
/*
* A driver can invoke this from its detach routine when DDI_SUSPEND is
* passed. Returns true if subsequent processing could result in power being
* removed from the device. The arg is not currently used because it is
*/
int
{
return (pm_powering_down);
}
/*
* processing for it.
*/
int
{
}
/*
* handling for them
*/
int
{
}
/*
* handling for them
*/
int
{
}
/*
* Old obsolete exported interface for drivers to create components.
* This is now handled by exporting the pm-components property.
*/
int
{
if (num_components < 1)
return (DDI_FAILURE);
if (!DEVI_IS_ATTACHING(dip)) {
return (DDI_FAILURE);
}
/* don't need to lock dip because attach is single threaded */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Obsolete interface previously called by drivers to destroy their components
* at detach time. This is now done automatically. However, we need to keep
* this for the old drivers.
*/
void
{
#ifdef DEBUG
#endif
/*
* We ignore this unless this is an old-style driver, except for
* printing the message above
*/
return;
}
/*
* pm_unmanage will clear info pointer later, after dealing with
* dependencies
*/
/*
* Now adjust parent's kidsupcnt. We check only comp 0.
* Parents that get notification are not adjusted because their
* kidsupcnt is always 0 (or 1 during probe and attach).
*/
#ifdef DEBUG
else {
}
#endif
/*
* Forget we ever knew anything about the components of this device
*/
}
/*
* Exported interface for a driver to set a component busy.
*/
int
{
return (DDI_FAILURE);
cp->pmc_busycount++;
cp->pmc_timestamp = 0;
return (DDI_SUCCESS);
}
/*
* Exported interface for a driver to set a component idle.
*/
int
{
return (DDI_FAILURE);
if (cp->pmc_busycount) {
if (--(cp->pmc_busycount) == 0)
} else {
}
/*
* if device becomes idle during idle down period, try scan it down
*/
return (DDI_SUCCESS);
}
/*
* handle scan not running with nexus threshold == 0
*/
}
return (DDI_SUCCESS);
}
/*
* This is the old obsolete interface called by drivers to set their normal
* power. Thus we can't fix its behavior or return a value.
* This functionality is replaced by the pm-component property.
* We'll only get components destroyed while no power management is
* going on (and the device is detached), so we don't need a mutex here
*/
void
{
#ifdef DEBUG
"(driver exporting pm-components property) ignored",
#endif
}
}
/*
* Called on a successfully detached driver to free pm resources
*/
static void
{
/* stopping scan, destroy scan data structure */
}
/*
* Old style driver may have called
* pm_destroy_components already, but just in case ...
*/
} else {
}
} else {
if (PM_NUMCMPTS(dip))
else {
} else if (pdip &&
}
}
}
}
}
/*
* The node is the subject of a reparse pm props ioctl. Throw away the old
* info and start over.
*/
int
{
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* Device has been attached, so process its pm properties
*/
void
{
char *pp;
int len;
int flags = 0;
/*
* It doesn't matter if we do this more than once, we should always
* get the same answers, and if not, then the last one in is the
* best one.
*/
} else {
}
}
/*
* This next segment (PMC_WANTS_NOTIFY) is in
* support of nexus drivers which will want to be involved in
* (or at least notified of) their child node's power level transitions.
* "pm-want-child-notification?" is defined by the parent.
*/
"no-involuntary-power-cycles"))
flags |= PMC_NO_INVOL;
/*
* Is the device a CPU device?
*/
flags |= PMC_CPU_DEVICE;
} else {
}
}
/* devfs single threads us */
}
/*
* This is the DDI_CTLOPS_POWER handler that is used when there is no ppm
* driver which has claimed a node.
* Sets old_power in arg struct.
*/
static int
{
int retval;
#ifdef PMDDEBUG
char *format;
#endif
/*
* The interface for doing the actual power level changes is now
* through the DDI_CTLOPS_POWER bus_ctl, so that we can plug in
* different platform-specific power control drivers.
*
* This driver implements the "default" version of this interface.
* If no ppm driver has been installed then this interface is called
* instead.
*/
switch (ctlop) {
case DDI_CTLOPS_POWER:
switch (reqp->request_type) {
case PMR_PPM_SET_POWER:
{
/* pass back old power for the PM_LEVEL_UNKNOWN case */
"chd" : "no chg")))
return (retval);
}
case PMR_PPM_PRE_DETACH:
case PMR_PPM_POST_DETACH:
case PMR_PPM_PRE_ATTACH:
case PMR_PPM_POST_ATTACH:
case PMR_PPM_PRE_PROBE:
case PMR_PPM_POST_PROBE:
case PMR_PPM_PRE_RESUME:
case PMR_PPM_INIT_CHILD:
case PMR_PPM_UNINIT_CHILD:
#ifdef PMDDEBUG
switch (reqp->request_type) {
case PMR_PPM_PRE_DETACH:
format = "%s: PMR_PPM_PRE_DETACH "
"%s@%s(%s#%d)\n";
break;
case PMR_PPM_POST_DETACH:
format = "%s: PMR_PPM_POST_DETACH "
"%s@%s(%s#%d) rets %d\n";
break;
case PMR_PPM_PRE_ATTACH:
format = "%s: PMR_PPM_PRE_ATTACH "
"%s@%s(%s#%d)\n";
break;
case PMR_PPM_POST_ATTACH:
format = "%s: PMR_PPM_POST_ATTACH "
"%s@%s(%s#%d) rets %d\n";
break;
case PMR_PPM_PRE_PROBE:
format = "%s: PMR_PPM_PRE_PROBE "
"%s@%s(%s#%d)\n";
break;
case PMR_PPM_POST_PROBE:
format = "%s: PMR_PPM_POST_PROBE "
"%s@%s(%s#%d) rets %d\n";
break;
case PMR_PPM_PRE_RESUME:
format = "%s: PMR_PPM_PRE_RESUME "
"%s@%s(%s#%d) rets %d\n";
break;
case PMR_PPM_INIT_CHILD:
format = "%s: PMR_PPM_INIT_CHILD "
"%s@%s(%s#%d)\n";
break;
case PMR_PPM_UNINIT_CHILD:
format = "%s: PMR_PPM_UNINIT_CHILD "
"%s@%s(%s#%d)\n";
break;
default:
break;
}
#endif
return (DDI_SUCCESS);
/*
* Nothing for us to do
*/
"%s@%s(%s#%d)[%d] %d->%d\n", pmf,
return (DDI_SUCCESS);
case PMR_PPM_UNMANAGE:
return (DDI_SUCCESS);
case PMR_PPM_LOCK_POWER:
return (DDI_SUCCESS);
case PMR_PPM_UNLOCK_POWER:
return (DDI_SUCCESS);
case PMR_PPM_TRY_LOCK_POWER:
*(int *)result = pm_try_locking_power_single(
return (DDI_SUCCESS);
case PMR_PPM_POWER_LOCK_OWNER:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
default:
return (DDI_FAILURE);
}
}
/*
* We overload the bus_ctl ops here--perhaps we ought to have a distinct
* power_ops struct for this functionality instead?
* However, we only ever do this on a ppm driver.
*/
int
{
int (*fp)();
/* if no ppm handler, call the default routine */
if (d == NULL) {
return (pm_default_ctlops(d, r, op, a, v));
}
if (!d || !r)
return (DDI_FAILURE);
}
/*
* Called on a node when attach completes or the driver makes its first pm
* call (whichever comes first).
* In the attach case, device may not be power manageable at all.
* Don't need to lock the dip because we're single threaded by the devfs code
*/
static int
{
int ret;
int e_pm_manage(dev_info_t *, int);
/*
* If this dip has already been processed, don't mess with it
* (but decrement the speculative count we did above, as whatever
* code put it under pm already will have dealt with it)
*/
if (PM_GET_PM_INFO(dip)) {
return (0);
}
/*
* keep the kidsupcount increment as is
*/
}
}
return (ret);
}
/*
* Keep a list of recorded thresholds. For now we just keep a list and
* search it linearly. We don't expect too many entries. Can always hash it
* later if we need to.
*/
void
{
/* replace this one */
if (pptr) {
} else {
pm_thresh_head = rp;
}
return;
}
continue;
}
/*
* There was not a match in the list, insert this one in front
*/
if (pm_thresh_head) {
pm_thresh_head = rp;
} else {
pm_thresh_head = rp;
}
}
/*
* Create a new dependency record and hang a new dependency entry off of it
*/
pm_pdr_t *
{
p->pdr_isprop = isprop;
p->pdr_kept_paths = NULL;
p->pdr_kept_count = 0;
return (p);
}
/*
* Keep a list of recorded dependencies. We only keep the
* keeper -> kept list for simplification. At this point We do not
* care about whether the devices are attached or not yet,
* this would be done in pm_keeper() and pm_kept().
* If a PM_RESET_PM happens, then we tear down and forget the dependencies,
* and it is up to the user to issue the ioctl again if they want it
* (e.g. pmconfig)
* Returns true if dependency already exists in the list.
*/
int
{
#ifdef DEBUG
prdeps("pm_record_keeper entry");
#endif
pdr->pdr_keeper))
return (1);
}
}
/*
* We did not find any match, so we have to make an entry
*/
if (ppdr) {
} else {
pm_dep_head = npdr;
}
#ifdef DEBUG
prdeps("pm_record_keeper after new record");
#endif
if (!isprop)
else
pm_prop_deps++;
return (0);
}
/*
* Look up this device in the set of devices we've seen ioctls for
* to see if we are holding a threshold spec for it. If so, make it so.
* At ioctl time, we were given the physical path of the device.
*/
int
{
char *path = 0;
continue;
return (1);
}
return (0);
}
static int
{
void prdeps(char *);
#ifdef DEBUG
prdeps("Before PAD\n");
#endif
"%s@%s(%s#%d), but the former is not power managed",
return (0);
}
"%s@%s(%s#%d), but the latter is not power managed",
return (0);
}
for (j = 0; j < PM_NUMCMPTS(keeper); j++) {
if (PM_CURPOWER(keeper, j)) {
up++;
break;
}
}
if (up) {
/* Bringup and maintain a hold on the kept */
}
#ifdef DEBUG
prdeps("After PAD\n");
#endif
return (1);
}
/*
* Should this device keep up another device?
* Look up this device in the set of devices we've seen ioctls for
* to see if we are holding a dependency spec for it. If so, make it so.
* Because we require the kept device to be attached already in order to
* make the list entry (and hold it), we only need to look for keepers.
* At ioctl time, we were given the physical path of the device.
*/
int
{
int ret = 0;
int i;
if (!pm_unresolved_deps && !pm_prop_deps)
return (0);
return (0);
if (!dp->pdr_isprop) {
if (!pm_unresolved_deps)
continue;
if (dp->pdr_satisfied) {
continue;
}
}
} else {
continue;
for (i = 0; i < dp->pdr_kept_count; i++) {
continue;
continue;
"kept=%s@%s(%s#%d) keptcnt=%d\n",
dp->pdr_kept_count))
}
}
}
}
return (ret);
}
/*
* Should this device be kept up by another device?
* Look up all dependency recorded from PM_ADD_DEPENDENT and
* PM_ADD_DEPENDENT_PROPERTY ioctls. Record down on the keeper's
* kept device lists.
*/
static int
{
int found = 0;
int ret = 0;
int i;
char **paths;
char *path;
return (0);
if (dp->pdr_isprop) {
/*
* Dont allow self dependency.
*/
continue;
continue;
#ifdef DEBUG
prdeps("Before Adding from pm_kept\n");
#endif
/*
* Add ourselves to the dip list.
*/
if (dp->pdr_kept_count == 0) {
path =
paths = kmem_alloc(sizeof (char **),
KM_SLEEP);
dp->pdr_kept_count++;
} else {
/* Check to see if already on list */
for (i = 0; i < dp->pdr_kept_count;
i++) {
dp->pdr_kept_paths[i])
== 0) {
found++;
break;
}
}
if (found) {
continue;
}
sizeof (char **);
paths = kmem_alloc(
length + sizeof (char **),
KM_SLEEP);
if (dp->pdr_kept_count) {
length);
}
path =
dp->pdr_kept_count++;
}
#ifdef DEBUG
prdeps("After from pm_kept\n");
#endif
if (keeper) {
}
}
} else {
/*
* pm_keeper would be called later to do
* the actual pm_set_keeping.
*/
#ifdef DEBUG
prdeps("Before Adding from pm_kept\n");
#endif
path =
paths = kmem_alloc(sizeof (char **),
KM_SLEEP);
dp->pdr_kept_count++;
}
}
#ifdef DEBUG
prdeps("After from pm_kept\n");
#endif
}
}
return (ret);
}
/*
* Apply a recorded dependency. dp specifies the dependency, and
* keeper is already known to be the device that keeps up the other (kept) one.
* We have to the whole tree for the "kept" device, then apply
* the dependency (which may already be applied).
*/
int
{
int ret = 0;
/*
* Device to Device dependency can only be 1 to 1.
*/
return (0);
return (0);
return (0);
if (kept) {
ret++;
}
}
return (ret);
}
/*
*/
int
{
}
/*
* External interface to sanity-check a power level.
*/
int
{
else {
return (0);
}
}
/*
* Called when a device that is direct power managed needs to change state.
* This routine arranges to block the request until the process managing
* the device makes the change (or some other incompatible change) or
*/
static int
{
int ret = 0;
void pm_dequeue_blocked(pm_rsvp_t *);
void pm_enqueue_blocked(pm_rsvp_t *);
/*
* truss may make the cv_wait_sig return prematurely
*/
while (ret == 0) {
/*
* Normally there will be no user context involved, but if
* there is (e.g. we are here via an ioctl call to a driver)
* then we should allow the process to abort the request,
* or we get an unkillable process if the same thread does
* PM_DIRECT_PM and pm_raise_power
*/
} else {
}
}
return (ret);
}
/*
* Returns true if the process is interested in power level changes (has issued
* PM_GET_STATE_CHANGE ioctl).
*/
int
{
return (pm_interest[clone]);
}
/*
* Process with clone has just done PM_DIRECT_PM on dip, or has asked to
* watch all state transitions (dip == NULL). Set up data
* structs to communicate with process about state changes.
*/
void
{
pscc_t *p;
/*
* We definitely need a control struct, then we have to search to see
* there is already an entries struct (in the dip != NULL case).
*/
if (dip) {
int found = 0;
for (p = pm_pscc_direct; p; p = p->pscc_next) {
/*
* Already an entry for this clone, so just use it
* for the new one (for the case where a single
* process is watching multiple devices)
*/
if (p->pscc_clone == clone) {
found++;
break;
}
}
if (!found) { /* create a new one */
psce->psce_first =
KM_SLEEP);
}
} else {
#ifdef DEBUG
for (p = pm_pscc_interest; p; p = p->pscc_next) {
/*
* Should not be an entry for this clone!
*/
}
#endif
}
}
/*
* Remove the given entry from the blocked list
*/
void
{
if (pm_blocked_list == p) {
pm_blocked_list = p->pr_next;
} else {
}
}
/*
* Remove the given control struct from the given list
*/
static void
{
if (*list == p) {
} else {
}
}
/*
* Stick the control struct specified on the front of the list
*/
static void
{
pscc_t *h; /* entry at head of list */
*list = p;
} else {
p->pscc_next = h;
h->pscc_prev = p;
*list = p;
}
}
/*
* If dip is NULL, process is closing "clone" clean up all its registrations.
* Otherwise only clean up those for dip because process is just giving up
* control of a direct device.
*/
void
{
int found = 0;
for (p = pm_pscc_interest; p; p = pn) {
if (p->pscc_clone == clone) {
psce = p->pscc_entries;
sizeof (pm_state_change_t) * PSCCOUNT);
kmem_free(p, sizeof (*p));
}
}
pm_interest[clone] = 0;
}
found = 0;
for (p = pm_pscc_direct; p; p = pn) {
found++;
/*
* Remove from control list
*/
/*
* If we're the last reference, free the
* entries struct.
*/
psce = p->pscc_entries;
PSCCOUNT * sizeof (pm_state_change_t));
} else {
psce->psce_references--;
}
kmem_free(p, sizeof (*p));
}
}
}
/*
* Search the indicated list for an entry that matches clone, and return a
* pointer to it. To be interesting, the entry must have something ready to
* be passed up to the controlling process.
* The returned entry will be locked upon return from this call.
*/
static psce_t *
{
pscc_t *p;
if (clone == p->pscc_clone) {
psce = p->pscc_entries;
return (psce);
} else {
}
}
}
return (NULL);
}
/*
* Find an entry for a particular clone in the direct list.
*/
psce_t *
{
}
/*
* Find an entry for a particular clone in the interest list.
*/
psce_t *
{
}
/*
* Put the given entry at the head of the blocked list
*/
void
{
if (pm_blocked_list != NULL) {
p->pr_next = pm_blocked_list;
pm_blocked_list->pr_prev = p;
pm_blocked_list = p;
} else {
pm_blocked_list = p;
}
}
/*
* Sets every power managed device back to its default threshold
*/
void
{
(void *) &pm_system_idle_threshold);
}
static int
{
if (!PM_GET_PM_INFO(dip))
return (DDI_WALK_CONTINUE);
return (DDI_WALK_CONTINUE);
}
/*
* Returns the current threshold value (in seconds) for the indicated component
*/
int
{
return (DDI_FAILURE);
} else {
return (DDI_SUCCESS);
}
}
/*
* To be called when changing the power level of a component of a device.
* On some platforms, changing power on one device may require that power
* be changed on other, related devices in the same transaction. Thus, we
* always pass this request to the platform power manager so that all the
* affected devices will be locked.
*/
void
{
int result;
}
/*
* Release the lock (or locks) acquired to change the power of a device.
* See comments for pm_lock_power.
*/
void
{
int result;
}
/*
* Attempt (without blocking) to acquire the lock(s) needed to change the
* power of a component of a device. See comments for pm_lock_power.
*
* Return: 1 if lock(s) acquired, 0 if not.
*/
int
{
int result;
return (result);
}
/*
* Lock power state of a device.
*
* The implementation handles a special case where another thread may have
* the lock cannot be acquired immediately, we check to see if this thread
* is registered as a borrower of the lock. If so, we may proceed without
* the lock. This assumes that the lending thread blocks on the completion
* of this thread.
*
* Note 1: for use by ppm only.
*
* Note 2: On failing to get the lock immediately, we search lock_loan list
* for curthread (as borrower of the lock). On a hit, we check that the
* lending thread already owns the lock we want. It is safe to compare
* devi_busy_thread and thread id of the lender because in the == case (the
* only one we care about) we know that the owner is blocked. Similarly,
* If we find that curthread isn't registered as a lock borrower, it is safe
* to use the blocking call (ndi_devi_enter) because we know that if we
* weren't already listed as a borrower (upstream on the call stack) we won't
* become one.
*/
void
{
/* if the lock is available, we are done. */
return;
/* see if our thread is registered as a lock borrower. */
break;
/* if this thread not already registered, it is safe to block */
else {
/* registered: does lender own the lock we want? */
} else /* no: just block for it */
}
}
/*
* Drop the lock on the device's power state. See comment for
* pm_lock_power_single() for special implementation considerations.
*
* Note: for use by ppm only.
*/
void
{
/* optimization: mutex not needed to check empty list */
return;
}
/* see if our thread is registered as a lock borrower. */
break;
/* we acquired the lock directly, so return it */
}
/*
* Try to take the lock for changing the power level of a component.
*
* Note: for use by ppm only.
*/
int
{
}
#ifdef DEBUG
/*
* The following are used only to print out data structures for debugging
*/
void
{
int i;
pm_log("%p: %s keeper %s, kept %s, kept count %d, next %p\n",
if (rp->pdr_kept_count != 0) {
pm_log("kept list = ");
i = 0;
while (i < rp->pdr_kept_count) {
i++;
}
pm_log("\n");
}
}
}
void
{
pm_log("\tmaj %d, flags %x, noinvolpm %d %s\n",
}
#endif
/*
* Attempt to apply the thresholds indicated by rp to the node specified by
* dip.
*/
void
{
int i, j;
return;
}
/*
* Here we do the special case of a device threshold
*/
if (PM_SCANABLE(dip))
return;
}
for (i = 0; i < comps; i++) {
for (j = 0; j < ep->pte_numthresh; j++) {
i, ep->pte_thresh[j]))
}
ep++;
}
if (PM_SCANABLE(dip))
}
/*
* Returns true if the threshold specified by rp could be applied to dip
* (that is, the number of components and transitions are the same)
*/
int
{
int comps, i;
rp->ptr_physpath))
return (0);
}
/*
* Special case: we represent the PM_SET_DEVICE_THRESHOLD case by
* an entry with numcomps == 0, (since we don't know how many
* components there are in advance). This is always a valid
* spec.
*/
if (rp->ptr_numcomps == 0) {
return (1);
}
return (0);
}
for (i = 0; i < comps; i++) {
if ((ep + i)->pte_numthresh !=
(ep + i)->pte_numthresh))
return (0);
}
}
return (1);
}
/*
* Remove any recorded threshold for device physpath
* We know there will be at most one.
*/
void
{
if (pptr) {
} else {
}
break;
}
}
}
/*
* Discard all recorded thresholds. We are returning to the default pm state.
*/
void
pm_discard_thresholds(void)
{
while (pm_thresh_head) {
rp = pm_thresh_head;
}
}
/*
* Discard all recorded dependencies. We are returning to the default pm state.
*/
void
pm_discard_dependencies(void)
{
int i;
#ifdef DEBUG
prdeps("Before discard\n");
#endif
#ifdef DEBUG
prdeps("After discard\n");
#endif
while (pm_dep_head) {
rp = pm_dep_head;
if (!rp->pdr_isprop) {
} else {
pm_prop_deps--;
}
if (rp->pdr_kept_count) {
for (i = 0; i < rp->pdr_kept_count; i++) {
}
rp->pdr_kept_count * sizeof (char **));
}
}
}
static int
{
char *pathbuf;
return (DDI_WALK_CONTINUE);
pm_free_keeper(pathbuf, 0);
return (DDI_WALK_CONTINUE);
}
static int
{
char *pathbuf;
return (DDI_WALK_CONTINUE);
}
static int
{
char *pathbuf;
return (DDI_WALK_CONTINUE);
}
static char *
{
switch (type) {
case PM_DEP_WK_POWER_ON:
return ("power on");
case PM_DEP_WK_POWER_OFF:
return ("power off");
case PM_DEP_WK_DETACH:
return ("detach");
case PM_DEP_WK_REMOVE_DEP:
return ("remove dep");
case PM_DEP_WK_BRINGUP_SELF:
return ("bringup self");
case PM_DEP_WK_RECORD_KEEPER:
return ("add dependent");
return ("add dependent property");
case PM_DEP_WK_KEPT:
return ("kept");
case PM_DEP_WK_KEEPER:
return ("keeper");
case PM_DEP_WK_ATTACH:
return ("attach");
case PM_DEP_WK_CHECK_KEPT:
return ("check kept");
case PM_DEP_WK_CPR_SUSPEND:
return ("suspend");
case PM_DEP_WK_CPR_RESUME:
return ("resume");
default:
return ("unknown");
}
}
static void
{
int count = 0;
continue;
continue;
if (kept) {
keeper))
}
}
}
}
/*
* Called when we are just released from direct PM. Bring ourself up
* if our keeper is up since dependency is not honored while a kept
* device is under direct PM.
*/
static void
{
int i, j;
return;
if (dp->pdr_kept_count == 0)
continue;
for (i = 0; i < dp->pdr_kept_count; i++) {
continue;
if (keeper) {
for (j = 0; j < PM_NUMCMPTS(keeper);
j++) {
if (PM_CURPOWER(keeper, j)) {
"%d is up\n", pmf, j))
up++;
}
}
if (up) {
}
}
}
}
}
static void
{
int ret;
case PM_DEP_WK_POWER_ON:
/* Bring up the kept devices and put a hold on them */
break;
case PM_DEP_WK_POWER_OFF:
/* Release the kept devices */
break;
case PM_DEP_WK_DETACH:
break;
case PM_DEP_WK_REMOVE_DEP:
break;
case PM_DEP_WK_BRINGUP_SELF:
/*
* We deferred satisfying our dependency till now, so satisfy
* it again and bring ourselves up.
*/
break;
case PM_DEP_WK_RECORD_KEEPER:
break;
break;
case PM_DEP_WK_KEPT:
ret))
break;
case PM_DEP_WK_KEEPER:
break;
case PM_DEP_WK_ATTACH:
break;
case PM_DEP_WK_CHECK_KEPT:
break;
case PM_DEP_WK_CPR_SUSPEND:
break;
case PM_DEP_WK_CPR_RESUME:
break;
default:
ASSERT(0);
break;
}
/*
* Free the work structure if the requester is not waiting
* Otherwise it is the requester's responsiblity to free it.
*/
if (work->pdw_keeper)
} else {
/*
* Notify requester if it is waiting for it.
*/
}
}
/*
* Process PM dependency requests.
*/
static void
pm_dep_thread(void)
{
"pm_dep_thread");
for (;;) {
if (pm_dep_thread_workq == NULL) {
}
if (pm_dep_thread_tail == work)
}
/*NOTREACHED*/
}
/*
* Set the power level of the indicated device to unknown (if it is not a
* backwards compatible device), as it has just been resumed, and it won't
* know if the power was removed or not. Adjust parent's kidsupcnt if necessary.
*/
void
{
int i, count = 0;
for (i = 0; i < PM_NUMCMPTS(dip); i++)
/*
* Count this as a power cycle if we care
*/
for (i = 0; i < PM_NUMCMPTS(dip); i++)
}
}
/*
* This function advises the caller whether it should make a power-off
* transition at this time or not. If the transition is not advised
* at this time, the time that the next power-off transition can
* be made from now is returned through "intervalp" pointer.
* This function returns:
*
* 1 power-off advised
* 0 power-off not advised, intervalp will point to seconds from
* now that a power-off is advised. If it is passed the number
* of years that policy specifies the device should last,
* a large number is returned as the time interval.
* -1 error
*/
int
{
char *ptr;
return (-1);
}
/*
* Power cycles of the scsi drives are distributed
* over 5 years with the following percentage ratio:
*
* 30%, 25%, 20%, 15%, and 10%
*
* The power cycle quota for each year is distributed
* linearly through out the year. The equation for
* determining the expected cycles is:
*
* e = a * (n / y)
*
* e = expected cycles
* a = allocated cycles for this year
* n = number of seconds since beginning of this year
* y = number of seconds in a year
*
* Note that beginning of the year starts the day that
* the drive has been put on service.
*
* If the drive has passed its expected cycles, we
* can determine when it can start to power cycle
* again to keep it on track to meet the 5-year
* life expectancy. The equation for determining
* when to power cycle is:
*
* w = y * (c / a)
*
* w = when it can power cycle again
* y = number of seconds in a year
* c = current number of cycles
* a = allocated cycles for the year
*
*/
return (-1);
}
return (0);
}
/*
* convert service date to time_t
*/
/*
* scsi standard does not specify WW data,
* could be (00-51) or (01-52)
*/
if (service_years < 0 ||
return (-1);
}
/*
* calculate service date in seconds-since-epoch,
* adding one day for each leap-year.
*
* (years-since-epoch + 2) fixes integer truncation,
* example: (8) leap-years during [1972, 2000]
* (2000 - 1970) = 30; and (30 + 2) / 4 = 8;
*/
(service_weeks * DC_SPW) +
now = gethrestime_sec();
/*
* since the granularity of 'svc_date' is day not second,
* 'now' should be rounded up to full day.
*/
if (service_seconds > now) {
return (-1);
}
/*
* NOTE - Leap years are not considered in the calculations
* below.
*/
if ((full_years >= DC_SCSI_NPY) &&
return (1);
/*
* Determine what is the normal cycle usage for the
* device at the beginning and the end of this year.
*/
lower_bound_cycles = (!full_years) ? 0 :
return (1);
/*
* The linear slope that determines how many cycles
* are allowed this year is number of seconds
* passed this year over total number of seconds in a year.
*/
return (1);
/*
* The transition is not advised now but we can
* determine when the next transition can be made.
*
* Depending on how many cycles the device has been
* over-used, we may need to skip years with
* different percentage quota in order to determine
* when the next transition can be made.
*/
while (cycles_over > cycles_diff) {
full_years++;
if (full_years >= DC_SCSI_NPY) {
return (0);
}
}
/*
* The linear slope that determines when the next transition
* can be made is the relative position of used cycles within a
* year over total number of cycles within that year.
*/
*intervalp))
return (0);
/*
* power cycles of SATA disks are reported from SMART
* attributes.
*/
return (0);
} else
return (1);
}
return (-1);
}
/*
* Nexus drivers call into pm framework to indicate which child driver is about
* to be installed. In some platforms, ppm may need to configure the hardware
* for successful installation of a driver.
*/
int
{
NULL));
} else {
#ifdef DEBUG
/* pass it to the default handler so we can debug things */
#endif
}
return (DDI_SUCCESS);
}
/*
* Bring parent of a node that is about to be probed up to full power, and
* arrange for it to stay up until pm_post_probe() or pm_post_attach() decide
* it is time to let it go down again
*/
void
{
int result;
} else {
#ifdef DEBUG
/* pass it to the default handler so we can debug things */
#endif
}
}
int
{
int ret;
} else if (!PM_GET_PM_INFO(dip))
return (DDI_SUCCESS);
if (ret != DDI_SUCCESS)
return (ret);
}
/*
* This routine is called by devfs during its walk to unconfigue a node.
* If the call is due to auto mod_unloads and the dip is not at its
* full power, we return DDI_FAILURE to terminate the walk, otherwise
* return DDI_SUCCESS.
*/
int
{
int ret;
} else if (!PM_GET_PM_INFO(dip))
return (DDI_SUCCESS);
flags))
*held = 0;
/*
* If the dip is a leaf node, don't power it up.
*/
if (!ddi_get_child(dip))
return (DDI_SUCCESS);
/*
* Do not power up the node if it is called due to auto-modunload.
*/
return (DDI_FAILURE);
*held = 1;
if (ret != DDI_SUCCESS) {
*held = 0;
}
return (ret);
}
/*
* Notify ppm of attach action. Parent is already held at full power by
* probe action.
*/
void
{
int result;
/*
* Initialize and fill in the PPM cookie
*/
/*
* DDI_ATTACH and DDI_RESUME cmds need to call platform specific
* Power Management stuff. DDI_RESUME also has to purge it's
* powerlevel information.
*/
switch (cmd) {
case DDI_ATTACH:
}
#ifdef DEBUG
else {
}
#endif
break;
case DDI_RESUME:
}
#ifdef DEBUG
else {
}
#endif
break;
case DDI_PM_RESUME:
break;
default:
}
}
/*
* Nexus drivers call into pm framework to indicate which child driver is
* being uninstalled. In some platforms, ppm may need to reconfigure the
* hardware since the device driver is no longer installed.
*/
int
{
NULL));
} else {
#ifdef DEBUG
/* pass it to the default handler so we can debug things */
#endif
}
return (DDI_SUCCESS);
}
/*
* Decrement kidsupcnt so scan can turn the parent back off if it is idle
* Also notify ppm of result of probe if there is a ppm that cares
*/
void
{
int result;
}
#ifdef DEBUG
else {
}
#endif
}
void
{
return;
} else if (!PM_GET_PM_INFO(dip))
return;
}
void
{
return;
} else if (!PM_GET_PM_INFO(dip))
return;
held))
if (!held)
return;
/*
* We have held power in pre_unconfig, release it here.
*/
}
/*
* Notify ppm of result of attach if there is a ppm that cares
*/
void
{
int result;
return;
if (ret == DDI_SUCCESS) {
/*
* Attach succeeded, so proceed to doing post-attach pm tasks
*/
} else {
/*
* Attach may have got pm started before failing
*/
}
}
#ifdef DEBUG
else {
}
#endif
}
/*
* Notify ppm of attach action. Parent is already held at full power by
* probe action.
*/
void
{
int result;
switch (cmd) {
case DDI_DETACH:
} else {
#ifdef DEBUG
/* pass to the default handler so we can debug things */
#endif
}
break;
default:
break;
}
}
/*
* Dip is either a leaf node that exported "no-involuntary-power-cycles" prop.,
* (if devi_pm_noinvol count is 0) or an ancestor of such a node. We need to
* make an entry to record the details, which includes certain flag settings.
*/
static void
{
major_t pm_path_to_major(char *);
/*
* If we haven't actually seen the node attached, it is hard to figure
* out its major. If we could hold the node by path, we would be much
* happier here.
*/
if (major == DDI_MAJOR_T_NONE) {
} else {
}
if (comp < 0) {
/* insert before current entry */
if (pp) {
} else {
}
#ifdef DEBUG
if (pm_debug & PMD_NOINVOL)
pr_noinvol("record_invol_path exit0");
#endif
return;
} else if (comp == 0) {
}
}
/*
* If we did not find an entry in the list that this should go before,
* then it must go at the end
*/
if (pp) {
} else {
}
#ifdef DEBUG
if (pm_debug & PMD_NOINVOL)
pr_noinvol("record_invol_path exit");
#endif
}
void
{
char *pathbuf;
int pm_all_components_off(dev_info_t *);
/*
* If this child's detach will be holding up its ancestors, then we
* allow for an exception to that if all children of this type have
* gone down voluntarily.
* Now walk down the tree incrementing devi_pm_noinvolpm
*/
dip);
}
void
{
int result;
case DDI_DETACH:
}
#ifdef DEBUG
else {
}
#endif
if (ret == DDI_SUCCESS) {
/*
* For hotplug detach we assume it is *really* gone
*/
(PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
~(PMC_NO_INVOL | PMC_NOINVOL_DONE);
/*
* If console fb is detaching, then we don't need to
* worry any more about it going off (pm_detaching has
* brought up all components)
*/
ASSERT(pm_cfb_comps_off == 0);
}
} else {
ASSERT(pm_cfb_comps_off == 0);
}
}
break;
case DDI_PM_SUSPEND:
break;
case DDI_SUSPEND:
break; /* legal, but nothing to do */
default:
#ifdef DEBUG
panic("pm_post_detach: unrecognized cmd %d for detach",
/*NOTREACHED*/
#else
break;
#endif
}
}
/*
* Called after vfs_mountroot has got the clock started to fix up timestamps
* that were set when root bush drivers attached. hresttime was 0 then, so the
* devices look busy but have a 0 busycnt
*/
int
{
int i;
if (!info)
return (DDI_WALK_CONTINUE);
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
}
return (DDI_WALK_CONTINUE);
}
/*
* Called at attach time to see if the device being attached has a record in
* the no involuntary power cycles list. If so, we do some bookkeeping on the
* parents and set a flag in the dip
*/
void
{
char *pathbuf;
int wasvolpmd;
int found = 0;
return;
found++;
break;
}
}
if (!found) {
return;
}
/*
* Handle special case of console fb
*/
}
(PMC_NO_INVOL | PMC_CONSOLE_FB)) ||
"wasvolpmd=%d, flags=%x, path=%s\n", pmf,
/*
* free the entry in hopes the list will now be empty
* and we won't have to search it any more until the
* device detaches
*/
if (pp) {
} else {
}
/*
* Now walk up the tree decrementing devi_pm_noinvolpm
* (and volpmd if appropriate)
*/
(void) pm_noinvol_update(PM_BP_NOINVOL_ATTACH, 0,
#ifdef DEBUG
if (pm_debug & PMD_NOINVOL)
pr_noinvol("noinvol_specd exit");
#endif
return;
}
}
}
int
{
int i;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
return (0);
}
return (1); /* all off */
}
/*
* Make sure that all "no involuntary power cycles" devices are attached.
* Called before doing a cpr suspend to make sure the driver has a say about
* the power cycle
*/
int
pm_reattach_noinvol(void)
{
char *path;
/*
* Prevent the modunload thread from unloading any modules until we
* have completely stopped all kernel threads.
*/
/*
* Forget we'v ever seen any entry
*/
ip->ni_persistent = 0;
}
#ifdef PMDDEBUG
#endif
if (ip->ni_persistent) {
/*
* If we weren't able to make this entry
* go away, then we give up, as
* resulted in this entry being deleted
*/
return (0);
}
ip->ni_persistent++;
"driver", path);
return (0);
} else {
/*
* Since the modunload thread is stopped, we
* don't have to keep the driver held, which
* saves a ton of bookkeeping
*/
goto restart;
}
} else {
continue;
}
}
return (1);
}
void
pm_reattach_noinvol_fini(void)
{
}
/*
* Display pm support code
*/
/*
* console frame-buffer power-mgmt gets enabled when debugging
* services are not present or console fbpm override is set
*/
void
{
extern int obpdebug;
char *devname;
int devname_len;
extern dev_info_t *fbdip;
/*
* By virtue of this function being called (from consconfig),
* we know stdout is a framebuffer.
*/
if (pm_cfb_override == 0) {
/*
* Console is frame buffer, but we want to suppress
* pm on it because of debugging setup
*/
pm_cfb_enabled = 0;
"console power management.");
/*
* however, we still need to know which is the console
* fb in order to suppress pm on it
*/
} else {
"kmdb(1M) for interaction with power management.");
}
}
#ifdef DEBUG
/*
* IF console is fb and is power managed, don't do prom_printfs from
* pm debug macro
*/
if (pm_cfb_enabled && !pm_debug_to_console) {
if (pm_debug)
prom_printf("pm debug output will be to log only\n");
}
#endif
/* if the driver is attached */
/*
* We set up here as if the driver were power manageable in case
* we get a later attach of a pm'able driver (which would result
* in a panic later)
*/
#ifdef DEBUG
}
#endif
} else {
char *ep;
*ep = '\0';
/*
* Walk up the tree incrementing
* devi_pm_noinvolpm
*/
(void) pm_noinvol_update(PM_BP_NOINVOL_CFB,
break;
} else {
}
}
}
}
void
pm_cfb_rele(void)
{
/*
* this call isn't using the console any more, it is ok to take it
* down if the count goes to 0
*/
cfb_inuse--;
}
/*
* software interrupt handler for fbpm; this function exists because we can't
* bring up the frame buffer power from above lock level. So if we need to,
* we instead schedule a softint that runs this routine and takes us into
* debug_enter (a bit delayed from the original request, but avoiding a panic).
*/
static uint_t
{
int rval = DDI_INTR_UNCLAIMED;
if (pm_soft_pending) {
debug_enter((char *)NULL);
/* acquired in debug_enter before calling pm_cfb_trigger */
pm_cfb_rele();
} else
return (rval);
}
void
pm_cfb_setup_intr(void)
{
extern void prom_set_outfuncs(void (*)(void), void (*)(void));
void pm_cfb_check_and_powerup(void);
#ifdef PMDDEBUG
#endif
if (!stdout_is_framebuffer) {
return;
}
/*
* setup software interrupt handler
*/
panic("pm: unable to register soft intr.");
}
/*
* Checks to see if it is safe to write to the console wrt power management
* (i.e. if the console is a framebuffer, then it must be at full power)
* returns 1 when power is off (power-up is needed)
* returns 0 when power is on (power-up not needed)
*/
int
pm_cfb_check_and_hold(void)
{
/*
* cfb_dip is set iff console is a power manageable frame buffer
* device
*/
extern int modrootloaded;
cfb_inuse++;
if (modrootloaded && cfb_dip) {
/*
* don't power down the frame buffer, the prom is using it
*/
if (pm_cfb_comps_off) {
return (1);
}
}
return (0);
}
/*
* turn on cfb power (which is known to be off).
* Must be called below lock level!
*/
void
pm_cfb_powerup(void)
{
int norm;
int unused;
#ifdef DEBUG
/*
* Can't reenter prom_prekern, so suppress pm debug messages
* (still go to circular buffer).
*/
#endif
PM_CANBLOCK_BYPASS, 0, &unused);
}
#ifdef DEBUG
#endif
}
/*
* Check if the console framebuffer is powered up. If not power it up.
* Note: Calling pm_cfb_check_and_hold has put a hold on the power state which
* must be released by calling pm_cfb_rele when the console fb operation
* is completed.
*/
void
pm_cfb_check_and_powerup(void)
{
if (pm_cfb_check_and_hold())
}
/*
* Trigger a low level interrupt to power up console frame buffer.
*/
void
pm_cfb_trigger(void)
{
return;
/*
* If the machine appears to be hung, pulling the keyboard connector of
* the console will cause a high level interrupt and go to debug_enter.
* But, if the fb is powered down, this routine will be called to bring
* it up (by generating a softint to do the work). If a second attempt
* at triggering this softint happens before the first one completes,
* we panic as softints are most likely not being handled.
*/
if (pm_soft_pending) {
panicstr = "pm_cfb_trigger: failed to enter the debugger";
/* NOTREACHED */
}
}
static major_t i_path_to_major(char *, char *);
{
np++;
else
*ap = '\0';
}
return (ret);
}
#ifdef DEBUG
#ifndef sparc
#endif
char *pm_msgp;
char *pm_bufend;
#if defined(__x86)
void pm_printf(char *s);
#endif
/*PRINTFLIKE1*/
void
{
}
if (!pm_divertdebug)
#if defined(__x86)
if (pm_tty) {
if (pm_extra_cr)
pm_printf("\r");
}
#endif
} else {
#if defined(__x86)
if (pm_tty) {
if (pm_extra_cr)
pm_printf("\r");
}
#endif
if (!pm_divertdebug)
}
}
#endif /* DEBUG */
/*
* We want to save the state of any directly pm'd devices over the suspend/
* resume process so that we can put them back the way the controlling
* process left them.
*/
void
pm_save_direct_levels(void)
{
pm_processes_stopped = 1;
}
static int
{
int i;
int *ip;
if (!info)
return (DDI_WALK_CONTINUE);
sizeof (int), KM_SLEEP);
} else {
}
/* autopm and processes are stopped, ok not to lock power */
for (i = 0; i < PM_NUMCMPTS(dip); i++)
/*
* There is a small window between stopping the
* processes and setting pm_processes_stopped where
* a driver could get hung up in a pm_raise_power()
* call. Free any such driver now.
*/
}
return (DDI_WALK_CONTINUE);
}
void
pm_restore_direct_levels(void)
{
/*
* If cpr didn't call pm_save_direct_levels, (because stopping user
* threads failed) then we don't want to try to restore them
*/
if (!pm_processes_stopped)
return;
pm_processes_stopped = 0;
}
static int
{
int *ip;
if (!info)
return (DDI_WALK_CONTINUE);
} else {
}
/*
* Because fb drivers fail attempts to turn off the
* fb when the monitor is on, but treat a request to
* turn on the monitor as a request to turn on the
* fb too, we process components in descending order
* Because autopm is disabled and processes aren't
* running, it is ok to examine current power outside
* of the power lock
*/
continue;
"to restore power level of "
"component %d of directly "
"power manged device %s@%s"
" to %d",
"%s@%s(%s#%d)[%d] exact(%d)->%d, "
}
}
if (nc > 2) {
}
}
return (DDI_WALK_CONTINUE);
}
/*
* Stolen from the bootdev module
* attempt to convert a path to a major number
*/
static major_t
{
}
return (maj);
}
/*
* When user calls rem_drv, we need to forget no-involuntary-power-cycles state
* An entry in the list means that the device is detached, so we need to
* adjust its ancestors as if they had just seen this attach, and any detached
* ancestors need to have their list entries adjusted.
*/
void
{
/*
* Serialize removal of drivers. This is to keep ancestors of
* a node that is being deleted from getting deleted and added back
* with different counters.
*/
}
static void adjust_ancestors(char *, int);
static int pm_is_noinvol_ancestor(pm_noinvol_t *);
static void pm_noinvol_process_ancestors(char *);
/*
* This routine is called recursively by pm_noinvol_process_ancestors()
*/
static void
{
int wasvolpmd;
continue;
/*
* If it is an ancestor of no-invol node, which is
* not removed, skip it. This is to cover the case of
* ancestor removed without removing its descendants.
*/
if (pm_is_noinvol_ancestor(ip)) {
continue;
}
/*
* remove the entry from the list
*/
if (pp) {
} else {
}
/*
* Had an ancestor been removed before this node, it would have
* been skipped. Adjust the no-invol counters for such skipped
* ancestors.
*/
goto again;
}
}
/*
* returns 1, if *aip is a ancestor of a no-invol node
* 0, otherwise
*/
static int
{
continue;
/*
* To be an ancestor, the path must be an initial substring of
* the descendent, and end just before a '/' in the
* descendent's path.
*/
return (1);
}
return (0);
}
/*
* scan through the pm_noinvolpm list adjusting ancestors of the current
* node; Modifies string *path.
*/
static void
{
char *cp;
char *pathbuf;
/*
* First we look up the ancestor's dip. If we find it, then we
* adjust counts up the tree
*/
/* if no ancestors, then nothing to do */
return;
}
*cp = '\0';
if (locked != DDI_MAJOR_T_NONE)
} else {
char *apath;
/*
* Now check for ancestors that exist only in the list
*/
/*
* This can only happen once. Since we have to drop
* the lock, we need to extract the relevant info.
*/
lp->ni_noinvolpm--;
}
/*
* remove the entry from the list, if there
* are no more no-invol descendants and node
* itself is not a no-invol node.
*/
if (!(lp->ni_noinvolpm ||
if (pp) {
"%s, prev is %s\n", pmf,
} else {
}
lock_held = 0;
/* restore apath */
}
break;
}
}
if (lock_held)
}
}
/*
* Do no-invol processing for any ancestors i.e. adjust counters of ancestors,
* which were skipped even though their drivers were removed.
*/
static void
{
return;
}
}
}
/*
* Returns true if (detached) device needs to be kept up because it exported the
* "no-involuntary-power-cycles" property or we're pretending it did (console
* fb case) or it is an ancestor of such a device and has used up the "one
* free cycle" allowed when all such leaf nodes have voluntarily powered down
* upon detach. In any event, we need an exact hit on the path or we return
* false.
*/
int
{
int ret = 0;
ret = 1;
break;
}
#ifdef DEBUG
path))
#endif
break;
}
}
return (ret);
}
int
{
}
#ifdef DEBUG
/*
* Return true if all components of the console frame buffer are at
* "normal" power, i.e., fully on. For the case where the console is not
* a framebuffer, we also return true
*/
int
pm_cfb_is_up(void)
{
return (pm_cfb_comps_off == 0);
}
#endif
/*
* Preventing scan from powering down the node by incrementing the
* kidsupcnt.
*/
void
{
}
/*
* Releasing the hold by decrementing the kidsupcnt allowing scan
* to power down the node if all conditions are met.
*/
void
{
}
/*
* A wrapper of pm_all_to_normal() to power up a dip
* to its normal level
*/
int
{
ASSERT(!(servicing_interrupt()));
/*
* in case this node is not already participating pm
*/
if (!PM_GET_PM_INFO(dip)) {
if (!DEVI_IS_ATTACHING(dip))
return (DDI_SUCCESS);
return (DDI_FAILURE);
if (!PM_GET_PM_INFO(dip))
return (DDI_SUCCESS);
}
}
int
{
return (DDI_WALK_CONTINUE);
/*
* Currently pm_cpr_callb/resume code is the only caller
* and it needs to make sure that stopped scan get
* reactivated. Otherwise, rescan walk needn't reactive
* stopped scan.
*/
return (DDI_WALK_CONTINUE);
}
static dev_info_t *
{
return (wdip);
}
return (NULL);
}
int
{
char *pathbuf;
int errno = 0;
pm_decode_op(op)))
switch (op) {
case BUS_POWER_CHILD_PWRCHG:
/*
* If the node is an involved parent, it needs to
* power up the node as it is needed. There is nothing
* else the framework can do here.
*/
if (PM_WANTS_NOTIFICATION(cdip)) {
}
/*
* we presume that the parent needs to be up in
* order for the child to change state (either
* because it must already be on if the child is on
* (and the pm_all_to_normal_nexus() will be a nop)
* or because it will need to be on for the child
* to come on; so we make the call regardless
*/
if (cinfo) {
if (ret != DDI_SUCCESS) {
return (ret);
}
}
result);
} else {
result);
}
return (ret);
case BUS_POWER_NEXUS_PWRUP:
*(int *)result = DDI_FAILURE;
return (DDI_FAILURE);
}
&errno;
return (ret);
case BUS_POWER_NOINVOL:
/* In case of rem_drv, the leaf node has been removed */
return (DDI_SUCCESS);
if (PM_WANTS_NOTIFICATION(cdip)) {
("%s: call bus_power for %s@%s(%s#%d)\n",
(void) pm_noinvol_update_node(cdip,
bpi);
return (ret);
} else {
("%s: walk down to %s@%s(%s#%d)\n", pmf,
/*
* Update the current node.
*/
(void) pm_noinvol_update_node(cdip,
bpi);
return (ret);
}
} else {
/*
* For attach, detach, power up:
* Do nothing for leaf node since its
* counts are already updated.
* For CFB and driver removal, since the
* path and the target dip passed in is up to and incl.
* the immediate ancestor, need to do the update.
*/
return (DDI_SUCCESS);
}
default:
return (DDI_FAILURE);
}
}
static int
{
#ifdef DEBUG
#endif
int comps_off_incr = 0;
int dodeps;
#ifdef PMDDEBUG
#endif
int work_type;
*iresp = DDI_SUCCESS;
*errnop = 0;
pm_decode_op(op)))
/*
* The following set of conditions indicate we are here to handle a
* driver's pm_[raise|lower]_power request, but the device is being
* power managed (PM_DIRECT_PM) by a user process. For that case
* we want to pm_block and pass a status back to the caller based
* on whether the controlling process's next activity on the device
* matches the current request or not. This distinction tells
* downstream functions to avoid calling into a driver or changing
* the framework's power state. To actually block, we need:
*
* PM_ISDIRECT(dip)
* no reason to block unless a process is directly controlling dev
* direction != PM_LEVEL_EXACT
* EXACT is used by controlling proc's PM_SET_CURRENT_POWER ioctl
* !pm_processes_stopped
* don't block if controlling proc already be stopped for cpr
* canblock != PM_CANBLOCK_BYPASS
* our caller must not have explicitly prevented blocking
*/
/* releases dip lock */
continue;
}
}
}
/* BC device is never scanned, so power will stick until we are done */
direction != PM_LEVEL_DOWNONLY) {
/* *resultp set by pm_set_power */
return (DDI_FAILURE);
}
}
if (PM_WANTS_NOTIFICATION(pdip)) {
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
} else {
/*
* Since we don't know what the actual power level is,
* we place a power hold on the parent no matter what
* component and level is changing.
*/
}
/*
* It's possible that a call was made to pm_update_maxpower()
* on another thread before we took the lock above. So, we need to
* make sure that this request isn't processed after the
* change of power executed on behalf of pm_update_maxpower().
*/
pmf))
ret = DDI_FAILURE;
*iresp = DDI_FAILURE;
goto post_notify;
}
switch (direction) {
case PM_LEVEL_UPONLY:
/* Powering up */
"at or above the requested level.\n", pmf))
*iresp = DDI_SUCCESS;
ret = DDI_SUCCESS;
goto post_notify;
}
break;
case PM_LEVEL_EXACT:
/* specific level request */
"at the requested level.\n", pmf))
*iresp = DDI_SUCCESS;
ret = DDI_SUCCESS;
goto post_notify;
if (!pm_cfb_enabled) {
("%s: !pm_cfb_enabled, fails\n", pmf))
*iresp = DDI_FAILURE;
ret = DDI_FAILURE;
goto post_notify;
}
while (cfb_inuse) {
ret = DDI_FAILURE;
*iresp = DDI_FAILURE;
goto post_notify;
}
}
}
break;
case PM_LEVEL_DOWNONLY:
/* Powering down */
(cp->pmc_busycount > 0) ||
#ifdef DEBUG
"kidsupcnt != 0\n", pmf))
if (cp->pmc_busycount > 0)
"device become busy\n", pmf))
"hasn't been idle long enough\n", pmf))
#endif
*iresp = DDI_FAILURE;
ret = DDI_FAILURE;
goto post_notify;
"or below the requested level.\n", pmf))
*iresp = DDI_SUCCESS;
ret = DDI_SUCCESS;
goto post_notify;
}
break;
}
/*
* Pre-adjust pm_cfb_comps_off if lowering a console fb
* component from full power. Remember that we tried to
* lower power in case it fails and we need to back out
* the adjustment.
*/
}
#ifdef DEBUG
/*
* All descendents of this node should already be powered off.
*/
(void *)&pdpchk);
}
}
#endif
/*
* Post-adjust pm_cfb_comps_off if we brought an fb component
* back up to full power.
*/
}
dodeps = 0;
} else {
int i;
dodeps = 1;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
/* if some component still on */
if (PM_CURPOWER(dip, i)) {
dodeps = 0;
break;
}
}
}
if (dodeps)
} else {
int i;
dodeps = 1;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
if (i == comp)
continue;
if (PM_CURPOWER(dip, i) > 0) {
dodeps = 0;
break;
}
}
}
if (dodeps)
}
if (dodeps) {
PM_DEP_NOWAIT, NULL, 0);
}
int old;
/* If old power cached during deadlock, use it. */
} else {
pm_ppm_devlist_t *p;
kmem_free(p, sizeof (pm_ppm_devlist_t));
}
}
/*
* If we are coming from a scan, don't do it again,
* else we can have infinite loops.
*/
if (!scan)
} else {
/* if we incremented pm_comps_off_count, but failed */
if (comps_off_incr > 0) {
}
}
/*
* This thread may have been in deadlock with pm_power_has_changed.
* Before releasing power lock, clear the flag which marks this
* condition.
*/
/*
* Update the old power level in the bus power structure with the
* actual power level before the transition was made to the new level.
* Some involved parents depend on this information to keep track of
* their children's power transition.
*/
if (*iresp != DDI_FAILURE)
if (PM_WANTS_NOTIFICATION(pdip)) {
} else {
/*
* Now that we know what power transition has occurred
* (if any), release the power hold. Leave the hold
* in effect in the case of OFF->ON transition.
*/
/*
* If the power transition was an ON->OFF transition,
* remove the power hold from the parent.
*/
}
return (DDI_FAILURE);
else
return (DDI_SUCCESS);
}
/*
* If an app (SunVTS or Xsun) has taken control, then block until it
* gives it up or makes the requested power level change, unless
* we have other instructions about blocking. Returns DDI_SUCCESS,
* DDI_FAILURE or EAGAIN (owner released device from directpm).
*/
static int
{
if (direction == PM_LEVEL_UPONLY) {
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
if (canblock == PM_CANBLOCK_FAIL) {
return (DDI_FAILURE);
}
if (canblock == PM_CANBLOCK_BLOCK) {
/*
* To avoid a deadlock, we must not hold the
* power lock when we pm_block.
*/
/* pm_block releases dip lock */
case PMP_RELEASE:
return (EAGAIN);
case PMP_SUCCEED:
return (DDI_SUCCESS);
case PMP_FAIL:
return (DDI_FAILURE);
}
} else {
ASSERT(0);
}
return (DDI_FAILURE); /* keep gcc happy */
}
static int
{
int *normal;
int i, ncomps;
int changefailed = 0;
return (DDI_FAILURE);
}
for (i = 0; i < ncomps; i++) {
changefailed++;
}
}
if (changefailed) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
{
int ret;
"volpmd %d wasvolpmd %d\n", pmf,
return (ret);
}
void
{
case PM_BP_NOINVOL_ATTACH:
if (req->bpni_wasvolpmd) {
"%s@%s(%s#%d) volpmd %d->%d\n", pmf,
}
break;
case PM_BP_NOINVOL_DETACH:
if (req->bpni_wasvolpmd) {
"%s@%s(%s#%d) volpmd %d->%d\n", pmf,
}
break;
case PM_BP_NOINVOL_REMDRV:
if (req->bpni_wasvolpmd) {
("%s: PM_BP_NOINVOL_REMDRV %s@%s(%s#%d) "
/*
* A power up could come in between and
* clear the volpmd, if that's the case,
* volpmd would be clear.
*/
}
break;
case PM_BP_NOINVOL_CFB:
("%s: PM_BP_NOIVOL_CFB %s@%s(%s#%d) noinvol %d->%d\n",
break;
case PM_BP_NOINVOL_POWER:
("%s: PM_BP_NOIVOL_PWR %s@%s(%s#%d) volpmd %d->%d\n",
req->bpni_volpmd))
break;
default:
break;
}
}
#ifdef DEBUG
static int
{
int i;
/* LINTED */
if (!info)
return (DDI_WALK_CONTINUE);
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
/* LINTED */
continue;
/* E_FUNC_SET_NOT_USED */
"%s@%s(%s#%d)[%d] is at %d\n", pmf,
"while its ancestor, %s@%s(%s#%d), is powering off!",
}
return (DDI_WALK_CONTINUE);
}
#endif
/*
* Record the fact that one thread is borrowing the lock on a device node.
* Use is restricted to the case where the lending thread will block until
* the borrowing thread (always curthread) completes.
*/
void
{
}
/*
* Return the borrowed lock. A thread can borrow only one.
*/
void
pm_return_lock(void)
{
break;
}
#if defined(__x86)
char
pm_getchar(void)
{
drv_usecwait(10);
return (inb(CPR_DATAREG));
}
void
pm_putchar(char c)
{
drv_usecwait(10);
outb(CPR_DATAREG, c);
}
void
pm_printf(char *s)
{
while (*s) {
pm_putchar(*s++);
}
}
#endif
int
{
int result = 0;
/* LINTED */
int ret;
return (result);
}