xref: /onnv-gate/usr/src/uts/common/io/pm.c (revision 3839)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53028Smh27603  * Common Development and Distribution License (the "License").
63028Smh27603  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3839Skchow  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * pm	This driver now only handles the ioctl interface.  The scanning
300Sstevel@tonic-gate  *	and policy stuff now lives in common/os/sunpm.c.
310Sstevel@tonic-gate  *	Not DDI compliant
320Sstevel@tonic-gate  */
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <sys/errno.h>
360Sstevel@tonic-gate #include <sys/modctl.h>
370Sstevel@tonic-gate #include <sys/conf.h>		/* driver flags and functions */
380Sstevel@tonic-gate #include <sys/open.h>		/* OTYP_CHR definition */
390Sstevel@tonic-gate #include <sys/stat.h>		/* S_IFCHR definition */
400Sstevel@tonic-gate #include <sys/pathname.h>	/* name -> dev_info xlation */
410Sstevel@tonic-gate #include <sys/kmem.h>		/* memory alloc stuff */
420Sstevel@tonic-gate #include <sys/debug.h>
430Sstevel@tonic-gate #include <sys/pm.h>
440Sstevel@tonic-gate #include <sys/ddi.h>
450Sstevel@tonic-gate #include <sys/sunddi.h>
460Sstevel@tonic-gate #include <sys/epm.h>
470Sstevel@tonic-gate #include <sys/vfs.h>
480Sstevel@tonic-gate #include <sys/mode.h>
490Sstevel@tonic-gate #include <sys/mkdev.h>
500Sstevel@tonic-gate #include <sys/promif.h>
510Sstevel@tonic-gate #include <sys/consdev.h>
520Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
530Sstevel@tonic-gate #include <sys/poll.h>
540Sstevel@tonic-gate #include <sys/note.h>
550Sstevel@tonic-gate #include <sys/taskq.h>
560Sstevel@tonic-gate #include <sys/policy.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate  * Minor number is instance<<8 + clone minor from range 1-255; (0 reserved
600Sstevel@tonic-gate  * for "original"
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate #define	PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE - 1))
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components)
650Sstevel@tonic-gate #define	PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB)
660Sstevel@tonic-gate #define	PM_MAJOR(dip) ddi_driver_major(dip)
670Sstevel@tonic-gate #define	PM_RELE(dip) ddi_release_devi(dip)
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #define	PM_IDLEDOWN_TIME	10
700Sstevel@tonic-gate 
710Sstevel@tonic-gate extern kmutex_t	pm_scan_lock;	/* protects autopm_enable, pm_scans_disabled */
720Sstevel@tonic-gate extern kmutex_t	pm_clone_lock;	/* protects pm_clones array */
730Sstevel@tonic-gate extern int	autopm_enabled;
743028Smh27603 extern pm_cpupm_t cpupm;
753028Smh27603 extern int	pm_default_idle_threshold;
763028Smh27603 extern int	pm_system_idle_threshold;
773028Smh27603 extern int	pm_cpu_idle_threshold;
780Sstevel@tonic-gate extern kcondvar_t pm_clones_cv[PM_MAX_CLONE];
790Sstevel@tonic-gate extern uint_t	pm_poll_cnt[PM_MAX_CLONE];
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate  * The soft state of the power manager.  Since there will only
830Sstevel@tonic-gate  * one of these, just reference it through a static pointer.
840Sstevel@tonic-gate  */
850Sstevel@tonic-gate static struct pmstate {
860Sstevel@tonic-gate 	dev_info_t	*pm_dip;		/* ptr to our dev_info node */
870Sstevel@tonic-gate 	int		pm_instance;		/* for ddi_get_instance() */
880Sstevel@tonic-gate 	timeout_id_t	pm_idledown_id;		/* pm idledown timeout id */
890Sstevel@tonic-gate 	uchar_t		pm_clones[PM_MAX_CLONE]; /* uniqueify multiple opens */
900Sstevel@tonic-gate 	struct cred	*pm_cred[PM_MAX_CLONE];	/* cred for each unique open */
910Sstevel@tonic-gate } pm_state = { NULL, -1, (timeout_id_t)0 };
920Sstevel@tonic-gate typedef struct pmstate *pm_state_t;
930Sstevel@tonic-gate static pm_state_t pmstp = &pm_state;
940Sstevel@tonic-gate 
950Sstevel@tonic-gate static int	pm_open(dev_t *, int, int, cred_t *);
960Sstevel@tonic-gate static int	pm_close(dev_t, int, int, cred_t *);
970Sstevel@tonic-gate static int	pm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
980Sstevel@tonic-gate static int	pm_chpoll(dev_t, short, int, short *, struct pollhead **);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate static struct cb_ops pm_cb_ops = {
1010Sstevel@tonic-gate 	pm_open,	/* open */
1020Sstevel@tonic-gate 	pm_close,	/* close */
1030Sstevel@tonic-gate 	nodev,		/* strategy */
1040Sstevel@tonic-gate 	nodev,		/* print */
1050Sstevel@tonic-gate 	nodev,		/* dump */
1060Sstevel@tonic-gate 	nodev,		/* read */
1070Sstevel@tonic-gate 	nodev,		/* write */
1080Sstevel@tonic-gate 	pm_ioctl,	/* ioctl */
1090Sstevel@tonic-gate 	nodev,		/* devmap */
1100Sstevel@tonic-gate 	nodev,		/* mmap */
1110Sstevel@tonic-gate 	nodev,		/* segmap */
1120Sstevel@tonic-gate 	pm_chpoll,	/* poll */
1130Sstevel@tonic-gate 	ddi_prop_op,	/* prop_op */
1140Sstevel@tonic-gate 	NULL,		/* streamtab */
1150Sstevel@tonic-gate 	D_NEW | D_MP	/* driver compatibility flag */
1160Sstevel@tonic-gate };
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate static int pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1190Sstevel@tonic-gate     void **result);
1200Sstevel@tonic-gate static int pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1210Sstevel@tonic-gate static int pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate static struct dev_ops pm_ops = {
1240Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1250Sstevel@tonic-gate 	0,			/* refcnt */
1260Sstevel@tonic-gate 	pm_getinfo,		/* info */
1270Sstevel@tonic-gate 	nulldev,		/* identify */
1280Sstevel@tonic-gate 	nulldev,		/* probe */
1290Sstevel@tonic-gate 	pm_attach,		/* attach */
1300Sstevel@tonic-gate 	pm_detach,		/* detach */
1310Sstevel@tonic-gate 	nodev,			/* reset */
1320Sstevel@tonic-gate 	&pm_cb_ops,		/* driver operations */
1330Sstevel@tonic-gate 	NULL,			/* bus operations */
1340Sstevel@tonic-gate 	NULL			/* power */
1350Sstevel@tonic-gate };
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate static struct modldrv modldrv = {
1380Sstevel@tonic-gate 	&mod_driverops,
1390Sstevel@tonic-gate 	"power management driver v%I%",
1400Sstevel@tonic-gate 	&pm_ops
1410Sstevel@tonic-gate };
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate static struct modlinkage modlinkage = {
1440Sstevel@tonic-gate 	MODREV_1, &modldrv, 0
1450Sstevel@tonic-gate };
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate /* Local functions */
1480Sstevel@tonic-gate #ifdef DEBUG
1490Sstevel@tonic-gate static int	print_info(dev_info_t *, void *);
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate #endif
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate int
1540Sstevel@tonic-gate _init(void)
1550Sstevel@tonic-gate {
1560Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate int
1600Sstevel@tonic-gate _fini(void)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1630Sstevel@tonic-gate }
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate int
1660Sstevel@tonic-gate _info(struct modinfo *modinfop)
1670Sstevel@tonic-gate {
1680Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate static int
1720Sstevel@tonic-gate pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate 	int		i;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	switch (cmd) {
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	case DDI_ATTACH:
1790Sstevel@tonic-gate 		if (pmstp->pm_instance != -1)	/* Only allow one instance */
1800Sstevel@tonic-gate 			return (DDI_FAILURE);
1810Sstevel@tonic-gate 		pmstp->pm_instance = ddi_get_instance(dip);
1820Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "pm", S_IFCHR,
1830Sstevel@tonic-gate 		    (pmstp->pm_instance << 8) + 0,
1840Sstevel@tonic-gate 			DDI_PSEUDO, 0) != DDI_SUCCESS) {
1850Sstevel@tonic-gate 			return (DDI_FAILURE);
1860Sstevel@tonic-gate 		}
1870Sstevel@tonic-gate 		pmstp->pm_dip = dip;	/* pm_init and getinfo depend on it */
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
1900Sstevel@tonic-gate 			cv_init(&pm_clones_cv[i], NULL, CV_DEFAULT, NULL);
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 		ddi_report_dev(dip);
1930Sstevel@tonic-gate 		return (DDI_SUCCESS);
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	default:
1960Sstevel@tonic-gate 		return (DDI_FAILURE);
1970Sstevel@tonic-gate 	}
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate /* ARGSUSED */
2010Sstevel@tonic-gate static int
2020Sstevel@tonic-gate pm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2030Sstevel@tonic-gate {
2040Sstevel@tonic-gate 	int i;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	switch (cmd) {
2070Sstevel@tonic-gate 	case DDI_DETACH:
2080Sstevel@tonic-gate 		/*
2090Sstevel@tonic-gate 		 * Don't detach while idledown timeout is pending.  Note that
2100Sstevel@tonic-gate 		 * we already know we're not in pm_ioctl() due to framework
2110Sstevel@tonic-gate 		 * synchronization, so this is a sufficient test
2120Sstevel@tonic-gate 		 */
2130Sstevel@tonic-gate 		if (pmstp->pm_idledown_id)
2140Sstevel@tonic-gate 			return (DDI_FAILURE);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 		for (i = 0; i < PM_MAX_CLONE; i++)
2170Sstevel@tonic-gate 			cv_destroy(&pm_clones_cv[i]);
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
2200Sstevel@tonic-gate 		pmstp->pm_instance = -1;
2210Sstevel@tonic-gate 		return (DDI_SUCCESS);
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	default:
2240Sstevel@tonic-gate 		return (DDI_FAILURE);
2250Sstevel@tonic-gate 	}
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate static int
2290Sstevel@tonic-gate pm_close_direct_pm_device(dev_info_t *dip, void *arg)
2300Sstevel@tonic-gate {
2310Sstevel@tonic-gate 	int clone;
2320Sstevel@tonic-gate 	char *pathbuf;
2330Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	clone = *((int *)arg);
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	if (!info)
2380Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2410Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
2420Sstevel@tonic-gate 	if (clone == info->pmi_clone) {
2430Sstevel@tonic-gate 		PMD(PMD_CLOSE, ("pm_close: found %s@%s(%s#%d)\n",
2440Sstevel@tonic-gate 		    PM_DEVICE(dip)))
2450Sstevel@tonic-gate 		ASSERT(PM_ISDIRECT(dip));
2460Sstevel@tonic-gate 		info->pmi_dev_pm_state &= ~PM_DIRECT;
2470Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2480Sstevel@tonic-gate 		pm_proceed(dip, PMP_RELEASE, -1, -1);
2490Sstevel@tonic-gate 		/* Bring ourselves up if there is a keeper that is up */
2500Sstevel@tonic-gate 		(void) ddi_pathname(dip, pathbuf);
2510Sstevel@tonic-gate 		pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL,
2520Sstevel@tonic-gate 		    pathbuf, PM_DEP_NOWAIT, NULL, 0);
2530Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
2540Sstevel@tonic-gate 		info->pmi_clone = 0;
2550Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2560Sstevel@tonic-gate 	} else {
2570Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
2580Sstevel@tonic-gate 	}
2590Sstevel@tonic-gate 	kmem_free(pathbuf, MAXPATHLEN);
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	/* restart autopm on device released from direct pm */
2620Sstevel@tonic-gate 	pm_rescan(dip);
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate #define	PM_REQ		1
2680Sstevel@tonic-gate #define	NOSTRUCT	2
2690Sstevel@tonic-gate #define	DIP		3
2700Sstevel@tonic-gate #define	NODIP		4
2710Sstevel@tonic-gate #define	NODEP		5
2720Sstevel@tonic-gate #define	DEP		6
2730Sstevel@tonic-gate #define	PM_PSC		7
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate #define	CHECKPERMS	0x001
2760Sstevel@tonic-gate #define	SU		0x002
2770Sstevel@tonic-gate #define	SG		0x004
2780Sstevel@tonic-gate #define	OWNER		0x008
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate #define	INWHO		0x001
2810Sstevel@tonic-gate #define	INDATAINT	0x002
2820Sstevel@tonic-gate #define	INDATASTRING	0x004
2830Sstevel@tonic-gate #define	INDEP		0x008
2840Sstevel@tonic-gate #define	INDATAOUT	0x010
2850Sstevel@tonic-gate #define	INDATA	(INDATAOUT | INDATAINT | INDATASTRING | INDEP)
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate struct pm_cmd_info {
2880Sstevel@tonic-gate 	int cmd;		/* command code */
2890Sstevel@tonic-gate 	char *name;		/* printable string */
2900Sstevel@tonic-gate 	int supported;		/* true if still supported */
2910Sstevel@tonic-gate 	int str_type;		/* PM_REQ or NOSTRUCT */
2920Sstevel@tonic-gate 	int inargs;		/* INWHO, INDATAINT, INDATASTRING, INDEP, */
2930Sstevel@tonic-gate 				/* INDATAOUT */
2940Sstevel@tonic-gate 	int diptype;		/* DIP or NODIP */
2950Sstevel@tonic-gate 	int deptype;		/* DEP or NODEP */
2960Sstevel@tonic-gate 	int permission;		/* SU, GU, or CHECKPERMS */
2970Sstevel@tonic-gate };
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate #ifdef DEBUG
3000Sstevel@tonic-gate char *pm_cmd_string;
3010Sstevel@tonic-gate int pm_cmd;
3020Sstevel@tonic-gate #endif
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate /*
3050Sstevel@tonic-gate  * Returns true if permission granted by credentials
3060Sstevel@tonic-gate  */
3070Sstevel@tonic-gate static int
3080Sstevel@tonic-gate pm_perms(int perm, cred_t *cr)
3090Sstevel@tonic-gate {
3100Sstevel@tonic-gate 	if (perm == 0)			/* no restrictions */
3110Sstevel@tonic-gate 		return (1);
3120Sstevel@tonic-gate 	if (perm == CHECKPERMS)		/* ok for now (is checked later) */
3130Sstevel@tonic-gate 		return (1);
3140Sstevel@tonic-gate 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
3150Sstevel@tonic-gate 		return (1);
3160Sstevel@tonic-gate 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
3170Sstevel@tonic-gate 		return (1);
3180Sstevel@tonic-gate 	return (0);
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate #ifdef DEBUG
3220Sstevel@tonic-gate static int
3230Sstevel@tonic-gate print_info(dev_info_t *dip, void *arg)
3240Sstevel@tonic-gate {
3250Sstevel@tonic-gate 	_NOTE(ARGUNUSED(arg))
3260Sstevel@tonic-gate 	pm_info_t	*info;
3270Sstevel@tonic-gate 	int		i, j;
3280Sstevel@tonic-gate 	struct pm_component *cp;
3290Sstevel@tonic-gate 	extern int pm_cur_power(pm_component_t *cp);
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	info = PM_GET_PM_INFO(dip);
3320Sstevel@tonic-gate 	if (!info)
3330Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
3340Sstevel@tonic-gate 	cmn_err(CE_CONT, "pm_info for %s\n", ddi_node_name(dip));
3350Sstevel@tonic-gate 	for (i = 0; i < PM_NUMCMPTS(dip); i++) {
3360Sstevel@tonic-gate 		cp = PM_CP(dip, i);
3370Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tThresholds[%d] =",  i);
3380Sstevel@tonic-gate 		for (j = 0; j < cp->pmc_comp.pmc_numlevels; j++)
3390Sstevel@tonic-gate 			cmn_err(CE_CONT, " %d", cp->pmc_comp.pmc_thresh[i]);
3400Sstevel@tonic-gate 		cmn_err(CE_CONT, "\n");
3410Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tCurrent power[%d] = %d\n", i,
3420Sstevel@tonic-gate 		    pm_cur_power(cp));
3430Sstevel@tonic-gate 	}
3440Sstevel@tonic-gate 	if (PM_ISDIRECT(dip))
3450Sstevel@tonic-gate 		cmn_err(CE_CONT, "\tDirect power management\n");
3460Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate #endif
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate /*
3510Sstevel@tonic-gate  * command, name, supported, str_type, inargs, diptype, deptype, permission
3520Sstevel@tonic-gate  */
3530Sstevel@tonic-gate static struct pm_cmd_info pmci[] = {
3540Sstevel@tonic-gate 	{PM_SCHEDULE, "PM_SCHEDULE", 0},
3550Sstevel@tonic-gate 	{PM_GET_IDLE_TIME, "PM_GET_IDLE_TIME", 0},
3560Sstevel@tonic-gate 	{PM_GET_NUM_CMPTS, "PM_GET_NUM_CMPTS", 0},
3570Sstevel@tonic-gate 	{PM_GET_THRESHOLD, "PM_GET_THRESHOLD", 0},
3580Sstevel@tonic-gate 	{PM_SET_THRESHOLD, "PM_SET_THRESHOLD", 0},
3590Sstevel@tonic-gate 	{PM_GET_NORM_PWR, "PM_GET_NORM_PWR", 0},
3600Sstevel@tonic-gate 	{PM_SET_CUR_PWR, "PM_SET_CUR_PWR", 0},
3610Sstevel@tonic-gate 	{PM_GET_CUR_PWR, "PM_GET_CUR_PWR", 0},
3620Sstevel@tonic-gate 	{PM_GET_NUM_DEPS, "PM_GET_NUM_DEPS", 0},
3630Sstevel@tonic-gate 	{PM_GET_DEP, "PM_GET_DEP", 0},
3640Sstevel@tonic-gate 	{PM_ADD_DEP, "PM_ADD_DEP", 0},
3650Sstevel@tonic-gate 	{PM_REM_DEP, "PM_REM_DEP", 0},
3660Sstevel@tonic-gate 	{PM_REM_DEVICE, "PM_REM_DEVICE", 0},
3670Sstevel@tonic-gate 	{PM_REM_DEVICES, "PM_REM_DEVICES", 0},
3680Sstevel@tonic-gate 	{PM_REPARSE_PM_PROPS, "PM_REPARSE_PM_PROPS", 1, PM_REQ, INWHO, DIP,
3690Sstevel@tonic-gate 	    NODEP},
3700Sstevel@tonic-gate 	{PM_DISABLE_AUTOPM, "PM_DISABLE_AUTOPM", 0},
3710Sstevel@tonic-gate 	{PM_REENABLE_AUTOPM, "PM_REENABLE_AUTOPM", 0},
3720Sstevel@tonic-gate 	{PM_SET_NORM_PWR, "PM_SET_NORM_PWR", 0 },
3730Sstevel@tonic-gate 	{PM_SET_DEVICE_THRESHOLD, "PM_SET_DEVICE_THRESHOLD", 1, PM_REQ,
3740Sstevel@tonic-gate 	    INWHO, NODIP, NODEP, SU},
3750Sstevel@tonic-gate 	{PM_GET_SYSTEM_THRESHOLD, "PM_GET_SYSTEM_THRESHOLD", 1, NOSTRUCT},
3760Sstevel@tonic-gate 	{PM_GET_DEFAULT_SYSTEM_THRESHOLD, "PM_GET_DEFAULT_SYSTEM_THRESHOLD",
3770Sstevel@tonic-gate 	    1, NOSTRUCT},
3780Sstevel@tonic-gate 	{PM_SET_SYSTEM_THRESHOLD, "PM_SET_SYSTEM_THRESHOLD", 1, NOSTRUCT,
3790Sstevel@tonic-gate 	    0, 0, 0, SU},
3800Sstevel@tonic-gate 	{PM_START_PM, "PM_START_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3810Sstevel@tonic-gate 	{PM_STOP_PM, "PM_STOP_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3820Sstevel@tonic-gate 	{PM_RESET_PM, "PM_RESET_PM", 1, NOSTRUCT, 0, 0, 0, SU},
3830Sstevel@tonic-gate 	{PM_GET_STATS, "PM_GET_STATS", 1, PM_REQ, INWHO | INDATAOUT,
3840Sstevel@tonic-gate 	    DIP, NODEP},
3850Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD, "PM_GET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO,
3860Sstevel@tonic-gate 	    DIP, NODEP},
3870Sstevel@tonic-gate 	{PM_GET_POWER_NAME, "PM_GET_POWER_NAME", 1, PM_REQ, INWHO | INDATAOUT,
3880Sstevel@tonic-gate 	    DIP, NODEP},
3890Sstevel@tonic-gate 	{PM_GET_POWER_LEVELS, "PM_GET_POWER_LEVELS", 1, PM_REQ,
3900Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
3910Sstevel@tonic-gate 	{PM_GET_NUM_COMPONENTS, "PM_GET_NUM_COMPONENTS", 1, PM_REQ, INWHO,
3920Sstevel@tonic-gate 	    DIP, NODEP},
3930Sstevel@tonic-gate 	{PM_GET_COMPONENT_NAME, "PM_GET_COMPONENT_NAME", 1, PM_REQ,
3940Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
3950Sstevel@tonic-gate 	{PM_GET_NUM_POWER_LEVELS, "PM_GET_NUM_POWER_LEVELS", 1, PM_REQ, INWHO,
3960Sstevel@tonic-gate 	    DIP, NODEP},
3970Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE, "PM_GET_STATE_CHANGE", 1, PM_PSC},
3980Sstevel@tonic-gate 	{PM_GET_STATE_CHANGE_WAIT, "PM_GET_STATE_CHANGE_WAIT", 1, PM_PSC},
3990Sstevel@tonic-gate 	{PM_DIRECT_PM, "PM_DIRECT_PM", 1, PM_REQ, INWHO, DIP, NODEP,
4000Sstevel@tonic-gate 	    (SU | SG)},
4010Sstevel@tonic-gate 	{PM_RELEASE_DIRECT_PM, "PM_RELEASE_DIRECT_PM", 1, PM_REQ, INWHO,
4020Sstevel@tonic-gate 	    DIP, NODEP},
4030Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY, "PM_DIRECT_NOTIFY", 1, PM_PSC},
4040Sstevel@tonic-gate 	{PM_DIRECT_NOTIFY_WAIT, "PM_DIRECT_NOTIFY_WAIT", 1, PM_PSC},
4050Sstevel@tonic-gate 	{PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ,
4060Sstevel@tonic-gate 	    INWHO, DIP, NODEP, SU},
4070Sstevel@tonic-gate 	{PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT},
4080Sstevel@tonic-gate 	{PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO,
4090Sstevel@tonic-gate 	    DIP, NODEP},
4100Sstevel@tonic-gate 	{PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4110Sstevel@tonic-gate 	    INWHO | INDATAINT, NODIP, NODEP, SU},
4120Sstevel@tonic-gate 	{PM_GET_COMPONENT_THRESHOLDS, "PM_GET_COMPONENT_THRESHOLDS", 1, PM_REQ,
4130Sstevel@tonic-gate 	    INWHO | INDATAOUT, DIP, NODEP},
4140Sstevel@tonic-gate 	{PM_IDLE_DOWN, "PM_IDLE_DOWN", 1, NOSTRUCT, 0, 0, 0, SU},
4150Sstevel@tonic-gate 	{PM_GET_DEVICE_THRESHOLD_BASIS, "PM_GET_DEVICE_THRESHOLD_BASIS", 1,
4160Sstevel@tonic-gate 	    PM_REQ, INWHO, DIP, NODEP},
4170Sstevel@tonic-gate 	{PM_SET_CURRENT_POWER, "PM_SET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4180Sstevel@tonic-gate 	    NODEP},
4190Sstevel@tonic-gate 	{PM_GET_CURRENT_POWER, "PM_GET_CURRENT_POWER", 1, PM_REQ, INWHO, DIP,
4200Sstevel@tonic-gate 	    NODEP},
4210Sstevel@tonic-gate 	{PM_GET_FULL_POWER, "PM_GET_FULL_POWER", 1, PM_REQ, INWHO, DIP,
4220Sstevel@tonic-gate 	    NODEP},
4230Sstevel@tonic-gate 	{PM_ADD_DEPENDENT, "PM_ADD_DEPENDENT", 1, PM_REQ, INWHO | INDATASTRING,
4240Sstevel@tonic-gate 	    DIP, DEP, SU},
4250Sstevel@tonic-gate 	{PM_GET_TIME_IDLE, "PM_GET_TIME_IDLE", 1, PM_REQ, INWHO, DIP, NODEP},
4260Sstevel@tonic-gate 	{PM_ADD_DEPENDENT_PROPERTY, "PM_ADD_DEPENDENT_PROPERTY", 1, PM_REQ,
4270Sstevel@tonic-gate 	    INWHO | INDATASTRING, NODIP, DEP, SU},
4283028Smh27603 	{PM_START_CPUPM, "PM_START_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
4293028Smh27603 	{PM_STOP_CPUPM, "PM_STOP_CPUPM", 1, NOSTRUCT, 0, 0, 0, SU},
4303028Smh27603 	{PM_GET_CPU_THRESHOLD, "PM_GET_CPU_THRESHOLD", 1, NOSTRUCT},
4313028Smh27603 	{PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
4323028Smh27603 	    0, 0, 0, SU},
4333028Smh27603 	{PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT},
4340Sstevel@tonic-gate 	{0, NULL}
4350Sstevel@tonic-gate };
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate struct pm_cmd_info *
4380Sstevel@tonic-gate pc_info(int cmd)
4390Sstevel@tonic-gate {
4400Sstevel@tonic-gate 	struct pm_cmd_info *pcip;
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	for (pcip = pmci; pcip->name; pcip++) {
4430Sstevel@tonic-gate 		if (cmd == pcip->cmd)
4440Sstevel@tonic-gate 			return (pcip);
4450Sstevel@tonic-gate 	}
4460Sstevel@tonic-gate 	return (NULL);
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate static char *
4500Sstevel@tonic-gate pm_decode_cmd(int cmd)
4510Sstevel@tonic-gate {
4520Sstevel@tonic-gate 	static char invbuf[64];
4530Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
4540Sstevel@tonic-gate 	if (pcip != NULL)
4550Sstevel@tonic-gate 		return (pcip->name);
4560Sstevel@tonic-gate 	(void) sprintf(invbuf, "ioctl: invalid command %d\n", cmd);
4570Sstevel@tonic-gate 	return (invbuf);
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate /*
4610Sstevel@tonic-gate  * Allocate scan resource, create taskq, then dispatch scan,
4620Sstevel@tonic-gate  * called only if autopm is enabled.
4630Sstevel@tonic-gate  */
4640Sstevel@tonic-gate int
4650Sstevel@tonic-gate pm_start_pm_walk(dev_info_t *dip, void *arg)
4660Sstevel@tonic-gate {
4673028Smh27603 	int cmd = *((int *)arg);
468*3839Skchow #ifdef PMDDEBUG
4693028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
470*3839Skchow #endif
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip))
4730Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
4740Sstevel@tonic-gate 
4753028Smh27603 	switch (cmd) {
4763028Smh27603 	case PM_START_CPUPM:
4773028Smh27603 		if (!PM_ISCPU(dip))
4783028Smh27603 			return (DDI_WALK_CONTINUE);
4793028Smh27603 		mutex_enter(&pm_scan_lock);
4803028Smh27603 		if (!PM_CPUPM_DISABLED)
4813028Smh27603 			pm_scan_init(dip);
4823028Smh27603 		mutex_exit(&pm_scan_lock);
4833028Smh27603 		break;
4843028Smh27603 	case PM_START_PM:
4853028Smh27603 		mutex_enter(&pm_scan_lock);
4863028Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_DISABLED) {
4873028Smh27603 			mutex_exit(&pm_scan_lock);
4883028Smh27603 			return (DDI_WALK_CONTINUE);
4893028Smh27603 		}
4903028Smh27603 		if (autopm_enabled)
4913028Smh27603 			pm_scan_init(dip);
4923028Smh27603 		mutex_exit(&pm_scan_lock);
4933028Smh27603 		break;
4943028Smh27603 	}
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	/*
4970Sstevel@tonic-gate 	 * Start doing pm on device: ensure pm_scan data structure initiated,
4983028Smh27603 	 * no need to guarantee a successful scan run.
4990Sstevel@tonic-gate 	 */
5000Sstevel@tonic-gate 	PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: scan %s@%s(%s#%d)\n", cmdstr,
5010Sstevel@tonic-gate 	    PM_DEVICE(dip)))
5020Sstevel@tonic-gate 	pm_rescan(dip);
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5050Sstevel@tonic-gate }
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate /*
5080Sstevel@tonic-gate  * Bring devices to full power level, then stop scan
5090Sstevel@tonic-gate  */
5100Sstevel@tonic-gate int
5110Sstevel@tonic-gate pm_stop_pm_walk(dev_info_t *dip, void *arg)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate 	pm_info_t *info = PM_GET_PM_INFO(dip);
5143028Smh27603 	int cmd = *((int *)arg);
515*3839Skchow #ifdef PMDDEBUG
5163028Smh27603 	char *cmdstr = pm_decode_cmd(cmd);
517*3839Skchow #endif
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	if (!info)
5200Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5213028Smh27603 
5223028Smh27603 	switch (cmd) {
5233028Smh27603 	case PM_STOP_PM:
5243028Smh27603 		/*
5253028Smh27603 		 * If CPU devices are being managed independently, then don't
5263028Smh27603 		 * stop them as part of PM_STOP_PM. Only stop them as part of
5273028Smh27603 		 * PM_STOP_CPUPM and PM_RESET_PM.
5283028Smh27603 		 */
5293028Smh27603 		if (PM_ISCPU(dip) && PM_CPUPM_ENABLED)
5303028Smh27603 			return (DDI_WALK_CONTINUE);
5313028Smh27603 		break;
5323028Smh27603 	case PM_STOP_CPUPM:
5333028Smh27603 		/*
5343028Smh27603 		 * If stopping CPU devices and this device is not marked
5353028Smh27603 		 * as a CPU device, then skip.
5363028Smh27603 		 */
5373028Smh27603 		if (!PM_ISCPU(dip))
5383028Smh27603 			return (DDI_WALK_CONTINUE);
5393028Smh27603 		break;
5403028Smh27603 	}
5413028Smh27603 
5420Sstevel@tonic-gate 	/*
5430Sstevel@tonic-gate 	 * Stop the current scan, and then bring it back to normal power.
5440Sstevel@tonic-gate 	 */
5450Sstevel@tonic-gate 	if (!PM_ISBC(dip)) {
5460Sstevel@tonic-gate 		PMD(PMD_SCAN | PMD_IOCTL, ("ioctl: %s: stop scan for "
5470Sstevel@tonic-gate 		    "%s@%s(%s#%d)\n", cmdstr, PM_DEVICE(dip)))
5480Sstevel@tonic-gate 		pm_scan_stop(dip);
5490Sstevel@tonic-gate 	}
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip) &&
5520Sstevel@tonic-gate 	    !pm_all_at_normal(dip)) {
5530Sstevel@tonic-gate 		PM_LOCK_DIP(dip);
5540Sstevel@tonic-gate 		if (info->pmi_dev_pm_state & PM_DETACHING) {
5550Sstevel@tonic-gate 			PMD(PMD_ALLNORM, ("ioctl: %s: deferring "
5560Sstevel@tonic-gate 			    "all_to_normal because %s@%s(%s#%d) is detaching\n",
5570Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
5580Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_ALLNORM_DEFERRED;
5590Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
5600Sstevel@tonic-gate 			return (DDI_WALK_CONTINUE);
5610Sstevel@tonic-gate 		}
5620Sstevel@tonic-gate 		PM_UNLOCK_DIP(dip);
5630Sstevel@tonic-gate 		if (pm_all_to_normal(dip, PM_CANBLOCK_FAIL) != DDI_SUCCESS) {
5640Sstevel@tonic-gate 			PMD(PMD_ERROR, ("ioctl: %s: could not bring %s@%s"
5650Sstevel@tonic-gate 			    "(%s#%d) to normal\n", cmdstr, PM_DEVICE(dip)))
5660Sstevel@tonic-gate 		}
5670Sstevel@tonic-gate 	}
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5700Sstevel@tonic-gate }
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate static int
5730Sstevel@tonic-gate pm_start_idledown(dev_info_t *dip, void *arg)
5740Sstevel@tonic-gate {
5750Sstevel@tonic-gate 	int		flag = (int)(intptr_t)arg;
5760Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	if (!scanp)
5790Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
5820Sstevel@tonic-gate 	scanp->ps_idle_down |= flag;
5830Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
5840Sstevel@tonic-gate 	pm_rescan(dip);
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate /*ARGSUSED*/
5900Sstevel@tonic-gate static int
5910Sstevel@tonic-gate pm_end_idledown(dev_info_t *dip, void *ignore)
5920Sstevel@tonic-gate {
5930Sstevel@tonic-gate 	pm_scan_t	*scanp = PM_GET_PM_SCAN(dip);
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	if (!scanp)
5960Sstevel@tonic-gate 		return (DDI_WALK_CONTINUE);
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	PM_LOCK_DIP(dip);
5990Sstevel@tonic-gate 	/*
6000Sstevel@tonic-gate 	 * The PMID_TIMERS bits are place holder till idledown expires.
6010Sstevel@tonic-gate 	 * The bits are also the base for regenerating PMID_SCANS bits.
6020Sstevel@tonic-gate 	 * While it's up to scan thread to clear up the PMID_SCANS bits
6030Sstevel@tonic-gate 	 * after each scan run, PMID_TIMERS ensure aggressive scan down
6040Sstevel@tonic-gate 	 * performance throughout the idledown period.
6050Sstevel@tonic-gate 	 */
6060Sstevel@tonic-gate 	scanp->ps_idle_down &= ~PMID_TIMERS;
6070Sstevel@tonic-gate 	PM_UNLOCK_DIP(dip);
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
6100Sstevel@tonic-gate }
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate /*ARGSUSED*/
6130Sstevel@tonic-gate static void
6140Sstevel@tonic-gate pm_end_idledown_walk(void *ignore)
6150Sstevel@tonic-gate {
6160Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: end_idledown: idledown_id(%lx) timer is "
6170Sstevel@tonic-gate 	    "off\n", (ulong_t)pmstp->pm_idledown_id));
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6200Sstevel@tonic-gate 	pmstp->pm_idledown_id = 0;
6210Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_end_idledown, NULL);
6240Sstevel@tonic-gate }
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate /*
6270Sstevel@tonic-gate  * pm_timeout_idledown - keep idledown effect for 10 seconds.
6280Sstevel@tonic-gate  *
6290Sstevel@tonic-gate  * Return 0 if another competing caller scheduled idledown timeout,
6300Sstevel@tonic-gate  * otherwise, return idledown timeout_id.
6310Sstevel@tonic-gate  */
6320Sstevel@tonic-gate static timeout_id_t
6330Sstevel@tonic-gate pm_timeout_idledown(void)
6340Sstevel@tonic-gate {
6350Sstevel@tonic-gate 	timeout_id_t	to_id;
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate 	/*
6380Sstevel@tonic-gate 	 * Keep idle-down in effect for either 10 seconds
6390Sstevel@tonic-gate 	 * or length of a scan interval, which ever is greater.
6400Sstevel@tonic-gate 	 */
6410Sstevel@tonic-gate 	mutex_enter(&pm_scan_lock);
6420Sstevel@tonic-gate 	if (pmstp->pm_idledown_id != 0) {
6430Sstevel@tonic-gate 		to_id = pmstp->pm_idledown_id;
6440Sstevel@tonic-gate 		pmstp->pm_idledown_id = 0;
6450Sstevel@tonic-gate 		mutex_exit(&pm_scan_lock);
6460Sstevel@tonic-gate 		(void) untimeout(to_id);
6470Sstevel@tonic-gate 		mutex_enter(&pm_scan_lock);
6480Sstevel@tonic-gate 		if (pmstp->pm_idledown_id != 0) {
6490Sstevel@tonic-gate 			PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: "
6500Sstevel@tonic-gate 			    "another caller got it, idledown_id(%lx)!\n",
6510Sstevel@tonic-gate 			    (ulong_t)pmstp->pm_idledown_id))
6520Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
6530Sstevel@tonic-gate 			return (0);
6540Sstevel@tonic-gate 		}
6550Sstevel@tonic-gate 	}
6560Sstevel@tonic-gate 	pmstp->pm_idledown_id = timeout(pm_end_idledown_walk, NULL,
6570Sstevel@tonic-gate 	    PM_IDLEDOWN_TIME * hz);
6580Sstevel@tonic-gate 	PMD(PMD_IDLEDOWN, ("ioctl: timeout_idledown: idledown_id(%lx)\n",
6590Sstevel@tonic-gate 	    (ulong_t)pmstp->pm_idledown_id))
6600Sstevel@tonic-gate 	mutex_exit(&pm_scan_lock);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	return (pmstp->pm_idledown_id);
6630Sstevel@tonic-gate }
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate static int
6660Sstevel@tonic-gate pm_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
6670Sstevel@tonic-gate 	struct pollhead **phpp)
6680Sstevel@tonic-gate {
6690Sstevel@tonic-gate 	extern struct pollhead pm_pollhead;	/* common/os/sunpm.c */
6700Sstevel@tonic-gate 	int	clone;
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
6730Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: pm_chpoll: clone %d\n", clone))
6740Sstevel@tonic-gate 	if ((events & (POLLIN | POLLRDNORM)) && pm_poll_cnt[clone]) {
6750Sstevel@tonic-gate 		*reventsp |= (POLLIN | POLLRDNORM);
6760Sstevel@tonic-gate 		PMD(PMD_IOCTL, ("ioctl: pm_chpoll: reventsp set\n"))
6770Sstevel@tonic-gate 	} else {
6780Sstevel@tonic-gate 		*reventsp = 0;
6790Sstevel@tonic-gate 		if (!anyyet) {
6800Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: not anyyet\n"))
6810Sstevel@tonic-gate 			*phpp = &pm_pollhead;
6820Sstevel@tonic-gate 		}
6830Sstevel@tonic-gate #ifdef DEBUG
6840Sstevel@tonic-gate 		else {
6850Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: pm_chpoll: anyyet\n"))
6860Sstevel@tonic-gate 		}
6870Sstevel@tonic-gate #endif
6880Sstevel@tonic-gate 	}
6890Sstevel@tonic-gate 	return (0);
6900Sstevel@tonic-gate }
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate /*
6930Sstevel@tonic-gate  * called by pm_dicard_entries to free up the memory. It also decrements
6940Sstevel@tonic-gate  * pm_poll_cnt, if direct is non zero.
6950Sstevel@tonic-gate  */
6960Sstevel@tonic-gate static void
6970Sstevel@tonic-gate pm_free_entries(psce_t *pscep, int clone, int direct)
6980Sstevel@tonic-gate {
6990Sstevel@tonic-gate 	pm_state_change_t	*p;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	if (pscep) {
7020Sstevel@tonic-gate 		p = pscep->psce_out;
7030Sstevel@tonic-gate 		while (p->size) {
7040Sstevel@tonic-gate 			if (direct) {
7050Sstevel@tonic-gate 				PMD(PMD_IOCTL, ("ioctl: discard: "
7060Sstevel@tonic-gate 				    "pm_poll_cnt[%d] is %d before "
7070Sstevel@tonic-gate 				    "ASSERT\n", clone,
7080Sstevel@tonic-gate 				    pm_poll_cnt[clone]))
7090Sstevel@tonic-gate 				ASSERT(pm_poll_cnt[clone]);
7100Sstevel@tonic-gate 				pm_poll_cnt[clone]--;
7110Sstevel@tonic-gate 			}
7120Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
7130Sstevel@tonic-gate 			p->size = 0;
7140Sstevel@tonic-gate 			if (p == pscep->psce_last)
7150Sstevel@tonic-gate 				p = pscep->psce_first;
7160Sstevel@tonic-gate 			else
7170Sstevel@tonic-gate 				p++;
7180Sstevel@tonic-gate 		}
7190Sstevel@tonic-gate 		pscep->psce_out = pscep->psce_first;
7200Sstevel@tonic-gate 		pscep->psce_in = pscep->psce_first;
7210Sstevel@tonic-gate 		mutex_exit(&pscep->psce_lock);
7220Sstevel@tonic-gate 	}
7230Sstevel@tonic-gate }
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate /*
7260Sstevel@tonic-gate  * Discard entries for this clone. Calls pm_free_entries to free up memory.
7270Sstevel@tonic-gate  */
7280Sstevel@tonic-gate static void
7290Sstevel@tonic-gate pm_discard_entries(int clone)
7300Sstevel@tonic-gate {
7310Sstevel@tonic-gate 	psce_t	*pscep;
7320Sstevel@tonic-gate 	psce_t			*pm_psc_clone_to_direct(int);
7330Sstevel@tonic-gate 	psce_t			*pm_psc_clone_to_interest(int);
7340Sstevel@tonic-gate 	int			direct = 0;
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
7370Sstevel@tonic-gate 	if ((pscep = pm_psc_clone_to_direct(clone)) != NULL)
7380Sstevel@tonic-gate 		direct = 1;
7390Sstevel@tonic-gate 	pm_free_entries(pscep, clone, direct);
7400Sstevel@tonic-gate 	pscep = pm_psc_clone_to_interest(clone);
7410Sstevel@tonic-gate 	pm_free_entries(pscep, clone, 0);
7420Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate 
7453028Smh27603 
7463028Smh27603 static void
7473028Smh27603 pm_set_idle_threshold(dev_info_t *dip, int thresh, int flag)
7480Sstevel@tonic-gate {
7490Sstevel@tonic-gate 	if (!PM_ISBC(dip) && !PM_ISDIRECT(dip)) {
7500Sstevel@tonic-gate 		switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
7510Sstevel@tonic-gate 		case PMC_DEF_THRESH:
7523028Smh27603 		case PMC_CPU_THRESH:
7533028Smh27603 			PMD(PMD_IOCTL, ("ioctl: set_idle_threshold: set "
7540Sstevel@tonic-gate 			    "%s@%s(%s#%d) default thresh to 0t%d\n",
7553028Smh27603 			    PM_DEVICE(dip), thresh))
7563028Smh27603 			pm_set_device_threshold(dip, thresh, flag);
7570Sstevel@tonic-gate 			break;
7580Sstevel@tonic-gate 		default:
7590Sstevel@tonic-gate 			break;
7600Sstevel@tonic-gate 		}
7610Sstevel@tonic-gate 	}
7623028Smh27603 }
7630Sstevel@tonic-gate 
7643028Smh27603 static int
7653028Smh27603 pm_set_idle_thresh_walk(dev_info_t *dip, void *arg)
7663028Smh27603 {
7673028Smh27603 	int cmd = *((int *)arg);
7683028Smh27603 
7693028Smh27603 	if (!PM_GET_PM_INFO(dip))
7703028Smh27603 		return (DDI_WALK_CONTINUE);
7713028Smh27603 
7723028Smh27603 	switch (cmd) {
7733028Smh27603 	case PM_SET_SYSTEM_THRESHOLD:
7743028Smh27603 		if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
7753028Smh27603 			break;
7763028Smh27603 		pm_set_idle_threshold(dip, pm_system_idle_threshold,
7773028Smh27603 		    PMC_DEF_THRESH);
7780Sstevel@tonic-gate 		pm_rescan(dip);
7793028Smh27603 		break;
7803028Smh27603 	case PM_SET_CPU_THRESHOLD:
7813028Smh27603 		if (!PM_ISCPU(dip))
7823028Smh27603 			break;
7833028Smh27603 		pm_set_idle_threshold(dip, pm_cpu_idle_threshold,
7843028Smh27603 		    PMC_CPU_THRESH);
7853028Smh27603 		pm_rescan(dip);
7863028Smh27603 		break;
7873028Smh27603 	}
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	return (DDI_WALK_CONTINUE);
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate /*ARGSUSED*/
7930Sstevel@tonic-gate static int
7940Sstevel@tonic-gate pm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
7950Sstevel@tonic-gate {
7960Sstevel@tonic-gate 	dev_t	dev;
7970Sstevel@tonic-gate 	int	instance;
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	switch (infocmd) {
8000Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
8010Sstevel@tonic-gate 		if (pmstp->pm_instance == -1)
8020Sstevel@tonic-gate 			return (DDI_FAILURE);
8030Sstevel@tonic-gate 		*result = pmstp->pm_dip;
8040Sstevel@tonic-gate 		return (DDI_SUCCESS);
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8070Sstevel@tonic-gate 		dev = (dev_t)arg;
8080Sstevel@tonic-gate 		instance = getminor(dev) >> 8;
8090Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
8100Sstevel@tonic-gate 		return (DDI_SUCCESS);
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	default:
8130Sstevel@tonic-gate 		return (DDI_FAILURE);
8140Sstevel@tonic-gate 	}
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate /*ARGSUSED1*/
8190Sstevel@tonic-gate static int
8200Sstevel@tonic-gate pm_open(dev_t *devp, int flag, int otyp, cred_t *cr)
8210Sstevel@tonic-gate {
8220Sstevel@tonic-gate 	int		clone;
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8250Sstevel@tonic-gate 		return (EINVAL);
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 	mutex_enter(&pm_clone_lock);
8280Sstevel@tonic-gate 	for (clone = 1; clone < PM_MAX_CLONE; clone++)
8290Sstevel@tonic-gate 		if (!pmstp->pm_clones[clone])
8300Sstevel@tonic-gate 			break;
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	if (clone == PM_MAX_CLONE) {
8330Sstevel@tonic-gate 		mutex_exit(&pm_clone_lock);
8340Sstevel@tonic-gate 		return (ENXIO);
8350Sstevel@tonic-gate 	}
8360Sstevel@tonic-gate 	pmstp->pm_cred[clone] = cr;
8370Sstevel@tonic-gate 	crhold(cr);
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (pmstp->pm_instance << 8) + clone);
8400Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 1;
8410Sstevel@tonic-gate 	mutex_exit(&pm_clone_lock);
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	return (0);
8440Sstevel@tonic-gate }
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate /*ARGSUSED1*/
8470Sstevel@tonic-gate static int
8480Sstevel@tonic-gate pm_close(dev_t dev, int flag, int otyp, cred_t *cr)
8490Sstevel@tonic-gate {
8500Sstevel@tonic-gate 	int clone;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
8530Sstevel@tonic-gate 		return (EINVAL);
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
8560Sstevel@tonic-gate 	PMD(PMD_CLOSE, ("pm_close: minor %x, clone %x\n", getminor(dev),
8570Sstevel@tonic-gate 	    clone))
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	/*
8600Sstevel@tonic-gate 	 * Walk the entire device tree to find the corresponding
8610Sstevel@tonic-gate 	 * device and operate on it.
8620Sstevel@tonic-gate 	 */
8630Sstevel@tonic-gate 	ddi_walk_devs(ddi_root_node(), pm_close_direct_pm_device,
8640Sstevel@tonic-gate 	    (void *) &clone);
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 	crfree(pmstp->pm_cred[clone]);
8670Sstevel@tonic-gate 	pmstp->pm_cred[clone] = 0;
8680Sstevel@tonic-gate 	pmstp->pm_clones[clone] = 0;
8690Sstevel@tonic-gate 	pm_discard_entries(clone);
8700Sstevel@tonic-gate 	ASSERT(pm_poll_cnt[clone] == 0);
8710Sstevel@tonic-gate 	pm_deregister_watcher(clone, NULL);
8720Sstevel@tonic-gate 	return (0);
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate /*ARGSUSED*/
8760Sstevel@tonic-gate static int
8770Sstevel@tonic-gate pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
8780Sstevel@tonic-gate {
8790Sstevel@tonic-gate 	struct pm_cmd_info *pc_info(int);
8800Sstevel@tonic-gate 	struct pm_cmd_info *pcip = pc_info(cmd);
8810Sstevel@tonic-gate 	pm_req_t	req;
8820Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
8830Sstevel@tonic-gate 	pm_info_t	*info = NULL;
8840Sstevel@tonic-gate 	int		clone;
8850Sstevel@tonic-gate 	char		*cmdstr = pm_decode_cmd(cmd);
8860Sstevel@tonic-gate 	/*
8870Sstevel@tonic-gate 	 * To keep devinfo nodes from going away while we're holding a
8880Sstevel@tonic-gate 	 * pointer to their dip, pm_name_to_dip() optionally holds
8890Sstevel@tonic-gate 	 * the devinfo node.  If we've done that, we set dipheld
8900Sstevel@tonic-gate 	 * so we know at the end of the ioctl processing to release the
8910Sstevel@tonic-gate 	 * node again.
8920Sstevel@tonic-gate 	 */
8930Sstevel@tonic-gate 	int		dipheld = 0;
8940Sstevel@tonic-gate 	int		icount = 0;
8950Sstevel@tonic-gate 	int		i;
8960Sstevel@tonic-gate 	int		comps;
8970Sstevel@tonic-gate 	size_t		lencopied;
8980Sstevel@tonic-gate 	int		ret = ENOTTY;
8990Sstevel@tonic-gate 	int		curpower;
9000Sstevel@tonic-gate 	char		who[MAXNAMELEN];
9010Sstevel@tonic-gate 	size_t		wholen;			/* copyinstr length */
9020Sstevel@tonic-gate 	size_t		deplen = MAXNAMELEN;
9030Sstevel@tonic-gate 	char		*dep, i_dep_buf[MAXNAMELEN];
9040Sstevel@tonic-gate 	char		*pathbuf;
9050Sstevel@tonic-gate 	struct pm_component *cp;
9060Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9070Sstevel@tonic-gate 	pm_state_change32_t		*pscp32;
9080Sstevel@tonic-gate 	pm_state_change32_t		psc32;
9090Sstevel@tonic-gate 	size_t				copysize32;
9100Sstevel@tonic-gate #endif
9110Sstevel@tonic-gate 	pm_state_change_t		*pscp;
9120Sstevel@tonic-gate 	pm_state_change_t		psc;
9130Sstevel@tonic-gate 	size_t		copysize;
9140Sstevel@tonic-gate 	extern void	pm_record_thresh(pm_thresh_rec_t *);
9150Sstevel@tonic-gate 	psce_t		*pm_psc_clone_to_direct(int);
9160Sstevel@tonic-gate 	psce_t		*pm_psc_clone_to_interest(int);
9170Sstevel@tonic-gate 	extern	void	pm_register_watcher(int, dev_info_t *);
9180Sstevel@tonic-gate 	extern	int	pm_get_current_power(dev_info_t *, int, int *);
9190Sstevel@tonic-gate 	extern	int	pm_interest_registered(int);
9200Sstevel@tonic-gate 	extern	void	pm_all_to_default_thresholds(void);
9210Sstevel@tonic-gate 	extern	int	pm_current_threshold(dev_info_t *, int, int *);
9220Sstevel@tonic-gate 	extern void	pm_deregister_watcher(int, dev_info_t *);
9230Sstevel@tonic-gate 	extern void	pm_unrecord_threshold(char *);
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate #ifdef DEBUG
9280Sstevel@tonic-gate 	if (cmd == 666) {
9290Sstevel@tonic-gate 		ddi_walk_devs(ddi_root_node(), print_info, NULL);
9300Sstevel@tonic-gate 		return (0);
9310Sstevel@tonic-gate 	}
9320Sstevel@tonic-gate 	ret = 0x0badcafe;			/* sanity checking */
9330Sstevel@tonic-gate 	pm_cmd = cmd;				/* for ASSERT debugging */
9340Sstevel@tonic-gate 	pm_cmd_string = cmdstr;	/* for ASSERT debugging */
9350Sstevel@tonic-gate #endif
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 	if (pcip == NULL) {
9390Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: unknown command %d\n", cmd))
9400Sstevel@tonic-gate 		return (ENOTTY);
9410Sstevel@tonic-gate 	}
9420Sstevel@tonic-gate 	if (pcip == NULL || pcip->supported == 0) {
9430Sstevel@tonic-gate 		PMD(PMD_ERROR, ("ioctl: command %s no longer supported\n",
9440Sstevel@tonic-gate 		    pcip->name))
9450Sstevel@tonic-gate 		return (ENOTTY);
9460Sstevel@tonic-gate 	}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	wholen = 0;
9490Sstevel@tonic-gate 	dep = i_dep_buf;
9500Sstevel@tonic-gate 	i_dep_buf[0] = 0;
9510Sstevel@tonic-gate 	clone = PM_MINOR_TO_CLONE(getminor(dev));
9520Sstevel@tonic-gate 	if (!pm_perms(pcip->permission, pmstp->pm_cred[clone])) {
9530Sstevel@tonic-gate 		ret = EPERM;
9540Sstevel@tonic-gate 		return (ret);
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 	switch (pcip->str_type) {
9570Sstevel@tonic-gate 	case PM_REQ:
9580Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
9590Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
9600Sstevel@tonic-gate 			pm_req32_t	req32;
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &req32,
9630Sstevel@tonic-gate 			    sizeof (req32), mode) != 0) {
9640Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
9650Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
9660Sstevel@tonic-gate 				ret = EFAULT;
9670Sstevel@tonic-gate 				break;
9680Sstevel@tonic-gate 			}
9690Sstevel@tonic-gate 			req.component = req32.component;
9700Sstevel@tonic-gate 			req.value = req32.value;
9710Sstevel@tonic-gate 			req.datasize = req32.datasize;
9720Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
9730Sstevel@tonic-gate 				ret = copyinstr((char *)(uintptr_t)
9740Sstevel@tonic-gate 				    req32.physpath, who, MAXNAMELEN, &wholen);
9750Sstevel@tonic-gate 				if (ret) {
9760Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9770Sstevel@tonic-gate 					    "copyinstr fails returning %d\n",
9780Sstevel@tonic-gate 					    cmdstr, ret))
9790Sstevel@tonic-gate 					break;
9800Sstevel@tonic-gate 				}
9810Sstevel@tonic-gate 				req.physpath = who;
9820Sstevel@tonic-gate 			}
9830Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
9840Sstevel@tonic-gate 			    req.physpath))
9850Sstevel@tonic-gate 			if (pcip->inargs & INDATA) {
9860Sstevel@tonic-gate 				req.data = (void *)(uintptr_t)req32.data;
9870Sstevel@tonic-gate 				req.datasize = req32.datasize;
9880Sstevel@tonic-gate 			} else {
9890Sstevel@tonic-gate 				req.data = NULL;
9900Sstevel@tonic-gate 				req.datasize = 0;
9910Sstevel@tonic-gate 			}
9920Sstevel@tonic-gate 			switch (pcip->diptype) {
9930Sstevel@tonic-gate 			case DIP:
9940Sstevel@tonic-gate 				if (!(dip =
9950Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
9960Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
9970Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
9980Sstevel@tonic-gate 					    cmdstr, req.physpath))
9990Sstevel@tonic-gate 					return (ENODEV);
10000Sstevel@tonic-gate 				}
10010Sstevel@tonic-gate 				ASSERT(!dipheld);
10020Sstevel@tonic-gate 				dipheld++;
10030Sstevel@tonic-gate 				break;
10040Sstevel@tonic-gate 			case NODIP:
10050Sstevel@tonic-gate 				break;
10060Sstevel@tonic-gate 			default:
10070Sstevel@tonic-gate 				/*
10080Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
10090Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
10100Sstevel@tonic-gate 				 */
10110Sstevel@tonic-gate #ifdef	DEBUG
10120Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
10130Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
10140Sstevel@tonic-gate #endif
10150Sstevel@tonic-gate 				ASSERT(0);
10160Sstevel@tonic-gate 				return (EIO);
10170Sstevel@tonic-gate 			}
10180Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
10190Sstevel@tonic-gate 				int32_t int32buf;
10200Sstevel@tonic-gate 				int32_t *i32p;
10210Sstevel@tonic-gate 				int *ip;
10220Sstevel@tonic-gate 				icount = req32.datasize / sizeof (int32_t);
10230Sstevel@tonic-gate 				if (icount <= 0) {
10240Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
10250Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
10260Sstevel@tonic-gate 					ret = EFAULT;
10270Sstevel@tonic-gate 					break;
10280Sstevel@tonic-gate 				}
10290Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
10300Sstevel@tonic-gate 				req.datasize = icount * sizeof (int);
10310Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
10320Sstevel@tonic-gate 				ip = req.data;
10330Sstevel@tonic-gate 				ret = 0;
10340Sstevel@tonic-gate 				for (i = 0,
10350Sstevel@tonic-gate 				    i32p = (int32_t *)(uintptr_t)req32.data;
10360Sstevel@tonic-gate 				    i < icount; i++, i32p++) {
10370Sstevel@tonic-gate 					if (ddi_copyin((void *)i32p, &int32buf,
10380Sstevel@tonic-gate 					    sizeof (int32_t), mode)) {
10390Sstevel@tonic-gate 						kmem_free(req.data,
10400Sstevel@tonic-gate 						    req.datasize);
10410Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10420Sstevel@tonic-gate 						    "entry %d EFAULT\n",
10430Sstevel@tonic-gate 						    cmdstr, i))
10440Sstevel@tonic-gate 						ret = EFAULT;
10450Sstevel@tonic-gate 						break;
10460Sstevel@tonic-gate 					}
10470Sstevel@tonic-gate 					*ip++ = (int)int32buf;
10480Sstevel@tonic-gate 				}
10490Sstevel@tonic-gate 				if (ret)
10500Sstevel@tonic-gate 					break;
10510Sstevel@tonic-gate 			}
10520Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
10530Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
10540Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
10550Sstevel@tonic-gate 				if (req32.data != NULL) {
10560Sstevel@tonic-gate 					size_t dummy;
10570Sstevel@tonic-gate 					if (copyinstr((void *)(uintptr_t)
10580Sstevel@tonic-gate 					    req32.data, dep, deplen, &dummy)) {
10590Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
10600Sstevel@tonic-gate 						    "0x%p dep size %lx, EFAULT"
10610Sstevel@tonic-gate 						    "\n", cmdstr,
10620Sstevel@tonic-gate 						    (void *)req.data, deplen))
10630Sstevel@tonic-gate 						ret = EFAULT;
10640Sstevel@tonic-gate 						break;
10650Sstevel@tonic-gate 					}
10660Sstevel@tonic-gate #ifdef DEBUG
10670Sstevel@tonic-gate 					else {
10680Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
10690Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
10700Sstevel@tonic-gate 					}
10710Sstevel@tonic-gate #endif
10720Sstevel@tonic-gate 				} else {
10730Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
10740Sstevel@tonic-gate 					    "dependent\n", cmdstr))
10750Sstevel@tonic-gate 					ret = EINVAL;
10760Sstevel@tonic-gate 					break;
10770Sstevel@tonic-gate 				}
10780Sstevel@tonic-gate 			}
10790Sstevel@tonic-gate 		} else
10800Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
10810Sstevel@tonic-gate 		{
10820Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg,
10830Sstevel@tonic-gate 			    &req, sizeof (req), mode) != 0) {
10840Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
10850Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
10860Sstevel@tonic-gate 				ret = EFAULT;
10870Sstevel@tonic-gate 				break;
10880Sstevel@tonic-gate 			}
10890Sstevel@tonic-gate 			if (pcip->inargs & INWHO) {
10900Sstevel@tonic-gate 				ret = copyinstr((char *)req.physpath, who,
10910Sstevel@tonic-gate 				    MAXNAMELEN, &wholen);
10920Sstevel@tonic-gate 				if (ret) {
10930Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s copyinstr"
10940Sstevel@tonic-gate 					    " fails returning %d\n", cmdstr,
10950Sstevel@tonic-gate 					    ret))
10960Sstevel@tonic-gate 					break;
10970Sstevel@tonic-gate 				}
10980Sstevel@tonic-gate 				req.physpath = who;
10990Sstevel@tonic-gate 			}
11000Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
11010Sstevel@tonic-gate 			    req.physpath))
11020Sstevel@tonic-gate 			if (!(pcip->inargs & INDATA)) {
11030Sstevel@tonic-gate 				req.data = NULL;
11040Sstevel@tonic-gate 				req.datasize = 0;
11050Sstevel@tonic-gate 			}
11060Sstevel@tonic-gate 			switch (pcip->diptype) {
11070Sstevel@tonic-gate 			case DIP:
11080Sstevel@tonic-gate 				if (!(dip =
11090Sstevel@tonic-gate 				    pm_name_to_dip(req.physpath, 1))) {
11100Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: "
11110Sstevel@tonic-gate 					    "pm_name_to_dip for %s failed\n",
11120Sstevel@tonic-gate 					    cmdstr, req.physpath))
11130Sstevel@tonic-gate 					return (ENODEV);
11140Sstevel@tonic-gate 				}
11150Sstevel@tonic-gate 				ASSERT(!dipheld);
11160Sstevel@tonic-gate 				dipheld++;
11170Sstevel@tonic-gate 				break;
11180Sstevel@tonic-gate 			case NODIP:
11190Sstevel@tonic-gate 				break;
11200Sstevel@tonic-gate 			default:
11210Sstevel@tonic-gate 				/*
11220Sstevel@tonic-gate 				 * Internal error, invalid ioctl description
11230Sstevel@tonic-gate 				 * force debug entry even if pm_debug not set
11240Sstevel@tonic-gate 				 */
11250Sstevel@tonic-gate #ifdef	DEBUG
11260Sstevel@tonic-gate 				pm_log("invalid diptype %d for cmd %d (%s)\n",
11270Sstevel@tonic-gate 				    pcip->diptype, cmd, pcip->name);
11280Sstevel@tonic-gate #endif
11290Sstevel@tonic-gate 				ASSERT(0);
11300Sstevel@tonic-gate 				return (EIO);
11310Sstevel@tonic-gate 			}
11320Sstevel@tonic-gate 			if (pcip->inargs & INDATAINT) {
11330Sstevel@tonic-gate 				int *ip;
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATASTRING));
11360Sstevel@tonic-gate 				ip = req.data;
11370Sstevel@tonic-gate 				icount = req.datasize / sizeof (int);
11380Sstevel@tonic-gate 				if (icount <= 0) {
11390Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: datasize"
11400Sstevel@tonic-gate 					    " 0 or neg EFAULT\n\n", cmdstr))
11410Sstevel@tonic-gate 					ret = EFAULT;
11420Sstevel@tonic-gate 					break;
11430Sstevel@tonic-gate 				}
11440Sstevel@tonic-gate 				req.data = kmem_alloc(req.datasize, KM_SLEEP);
11450Sstevel@tonic-gate 				if (ddi_copyin((caddr_t)ip, req.data,
11460Sstevel@tonic-gate 				    req.datasize, mode) != 0) {
11470Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
11480Sstevel@tonic-gate 					    "EFAULT\n\n", cmdstr))
11490Sstevel@tonic-gate 					ret = EFAULT;
11500Sstevel@tonic-gate 					break;
11510Sstevel@tonic-gate 				}
11520Sstevel@tonic-gate 			}
11530Sstevel@tonic-gate 			if (pcip->inargs & INDATASTRING) {
11540Sstevel@tonic-gate 				ASSERT(!(pcip->inargs & INDATAINT));
11550Sstevel@tonic-gate 				ASSERT(pcip->deptype == DEP);
11560Sstevel@tonic-gate 				if (req.data != NULL) {
11570Sstevel@tonic-gate 					size_t dummy;
11580Sstevel@tonic-gate 					if (copyinstr((caddr_t)req.data,
11590Sstevel@tonic-gate 					    dep, deplen, &dummy)) {
11600Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
11610Sstevel@tonic-gate 						    "0x%p dep size %lu, "
11620Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
11630Sstevel@tonic-gate 						    (void *)req.data, deplen))
11640Sstevel@tonic-gate 						ret = EFAULT;
11650Sstevel@tonic-gate 						break;
11660Sstevel@tonic-gate 					}
11670Sstevel@tonic-gate #ifdef DEBUG
11680Sstevel@tonic-gate 					else {
11690Sstevel@tonic-gate 						PMD(PMD_DEP, ("ioctl: %s: "
11700Sstevel@tonic-gate 						    "dep %s\n", cmdstr, dep))
11710Sstevel@tonic-gate 					}
11720Sstevel@tonic-gate #endif
11730Sstevel@tonic-gate 				} else {
11740Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: no "
11750Sstevel@tonic-gate 					    "dependent\n", cmdstr))
11760Sstevel@tonic-gate 					ret = EINVAL;
11770Sstevel@tonic-gate 					break;
11780Sstevel@tonic-gate 				}
11790Sstevel@tonic-gate 			}
11800Sstevel@tonic-gate 		}
11810Sstevel@tonic-gate 		/*
11820Sstevel@tonic-gate 		 * Now we've got all the args in for the commands that
11830Sstevel@tonic-gate 		 * use the new pm_req struct.
11840Sstevel@tonic-gate 		 */
11850Sstevel@tonic-gate 		switch (cmd) {
11860Sstevel@tonic-gate 		case PM_REPARSE_PM_PROPS:
11870Sstevel@tonic-gate 		{
11880Sstevel@tonic-gate 			struct dev_ops	*drv;
11890Sstevel@tonic-gate 			struct cb_ops	*cb;
11900Sstevel@tonic-gate 			void		*propval;
11910Sstevel@tonic-gate 			int length;
11920Sstevel@tonic-gate 			/*
11930Sstevel@tonic-gate 			 * This ioctl is provided only for the ddivs pm test.
11940Sstevel@tonic-gate 			 * We only do it to a driver which explicitly allows
11950Sstevel@tonic-gate 			 * us to do so by exporting a pm-reparse-ok property.
11960Sstevel@tonic-gate 			 * We only care whether the property exists or not.
11970Sstevel@tonic-gate 			 */
11980Sstevel@tonic-gate 			if ((drv = ddi_get_driver(dip)) == NULL) {
11990Sstevel@tonic-gate 				ret = EINVAL;
12000Sstevel@tonic-gate 				break;
12010Sstevel@tonic-gate 			}
12020Sstevel@tonic-gate 			if ((cb = drv->devo_cb_ops) != NULL) {
12030Sstevel@tonic-gate 				if ((*cb->cb_prop_op)(DDI_DEV_T_ANY, dip,
12040Sstevel@tonic-gate 				    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12050Sstevel@tonic-gate 				    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12060Sstevel@tonic-gate 				    "pm-reparse-ok", (caddr_t)&propval,
12070Sstevel@tonic-gate 				    &length) != DDI_SUCCESS) {
12080Sstevel@tonic-gate 					ret = EINVAL;
12090Sstevel@tonic-gate 					break;
12100Sstevel@tonic-gate 				}
12110Sstevel@tonic-gate 			} else if (ddi_prop_op(DDI_DEV_T_ANY, dip,
12120Sstevel@tonic-gate 			    PROP_LEN_AND_VAL_ALLOC, (DDI_PROP_CANSLEEP |
12130Sstevel@tonic-gate 			    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
12140Sstevel@tonic-gate 			    "pm-reparse-ok", (caddr_t)&propval,
12150Sstevel@tonic-gate 			    &length) != DDI_SUCCESS) {
12160Sstevel@tonic-gate 				ret = EINVAL;
12170Sstevel@tonic-gate 				break;
12180Sstevel@tonic-gate 			}
12190Sstevel@tonic-gate 			kmem_free(propval, length);
12200Sstevel@tonic-gate 			ret =  e_new_pm_props(dip);
12210Sstevel@tonic-gate 			break;
12220Sstevel@tonic-gate 		}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD:
12250Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12260Sstevel@tonic-gate 			if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
12270Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12280Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ENODEV\n",
12290Sstevel@tonic-gate 				    cmdstr))
12300Sstevel@tonic-gate 				ret = ENODEV;
12310Sstevel@tonic-gate 				break;
12320Sstevel@tonic-gate 			}
12330Sstevel@tonic-gate 			*rval_p = DEVI(dip)->devi_pm_dev_thresh;
12340Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12350Sstevel@tonic-gate 			ret = 0;
12360Sstevel@tonic-gate 			break;
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate 		case PM_DIRECT_PM:
12390Sstevel@tonic-gate 		{
12400Sstevel@tonic-gate 			int has_dep;
12410Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12420Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12430Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12440Sstevel@tonic-gate 				ret = ENODEV;
12450Sstevel@tonic-gate 				break;
12460Sstevel@tonic-gate 			}
12470Sstevel@tonic-gate 			/*
12480Sstevel@tonic-gate 			 * Check to see if we are there is a dependency on
12490Sstevel@tonic-gate 			 * this kept device, if so, return EBUSY.
12500Sstevel@tonic-gate 			 */
12510Sstevel@tonic-gate 			pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
12520Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
12530Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
12540Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
12550Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
12560Sstevel@tonic-gate 			if (has_dep) {
12570Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
12580Sstevel@tonic-gate 				    cmdstr))
12590Sstevel@tonic-gate 				ret = EBUSY;
12600Sstevel@tonic-gate 				break;
12610Sstevel@tonic-gate 			}
12620Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12630Sstevel@tonic-gate 			if (PM_ISDIRECT(dip) || (info->pmi_clone != 0)) {
12640Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12650Sstevel@tonic-gate 				    "%s@%s(%s#%d): EBUSY\n", cmdstr,
12660Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12670Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12680Sstevel@tonic-gate 				ret = EBUSY;
12690Sstevel@tonic-gate 				break;
12700Sstevel@tonic-gate 			}
12710Sstevel@tonic-gate 			info->pmi_dev_pm_state |= PM_DIRECT;
12720Sstevel@tonic-gate 			info->pmi_clone = clone;
12730Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
12740Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: info %p, pmi_clone %d\n",
12750Sstevel@tonic-gate 			    cmdstr, (void *)info, clone))
12760Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
12770Sstevel@tonic-gate 			pm_register_watcher(clone, dip);
12780Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
12790Sstevel@tonic-gate 			ret = 0;
12800Sstevel@tonic-gate 			break;
12810Sstevel@tonic-gate 		}
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate 		case PM_RELEASE_DIRECT_PM:
12840Sstevel@tonic-gate 		{
12850Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
12860Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12870Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
12880Sstevel@tonic-gate 				ret = ENODEV;
12890Sstevel@tonic-gate 				break;
12900Sstevel@tonic-gate 			}
12910Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
12920Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
12930Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
12940Sstevel@tonic-gate 				    "%s@%s(%s#%d) EINVAL\n", cmdstr,
12950Sstevel@tonic-gate 				    PM_DEVICE(dip)))
12960Sstevel@tonic-gate 				ret = EINVAL;
12970Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
12980Sstevel@tonic-gate 				break;
12990Sstevel@tonic-gate 			}
13000Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13010Sstevel@tonic-gate 			info->pmi_dev_pm_state &= ~PM_DIRECT;
13020Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13030Sstevel@tonic-gate 			/* Bring ourselves up if there is a keeper. */
13040Sstevel@tonic-gate 			pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
13050Sstevel@tonic-gate 			(void) ddi_pathname(dip, pathbuf);
13060Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
13070Sstevel@tonic-gate 			    NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
13080Sstevel@tonic-gate 			kmem_free(pathbuf, MAXPATHLEN);
13090Sstevel@tonic-gate 			pm_discard_entries(clone);
13100Sstevel@tonic-gate 			pm_deregister_watcher(clone, dip);
13110Sstevel@tonic-gate 			/*
13120Sstevel@tonic-gate 			 * Now we could let the other threads that are
13130Sstevel@tonic-gate 			 * trying to do a DIRECT_PM thru
13140Sstevel@tonic-gate 			 */
13150Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
13160Sstevel@tonic-gate 			info->pmi_clone = 0;
13170Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
13180Sstevel@tonic-gate 			pm_proceed(dip, PMP_RELEASE, -1, -1);
13190Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
13200Sstevel@tonic-gate 			    cmdstr))
13210Sstevel@tonic-gate 			pm_rescan(dip);
13220Sstevel@tonic-gate 			ret = 0;
13230Sstevel@tonic-gate 			break;
13240Sstevel@tonic-gate 		}
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate 		case PM_SET_CURRENT_POWER:
13270Sstevel@tonic-gate 		{
13280Sstevel@tonic-gate 			int comp = req.component;
13290Sstevel@tonic-gate 			int  value = req.value;
13300Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s component %d to value "
13310Sstevel@tonic-gate 			    "%d\n", cmdstr, req.physpath, comp, value))
13320Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, NULL) ||
13330Sstevel@tonic-gate 			    !e_pm_valid_power(dip, comp, value)) {
13340Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13350Sstevel@tonic-gate 				    "physpath=%s, comp=%d, level=%d, fails\n",
13360Sstevel@tonic-gate 				    cmdstr, req.physpath, comp, value))
13370Sstevel@tonic-gate 				ret = EINVAL;
13380Sstevel@tonic-gate 				break;
13390Sstevel@tonic-gate 			}
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
13420Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13430Sstevel@tonic-gate 				    "ENODEV\n", cmdstr))
13440Sstevel@tonic-gate 				ret = ENODEV;
13450Sstevel@tonic-gate 				break;
13460Sstevel@tonic-gate 			}
13470Sstevel@tonic-gate 			if (info->pmi_clone != clone) {
13480Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13490Sstevel@tonic-gate 				    "(not owner) %s fails; clone %d, owner %d"
13500Sstevel@tonic-gate 				    "\n", cmdstr, req.physpath, clone,
13510Sstevel@tonic-gate 				    info->pmi_clone))
13520Sstevel@tonic-gate 				ret = EINVAL;
13530Sstevel@tonic-gate 				break;
13540Sstevel@tonic-gate 			}
13550Sstevel@tonic-gate 			ASSERT(PM_ISDIRECT(dip));
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 			if (pm_set_power(dip, comp, value, PM_LEVEL_EXACT,
13580Sstevel@tonic-gate 			    PM_CANBLOCK_BLOCK, 0, &ret) != DDI_SUCCESS) {
13590Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s: "
13600Sstevel@tonic-gate 				    "pm_set_power for %s fails, errno=%d\n",
13610Sstevel@tonic-gate 				    cmdstr, req.physpath, ret))
13620Sstevel@tonic-gate 				break;
13630Sstevel@tonic-gate 			}
13640Sstevel@tonic-gate 
13650Sstevel@tonic-gate 			pm_proceed(dip, PMP_SETPOWER, comp, value);
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 			/*
13680Sstevel@tonic-gate 			 * Power down all idle components if console framebuffer
13690Sstevel@tonic-gate 			 * is powered off.
13700Sstevel@tonic-gate 			 */
13710Sstevel@tonic-gate 			if (PM_IS_CFB(dip) && (pm_system_idle_threshold ==
13720Sstevel@tonic-gate 			    pm_default_idle_threshold)) {
13730Sstevel@tonic-gate 				dev_info_t	*root = ddi_root_node();
13740Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
13750Sstevel@tonic-gate 					if (comp == 0 && value == 0 &&
13760Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13770Sstevel@tonic-gate 						ddi_walk_devs(root,
13780Sstevel@tonic-gate 						    pm_start_idledown,
13790Sstevel@tonic-gate 						    (void *)PMID_CFB);
13800Sstevel@tonic-gate 					}
13810Sstevel@tonic-gate 				} else {
13820Sstevel@tonic-gate 					int count = 0;
13830Sstevel@tonic-gate 					for (i = 0; i < PM_NUMCMPTS(dip); i++) {
13840Sstevel@tonic-gate 						ret = pm_get_current_power(dip,
13850Sstevel@tonic-gate 						    i, &curpower);
13860Sstevel@tonic-gate 						if (ret == DDI_SUCCESS &&
13870Sstevel@tonic-gate 						    curpower == 0)
13880Sstevel@tonic-gate 							count++;
13890Sstevel@tonic-gate 					}
13900Sstevel@tonic-gate 					if ((count == PM_NUMCMPTS(dip)) &&
13910Sstevel@tonic-gate 					    (pm_timeout_idledown() != 0)) {
13920Sstevel@tonic-gate 						ddi_walk_devs(root,
13930Sstevel@tonic-gate 						    pm_start_idledown,
13940Sstevel@tonic-gate 						    (void *)PMID_CFB);
13950Sstevel@tonic-gate 					}
13960Sstevel@tonic-gate 				}
13970Sstevel@tonic-gate 			}
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 			PMD(PMD_RESCAN | PMD_DPM, ("ioctl: %s: rescan\n",
14000Sstevel@tonic-gate 			    cmdstr))
14010Sstevel@tonic-gate 			pm_rescan(dip);
14020Sstevel@tonic-gate 			*rval_p = 0;
14030Sstevel@tonic-gate 			ret = 0;
14040Sstevel@tonic-gate 			break;
14050Sstevel@tonic-gate 		}
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 		case PM_GET_FULL_POWER:
14080Sstevel@tonic-gate 		{
14090Sstevel@tonic-gate 			int normal;
14100Sstevel@tonic-gate 			ASSERT(dip);
14110Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: %s component %d\n",
14120Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component))
14130Sstevel@tonic-gate 			normal =  pm_get_normal_power(dip, req.component);
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 			if (normal == DDI_FAILURE) {
14160Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_NORM, ("ioctl: %s: "
14170Sstevel@tonic-gate 				    "returns EINVAL\n", cmdstr))
14180Sstevel@tonic-gate 				ret = EINVAL;
14190Sstevel@tonic-gate 				break;
14200Sstevel@tonic-gate 			}
14210Sstevel@tonic-gate 			*rval_p = normal;
14220Sstevel@tonic-gate 			PMD(PMD_NORM, ("ioctl: %s: returns %d\n",
14230Sstevel@tonic-gate 			    cmdstr, normal))
14240Sstevel@tonic-gate 			ret = 0;
14250Sstevel@tonic-gate 			break;
14260Sstevel@tonic-gate 		}
14270Sstevel@tonic-gate 
14280Sstevel@tonic-gate 		case PM_GET_CURRENT_POWER:
14290Sstevel@tonic-gate 			if (pm_get_current_power(dip, req.component,
14300Sstevel@tonic-gate 			    rval_p) != DDI_SUCCESS) {
14310Sstevel@tonic-gate 				PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
14320Sstevel@tonic-gate 				    "EINVAL\n", cmdstr))
14330Sstevel@tonic-gate 				ret = EINVAL;
14340Sstevel@tonic-gate 				break;
14350Sstevel@tonic-gate 			}
14360Sstevel@tonic-gate 			PMD(PMD_DPM, ("ioctl: %s: %s comp %d returns %d\n",
14370Sstevel@tonic-gate 			    cmdstr, req.physpath, req.component, *rval_p))
14380Sstevel@tonic-gate 			if (*rval_p == PM_LEVEL_UNKNOWN)
14390Sstevel@tonic-gate 				ret = EAGAIN;
14400Sstevel@tonic-gate 			else
14410Sstevel@tonic-gate 				ret = 0;
14420Sstevel@tonic-gate 			break;
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate 		case PM_GET_TIME_IDLE:
14450Sstevel@tonic-gate 		{
14460Sstevel@tonic-gate 			time_t timestamp;
14470Sstevel@tonic-gate 			int comp = req.component;
14480Sstevel@tonic-gate 			pm_component_t *cp;
14490Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, comp, &cp)) {
14500Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
14510Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
14520Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), comp,
14530Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
14540Sstevel@tonic-gate 				ret = EINVAL;
14550Sstevel@tonic-gate 				break;
14560Sstevel@tonic-gate 			}
14570Sstevel@tonic-gate 			timestamp = cp->pmc_timestamp;
14580Sstevel@tonic-gate 			if (timestamp) {
14590Sstevel@tonic-gate 				time_t now;
14600Sstevel@tonic-gate 				(void) drv_getparm(TIME, &now);
14610Sstevel@tonic-gate 				*rval_p = (now - timestamp);
14620Sstevel@tonic-gate 			} else {
14630Sstevel@tonic-gate 				*rval_p = 0;
14640Sstevel@tonic-gate 			}
14650Sstevel@tonic-gate 			ret = 0;
14660Sstevel@tonic-gate 			break;
14670Sstevel@tonic-gate 		}
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate 		case PM_ADD_DEPENDENT:
14700Sstevel@tonic-gate 		{
14710Sstevel@tonic-gate 			dev_info_t	*kept_dip;
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 			PMD(PMD_KEEPS, ("%s, kept %s, keeper %s\n", cmdstr,
14740Sstevel@tonic-gate 			    dep, req.physpath))
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate 			/*
14770Sstevel@tonic-gate 			 * hold and install kept while processing dependency
14780Sstevel@tonic-gate 			 * keeper (in .physpath) has already been held.
14790Sstevel@tonic-gate 			 */
14800Sstevel@tonic-gate 			if (dep[0] == '\0') {
14810Sstevel@tonic-gate 				PMD(PMD_ERROR, ("kept NULL or null\n"))
14820Sstevel@tonic-gate 				ret = EINVAL;
14830Sstevel@tonic-gate 				break;
14840Sstevel@tonic-gate 			} else if ((kept_dip =
14850Sstevel@tonic-gate 			    pm_name_to_dip(dep, 1)) == NULL) {
14860Sstevel@tonic-gate 				PMD(PMD_ERROR, ("no dip for kept %s\n", dep))
14870Sstevel@tonic-gate 				ret = ENODEV;
14880Sstevel@tonic-gate 				break;
14890Sstevel@tonic-gate 			} else if (kept_dip == dip) {
14900Sstevel@tonic-gate 				PMD(PMD_ERROR, ("keeper(%s, %p) - kept(%s, %p) "
14910Sstevel@tonic-gate 				    "self-dependency not allowed.\n",
14920Sstevel@tonic-gate 				    dep, (void *)kept_dip, req.physpath,
14930Sstevel@tonic-gate 				    (void *) dip))
14940Sstevel@tonic-gate 				PM_RELE(dip);	/* release "double" hold */
14950Sstevel@tonic-gate 				ret = EINVAL;
14960Sstevel@tonic-gate 				break;
14970Sstevel@tonic-gate 			}
14980Sstevel@tonic-gate 			ASSERT(!(strcmp(req.physpath, (char *)dep) == 0));
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 			/*
15010Sstevel@tonic-gate 			 * record dependency, then walk through device tree
15020Sstevel@tonic-gate 			 * independently on behalf of kept and keeper to
15030Sstevel@tonic-gate 			 * establish newly created dependency.
15040Sstevel@tonic-gate 			 */
15050Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_RECORD_KEEPER,
15060Sstevel@tonic-gate 			    req.physpath, dep, PM_DEP_WAIT, NULL, 0);
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 			/*
15090Sstevel@tonic-gate 			 * release kept after establishing dependency, keeper
15100Sstevel@tonic-gate 			 * is released as part of ioctl exit processing.
15110Sstevel@tonic-gate 			 */
15120Sstevel@tonic-gate 			PM_RELE(kept_dip);
15130Sstevel@tonic-gate 			*rval_p = 0;
15140Sstevel@tonic-gate 			ret = 0;
15150Sstevel@tonic-gate 			break;
15160Sstevel@tonic-gate 		}
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 		case PM_ADD_DEPENDENT_PROPERTY:
15190Sstevel@tonic-gate 		{
15200Sstevel@tonic-gate 			char *keeper, *kept;
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 			if (dep[0] == '\0') {
15230Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: dep NULL or "
15240Sstevel@tonic-gate 				    "null\n", cmdstr))
15250Sstevel@tonic-gate 				ret = EINVAL;
15260Sstevel@tonic-gate 				break;
15270Sstevel@tonic-gate 			}
15280Sstevel@tonic-gate 			kept = dep;
15290Sstevel@tonic-gate 			keeper = req.physpath;
15300Sstevel@tonic-gate 			/*
15310Sstevel@tonic-gate 			 * record keeper - kept dependency, then walk through
15320Sstevel@tonic-gate 			 * device tree to find out all attached keeper, walk
15330Sstevel@tonic-gate 			 * through again to apply dependency to all the
15340Sstevel@tonic-gate 			 * potential kept.
15350Sstevel@tonic-gate 			 */
15360Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(
15370Sstevel@tonic-gate 			    PM_DEP_WK_RECORD_KEEPER_PROP, keeper, kept,
15380Sstevel@tonic-gate 			    PM_DEP_WAIT, NULL, 0);
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 			*rval_p = 0;
15410Sstevel@tonic-gate 			ret = 0;
15420Sstevel@tonic-gate 			break;
15430Sstevel@tonic-gate 		}
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate 		case PM_SET_DEVICE_THRESHOLD:
15460Sstevel@tonic-gate 		{
15470Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
15480Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
15490Sstevel@tonic-gate 			int *tp;	/* threshold storage */
15500Sstevel@tonic-gate 			size_t size;
15510Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate 			/*
15540Sstevel@tonic-gate 			 * The header struct plus one entry struct plus one
15550Sstevel@tonic-gate 			 * threshold plus the length of the string
15560Sstevel@tonic-gate 			 */
15570Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
15580Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * 1) +
15590Sstevel@tonic-gate 			    (1 * sizeof (int)) +
15600Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
15630Sstevel@tonic-gate 			rp->ptr_size = size;
15640Sstevel@tonic-gate 			rp->ptr_numcomps = 0;	/* means device threshold */
15650Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
15660Sstevel@tonic-gate 			rp->ptr_entries = ep;
15670Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
15680Sstevel@tonic-gate 			    (1 * sizeof (pm_pte_t)));
15690Sstevel@tonic-gate 			ep->pte_numthresh = 1;
15700Sstevel@tonic-gate 			ep->pte_thresh = tp;
15710Sstevel@tonic-gate 			*tp++ = req.value;
15720Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
15730Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
15740Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
15750Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
15760Sstevel@tonic-gate 			PMD(PMD_THRESH, ("ioctl: %s: record thresh %d for "
15770Sstevel@tonic-gate 			    "%s\n", cmdstr, req.value, req.physpath))
15780Sstevel@tonic-gate 			pm_record_thresh(rp);
15790Sstevel@tonic-gate 			/*
15800Sstevel@tonic-gate 			 * Don't free rp, pm_record_thresh() keeps it.
15810Sstevel@tonic-gate 			 * We don't try to apply it ourselves because we'd need
15820Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
15830Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
15840Sstevel@tonic-gate 			 * we get here
15850Sstevel@tonic-gate 			 */
15860Sstevel@tonic-gate 			ASSERT(dip == NULL);
15870Sstevel@tonic-gate 			ret = 0;		/* can't fail now */
15880Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
15890Sstevel@tonic-gate 				break;
15900Sstevel@tonic-gate 			}
15910Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
15920Sstevel@tonic-gate 			PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d)\n",
15930Sstevel@tonic-gate 			    cmdstr, PM_DEVICE(dip)))
15940Sstevel@tonic-gate 			PM_RELE(dip);
15950Sstevel@tonic-gate 			break;
15960Sstevel@tonic-gate 		}
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate 		case PM_RESET_DEVICE_THRESHOLD:
15990Sstevel@tonic-gate 		{
16000Sstevel@tonic-gate 			/*
16010Sstevel@tonic-gate 			 * This only applies to a currently attached and power
16020Sstevel@tonic-gate 			 * managed node
16030Sstevel@tonic-gate 			 */
16040Sstevel@tonic-gate 			/*
16050Sstevel@tonic-gate 			 * We don't do this to old-style drivers
16060Sstevel@tonic-gate 			 */
16070Sstevel@tonic-gate 			info = PM_GET_PM_INFO(dip);
16080Sstevel@tonic-gate 			if (info == NULL) {
16090Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s not power "
16100Sstevel@tonic-gate 				    "managed\n", cmdstr, req.physpath))
16110Sstevel@tonic-gate 				ret = EINVAL;
16120Sstevel@tonic-gate 				break;
16130Sstevel@tonic-gate 			}
16140Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16150Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s is BC\n",
16160Sstevel@tonic-gate 				    cmdstr, req.physpath))
16170Sstevel@tonic-gate 				ret = EINVAL;
16180Sstevel@tonic-gate 				break;
16190Sstevel@tonic-gate 			}
16200Sstevel@tonic-gate 			pm_unrecord_threshold(req.physpath);
16213028Smh27603 			if (DEVI(dip)->devi_pm_flags & PMC_CPU_THRESH)
16223028Smh27603 				pm_set_device_threshold(dip,
16233028Smh27603 				    pm_cpu_idle_threshold, PMC_CPU_THRESH);
16243028Smh27603 			else
16253028Smh27603 				pm_set_device_threshold(dip,
16263028Smh27603 				    pm_system_idle_threshold, PMC_DEF_THRESH);
16270Sstevel@tonic-gate 			ret = 0;
16280Sstevel@tonic-gate 			break;
16290Sstevel@tonic-gate 		}
16300Sstevel@tonic-gate 
16310Sstevel@tonic-gate 		case PM_GET_NUM_COMPONENTS:
16320Sstevel@tonic-gate 			ret = 0;
16330Sstevel@tonic-gate 			*rval_p = PM_NUMCMPTS(dip);
16340Sstevel@tonic-gate 			break;
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate 		case PM_GET_DEVICE_TYPE:
16370Sstevel@tonic-gate 			ret = 0;
16380Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
16390Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
16400Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
16410Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
16420Sstevel@tonic-gate 				break;
16430Sstevel@tonic-gate 			}
16440Sstevel@tonic-gate 			if (PM_ISBC(dip)) {
16450Sstevel@tonic-gate 				*rval_p = PM_CREATE_COMPONENTS;
16460Sstevel@tonic-gate 			} else {
16470Sstevel@tonic-gate 				*rval_p = PM_AUTOPM;
16480Sstevel@tonic-gate 			}
16490Sstevel@tonic-gate 			break;
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 		case PM_SET_COMPONENT_THRESHOLDS:
16520Sstevel@tonic-gate 		{
16530Sstevel@tonic-gate 			int comps = 0;
16540Sstevel@tonic-gate 			int *end = (int *)req.data + icount;
16550Sstevel@tonic-gate 			pm_thresh_rec_t *rp;
16560Sstevel@tonic-gate 			pm_pte_t *ep;	/* threshold header storage */
16570Sstevel@tonic-gate 			int *tp;	/* threshold storage */
16580Sstevel@tonic-gate 			int *ip;
16590Sstevel@tonic-gate 			int j;
16600Sstevel@tonic-gate 			size_t size;
16610Sstevel@tonic-gate 			extern int pm_thresh_specd(dev_info_t *);
16620Sstevel@tonic-gate 			extern int pm_valid_thresh(dev_info_t *,
16630Sstevel@tonic-gate 			    pm_thresh_rec_t *);
16640Sstevel@tonic-gate 
16650Sstevel@tonic-gate 			for (ip = req.data; *ip; ip++) {
16660Sstevel@tonic-gate 				if (ip >= end) {
16670Sstevel@tonic-gate 					ret = EFAULT;
16680Sstevel@tonic-gate 					break;
16690Sstevel@tonic-gate 				}
16700Sstevel@tonic-gate 				comps++;
16710Sstevel@tonic-gate 				/* skip over indicated number of entries */
16720Sstevel@tonic-gate 				for (j = *ip; j; j--) {
16730Sstevel@tonic-gate 					if (++ip >= end) {
16740Sstevel@tonic-gate 						ret = EFAULT;
16750Sstevel@tonic-gate 						break;
16760Sstevel@tonic-gate 					}
16770Sstevel@tonic-gate 				}
16780Sstevel@tonic-gate 				if (ret)
16790Sstevel@tonic-gate 					break;
16800Sstevel@tonic-gate 			}
16810Sstevel@tonic-gate 			if (ret)
16820Sstevel@tonic-gate 				break;
16830Sstevel@tonic-gate 			if ((intptr_t)ip != (intptr_t)end - sizeof (int)) {
16840Sstevel@tonic-gate 				/* did not exactly fill buffer */
16850Sstevel@tonic-gate 				ret = EINVAL;
16860Sstevel@tonic-gate 				break;
16870Sstevel@tonic-gate 			}
16880Sstevel@tonic-gate 			if (comps == 0) {
16890Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s 0 components"
16900Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, req.physpath))
16910Sstevel@tonic-gate 				ret = EINVAL;
16920Sstevel@tonic-gate 				break;
16930Sstevel@tonic-gate 			}
16940Sstevel@tonic-gate 			/*
16950Sstevel@tonic-gate 			 * The header struct plus one entry struct per component
16960Sstevel@tonic-gate 			 * plus the size of the lists minus the counts
16970Sstevel@tonic-gate 			 * plus the length of the string
16980Sstevel@tonic-gate 			 */
16990Sstevel@tonic-gate 			size = sizeof (pm_thresh_rec_t) +
17000Sstevel@tonic-gate 			    (sizeof (pm_pte_t) * comps) + req.datasize -
17010Sstevel@tonic-gate 			    ((comps + 1) * sizeof (int)) +
17020Sstevel@tonic-gate 			    strlen(req.physpath) + 1;
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 			rp = kmem_zalloc(size, KM_SLEEP);
17050Sstevel@tonic-gate 			rp->ptr_size = size;
17060Sstevel@tonic-gate 			rp->ptr_numcomps = comps;
17070Sstevel@tonic-gate 			ep = (pm_pte_t *)((intptr_t)rp + sizeof (*rp));
17080Sstevel@tonic-gate 			rp->ptr_entries = ep;
17090Sstevel@tonic-gate 			tp = (int *)((intptr_t)ep +
17100Sstevel@tonic-gate 			    (comps * sizeof (pm_pte_t)));
17110Sstevel@tonic-gate 			for (ip = req.data; *ip; ep++) {
17120Sstevel@tonic-gate 				ep->pte_numthresh = *ip;
17130Sstevel@tonic-gate 				ep->pte_thresh = tp;
17140Sstevel@tonic-gate 				for (j = *ip++; j; j--) {
17150Sstevel@tonic-gate 					*tp++ = *ip++;
17160Sstevel@tonic-gate 				}
17170Sstevel@tonic-gate 			}
17180Sstevel@tonic-gate 			(void) strcat((char *)tp, req.physpath);
17190Sstevel@tonic-gate 			rp->ptr_physpath = (char *)tp;
17200Sstevel@tonic-gate 			ASSERT((intptr_t)end == (intptr_t)ip + sizeof (int));
17210Sstevel@tonic-gate 			ASSERT((intptr_t)tp + strlen(req.physpath) + 1 ==
17220Sstevel@tonic-gate 			    (intptr_t)rp + rp->ptr_size);
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate 			ASSERT(dip == NULL);
17250Sstevel@tonic-gate 			/*
17260Sstevel@tonic-gate 			 * If this is not a currently power managed node,
17270Sstevel@tonic-gate 			 * then we can't check for validity of the thresholds
17280Sstevel@tonic-gate 			 */
17290Sstevel@tonic-gate 			if (!(dip = pm_name_to_dip(req.physpath, 1))) {
17300Sstevel@tonic-gate 				/* don't free rp, pm_record_thresh uses it */
17310Sstevel@tonic-gate 				pm_record_thresh(rp);
17320Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: pm_name_to_dip "
17330Sstevel@tonic-gate 				    "for %s failed\n", cmdstr, req.physpath))
17340Sstevel@tonic-gate 				ret = 0;
17350Sstevel@tonic-gate 				break;
17360Sstevel@tonic-gate 			}
17370Sstevel@tonic-gate 			ASSERT(!dipheld);
17380Sstevel@tonic-gate 			dipheld++;
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 			if (!pm_valid_thresh(dip, rp)) {
17410Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: invalid thresh "
17420Sstevel@tonic-gate 				    "for %s@%s(%s#%d)\n", cmdstr,
17430Sstevel@tonic-gate 				    PM_DEVICE(dip)))
17440Sstevel@tonic-gate 				kmem_free(rp, size);
17450Sstevel@tonic-gate 				ret = EINVAL;
17460Sstevel@tonic-gate 				break;
17470Sstevel@tonic-gate 			}
17480Sstevel@tonic-gate 			/*
17490Sstevel@tonic-gate 			 * We don't just apply it ourselves because we'd need
17500Sstevel@tonic-gate 			 * to know too much about locking.  Since we don't
17510Sstevel@tonic-gate 			 * hold a lock the entry could be removed before
17520Sstevel@tonic-gate 			 * we get here
17530Sstevel@tonic-gate 			 */
17540Sstevel@tonic-gate 			pm_record_thresh(rp);
17550Sstevel@tonic-gate 			(void) pm_thresh_specd(dip);
17560Sstevel@tonic-gate 			ret = 0;
17570Sstevel@tonic-gate 			break;
17580Sstevel@tonic-gate 		}
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 		case PM_GET_COMPONENT_THRESHOLDS:
17610Sstevel@tonic-gate 		{
17620Sstevel@tonic-gate 			int musthave;
17630Sstevel@tonic-gate 			int numthresholds = 0;
17640Sstevel@tonic-gate 			int wordsize;
17650Sstevel@tonic-gate 			int numcomps;
17660Sstevel@tonic-gate 			caddr_t uaddr = req.data;	/* user address */
17670Sstevel@tonic-gate 			int val;	/* int value to be copied out */
17680Sstevel@tonic-gate 			int32_t val32;	/* int32 value to be copied out */
17690Sstevel@tonic-gate 			caddr_t vaddr;	/* address to copyout from */
17700Sstevel@tonic-gate 			int j;
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
17730Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
17740Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
17750Sstevel@tonic-gate 			} else
17760Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
17770Sstevel@tonic-gate 			{
17780Sstevel@tonic-gate 				wordsize = sizeof (int);
17790Sstevel@tonic-gate 			}
17800Sstevel@tonic-gate 
17810Sstevel@tonic-gate 			ASSERT(dip);
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 			numcomps = PM_NUMCMPTS(dip);
17840Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
17850Sstevel@tonic-gate 				cp = PM_CP(dip, i);
17860Sstevel@tonic-gate 				numthresholds += cp->pmc_comp.pmc_numlevels - 1;
17870Sstevel@tonic-gate 			}
17880Sstevel@tonic-gate 			musthave = (numthresholds + numcomps + 1) *  wordsize;
17890Sstevel@tonic-gate 			if (req.datasize < musthave) {
17900Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %ld, need "
17910Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
17920Sstevel@tonic-gate 				    musthave))
17930Sstevel@tonic-gate 				ret = EINVAL;
17940Sstevel@tonic-gate 				break;
17950Sstevel@tonic-gate 			}
17960Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
17970Sstevel@tonic-gate 			for (i = 0; i < numcomps; i++) {
17980Sstevel@tonic-gate 				int *thp;
17990Sstevel@tonic-gate 				cp = PM_CP(dip, i);
18000Sstevel@tonic-gate 				thp = cp->pmc_comp.pmc_thresh;
18010Sstevel@tonic-gate 				/* first copyout the count */
18020Sstevel@tonic-gate 				if (wordsize == sizeof (int32_t)) {
18030Sstevel@tonic-gate 					val32 = cp->pmc_comp.pmc_numlevels - 1;
18040Sstevel@tonic-gate 					vaddr = (caddr_t)&val32;
18050Sstevel@tonic-gate 				} else {
18060Sstevel@tonic-gate 					val = cp->pmc_comp.pmc_numlevels - 1;
18070Sstevel@tonic-gate 					vaddr = (caddr_t)&val;
18080Sstevel@tonic-gate 				}
18090Sstevel@tonic-gate 				if (ddi_copyout(vaddr, (void *)uaddr,
18100Sstevel@tonic-gate 				    wordsize, mode) != 0) {
18110Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
18120Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
18130Sstevel@tonic-gate 					    "(%s#%d) vaddr %p EFAULT\n",
18140Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
18150Sstevel@tonic-gate 					    (void*)vaddr))
18160Sstevel@tonic-gate 					ret = EFAULT;
18170Sstevel@tonic-gate 					break;
18180Sstevel@tonic-gate 				}
18190Sstevel@tonic-gate 				vaddr = uaddr;
18200Sstevel@tonic-gate 				vaddr += wordsize;
18210Sstevel@tonic-gate 				uaddr = (caddr_t)vaddr;
18220Sstevel@tonic-gate 				/* then copyout each threshold value */
18230Sstevel@tonic-gate 				for (j = 0; j < cp->pmc_comp.pmc_numlevels - 1;
18240Sstevel@tonic-gate 				    j++) {
18250Sstevel@tonic-gate 					if (wordsize == sizeof (int32_t)) {
18260Sstevel@tonic-gate 						val32 = thp[j + 1];
18270Sstevel@tonic-gate 						vaddr = (caddr_t)&val32;
18280Sstevel@tonic-gate 					} else {
18290Sstevel@tonic-gate 						val = thp[i + 1];
18300Sstevel@tonic-gate 						vaddr = (caddr_t)&val;
18310Sstevel@tonic-gate 					}
18320Sstevel@tonic-gate 					if (ddi_copyout(vaddr, (void *) uaddr,
18330Sstevel@tonic-gate 					    wordsize, mode) != 0) {
18340Sstevel@tonic-gate 						PM_UNLOCK_DIP(dip);
18350Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
18360Sstevel@tonic-gate 						    "%s@%s(%s#%d) uaddr %p "
18370Sstevel@tonic-gate 						    "EFAULT\n", cmdstr,
18380Sstevel@tonic-gate 						    PM_DEVICE(dip),
18390Sstevel@tonic-gate 						    (void *)uaddr))
18400Sstevel@tonic-gate 						ret = EFAULT;
18410Sstevel@tonic-gate 						break;
18420Sstevel@tonic-gate 					}
18430Sstevel@tonic-gate 					vaddr = uaddr;
18440Sstevel@tonic-gate 					vaddr += wordsize;
18450Sstevel@tonic-gate 					uaddr = (caddr_t)vaddr;
18460Sstevel@tonic-gate 				}
18470Sstevel@tonic-gate 			}
18480Sstevel@tonic-gate 			if (ret)
18490Sstevel@tonic-gate 				break;
18500Sstevel@tonic-gate 			/* last copyout a terminating 0 count */
18510Sstevel@tonic-gate 			if (wordsize == sizeof (int32_t)) {
18520Sstevel@tonic-gate 				val32 = 0;
18530Sstevel@tonic-gate 				vaddr = (caddr_t)&val32;
18540Sstevel@tonic-gate 			} else {
18550Sstevel@tonic-gate 				ASSERT(wordsize == sizeof (int));
18560Sstevel@tonic-gate 				val = 0;
18570Sstevel@tonic-gate 				vaddr = (caddr_t)&val;
18580Sstevel@tonic-gate 			}
18590Sstevel@tonic-gate 			if (ddi_copyout(vaddr, uaddr, wordsize, mode) != 0) {
18600Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
18610Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
18620Sstevel@tonic-gate 				    "vaddr %p (0 count) EFAULT\n", cmdstr,
18630Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)vaddr))
18640Sstevel@tonic-gate 				ret = EFAULT;
18650Sstevel@tonic-gate 				break;
18660Sstevel@tonic-gate 			}
18670Sstevel@tonic-gate 			/* finished, so don't need to increment addresses */
18680Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
18690Sstevel@tonic-gate 			ret = 0;
18700Sstevel@tonic-gate 			break;
18710Sstevel@tonic-gate 		}
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 		case PM_GET_STATS:
18740Sstevel@tonic-gate 		{
18750Sstevel@tonic-gate 			time_t now;
18760Sstevel@tonic-gate 			time_t *timestamp;
18770Sstevel@tonic-gate 			extern int pm_cur_power(pm_component_t *cp);
18780Sstevel@tonic-gate 			int musthave;
18790Sstevel@tonic-gate 			int wordsize;
18800Sstevel@tonic-gate 
18810Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
18820Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
18830Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
18840Sstevel@tonic-gate 			} else
18850Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
18860Sstevel@tonic-gate 			{
18870Sstevel@tonic-gate 				wordsize = sizeof (int);
18880Sstevel@tonic-gate 			}
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 			comps = PM_NUMCMPTS(dip);
18910Sstevel@tonic-gate 			if (comps == 0 || PM_GET_PM_INFO(dip) == NULL) {
18920Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s no components"
18930Sstevel@tonic-gate 				    " or not power managed--EINVAL\n", cmdstr,
18940Sstevel@tonic-gate 				    req.physpath))
18950Sstevel@tonic-gate 				ret = EINVAL;
18960Sstevel@tonic-gate 				break;
18970Sstevel@tonic-gate 			}
18980Sstevel@tonic-gate 			musthave = comps * 2 * wordsize;
18990Sstevel@tonic-gate 			if (req.datasize < musthave) {
19000Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
19010Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
19020Sstevel@tonic-gate 				    musthave))
19030Sstevel@tonic-gate 				ret = EINVAL;
19040Sstevel@tonic-gate 				break;
19050Sstevel@tonic-gate 			}
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
19080Sstevel@tonic-gate 			(void) drv_getparm(TIME, &now);
19090Sstevel@tonic-gate 			timestamp = kmem_zalloc(comps * sizeof (time_t),
19100Sstevel@tonic-gate 			    KM_SLEEP);
19110Sstevel@tonic-gate 			pm_get_timestamps(dip, timestamp);
19120Sstevel@tonic-gate 			/*
19130Sstevel@tonic-gate 			 * First the current power levels
19140Sstevel@tonic-gate 			 */
19150Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19160Sstevel@tonic-gate 				int curpwr;
19170Sstevel@tonic-gate 				int32_t curpwr32;
19180Sstevel@tonic-gate 				caddr_t cpaddr;
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19210Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19220Sstevel@tonic-gate 					curpwr = pm_cur_power(cp);
19230Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr;
19240Sstevel@tonic-gate 				} else {
19250Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19260Sstevel@tonic-gate 					curpwr32 = pm_cur_power(cp);
19270Sstevel@tonic-gate 					cpaddr = (caddr_t)&curpwr32;
19280Sstevel@tonic-gate 				}
19290Sstevel@tonic-gate 				if (ddi_copyout(cpaddr, (void *) req.data,
19300Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19310Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19320Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19330Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19340Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19350Sstevel@tonic-gate 					    (void *)req.data))
19360Sstevel@tonic-gate 					ASSERT(!dipheld);
19370Sstevel@tonic-gate 					return (EFAULT);
19380Sstevel@tonic-gate 				}
19390Sstevel@tonic-gate 				cpaddr = (caddr_t)req.data;
19400Sstevel@tonic-gate 				cpaddr += wordsize;
19410Sstevel@tonic-gate 				req.data = cpaddr;
19420Sstevel@tonic-gate 			}
19430Sstevel@tonic-gate 			/*
19440Sstevel@tonic-gate 			 * Then the times remaining
19450Sstevel@tonic-gate 			 */
19460Sstevel@tonic-gate 			for (i = 0; i < comps; i++) {
19470Sstevel@tonic-gate 				int retval;
19480Sstevel@tonic-gate 				int32_t retval32;
19490Sstevel@tonic-gate 				caddr_t rvaddr;
19500Sstevel@tonic-gate 				int curpwr;
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 				cp = PM_CP(dip, i);
19530Sstevel@tonic-gate 				curpwr = cp->pmc_cur_pwr;
19540Sstevel@tonic-gate 				if (curpwr == 0 || timestamp[i] == 0) {
19550Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: "
19560Sstevel@tonic-gate 					    "cur_pwer %x, timestamp %lx\n",
19570Sstevel@tonic-gate 					    cmdstr, curpwr, timestamp[i]))
19580Sstevel@tonic-gate 					retval = INT_MAX;
19590Sstevel@tonic-gate 				} else {
19600Sstevel@tonic-gate 					int thresh;
19610Sstevel@tonic-gate 					(void) pm_current_threshold(dip, i,
19620Sstevel@tonic-gate 					    &thresh);
19630Sstevel@tonic-gate 					retval = thresh - (now - timestamp[i]);
19640Sstevel@tonic-gate 					PMD(PMD_STATS, ("ioctl: %s: current "
19650Sstevel@tonic-gate 					    "thresh %x, now %lx, timestamp %lx,"
19660Sstevel@tonic-gate 					    " retval %x\n", cmdstr, thresh, now,
19670Sstevel@tonic-gate 					    timestamp[i], retval))
19680Sstevel@tonic-gate 				}
19690Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
19700Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval;
19710Sstevel@tonic-gate 				} else {
19720Sstevel@tonic-gate 					ASSERT(wordsize == sizeof (int32_t));
19730Sstevel@tonic-gate 					retval32 = retval;
19740Sstevel@tonic-gate 					rvaddr = (caddr_t)&retval32;
19750Sstevel@tonic-gate 				}
19760Sstevel@tonic-gate 				if (ddi_copyout(rvaddr, (void *) req.data,
19770Sstevel@tonic-gate 				    wordsize, mode) != 0) {
19780Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
19790Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
19800Sstevel@tonic-gate 					    "(%s#%d) req.data %p EFAULT\n",
19810Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
19820Sstevel@tonic-gate 					    (void *)req.data))
19830Sstevel@tonic-gate 					ASSERT(!dipheld);
19840Sstevel@tonic-gate 					return (EFAULT);
19850Sstevel@tonic-gate 				}
19860Sstevel@tonic-gate 				rvaddr = (caddr_t)req.data;
19870Sstevel@tonic-gate 				rvaddr += wordsize;
19880Sstevel@tonic-gate 				req.data = (int *)rvaddr;
19890Sstevel@tonic-gate 			}
19900Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
19910Sstevel@tonic-gate 			*rval_p = comps;
19920Sstevel@tonic-gate 			ret = 0;
19930Sstevel@tonic-gate 			kmem_free(timestamp, comps * sizeof (time_t));
19940Sstevel@tonic-gate 			break;
19950Sstevel@tonic-gate 		}
19960Sstevel@tonic-gate 
19970Sstevel@tonic-gate 		case PM_GET_COMPONENT_NAME:
19980Sstevel@tonic-gate 			ASSERT(dip);
19990Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20000Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20010Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20020Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20030Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20040Sstevel@tonic-gate 				ret = EINVAL;
20050Sstevel@tonic-gate 				break;
20060Sstevel@tonic-gate 			}
20070Sstevel@tonic-gate 			if (ret = copyoutstr(cp->pmc_comp.pmc_name,
20080Sstevel@tonic-gate 			    (char *)req.data, req.datasize, &lencopied)) {
20090Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20100Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20110Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20120Sstevel@tonic-gate 				break;
20130Sstevel@tonic-gate 			}
20140Sstevel@tonic-gate 			*rval_p = lencopied;
20150Sstevel@tonic-gate 			ret = 0;
20160Sstevel@tonic-gate 			break;
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 		case PM_GET_POWER_NAME:
20190Sstevel@tonic-gate 		{
20200Sstevel@tonic-gate 			int i;
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate 			ASSERT(dip);
20230Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20240Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20250Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
20260Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
20270Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
20280Sstevel@tonic-gate 				ret = EINVAL;
20290Sstevel@tonic-gate 				break;
20300Sstevel@tonic-gate 			}
20310Sstevel@tonic-gate 			if ((i = req.value) < 0 ||
20320Sstevel@tonic-gate 			    i > cp->pmc_comp.pmc_numlevels - 1) {
20330Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20340Sstevel@tonic-gate 				    "value %d > num_levels - 1 %d--EINVAL\n",
20350Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.value,
20360Sstevel@tonic-gate 				    cp->pmc_comp.pmc_numlevels - 1))
20370Sstevel@tonic-gate 				ret = EINVAL;
20380Sstevel@tonic-gate 				break;
20390Sstevel@tonic-gate 			}
20400Sstevel@tonic-gate 			dep = cp->pmc_comp.pmc_lnames[req.value];
20410Sstevel@tonic-gate 			if (ret = copyoutstr(dep,
20420Sstevel@tonic-gate 			    req.data, req.datasize, &lencopied)) {
20430Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20440Sstevel@tonic-gate 				    "copyoutstr %p failed--EFAULT\n", cmdstr,
20450Sstevel@tonic-gate 				    PM_DEVICE(dip), (void *)req.data))
20460Sstevel@tonic-gate 				break;
20470Sstevel@tonic-gate 			}
20480Sstevel@tonic-gate 			*rval_p = lencopied;
20490Sstevel@tonic-gate 			ret = 0;
20500Sstevel@tonic-gate 			break;
20510Sstevel@tonic-gate 		}
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate 		case PM_GET_POWER_LEVELS:
20540Sstevel@tonic-gate 		{
20550Sstevel@tonic-gate 			int musthave;
20560Sstevel@tonic-gate 			int numlevels;
20570Sstevel@tonic-gate 			int wordsize;
20580Sstevel@tonic-gate 
20590Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
20600Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
20610Sstevel@tonic-gate 				wordsize = sizeof (int32_t);
20620Sstevel@tonic-gate 			} else
20630Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
20640Sstevel@tonic-gate 			{
20650Sstevel@tonic-gate 				wordsize = sizeof (int);
20660Sstevel@tonic-gate 			}
20670Sstevel@tonic-gate 			ASSERT(dip);
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
20700Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
20710Sstevel@tonic-gate 				    "has %d components, component %d requested"
20720Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, PM_DEVICE(dip),
20730Sstevel@tonic-gate 				    PM_NUMCMPTS(dip), req.component))
20740Sstevel@tonic-gate 				ret = EINVAL;
20750Sstevel@tonic-gate 				break;
20760Sstevel@tonic-gate 			}
20770Sstevel@tonic-gate 			numlevels = cp->pmc_comp.pmc_numlevels;
20780Sstevel@tonic-gate 			musthave = numlevels *  wordsize;
20790Sstevel@tonic-gate 			if (req.datasize < musthave) {
20800Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: size %lu, need "
20810Sstevel@tonic-gate 				    "%d--EINVAL\n", cmdstr, req.datasize,
20820Sstevel@tonic-gate 				    musthave))
20830Sstevel@tonic-gate 				ret = EINVAL;
20840Sstevel@tonic-gate 				break;
20850Sstevel@tonic-gate 			}
20860Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
20870Sstevel@tonic-gate 			for (i = 0; i < numlevels; i++) {
20880Sstevel@tonic-gate 				int level;
20890Sstevel@tonic-gate 				int32_t level32;
20900Sstevel@tonic-gate 				caddr_t laddr;
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 				if (wordsize == sizeof (int)) {
20930Sstevel@tonic-gate 					level = cp->pmc_comp.pmc_lvals[i];
20940Sstevel@tonic-gate 					laddr = (caddr_t)&level;
20950Sstevel@tonic-gate 				} else {
20960Sstevel@tonic-gate 					level32 = cp->pmc_comp.pmc_lvals[i];
20970Sstevel@tonic-gate 					laddr = (caddr_t)&level32;
20980Sstevel@tonic-gate 				}
20990Sstevel@tonic-gate 				if (ddi_copyout(laddr, (void *) req.data,
21000Sstevel@tonic-gate 				    wordsize, mode) != 0) {
21010Sstevel@tonic-gate 					PM_UNLOCK_DIP(dip);
21020Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: %s@%s"
21030Sstevel@tonic-gate 					    "(%s#%d) laddr %p EFAULT\n",
21040Sstevel@tonic-gate 					    cmdstr, PM_DEVICE(dip),
21050Sstevel@tonic-gate 					    (void *)laddr))
21060Sstevel@tonic-gate 					ASSERT(!dipheld);
21070Sstevel@tonic-gate 					return (EFAULT);
21080Sstevel@tonic-gate 				}
21090Sstevel@tonic-gate 				laddr = (caddr_t)req.data;
21100Sstevel@tonic-gate 				laddr += wordsize;
21110Sstevel@tonic-gate 				req.data = (int *)laddr;
21120Sstevel@tonic-gate 			}
21130Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21140Sstevel@tonic-gate 			*rval_p = numlevels;
21150Sstevel@tonic-gate 			ret = 0;
21160Sstevel@tonic-gate 			break;
21170Sstevel@tonic-gate 		}
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 		case PM_GET_NUM_POWER_LEVELS:
21210Sstevel@tonic-gate 			if (!e_pm_valid_comp(dip, req.component, &cp)) {
21220Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
21230Sstevel@tonic-gate 				    "component %d > numcmpts - 1 %d--EINVAL\n",
21240Sstevel@tonic-gate 				    cmdstr, PM_DEVICE(dip), req.component,
21250Sstevel@tonic-gate 				    PM_NUMCMPTS(dip) - 1))
21260Sstevel@tonic-gate 				ret = EINVAL;
21270Sstevel@tonic-gate 				break;
21280Sstevel@tonic-gate 			}
21290Sstevel@tonic-gate 			*rval_p = cp->pmc_comp.pmc_numlevels;
21300Sstevel@tonic-gate 			ret = 0;
21310Sstevel@tonic-gate 			break;
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 		case PM_GET_DEVICE_THRESHOLD_BASIS:
21340Sstevel@tonic-gate 			ret = 0;
21350Sstevel@tonic-gate 			PM_LOCK_DIP(dip);
21360Sstevel@tonic-gate 			if ((info = PM_GET_PM_INFO(dip)) == NULL) {
21370Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21380Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: "
21390Sstevel@tonic-gate 				    "PM_NO_PM_COMPONENTS\n", cmdstr))
21400Sstevel@tonic-gate 				*rval_p = PM_NO_PM_COMPONENTS;
21410Sstevel@tonic-gate 				break;
21420Sstevel@tonic-gate 			}
21430Sstevel@tonic-gate 			if (PM_ISDIRECT(dip)) {
21440Sstevel@tonic-gate 				PM_UNLOCK_DIP(dip);
21450Sstevel@tonic-gate 				*rval_p = PM_DIRECTLY_MANAGED;
21460Sstevel@tonic-gate 				break;
21470Sstevel@tonic-gate 			}
21480Sstevel@tonic-gate 			switch (DEVI(dip)->devi_pm_flags & PMC_THRESH_ALL) {
21490Sstevel@tonic-gate 			case PMC_DEF_THRESH:
21500Sstevel@tonic-gate 			case PMC_NEXDEF_THRESH:
21510Sstevel@tonic-gate 				*rval_p = PM_DEFAULT_THRESHOLD;
21520Sstevel@tonic-gate 				break;
21530Sstevel@tonic-gate 			case PMC_DEV_THRESH:
21540Sstevel@tonic-gate 				*rval_p = PM_DEVICE_THRESHOLD;
21550Sstevel@tonic-gate 				break;
21560Sstevel@tonic-gate 			case PMC_COMP_THRESH:
21570Sstevel@tonic-gate 				*rval_p = PM_COMPONENT_THRESHOLD;
21580Sstevel@tonic-gate 				break;
21593028Smh27603 			case PMC_CPU_THRESH:
21603028Smh27603 				*rval_p = PM_CPU_THRESHOLD;
21613028Smh27603 				break;
21620Sstevel@tonic-gate 			default:
21630Sstevel@tonic-gate 				if (PM_ISBC(dip)) {
21640Sstevel@tonic-gate 					*rval_p = PM_OLD_THRESHOLD;
21650Sstevel@tonic-gate 					break;
21660Sstevel@tonic-gate 				}
21670Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: default, not "
21680Sstevel@tonic-gate 				    "BC--EINVAL", cmdstr))
21690Sstevel@tonic-gate 				ret = EINVAL;
21700Sstevel@tonic-gate 				break;
21710Sstevel@tonic-gate 			}
21720Sstevel@tonic-gate 			PM_UNLOCK_DIP(dip);
21730Sstevel@tonic-gate 			break;
21740Sstevel@tonic-gate 		}
21750Sstevel@tonic-gate 		break;
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 	case PM_PSC:
21780Sstevel@tonic-gate 		/*
21790Sstevel@tonic-gate 		 * Commands that require pm_state_change_t as arg
21800Sstevel@tonic-gate 		 */
21810Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
21820Sstevel@tonic-gate 		if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
21830Sstevel@tonic-gate 			pscp32 = (pm_state_change32_t *)arg;
21840Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc32,
21850Sstevel@tonic-gate 			    sizeof (psc32), mode) != 0) {
21860Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
21870Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
21880Sstevel@tonic-gate 				ASSERT(!dipheld);
21890Sstevel@tonic-gate 				return (EFAULT);
21900Sstevel@tonic-gate 			}
21910Sstevel@tonic-gate 			psc.physpath = (caddr_t)(uintptr_t)psc32.physpath;
21920Sstevel@tonic-gate 			psc.size = psc32.size;
21930Sstevel@tonic-gate 		} else
21940Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
21950Sstevel@tonic-gate 		{
21960Sstevel@tonic-gate 			pscp = (pm_state_change_t *)arg;
21970Sstevel@tonic-gate 			if (ddi_copyin((caddr_t)arg, &psc,
21980Sstevel@tonic-gate 			    sizeof (psc), mode) != 0) {
21990Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
22000Sstevel@tonic-gate 				    "EFAULT\n\n", cmdstr))
22010Sstevel@tonic-gate 				ASSERT(!dipheld);
22020Sstevel@tonic-gate 				return (EFAULT);
22030Sstevel@tonic-gate 			}
22040Sstevel@tonic-gate 		}
22050Sstevel@tonic-gate 		switch (cmd) {
22060Sstevel@tonic-gate 
22070Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE:
22080Sstevel@tonic-gate 		case PM_GET_STATE_CHANGE_WAIT:
22090Sstevel@tonic-gate 		{
22100Sstevel@tonic-gate 			psce_t			*pscep;
22110Sstevel@tonic-gate 			pm_state_change_t	*p;
22120Sstevel@tonic-gate 			caddr_t			physpath;
22130Sstevel@tonic-gate 			size_t			physlen;
22140Sstevel@tonic-gate 
22150Sstevel@tonic-gate 			/*
22160Sstevel@tonic-gate 			 * We want to know if any device has changed state.
22170Sstevel@tonic-gate 			 * We look up by clone.  In case we have another thread
22180Sstevel@tonic-gate 			 * from the same process, we loop.
22190Sstevel@tonic-gate 			 * pm_psc_clone_to_interest() returns a locked entry.
22200Sstevel@tonic-gate 			 * We create an internal copy of the event entry prior
22210Sstevel@tonic-gate 			 * to copyout to user space because we don't want to
22220Sstevel@tonic-gate 			 * hold the psce_lock while doing copyout as we might
22230Sstevel@tonic-gate 			 * hit page fault  which eventually brings us back
22240Sstevel@tonic-gate 			 * here requesting the same lock.
22250Sstevel@tonic-gate 			 */
22260Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
22270Sstevel@tonic-gate 			if (!pm_interest_registered(clone))
22280Sstevel@tonic-gate 				pm_register_watcher(clone, NULL);
22290Sstevel@tonic-gate 			while ((pscep =
22300Sstevel@tonic-gate 			    pm_psc_clone_to_interest(clone)) == NULL) {
22310Sstevel@tonic-gate 				if (cmd == PM_GET_STATE_CHANGE) {
22320Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
22330Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
22340Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
22350Sstevel@tonic-gate 					ASSERT(!dipheld);
22360Sstevel@tonic-gate 					return (EWOULDBLOCK);
22370Sstevel@tonic-gate 				} else {
22380Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
22390Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
22400Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
22410Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s "
22420Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
22430Sstevel@tonic-gate 						ASSERT(!dipheld);
22440Sstevel@tonic-gate 						return (EINTR);
22450Sstevel@tonic-gate 					}
22460Sstevel@tonic-gate 				}
22470Sstevel@tonic-gate 			}
22480Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
22490Sstevel@tonic-gate 
22500Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
22510Sstevel@tonic-gate 			physpath = NULL;
22520Sstevel@tonic-gate 			/*
22530Sstevel@tonic-gate 			 * If we were unable to store the path while bringing
22540Sstevel@tonic-gate 			 * up the console fb upon entering the prom, we give
22550Sstevel@tonic-gate 			 * a "" name with the overrun event set
22560Sstevel@tonic-gate 			 */
22570Sstevel@tonic-gate 			if (physlen == (size_t)-1) {	/* kmemalloc failed */
22580Sstevel@tonic-gate 				physpath = kmem_zalloc(1, KM_SLEEP);
22590Sstevel@tonic-gate 				physlen = 1;
22600Sstevel@tonic-gate 			}
22610Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
22620Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n", cmdstr))
22630Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
22640Sstevel@tonic-gate 				ret = EFAULT;
22650Sstevel@tonic-gate 				break;
22660Sstevel@tonic-gate 			}
22670Sstevel@tonic-gate 			if (physpath == NULL) {
22680Sstevel@tonic-gate 				physpath = kmem_zalloc(physlen, KM_SLEEP);
22690Sstevel@tonic-gate 				bcopy((const void *) pscep->psce_out->physpath,
22700Sstevel@tonic-gate 				    (void *) physpath, physlen);
22710Sstevel@tonic-gate 			}
22720Sstevel@tonic-gate 
22730Sstevel@tonic-gate 			p = pscep->psce_out;
22740Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
22750Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
22760Sstevel@tonic-gate #ifdef DEBUG
22770Sstevel@tonic-gate 				size_t usrcopysize;
22780Sstevel@tonic-gate #endif
22790Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
22800Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
22810Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
22820Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
22830Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
22840Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
22850Sstevel@tonic-gate 				copysize32 = ((intptr_t)&psc32.size -
22860Sstevel@tonic-gate 				    (intptr_t)&psc32.component);
22870Sstevel@tonic-gate #ifdef DEBUG
22880Sstevel@tonic-gate 				usrcopysize = ((intptr_t)&pscp32->size -
22890Sstevel@tonic-gate 				    (intptr_t)&pscp32->component);
22900Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
22910Sstevel@tonic-gate #endif
22920Sstevel@tonic-gate 			} else
22930Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
22940Sstevel@tonic-gate 			{
22950Sstevel@tonic-gate 				psc.flags = p->flags;
22960Sstevel@tonic-gate 				psc.event = p->event;
22970Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
22980Sstevel@tonic-gate 				psc.component = p->component;
22990Sstevel@tonic-gate 				psc.old_level = p->old_level;
23000Sstevel@tonic-gate 				psc.new_level = p->new_level;
23010Sstevel@tonic-gate 				copysize = ((long)&p->size -
23020Sstevel@tonic-gate 				    (long)&p->component);
23030Sstevel@tonic-gate 			}
23040Sstevel@tonic-gate 			if (p->size != (size_t)-1)
23050Sstevel@tonic-gate 				kmem_free(p->physpath, p->size);
23060Sstevel@tonic-gate 			p->size = 0;
23070Sstevel@tonic-gate 			p->physpath = NULL;
23080Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
23090Sstevel@tonic-gate 				p = pscep->psce_first;
23100Sstevel@tonic-gate 			else
23110Sstevel@tonic-gate 				p++;
23120Sstevel@tonic-gate 			pscep->psce_out = p;
23130Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
23140Sstevel@tonic-gate 
23150Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
23160Sstevel@tonic-gate 			    physlen, &lencopied);
23170Sstevel@tonic-gate 			kmem_free(physpath, physlen);
23180Sstevel@tonic-gate 			if (ret) {
23190Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
23200Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
23210Sstevel@tonic-gate 				    (void *)psc.physpath))
23220Sstevel@tonic-gate 				break;
23230Sstevel@tonic-gate 			}
23240Sstevel@tonic-gate 
23250Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23260Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
23270Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
23280Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
23290Sstevel@tonic-gate 				    != 0) {
23300Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23310Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23320Sstevel@tonic-gate 					ret = EFAULT;
23330Sstevel@tonic-gate 					break;
23340Sstevel@tonic-gate 				}
23350Sstevel@tonic-gate 			} else
23360Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
23370Sstevel@tonic-gate 			{
23380Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
23390Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
23400Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
23410Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
23420Sstevel@tonic-gate 					ret = EFAULT;
23430Sstevel@tonic-gate 					break;
23440Sstevel@tonic-gate 				}
23450Sstevel@tonic-gate 			}
23460Sstevel@tonic-gate 			ret = 0;
23470Sstevel@tonic-gate 			break;
23480Sstevel@tonic-gate 		}
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY:
23510Sstevel@tonic-gate 		case PM_DIRECT_NOTIFY_WAIT:
23520Sstevel@tonic-gate 		{
23530Sstevel@tonic-gate 			psce_t			*pscep;
23540Sstevel@tonic-gate 			pm_state_change_t	*p;
23550Sstevel@tonic-gate 			caddr_t			physpath;
23560Sstevel@tonic-gate 			size_t			physlen;
23570Sstevel@tonic-gate 			/*
23580Sstevel@tonic-gate 			 * We want to know if any direct device of ours has
23590Sstevel@tonic-gate 			 * something we should know about.  We look up by clone.
23600Sstevel@tonic-gate 			 * In case we have another thread from the same process,
23610Sstevel@tonic-gate 			 * we loop.
23620Sstevel@tonic-gate 			 * pm_psc_clone_to_direct() returns a locked entry.
23630Sstevel@tonic-gate 			 */
23640Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
23650Sstevel@tonic-gate 			while (pm_poll_cnt[clone] == 0 ||
23660Sstevel@tonic-gate 			    (pscep = pm_psc_clone_to_direct(clone)) == NULL) {
23670Sstevel@tonic-gate 				if (cmd == PM_DIRECT_NOTIFY) {
23680Sstevel@tonic-gate 					PMD(PMD_IOCTL, ("ioctl: %s: "
23690Sstevel@tonic-gate 					    "EWOULDBLOCK\n", cmdstr))
23700Sstevel@tonic-gate 					mutex_exit(&pm_clone_lock);
23710Sstevel@tonic-gate 					ASSERT(!dipheld);
23720Sstevel@tonic-gate 					return (EWOULDBLOCK);
23730Sstevel@tonic-gate 				} else {
23740Sstevel@tonic-gate 					if (cv_wait_sig(&pm_clones_cv[clone],
23750Sstevel@tonic-gate 					    &pm_clone_lock) == 0) {
23760Sstevel@tonic-gate 						mutex_exit(&pm_clone_lock);
23770Sstevel@tonic-gate 						PMD(PMD_ERROR, ("ioctl: %s: "
23780Sstevel@tonic-gate 						    "EINTR\n", cmdstr))
23790Sstevel@tonic-gate 						ASSERT(!dipheld);
23800Sstevel@tonic-gate 						return (EINTR);
23810Sstevel@tonic-gate 					}
23820Sstevel@tonic-gate 				}
23830Sstevel@tonic-gate 			}
23840Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
23850Sstevel@tonic-gate 			physlen = pscep->psce_out->size;
23860Sstevel@tonic-gate 			if ((psc.physpath == NULL) || (psc.size < physlen)) {
23870Sstevel@tonic-gate 				mutex_exit(&pscep->psce_lock);
23880Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EFAULT\n",
23890Sstevel@tonic-gate 				    cmdstr))
23900Sstevel@tonic-gate 				ret = EFAULT;
23910Sstevel@tonic-gate 				break;
23920Sstevel@tonic-gate 			}
23930Sstevel@tonic-gate 			physpath = kmem_zalloc(physlen, KM_SLEEP);
23940Sstevel@tonic-gate 			bcopy((const void *) pscep->psce_out->physpath,
23950Sstevel@tonic-gate 			    (void *) physpath, physlen);
23960Sstevel@tonic-gate 
23970Sstevel@tonic-gate 			p = pscep->psce_out;
23980Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
23990Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24000Sstevel@tonic-gate #ifdef DEBUG
24010Sstevel@tonic-gate 				size_t usrcopysize;
24020Sstevel@tonic-gate #endif
24030Sstevel@tonic-gate 				psc32.component = (int32_t)p->component;
24040Sstevel@tonic-gate 				psc32.flags = (ushort_t)p->flags;
24050Sstevel@tonic-gate 				psc32.event = (ushort_t)p->event;
24060Sstevel@tonic-gate 				psc32.timestamp = (int32_t)p->timestamp;
24070Sstevel@tonic-gate 				psc32.old_level = (int32_t)p->old_level;
24080Sstevel@tonic-gate 				psc32.new_level = (int32_t)p->new_level;
24090Sstevel@tonic-gate 				copysize32 = (intptr_t)&psc32.size -
24100Sstevel@tonic-gate 				    (intptr_t)&psc32.component;
24110Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN32 %s, comp %d "
24120Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24130Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24140Sstevel@tonic-gate #ifdef DEBUG
24150Sstevel@tonic-gate 				usrcopysize = (intptr_t)&pscp32->size -
24160Sstevel@tonic-gate 				    (intptr_t)&pscp32->component;
24170Sstevel@tonic-gate 				ASSERT(usrcopysize == copysize32);
24180Sstevel@tonic-gate #endif
24190Sstevel@tonic-gate 			} else
24200Sstevel@tonic-gate #endif
24210Sstevel@tonic-gate 			{
24220Sstevel@tonic-gate 				psc.component = p->component;
24230Sstevel@tonic-gate 				psc.flags = p->flags;
24240Sstevel@tonic-gate 				psc.event = p->event;
24250Sstevel@tonic-gate 				psc.timestamp = p->timestamp;
24260Sstevel@tonic-gate 				psc.old_level = p->old_level;
24270Sstevel@tonic-gate 				psc.new_level = p->new_level;
24280Sstevel@tonic-gate 				copysize = (intptr_t)&p->size -
24290Sstevel@tonic-gate 				    (intptr_t)&p->component;
24300Sstevel@tonic-gate 				PMD(PMD_DPM, ("ioctl: %s: PDN %s, comp %d "
24310Sstevel@tonic-gate 				    "%d -> %d\n", cmdstr, physpath,
24320Sstevel@tonic-gate 				    p->component, p->old_level, p->new_level))
24330Sstevel@tonic-gate 			}
24340Sstevel@tonic-gate 			mutex_enter(&pm_clone_lock);
24350Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: pm_poll_cnt[%d] is %d "
24360Sstevel@tonic-gate 			    "before decrement\n", cmdstr, clone,
24370Sstevel@tonic-gate 			    pm_poll_cnt[clone]))
24380Sstevel@tonic-gate 			pm_poll_cnt[clone]--;
24390Sstevel@tonic-gate 			mutex_exit(&pm_clone_lock);
24400Sstevel@tonic-gate 			kmem_free(p->physpath, p->size);
24410Sstevel@tonic-gate 			p->size = 0;
24420Sstevel@tonic-gate 			p->physpath = NULL;
24430Sstevel@tonic-gate 			if (pscep->psce_out == pscep->psce_last)
24440Sstevel@tonic-gate 				p = pscep->psce_first;
24450Sstevel@tonic-gate 			else
24460Sstevel@tonic-gate 				p++;
24470Sstevel@tonic-gate 			pscep->psce_out = p;
24480Sstevel@tonic-gate 			mutex_exit(&pscep->psce_lock);
24490Sstevel@tonic-gate 
24500Sstevel@tonic-gate 			ret = copyoutstr(physpath, psc.physpath,
24510Sstevel@tonic-gate 			    physlen, &lencopied);
24520Sstevel@tonic-gate 			kmem_free(physpath, physlen);
24530Sstevel@tonic-gate 			if (ret) {
24540Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: copyoutstr %p "
24550Sstevel@tonic-gate 				    "failed--EFAULT\n", cmdstr,
24560Sstevel@tonic-gate 				    (void *)psc.physpath))
24570Sstevel@tonic-gate 				break;
24580Sstevel@tonic-gate 			}
24590Sstevel@tonic-gate 
24600Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
24610Sstevel@tonic-gate 			if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
24620Sstevel@tonic-gate 				if (ddi_copyout(&psc32.component,
24630Sstevel@tonic-gate 				    &pscp32->component, copysize32, mode)
24640Sstevel@tonic-gate 					!= 0) {
24650Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24660Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24670Sstevel@tonic-gate 					ret = EFAULT;
24680Sstevel@tonic-gate 					break;
24690Sstevel@tonic-gate 				}
24700Sstevel@tonic-gate 			} else
24710Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
24720Sstevel@tonic-gate 			{
24730Sstevel@tonic-gate 				if (ddi_copyout(&psc.component,
24740Sstevel@tonic-gate 				    &pscp->component, copysize, mode) != 0) {
24750Sstevel@tonic-gate 					PMD(PMD_ERROR, ("ioctl: %s: copyout "
24760Sstevel@tonic-gate 					    "failed--EFAULT\n", cmdstr))
24770Sstevel@tonic-gate 					ret = EFAULT;
24780Sstevel@tonic-gate 					break;
24790Sstevel@tonic-gate 				}
24800Sstevel@tonic-gate 			}
24810Sstevel@tonic-gate 			ret = 0;
24820Sstevel@tonic-gate 			break;
24830Sstevel@tonic-gate 		}
24840Sstevel@tonic-gate 		default:
24850Sstevel@tonic-gate 			ASSERT(0);
24860Sstevel@tonic-gate 		}
24870Sstevel@tonic-gate 		break;
24880Sstevel@tonic-gate 
24890Sstevel@tonic-gate 	case NOSTRUCT:
24900Sstevel@tonic-gate 		switch (cmd) {
24910Sstevel@tonic-gate 		case PM_START_PM:
24923028Smh27603 		case PM_START_CPUPM:
24930Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
24943028Smh27603 			if ((cmd == PM_START_PM && autopm_enabled) ||
24953028Smh27603 			    (cmd == PM_START_CPUPM && PM_CPUPM_ENABLED)) {
24960Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
24970Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
24980Sstevel@tonic-gate 				    cmdstr))
24990Sstevel@tonic-gate 				ret = EBUSY;
25000Sstevel@tonic-gate 				break;
25010Sstevel@tonic-gate 			}
25023028Smh27603 			if (cmd == PM_START_PM)
25033028Smh27603 			    autopm_enabled = 1;
25043028Smh27603 			else
25053028Smh27603 			    cpupm = PM_CPUPM_ENABLE;
25060Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
25070Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd);
25080Sstevel@tonic-gate 			ret = 0;
25090Sstevel@tonic-gate 			break;
25100Sstevel@tonic-gate 
25110Sstevel@tonic-gate 		case PM_RESET_PM:
25120Sstevel@tonic-gate 		case PM_STOP_PM:
25133028Smh27603 		case PM_STOP_CPUPM:
25140Sstevel@tonic-gate 		{
25150Sstevel@tonic-gate 			extern void pm_discard_thresholds(void);
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate 			mutex_enter(&pm_scan_lock);
25183028Smh27603 			if ((cmd == PM_STOP_PM && !autopm_enabled) ||
25193028Smh27603 			    (cmd == PM_STOP_CPUPM && PM_CPUPM_DISABLED)) {
25200Sstevel@tonic-gate 				mutex_exit(&pm_scan_lock);
25210Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: EINVAL\n",
25220Sstevel@tonic-gate 				    cmdstr))
25230Sstevel@tonic-gate 				ret = EINVAL;
25240Sstevel@tonic-gate 				break;
25250Sstevel@tonic-gate 			}
25263028Smh27603 			if (cmd == PM_STOP_PM)
25273028Smh27603 			    autopm_enabled = 0;
25283028Smh27603 			else if (cmd == PM_STOP_CPUPM)
25293028Smh27603 			    cpupm = PM_CPUPM_DISABLE;
25303028Smh27603 			else {
25313028Smh27603 			    autopm_enabled = 0;
25323028Smh27603 			    cpupm = PM_CPUPM_NOTSET;
25333028Smh27603 			}
25340Sstevel@tonic-gate 			mutex_exit(&pm_scan_lock);
25353028Smh27603 
25360Sstevel@tonic-gate 			/*
25370Sstevel@tonic-gate 			 * bring devices to full power level, stop scan
25380Sstevel@tonic-gate 			 */
25390Sstevel@tonic-gate 			ddi_walk_devs(ddi_root_node(), pm_stop_pm_walk, &cmd);
25400Sstevel@tonic-gate 			ret = 0;
25413028Smh27603 			if (cmd == PM_STOP_PM || cmd == PM_STOP_CPUPM)
25420Sstevel@tonic-gate 				break;
25430Sstevel@tonic-gate 			/*
25440Sstevel@tonic-gate 			 * Now do only PM_RESET_PM stuff.
25450Sstevel@tonic-gate 			 */
25460Sstevel@tonic-gate 			pm_system_idle_threshold = pm_default_idle_threshold;
25473028Smh27603 			pm_cpu_idle_threshold = 0;
25480Sstevel@tonic-gate 			pm_discard_thresholds();
25490Sstevel@tonic-gate 			pm_all_to_default_thresholds();
25500Sstevel@tonic-gate 			pm_dispatch_to_dep_thread(PM_DEP_WK_REMOVE_DEP,
25510Sstevel@tonic-gate 			    NULL, NULL, PM_DEP_WAIT, NULL, 0);
25520Sstevel@tonic-gate 			break;
25530Sstevel@tonic-gate 		}
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 		case PM_GET_SYSTEM_THRESHOLD:
25560Sstevel@tonic-gate 			*rval_p = pm_system_idle_threshold;
25570Sstevel@tonic-gate 			ret = 0;
25580Sstevel@tonic-gate 			break;
25590Sstevel@tonic-gate 
25600Sstevel@tonic-gate 		case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
25610Sstevel@tonic-gate 			*rval_p = pm_default_idle_threshold;
25620Sstevel@tonic-gate 			ret = 0;
25630Sstevel@tonic-gate 			break;
25640Sstevel@tonic-gate 
25653028Smh27603 		case PM_GET_CPU_THRESHOLD:
25663028Smh27603 			*rval_p = pm_cpu_idle_threshold;
25673028Smh27603 			ret = 0;
25683028Smh27603 			break;
25693028Smh27603 
25700Sstevel@tonic-gate 		case PM_SET_SYSTEM_THRESHOLD:
25713028Smh27603 		case PM_SET_CPU_THRESHOLD:
25720Sstevel@tonic-gate 			if ((int)arg < 0) {
25730Sstevel@tonic-gate 				PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
25740Sstevel@tonic-gate 				    "--EINVAL\n", cmdstr, (int)arg))
25750Sstevel@tonic-gate 				ret = EINVAL;
25760Sstevel@tonic-gate 				break;
25770Sstevel@tonic-gate 			}
25780Sstevel@tonic-gate 			PMD(PMD_IOCTL, ("ioctl: %s: 0x%x 0t%d\n", cmdstr,
25790Sstevel@tonic-gate 			    (int)arg, (int)arg))
25803028Smh27603 			if (cmd == PM_SET_SYSTEM_THRESHOLD)
25813028Smh27603 				pm_system_idle_threshold = (int)arg;
25823028Smh27603 			else {
25833028Smh27603 				pm_cpu_idle_threshold = (int)arg;
25843028Smh27603 			}
25853028Smh27603 			ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
25863028Smh27603 				    (void *) &cmd);
25873028Smh27603 
25880Sstevel@tonic-gate 			ret = 0;
25890Sstevel@tonic-gate 			break;
25900Sstevel@tonic-gate 
25910Sstevel@tonic-gate 		case PM_IDLE_DOWN:
25920Sstevel@tonic-gate 			if (pm_timeout_idledown() != 0) {
25930Sstevel@tonic-gate 				ddi_walk_devs(ddi_root_node(),
25940Sstevel@tonic-gate 				    pm_start_idledown, (void *)PMID_IOC);
25950Sstevel@tonic-gate 			}
25960Sstevel@tonic-gate 			ret = 0;
25970Sstevel@tonic-gate 			break;
25980Sstevel@tonic-gate 
25990Sstevel@tonic-gate 		case PM_GET_PM_STATE:
26000Sstevel@tonic-gate 			if (autopm_enabled) {
26010Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_ENABLED;
26020Sstevel@tonic-gate 			} else {
26030Sstevel@tonic-gate 				*rval_p = PM_SYSTEM_PM_DISABLED;
26040Sstevel@tonic-gate 			}
26050Sstevel@tonic-gate 			ret = 0;
26060Sstevel@tonic-gate 			break;
26073028Smh27603 
26083028Smh27603 		case PM_GET_CPUPM_STATE:
26093028Smh27603 			if (PM_CPUPM_ENABLED)
26103028Smh27603 				*rval_p = PM_CPU_PM_ENABLED;
26113028Smh27603 			else if (PM_CPUPM_DISABLED)
26123028Smh27603 				*rval_p = PM_CPU_PM_DISABLED;
26133028Smh27603 			else
26143028Smh27603 				*rval_p = PM_CPU_PM_NOTSET;
26153028Smh27603 			ret = 0;
26163028Smh27603 			break;
26170Sstevel@tonic-gate 		}
26180Sstevel@tonic-gate 		break;
26190Sstevel@tonic-gate 
26200Sstevel@tonic-gate 	default:
26210Sstevel@tonic-gate 		/*
26220Sstevel@tonic-gate 		 * Internal error, invalid ioctl description
26230Sstevel@tonic-gate 		 * force debug entry even if pm_debug not set
26240Sstevel@tonic-gate 		 */
26250Sstevel@tonic-gate #ifdef	DEBUG
26260Sstevel@tonic-gate 		pm_log("ioctl: invalid str_type %d for cmd %d (%s)\n",
26270Sstevel@tonic-gate 		    pcip->str_type, cmd, pcip->name);
26280Sstevel@tonic-gate #endif
26290Sstevel@tonic-gate 		ASSERT(0);
26300Sstevel@tonic-gate 		return (EIO);
26310Sstevel@tonic-gate 	}
26320Sstevel@tonic-gate 	ASSERT(ret != 0x0badcafe);	/* some cmd in wrong case! */
26330Sstevel@tonic-gate 	if (dipheld) {
26340Sstevel@tonic-gate 		ASSERT(dip);
26350Sstevel@tonic-gate 		PMD(PMD_DHR, ("ioctl: %s: releasing %s@%s(%s#%d) for "
26360Sstevel@tonic-gate 		    "exiting pm_ioctl\n", cmdstr, PM_DEVICE(dip)))
26370Sstevel@tonic-gate 		PM_RELE(dip);
26380Sstevel@tonic-gate 	}
26390Sstevel@tonic-gate 	PMD(PMD_IOCTL, ("ioctl: %s: end, ret=%d\n", cmdstr, ret))
26400Sstevel@tonic-gate 	return (ret);
26410Sstevel@tonic-gate }
2642