pm.c revision c42872d4489d6f0fbccfabe2a62f3c976ee1e5d6
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* pm This driver now only handles the ioctl interface. The scanning
* Not DDI compliant
*/
#include <sys/ddi_impldefs.h>
/*
* Minor number is instance<<8 + clone minor from range 1-255; (0 reserved
* for "original"
*/
#define PM_IDLEDOWN_TIME 10
extern int autopm_enabled;
extern pm_cpupm_t cpupm;
extern int pm_default_idle_threshold;
extern int pm_system_idle_threshold;
extern int pm_cpu_idle_threshold;
/*
* The soft state of the power manager. Since there will only
* one of these, just reference it through a static pointer.
*/
static struct pmstate {
int pm_instance; /* for ddi_get_instance() */
typedef struct pmstate *pm_state_t;
pm_open, /* open */
pm_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
pm_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
pm_chpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL, /* streamtab */
};
void **result);
DEVO_REV, /* devo_rev */
0, /* refcnt */
pm_getinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
pm_attach, /* attach */
pm_detach, /* detach */
nodev, /* reset */
&pm_cb_ops, /* driver operations */
NULL, /* bus operations */
NULL /* power */
};
"power management driver v%I%",
};
static struct modlinkage modlinkage = {
};
/* Local functions */
#ifdef DEBUG
static int print_info(dev_info_t *, void *);
#endif
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static int
{
int i;
switch (cmd) {
case DDI_ATTACH:
return (DDI_FAILURE);
DDI_PSEUDO, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
for (i = 0; i < PM_MAX_CLONE; i++)
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
{
int i;
switch (cmd) {
case DDI_DETACH:
/*
* Don't detach while idledown timeout is pending. Note that
* we already know we're not in pm_ioctl() due to framework
* synchronization, so this is a sufficient test
*/
if (pmstp->pm_idledown_id)
return (DDI_FAILURE);
for (i = 0; i < PM_MAX_CLONE; i++)
cv_destroy(&pm_clones_cv[i]);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int clone;
char *pathbuf;
if (!info)
return (DDI_WALK_CONTINUE);
/* Bring ourselves up if there is a keeper that is up */
} else {
}
/* restart autopm on device released from direct pm */
return (DDI_WALK_CONTINUE);
}
#define PM_REQ 1
#define NOSTRUCT 2
#define DIP 3
#define NODIP 4
#define NODEP 5
#define DEP 6
#define PM_PSC 7
#define CHECKPERMS 0x001
#define SU 0x002
#define SG 0x004
#define OWNER 0x008
#define INWHO 0x001
#define INDATAINT 0x002
#define INDATASTRING 0x004
#define INDEP 0x008
#define INDATAOUT 0x010
struct pm_cmd_info {
int cmd; /* command code */
char *name; /* printable string */
int supported; /* true if still supported */
int str_type; /* PM_REQ or NOSTRUCT */
int inargs; /* INWHO, INDATAINT, INDATASTRING, INDEP, */
/* INDATAOUT */
int diptype; /* DIP or NODIP */
int deptype; /* DEP or NODEP */
int permission; /* SU, GU, or CHECKPERMS */
};
#ifdef DEBUG
char *pm_cmd_string;
int pm_cmd;
#endif
/*
* Returns true if permission granted by credentials
*/
static int
{
if (perm == 0) /* no restrictions */
return (1);
return (1);
return (1);
return (1);
return (0);
}
#ifdef DEBUG
static int
{
int i, j;
struct pm_component *cp;
if (!info)
return (DDI_WALK_CONTINUE);
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
pm_cur_power(cp));
}
if (PM_ISDIRECT(dip))
return (DDI_WALK_CONTINUE);
}
#endif
/*
* command, name, supported, str_type, inargs, diptype, deptype, permission
*/
static struct pm_cmd_info pmci[] = {
{PM_SCHEDULE, "PM_SCHEDULE", 0},
{PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0},
{PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0},
{PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0},
{PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0},
{PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0},
{PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0},
{PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0},
{PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0},
{PM_GET_DEP, "PM_GET_DEP", 0},
{PM_ADD_DEP, "PM_ADD_DEP", 0},
{PM_REM_DEP, "PM_REM_DEP", 0},
{PM_REM_DEVICE, "PM_REM_DEVICE", 0},
{PM_REM_DEVICES, "PM_REM_DEVICES", 0},
NODEP},
{PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0},
{PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0},
{PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 },
{PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD",
1, NOSTRUCT},
0, 0, 0, SU},
NODEP},
NODEP},
NODEP},
0, 0, 0, SU},
{0, NULL}
};
struct pm_cmd_info *
{
struct pm_cmd_info *pcip;
return (pcip);
}
return (NULL);
}
static char *
pm_decode_cmd(int cmd)
{
static char invbuf[64];
return (invbuf);
}
/*
* Allocate scan resource, create taskq, then dispatch scan,
* called only if autopm is enabled.
*/
int
{
return (DDI_WALK_CONTINUE);
switch (cmd) {
case PM_START_CPUPM:
return (DDI_WALK_CONTINUE);
if (!PM_CPUPM_DISABLED)
break;
case PM_START_PM:
return (DDI_WALK_CONTINUE);
}
if (autopm_enabled)
break;
}
/*
* Start doing pm on device: ensure pm_scan data structure initiated,
* no need to guarantee a successful scan run.
*/
return (DDI_WALK_CONTINUE);
}
/*
* Bring devices to full power level, then stop scan
*/
int
{
if (!info)
return (DDI_WALK_CONTINUE);
switch (cmd) {
case PM_STOP_PM:
/*
* If CPU devices are being managed independently, then don't
* stop them as part of PM_STOP_PM. Only stop them as part of
* PM_STOP_CPUPM and PM_RESET_PM.
*/
return (DDI_WALK_CONTINUE);
break;
case PM_STOP_CPUPM:
/*
* If stopping CPU devices and this device is not marked
* as a CPU device, then skip.
*/
return (DDI_WALK_CONTINUE);
break;
}
/*
* Stop the current scan, and then bring it back to normal power.
*/
}
!pm_all_at_normal(dip)) {
"all_to_normal because %s@%s(%s#%d) is detaching\n",
return (DDI_WALK_CONTINUE);
}
}
}
return (DDI_WALK_CONTINUE);
}
static int
{
if (!scanp)
return (DDI_WALK_CONTINUE);
return (DDI_WALK_CONTINUE);
}
/*ARGSUSED*/
static int
{
if (!scanp)
return (DDI_WALK_CONTINUE);
/*
* The PMID_TIMERS bits are place holder till idledown expires.
* The bits are also the base for regenerating PMID_SCANS bits.
* While it's up to scan thread to clear up the PMID_SCANS bits
* after each scan run, PMID_TIMERS ensure aggressive scan down
* performance throughout the idledown period.
*/
return (DDI_WALK_CONTINUE);
}
/*ARGSUSED*/
static void
pm_end_idledown_walk(void *ignore)
{
pmstp->pm_idledown_id = 0;
}
/*
* pm_timeout_idledown - keep idledown effect for 10 seconds.
*
* Return 0 if another competing caller scheduled idledown timeout,
* otherwise, return idledown timeout_id.
*/
static timeout_id_t
pm_timeout_idledown(void)
{
/*
* Keep idle-down in effect for either 10 seconds
* or length of a scan interval, which ever is greater.
*/
if (pmstp->pm_idledown_id != 0) {
pmstp->pm_idledown_id = 0;
if (pmstp->pm_idledown_id != 0) {
"another caller got it, idledown_id(%lx)!\n",
return (0);
}
}
PM_IDLEDOWN_TIME * hz);
return (pmstp->pm_idledown_id);
}
static int
{
int clone;
} else {
*reventsp = 0;
if (!anyyet) {
*phpp = &pm_pollhead;
}
#ifdef DEBUG
else {
}
#endif
}
return (0);
}
/*
* called by pm_dicard_entries to free up the memory. It also decrements
* pm_poll_cnt, if direct is non zero.
*/
static void
{
if (pscep) {
while (p->size) {
if (direct) {
"pm_poll_cnt[%d] is %d before "
"ASSERT\n", clone,
pm_poll_cnt[clone]))
pm_poll_cnt[clone]--;
}
p->size = 0;
p = pscep->psce_first;
else
p++;
}
}
}
/*
* Discard entries for this clone. Calls pm_free_entries to free up memory.
*/
static void
pm_discard_entries(int clone)
{
psce_t *pm_psc_clone_to_direct(int);
psce_t *pm_psc_clone_to_interest(int);
int direct = 0;
direct = 1;
}
static void
{
case PMC_DEF_THRESH:
case PMC_CPU_THRESH:
"%s@%s(%s#%d) default thresh to 0t%d\n",
break;
default:
break;
}
}
}
static int
{
if (!PM_GET_PM_INFO(dip))
return (DDI_WALK_CONTINUE);
switch (cmd) {
case PM_SET_SYSTEM_THRESHOLD:
break;
break;
case PM_SET_CPU_THRESHOLD:
break;
break;
}
return (DDI_WALK_CONTINUE);
}
/*ARGSUSED*/
static int
{
int instance;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED1*/
static int
{
int clone;
return (EINVAL);
break;
if (clone == PM_MAX_CLONE) {
return (ENXIO);
}
return (0);
}
/*ARGSUSED1*/
static int
{
int clone;
return (EINVAL);
clone))
/*
* Walk the entire device tree to find the corresponding
* device and operate on it.
*/
(void *) &clone);
return (0);
}
/*ARGSUSED*/
static int
{
struct pm_cmd_info *pc_info(int);
int clone;
/*
* To keep devinfo nodes from going away while we're holding a
* pointer to their dip, pm_name_to_dip() optionally holds
* the devinfo node. If we've done that, we set dipheld
* so we know at the end of the ioctl processing to release the
* node again.
*/
int dipheld = 0;
int icount = 0;
int i;
int comps;
int curpower;
char who[MAXNAMELEN];
char *pathbuf;
struct pm_component *cp;
#ifdef _MULTI_DATAMODEL
#endif
extern void pm_record_thresh(pm_thresh_rec_t *);
psce_t *pm_psc_clone_to_direct(int);
psce_t *pm_psc_clone_to_interest(int);
extern void pm_register_watcher(int, dev_info_t *);
extern int pm_get_current_power(dev_info_t *, int, int *);
extern int pm_interest_registered(int);
extern void pm_all_to_default_thresholds(void);
extern int pm_current_threshold(dev_info_t *, int, int *);
extern void pm_deregister_watcher(int, dev_info_t *);
extern void pm_unrecord_threshold(char *);
#ifdef DEBUG
if (cmd == 666) {
return (0);
}
#endif
return (ENOTTY);
}
return (ENOTTY);
}
wholen = 0;
i_dep_buf[0] = 0;
return (ret);
}
case PM_REQ:
#ifdef _MULTI_DATAMODEL
"EFAULT\n\n", cmdstr))
break;
}
if (ret) {
"copyinstr fails returning %d\n",
break;
}
}
} else {
}
case DIP:
if (!(dip =
"pm_name_to_dip for %s failed\n",
return (ENODEV);
}
dipheld++;
break;
case NODIP:
break;
default:
/*
* Internal error, invalid ioctl description
* force debug entry even if pm_debug not set
*/
#ifdef DEBUG
pm_log("invalid diptype %d for cmd %d (%s)\n",
#endif
ASSERT(0);
return (EIO);
}
int *ip;
if (icount <= 0) {
" 0 or neg EFAULT\n\n", cmdstr))
break;
}
ret = 0;
for (i = 0,
"entry %d EFAULT\n",
cmdstr, i))
break;
}
}
if (ret)
break;
}
"0x%p dep size %lx, EFAULT"
"\n", cmdstr,
break;
}
#ifdef DEBUG
else {
}
#endif
} else {
"dependent\n", cmdstr))
break;
}
}
} else
#endif /* _MULTI_DATAMODEL */
{
"EFAULT\n\n", cmdstr))
break;
}
MAXNAMELEN, &wholen);
if (ret) {
" fails returning %d\n", cmdstr,
ret))
break;
}
}
}
case DIP:
if (!(dip =
"pm_name_to_dip for %s failed\n",
return (ENODEV);
}
dipheld++;
break;
case NODIP:
break;
default:
/*
* Internal error, invalid ioctl description
* force debug entry even if pm_debug not set
*/
#ifdef DEBUG
pm_log("invalid diptype %d for cmd %d (%s)\n",
#endif
ASSERT(0);
return (EIO);
}
int *ip;
if (icount <= 0) {
" 0 or neg EFAULT\n\n", cmdstr))
break;
}
"EFAULT\n\n", cmdstr))
break;
}
}
"0x%p dep size %lu, "
"EFAULT\n", cmdstr,
break;
}
#ifdef DEBUG
else {
}
#endif
} else {
"dependent\n", cmdstr))
break;
}
}
}
/*
* Now we've got all the args in for the commands that
* use the new pm_req struct.
*/
switch (cmd) {
case PM_REPARSE_PM_PROPS:
{
void *propval;
int length;
/*
* This ioctl is provided only for the ddivs pm test.
* We only do it to a driver which explicitly allows
* us to do so by exporting a pm-reparse-ok property.
* We only care whether the property exists or not.
*/
break;
}
&length) != DDI_SUCCESS) {
break;
}
&length) != DDI_SUCCESS) {
break;
}
break;
}
case PM_GET_DEVICE_THRESHOLD:
cmdstr))
break;
}
ret = 0;
break;
case PM_DIRECT_PM:
{
int has_dep;
"ENODEV\n", cmdstr))
break;
}
/*
* Check to see if we are there is a dependency on
* this kept device, if so, return EBUSY.
*/
if (has_dep) {
cmdstr))
break;
}
"%s@%s(%s#%d): EBUSY\n", cmdstr,
break;
}
ret = 0;
break;
}
case PM_RELEASE_DIRECT_PM:
{
"ENODEV\n", cmdstr))
break;
}
"%s@%s(%s#%d) EINVAL\n", cmdstr,
break;
}
/* Bring ourselves up if there is a keeper. */
/*
* Now we could let the other threads that are
* trying to do a DIRECT_PM thru
*/
cmdstr))
ret = 0;
break;
}
case PM_SET_CURRENT_POWER:
{
"physpath=%s, comp=%d, level=%d, fails\n",
break;
}
"ENODEV\n", cmdstr))
break;
}
"(not owner) %s fails; clone %d, owner %d"
break;
}
"pm_set_power for %s fails, errno=%d\n",
break;
}
/*
* Power down all idle components if console framebuffer
* is powered off.
*/
(pm_timeout_idledown() != 0)) {
(void *)PMID_CFB);
}
} else {
int count = 0;
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
i, &curpower);
if (ret == DDI_SUCCESS &&
curpower == 0)
count++;
}
(pm_timeout_idledown() != 0)) {
(void *)PMID_CFB);
}
}
}
cmdstr))
*rval_p = 0;
ret = 0;
break;
}
case PM_GET_FULL_POWER:
{
int normal;
if (normal == DDI_FAILURE) {
"returns EINVAL\n", cmdstr))
break;
}
ret = 0;
break;
}
case PM_GET_CURRENT_POWER:
rval_p) != DDI_SUCCESS) {
"EINVAL\n", cmdstr))
break;
}
if (*rval_p == PM_LEVEL_UNKNOWN)
else
ret = 0;
break;
case PM_GET_TIME_IDLE:
{
"component %d > numcmpts - 1 %d--EINVAL\n",
break;
}
if (timestamp) {
} else {
*rval_p = 0;
}
ret = 0;
break;
}
case PM_ADD_DEPENDENT:
{
/*
* hold and install kept while processing dependency
* keeper (in .physpath) has already been held.
*/
if (dep[0] == '\0') {
break;
} else if ((kept_dip =
break;
"self-dependency not allowed.\n",
(void *) dip))
break;
}
/*
* record dependency, then walk through device tree
* independently on behalf of kept and keeper to
* establish newly created dependency.
*/
/*
* release kept after establishing dependency, keeper
* is released as part of ioctl exit processing.
*/
*rval_p = 0;
ret = 0;
break;
}
{
if (dep[0] == '\0') {
"null\n", cmdstr))
break;
}
/*
* record keeper - kept dependency, then walk through
* device tree to find out all attached keeper, walk
* through again to apply dependency to all the
* potential kept.
*/
PM_DEP_WAIT, NULL, 0);
*rval_p = 0;
ret = 0;
break;
}
case PM_SET_DEVICE_THRESHOLD:
{
int *tp; /* threshold storage */
extern int pm_thresh_specd(dev_info_t *);
/*
* The header struct plus one entry struct plus one
* threshold plus the length of the string
*/
size = sizeof (pm_thresh_rec_t) +
(sizeof (pm_pte_t) * 1) +
(1 * sizeof (int)) +
(1 * sizeof (pm_pte_t)));
/*
* Don't free rp, pm_record_thresh() keeps it.
* We don't try to apply it ourselves because we'd need
* to know too much about locking. Since we don't
* hold a lock the entry could be removed before
* we get here
*/
ret = 0; /* can't fail now */
break;
}
(void) pm_thresh_specd(dip);
break;
}
{
/*
* This only applies to a currently attached and power
* managed node
*/
/*
* We don't do this to old-style drivers
*/
break;
}
break;
}
else
ret = 0;
break;
}
case PM_GET_NUM_COMPONENTS:
ret = 0;
break;
case PM_GET_DEVICE_TYPE:
ret = 0;
"PM_NO_PM_COMPONENTS\n", cmdstr))
break;
}
} else {
}
break;
{
int comps = 0;
int *tp; /* threshold storage */
int *ip;
int j;
extern int pm_thresh_specd(dev_info_t *);
extern int pm_valid_thresh(dev_info_t *,
pm_thresh_rec_t *);
break;
}
comps++;
/* skip over indicated number of entries */
for (j = *ip; j; j--) {
break;
}
}
if (ret)
break;
}
if (ret)
break;
/* did not exactly fill buffer */
break;
}
if (comps == 0) {
break;
}
/*
* The header struct plus one entry struct per component
* plus the size of the lists minus the counts
* plus the length of the string
*/
size = sizeof (pm_thresh_rec_t) +
((comps + 1) * sizeof (int)) +
for (j = *ip++; j; j--) {
}
}
/*
* If this is not a currently power managed node,
* then we can't check for validity of the thresholds
*/
/* don't free rp, pm_record_thresh uses it */
ret = 0;
break;
}
dipheld++;
"for %s@%s(%s#%d)\n", cmdstr,
break;
}
/*
* We don't just apply it ourselves because we'd need
* to know too much about locking. Since we don't
* hold a lock the entry could be removed before
* we get here
*/
(void) pm_thresh_specd(dip);
ret = 0;
break;
}
{
int musthave;
int numthresholds = 0;
int wordsize;
int numcomps;
int val; /* int value to be copied out */
int j;
#ifdef _MULTI_DATAMODEL
} else
#endif /* _MULTI_DATAMODEL */
{
wordsize = sizeof (int);
}
for (i = 0; i < numcomps; i++) {
}
musthave))
break;
}
for (i = 0; i < numcomps; i++) {
int *thp;
/* first copyout the count */
} else {
}
"(%s#%d) vaddr %p EFAULT\n",
(void*)vaddr))
break;
}
/* then copyout each threshold value */
j++) {
} else {
}
"%s@%s(%s#%d) uaddr %p "
"EFAULT\n", cmdstr,
(void *)uaddr))
break;
}
}
}
if (ret)
break;
/* last copyout a terminating 0 count */
val32 = 0;
} else {
val = 0;
}
"vaddr %p (0 count) EFAULT\n", cmdstr,
break;
}
/* finished, so don't need to increment addresses */
ret = 0;
break;
}
case PM_GET_STATS:
{
int musthave;
int wordsize;
#ifdef _MULTI_DATAMODEL
} else
#endif /* _MULTI_DATAMODEL */
{
wordsize = sizeof (int);
}
" or not power managed--EINVAL\n", cmdstr,
break;
}
musthave))
break;
}
KM_SLEEP);
/*
* First the current power levels
*/
for (i = 0; i < comps; i++) {
int curpwr;
if (wordsize == sizeof (int)) {
} else {
}
"(%s#%d) req.data %p EFAULT\n",
return (EFAULT);
}
}
/*
* Then the times remaining
*/
for (i = 0; i < comps; i++) {
int retval;
int curpwr;
"cur_pwer %x, timestamp %lx\n",
} else {
int thresh;
(void) pm_current_threshold(dip, i,
&thresh);
"thresh %x, now %lx, timestamp %lx,"
}
if (wordsize == sizeof (int)) {
} else {
}
"(%s#%d) req.data %p EFAULT\n",
return (EFAULT);
}
}
ret = 0;
break;
}
case PM_GET_COMPONENT_NAME:
"component %d > numcmpts - 1 %d--EINVAL\n",
break;
}
"copyoutstr %p failed--EFAULT\n", cmdstr,
break;
}
ret = 0;
break;
case PM_GET_POWER_NAME:
{
int i;
"component %d > numcmpts - 1 %d--EINVAL\n",
break;
}
"value %d > num_levels - 1 %d--EINVAL\n",
break;
}
"copyoutstr %p failed--EFAULT\n", cmdstr,
break;
}
ret = 0;
break;
}
case PM_GET_POWER_LEVELS:
{
int musthave;
int numlevels;
int wordsize;
#ifdef _MULTI_DATAMODEL
} else
#endif /* _MULTI_DATAMODEL */
{
wordsize = sizeof (int);
}
"has %d components, component %d requested"
break;
}
musthave))
break;
}
for (i = 0; i < numlevels; i++) {
int level;
if (wordsize == sizeof (int)) {
} else {
}
"(%s#%d) laddr %p EFAULT\n",
(void *)laddr))
return (EFAULT);
}
}
ret = 0;
break;
}
case PM_GET_NUM_POWER_LEVELS:
"component %d > numcmpts - 1 %d--EINVAL\n",
break;
}
ret = 0;
break;
ret = 0;
"PM_NO_PM_COMPONENTS\n", cmdstr))
break;
}
if (PM_ISDIRECT(dip)) {
break;
}
case PMC_DEF_THRESH:
case PMC_NEXDEF_THRESH:
break;
case PMC_DEV_THRESH:
break;
case PMC_COMP_THRESH:
break;
case PMC_CPU_THRESH:
break;
default:
break;
}
"BC--EINVAL", cmdstr))
break;
}
break;
}
break;
case PM_PSC:
/*
* Commands that require pm_state_change_t as arg
*/
#ifdef _MULTI_DATAMODEL
"EFAULT\n\n", cmdstr))
return (EFAULT);
}
} else
#endif /* _MULTI_DATAMODEL */
{
"EFAULT\n\n", cmdstr))
return (EFAULT);
}
}
switch (cmd) {
case PM_GET_STATE_CHANGE:
case PM_GET_STATE_CHANGE_WAIT:
{
/*
* We want to know if any device has changed state.
* We look up by clone. In case we have another thread
* from the same process, we loop.
* pm_psc_clone_to_interest() returns a locked entry.
* We create an internal copy of the event entry prior
* to copyout to user space because we don't want to
* hold the psce_lock while doing copyout as we might
* hit page fault which eventually brings us back
* here requesting the same lock.
*/
if (!pm_interest_registered(clone))
while ((pscep =
if (cmd == PM_GET_STATE_CHANGE) {
"EWOULDBLOCK\n", cmdstr))
return (EWOULDBLOCK);
} else {
&pm_clone_lock) == 0) {
"EINTR\n", cmdstr))
return (EINTR);
}
}
}
/*
* If we were unable to store the path while bringing
* up the console fb upon entering the prom, we give
* a "" name with the overrun event set
*/
physlen = 1;
}
break;
}
}
#ifdef _MULTI_DATAMODEL
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
} else
#endif /* _MULTI_DATAMODEL */
{
(long)&p->component);
}
p->size = 0;
p = pscep->psce_first;
else
p++;
if (ret) {
"failed--EFAULT\n", cmdstr,
break;
}
#ifdef _MULTI_DATAMODEL
!= 0) {
"failed--EFAULT\n", cmdstr))
break;
}
} else
#endif /* _MULTI_DATAMODEL */
{
"failed--EFAULT\n", cmdstr))
break;
}
}
ret = 0;
break;
}
case PM_DIRECT_NOTIFY:
case PM_DIRECT_NOTIFY_WAIT:
{
/*
* We want to know if any direct device of ours has
* something we should know about. We look up by clone.
* In case we have another thread from the same process,
* we loop.
* pm_psc_clone_to_direct() returns a locked entry.
*/
while (pm_poll_cnt[clone] == 0 ||
if (cmd == PM_DIRECT_NOTIFY) {
"EWOULDBLOCK\n", cmdstr))
return (EWOULDBLOCK);
} else {
&pm_clone_lock) == 0) {
"EINTR\n", cmdstr))
return (EINTR);
}
}
}
cmdstr))
break;
}
#ifdef _MULTI_DATAMODEL
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
} else
#endif
{
}
pm_poll_cnt[clone]))
pm_poll_cnt[clone]--;
p->size = 0;
p = pscep->psce_first;
else
p++;
if (ret) {
"failed--EFAULT\n", cmdstr,
break;
}
#ifdef _MULTI_DATAMODEL
!= 0) {
"failed--EFAULT\n", cmdstr))
break;
}
} else
#endif /* _MULTI_DATAMODEL */
{
"failed--EFAULT\n", cmdstr))
break;
}
}
ret = 0;
break;
}
default:
ASSERT(0);
}
break;
case NOSTRUCT:
switch (cmd) {
case PM_START_PM:
case PM_START_CPUPM:
cmdstr))
break;
}
if (cmd == PM_START_PM)
autopm_enabled = 1;
else
ret = 0;
break;
case PM_RESET_PM:
case PM_STOP_PM:
case PM_STOP_CPUPM:
{
extern void pm_discard_thresholds(void);
cmdstr))
break;
}
if (cmd == PM_STOP_PM)
autopm_enabled = 0;
else if (cmd == PM_STOP_CPUPM)
else {
autopm_enabled = 0;
}
/*
* bring devices to full power level, stop scan
*/
ret = 0;
break;
/*
* Now do only PM_RESET_PM stuff.
*/
break;
}
case PM_GET_SYSTEM_THRESHOLD:
ret = 0;
break;
ret = 0;
break;
case PM_GET_CPU_THRESHOLD:
ret = 0;
break;
case PM_SET_SYSTEM_THRESHOLD:
case PM_SET_CPU_THRESHOLD:
if ((int)arg < 0) {
break;
}
if (cmd == PM_SET_SYSTEM_THRESHOLD)
pm_system_idle_threshold = (int)arg;
else {
pm_cpu_idle_threshold = (int)arg;
}
(void *) &cmd);
ret = 0;
break;
case PM_IDLE_DOWN:
if (pm_timeout_idledown() != 0) {
pm_start_idledown, (void *)PMID_IOC);
}
ret = 0;
break;
case PM_GET_PM_STATE:
if (autopm_enabled) {
} else {
}
ret = 0;
break;
case PM_GET_CPUPM_STATE:
if (PM_CPUPM_ENABLED)
else if (PM_CPUPM_DISABLED)
else
ret = 0;
break;
}
break;
default:
/*
* Internal error, invalid ioctl description
* force debug entry even if pm_debug not set
*/
#ifdef DEBUG
pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
#endif
ASSERT(0);
return (EIO);
}
if (dipheld) {
}
return (ret);
}